view gen/passes/SimplifyDRuntimeCalls.cpp @ 1275:bedf0bfb8fdb

Implement first D-specific optimization pass: -simplify-drtcalls. It uses the machinery of the standard -simplify-libcalls pass, but optimizes calls to the D runtime instead of calls to C libraries. At the moment, these optimizations are implemented by this pass: - Avoid the runtime call for `arr.length = newlen` if it can determine that the new length isn't longer than the old one. - Ditto for `cast(T[]) arr` if it will clearly always succeed. (e.g. if the length of the original array is zero, or if the old element size is a multiple of the new element size)
author Frits van Bommel <fvbommel wxs.nl>
date Tue, 28 Apr 2009 21:58:06 +0200
parents
children 23b23b74e326
line wrap: on
line source
//===- SimplifyDRuntimeCalls - Optimize calls to the D runtime library ----===//
//
//                             The LLVM D Compiler
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a simple pass that applies a variety of small
// optimizations for calls to specific functions in the D runtime.
//
// The machinery was copied from the standard -simplify-libcalls LLVM pass.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "simplify-drtcalls"

#include "Passes.h"

#include "llvm/Pass.h"
#include "llvm/Support/IRBuilder.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Target/TargetData.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
using namespace llvm;

STATISTIC(NumSimplified, "Number of runtime calls simplified");

//===----------------------------------------------------------------------===//
// Optimizer Base Class
//===----------------------------------------------------------------------===//

/// This class is the abstract base class for the set of optimizations that
/// corresponds to one library call.
namespace {
    class VISIBILITY_HIDDEN LibCallOptimization {
    protected:
        Function *Caller;
        const TargetData *TD;
    public:
        LibCallOptimization() { }
        virtual ~LibCallOptimization() {}
        
        /// CallOptimizer - This pure virtual method is implemented by base classes to
        /// do various optimizations.  If this returns null then no transformation was
        /// performed.  If it returns CI, then it transformed the call and CI is to be
        /// deleted.  If it returns something else, replace CI with the new value and
        /// delete CI.
        virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B)=0;
        
        Value *OptimizeCall(CallInst *CI, const TargetData &TD, IRBuilder<> &B) {
            Caller = CI->getParent()->getParent();
            this->TD = &TD;
            return CallOptimizer(CI->getCalledFunction(), CI, B);
        }
    };
} // End anonymous namespace.


//===----------------------------------------------------------------------===//
// Miscellaneous LibCall Optimizations
//===----------------------------------------------------------------------===//

namespace {
//===---------------------------------------===//
// '_d_arraysetlengthT'/'_d_arraysetlengthiT' Optimizations

/// ArraySetLengthOpt - remove libcall for arr.length = N if N <= arr.length
struct VISIBILITY_HIDDEN ArraySetLengthOpt : public LibCallOptimization {
    virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
        // Verify we have a reasonable prototype for _d_arraysetlength[i]T
        const FunctionType *FT = Callee->getFunctionType();
        if (Callee->arg_size() != 4 || !isa<PointerType>(FT->getReturnType()) ||
            !isa<IntegerType>(FT->getParamType(1)) ||
            FT->getParamType(1) != FT->getParamType(2) ||
            FT->getParamType(3) != FT->getReturnType())
          return 0;
        
        Value* NewLen = CI->getOperand(2);
        if (Constant* NewCst = dyn_cast<Constant>(NewLen)) {
            Value* Data = CI->getOperand(4);
            
            // For now, we just catch the simplest of cases.
            //
            // TODO: Implement a more general way to compare old and new
            //       lengths, to catch cases like "arr.length = arr.length - 1;"
            //       (But beware of unsigned overflow! For example, we can't
            //       safely transform that example if arr.length may be 0)
            
            // Setting length to 0 never reallocates, so replace by data argument
            if (NewCst->isNullValue())
                return Data;
            
            // If both lengths are constant integers, see if NewLen <= OldLen
            Value* OldLen = CI->getOperand(3);
            if (ConstantInt* OldInt = dyn_cast<ConstantInt>(OldLen))
                if (ConstantInt* NewInt = dyn_cast<ConstantInt>(NewCst))
                    if (NewInt->getValue().ule(OldInt->getValue()))
                        return Data;
        }
        return 0;
    }
};

/// ArrayCastLenOpt - remove libcall for cast(T[]) arr if it's safe to do so.
struct VISIBILITY_HIDDEN ArrayCastLenOpt : public LibCallOptimization {
    virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
        // Verify we have a reasonable prototype for _d_array_cast_len
        const FunctionType *FT = Callee->getFunctionType();
        const Type* RetTy = FT->getReturnType();
        if (Callee->arg_size() != 3 || !isa<IntegerType>(RetTy) ||
            FT->getParamType(1) != RetTy || FT->getParamType(2) != RetTy)
          return 0;
        
