view gen/passes/SimplifyDRuntimeCalls.cpp @ 1317:4099548c80e0

Allocate objects on the stack if they (a) don't have a destructor, and (b) don't override the delete operator (on top of the regular conditions for stack allocation that also apply to arrays, structs, etc.). The "no destructor" clause is not strictly necessary, but calling them at the right time would be tricky to say the least; it would involve, among other things, "manually" inserting a try-finally block around anything that might throw exceptions not caught in the current function. Note: objects with custom new operators are automatically ignored because they don't use the regular allocation runtime call, so there's no need to pay special attention to them.
author Frits van Bommel <fvbommel wxs.nl>
date Sat, 09 May 2009 00:50:15 +0200
parents 23b23b74e326
children c32e27f9a61d
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;
    }
};

/// DeleteUnusedOpt - remove libcall if the return value is unused.
struct VISIBILITY_HIDDEN DeleteUnusedOpt : public LibCallOptimization {
    virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
        if (CI->use_empty())
            return CI;
        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;
        
        // GC allocations
        DeleteUnusedOpt DeleteUnused;
        
        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() {
    // Some array-related optimizations
    Optimizations["_d_arraysetlengthT"] = &ArraySetLength;
    Optimizations["_d_arraysetlengthiT"] = &ArraySetLength;
    Optimizations["_d_array_cast_len"] = &ArrayCastLen;
    
    /* Delete calls to runtime functions which aren't needed if their result is
     * unused. That comes down to functions that don't do anything but
     * GC-allocate and initialize some memory.
     * We don't need to do this for functions which are marked 'readnone' or
     * 'readonly', since LLVM doesn't need our help figuring out when those can
     * be deleted.
     * (We can't mark allocating calls as readonly/readnone because they don't
     * return the same pointer every time when called with the same arguments)
     */
    Optimizations["_d_allocmemoryT"] = &DeleteUnused;
    Optimizations["_d_newarrayT"] = &DeleteUnused;
    Optimizations["_d_newarrayiT"] = &DeleteUnused;
    Optimizations["_d_newarrayvT"] = &DeleteUnused;
    Optimizations["_d_newarraymT"] = &DeleteUnused;
    Optimizations["_d_newarraymiT"] = &DeleteUnused;
    Optimizations["_d_newarraymvT"] = &DeleteUnused;
    Optimizations["_d_allocclass"] = &DeleteUnused;
}


/// 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;
}