From fa793291024f8d2b6b679b557ae6ec4271ebd18e Mon Sep 17 00:00:00 2001 From: Frits van Bommel Date: Sun, 3 May 2009 18:01:45 +0200 Subject: [PATCH] Stack promotion for _d_newarrayvT. Array literals, concatenations (a ~ b) and such are eligible for stack-allocation now. --- gen/passes/GarbageCollect2Stack.cpp | 79 ++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/gen/passes/GarbageCollect2Stack.cpp b/gen/passes/GarbageCollect2Stack.cpp index 7f2464fa..1be5f879 100644 --- a/gen/passes/GarbageCollect2Stack.cpp +++ b/gen/passes/GarbageCollect2Stack.cpp @@ -29,6 +29,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/IRBuilder.h" #include "llvm/Analysis/CaptureTracking.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Target/TargetData.h" #include "llvm/ADT/StringMap.h" @@ -37,7 +38,8 @@ #include "llvm/Support/Debug.h" using namespace llvm; -STATISTIC(NumGcToStack, "Number of GC calls promoted to stack allocations"); +STATISTIC(NumGcToStack, "Number of calls promoted to constant-size allocas"); +STATISTIC(NumToDynSize, "Number of calls promoted to dynamically-sized allocas"); STATISTIC(NumDeleted, "Number of GC calls deleted because the return value was unused"); //===----------------------------------------------------------------------===// @@ -47,14 +49,12 @@ STATISTIC(NumDeleted, "Number of GC calls deleted because the return value was u namespace { struct FunctionInfo { unsigned TypeInfoArgNr; + int ArrSizeArgNr; bool SafeToDelete; - FunctionInfo(unsigned typeInfoArgNr, bool safeToDelete) - : TypeInfoArgNr(typeInfoArgNr), SafeToDelete(safeToDelete) {} - - Value* getArraySize(CallSite CS) { - return 0; - } + FunctionInfo(unsigned typeInfoArgNr, int arrSizeArgNr, bool safeToDelete) + : TypeInfoArgNr(typeInfoArgNr), ArrSizeArgNr(arrSizeArgNr), + SafeToDelete(safeToDelete) {} }; /// This pass replaces GC calls with alloca's @@ -92,7 +92,8 @@ FunctionPass *createGarbageCollect2Stack() { bool GarbageCollect2Stack::doInitialization(Module &M) { this->M = &M; - KnownFunctions["_d_allocmemoryT"] = new FunctionInfo(0, true); + KnownFunctions["_d_allocmemoryT"] = new FunctionInfo(0, -1, true); + KnownFunctions["_d_newarrayvT"] = new FunctionInfo(0, 1, true); } /// runOnFunction - Top level algorithm. @@ -100,7 +101,7 @@ bool GarbageCollect2Stack::doInitialization(Module &M) { bool GarbageCollect2Stack::runOnFunction(Function &F) { DEBUG(DOUT << "Running -dgc2stack on function " << F.getName() << '\n'); - const TargetData &TD = getAnalysis(); + TargetData &TD = getAnalysis(); const LoopInfo &LI = getAnalysis(); BasicBlock& Entry = F.getEntryBlock(); @@ -161,26 +162,76 @@ bool GarbageCollect2Stack::runOnFunction(Function &F) { continue; } + Value* arrSize = 0; + if (info->ArrSizeArgNr != -1) { + arrSize = CS.getArgument(info->ArrSizeArgNr); + const IntegerType* SizeType = + dyn_cast(arrSize->getType()); + if (!SizeType) + continue; + unsigned bits = SizeType->getBitWidth(); + if (bits > 32) { + // The array size of an alloca must be an i32, so make sure + // the conversion is safe. + APInt Mask = APInt::getHighBitsSet(bits, bits - 32); + APInt KnownZero(bits, 0), KnownOne(bits, 0); + ComputeMaskedBits(arrSize, Mask, KnownZero, KnownOne, &TD); + if ((KnownZero & Mask) != Mask) + continue; + } + // Extract the element type from the array type. + const StructType* ArrTy = dyn_cast(Ty); + assert(ArrTy && "Dynamic array type not a struct?"); + assert(isa(ArrTy->getElementType(0))); + const PointerType* PtrTy = + cast(ArrTy->getElementType(1)); + Ty = PtrTy->getElementType(); + } + // Let's alloca this! Changed = true; - NumGcToStack++; - Value* arrSize = info->getArraySize(CS); - Value* newVal = AllocaBuilder.CreateAlloca(Ty, arrSize, ".nongc_mem"); + IRBuilder<> Builder(BB, I); + // If the allocation is of constant size it's best to put it in the + // entry block, so do so if we're not already there. + // For dynamically-sized allocations it's best to avoid the overhead + // of allocating them if possible, so leave those where they are. + // While we're at it, update statistics too. + if (!arrSize || isa(arrSize)) { + if (&*BB != &Entry) + Builder = AllocaBuilder; + NumGcToStack++; + } else { + NumToDynSize++; + } + + // Convert array size to 32 bits if necessary + if (arrSize) + arrSize = Builder.CreateIntCast(arrSize, Type::Int32Ty, false); + + Value* newVal = Builder.CreateAlloca(Ty, arrSize, ".nongc_mem"); + + // Make sure the type is the same as it was before, and replace all + // uses of the runtime call with the alloca. if (newVal->getType() != Inst->getType()) - newVal = AllocaBuilder.CreateBitCast(newVal, Inst->getType()); - + newVal = Builder.CreateBitCast(newVal, Inst->getType()); Inst->replaceAllUsesWith(newVal); + // If this was an invoke instruction, update the control flow. if (InvokeInst* Invoke = dyn_cast(Inst)) { + // Notify the exception landing pad block that we won't be + // going there anymore. Invoke->getUnwindDest()->removePredecessor(Invoke->getParent()); // Create a branch to the "normal" destination. BranchInst::Create(Invoke->getNormalDest(), Invoke->getParent()); } + + // Finally, remove the runtime call. Inst->eraseFromParent(); } } + return Changed; }