        Value* OldLen = CI->getOperand(1);
        Value* OldSize = CI->getOperand(2);
        Value* NewSize = CI->getOperand(3);
        
        // If the old length was zero, always return zero.
        if (Constant* LenCst = dyn_cast<Constant>(OldLen))
            if (LenCst->isNullValue())
                return OldLen;
        
        // Equal sizes are much faster to check for, so do so now.
        if (OldSize == NewSize)
            return OldLen;
        
        // If both sizes are constant integers, see if OldSize is a multiple of NewSize
        if (ConstantInt* OldInt = dyn_cast<ConstantInt>(OldSize))
            if (ConstantInt* NewInt = dyn_cast<ConstantInt>(NewSize)) {
                // Don't crash on NewSize == 0, even though it shouldn't happen.
                if (NewInt->isNullValue())
                    return 0;
                
                APInt Quot, Rem;
                APInt::udivrem(OldInt->getValue(), NewInt->getValue(), Quot, Rem);
                if (Rem == 0)
                    return B.CreateMul(OldLen, ConstantInt::get(Quot));
            }
        return 0;
    }
};

// TODO: More optimizations! :)

} // end anonymous namespace.

//===----------------------------------------------------------------------===//
// SimplifyDRuntimeCalls Pass Implementation
//===----------------------------------------------------------------------===//

namespace {
    /// This pass optimizes library functions from the D runtime as used by LDC.
    ///
    class VISIBILITY_HIDDEN SimplifyDRuntimeCalls : public FunctionPass {
        StringMap<LibCallOptimization*> Optimizations;
        
        // Array operations
        ArraySetLengthOpt ArraySetLength;
        ArrayCastLenOpt ArrayCastLen;
        
        public:
        static char ID; // Pass identification
        SimplifyDRuntimeCalls() : FunctionPass(&ID) {}
        
        void InitOptimizations();
        bool runOnFunction(Function &F);
        
        virtual void getAnalysisUsage(AnalysisUsage &AU) const {
          AU.addRequired<TargetData>();
        }
    };
    char SimplifyDRuntimeCalls::ID = 0;
} // end anonymous namespace.

static RegisterPass<SimplifyDRuntimeCalls>
X("simplify-drtcalls", "Simplify calls to D runtime");

// Public interface to the pass.
FunctionPass *createSimplifyDRuntimeCalls() {
  return new SimplifyDRuntimeCalls(); 
}

/// Optimizations - Populate the Optimizations map with all the optimizations
/// we know.
void SimplifyDRuntimeCalls::InitOptimizations() {
    Optimizations["_d_arraysetlengthT"] = &ArraySetLength;
    Optimizations["_d_arraysetlengthiT"] = &ArraySetLength;
    Optimizations["_d_array_cast_len"] = &ArrayCastLen;
}


/// runOnFunction - Top level algorithm.
///
bool SimplifyDRuntimeCalls::runOnFunction(Function &F) {
    if (Optimizations.empty())
        InitOptimizations();
    
    const TargetData &TD = getAnalysis<TargetData>();
    
    IRBuilder<> Builder;
    
    bool Changed = false;
    for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) {
        for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
            // Ignore non-calls.
            CallInst *CI = dyn_cast<CallInst>(I++);
            if (!CI) continue;
            
            // Ignore indirect calls and calls to non-external functions.
            Function *Callee = CI->getCalledFunction();
            if (Callee == 0 || !Callee->isDeclaration() ||
                    !(Callee->hasExternalLinkage() || Callee->hasDLLImportLinkage()))
                continue;
            
            DEBUG(DOUT << "SimplifyDRuntimeCalls inspecting: " << *CI);
            
            // Ignore unknown calls.
            const char *CalleeName = Callee->getNameStart();
            StringMap<LibCallOptimization*>::iterator OMI =
                Optimizations.find(CalleeName, CalleeName+Callee->getNameLen());
            if (OMI == Optimizations.end()) continue;
            
            // Set the builder to the instruction after the call.
            Builder.SetInsertPoint(BB, I);
            
            // Try to optimize this call.
            Value *Result = OMI->second->OptimizeCall(CI, TD, Builder);
            if (Result == 0) continue;
            
            DEBUG(DOUT << "SimplifyDRuntimeCalls simplified: " << *CI;
                  DOUT << "  into: " << *Result << "\n");
            
            // Something changed!
            Changed = true;
            ++NumSimplified;
            
            // Inspect the instruction after the call (which was potentially just
            // added) next.
            I = CI; ++I;
            
            if (CI != Result && !CI->use_empty()) {
                CI->replaceAllUsesWith(Result);
                if (!Result->hasName())
                    Result->takeName(CI);
            }
            CI->eraseFromParent();
        }
    }
    return Changed;
}