//===- GarbageCollect2Stack - Optimize calls to the D garbage collector ---===// // // The LLVM D Compiler // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file attempts to turn allocations on the garbage-collected heap into // stack allocations. // //===----------------------------------------------------------------------===// #include "gen/metadata.h" // This pass doesn't work without metadata, so #ifdef it out entirely if the // LLVM version in use doesn't support it. #ifdef USE_METADATA #define DEBUG_TYPE "dgc2stack" #include "Passes.h" #include "llvm/Pass.h" #include "llvm/Module.h" #include "llvm/Support/CallSite.h" #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" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Debug.h" using namespace llvm; 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"); //===----------------------------------------------------------------------===// // GarbageCollect2Stack Pass Implementation //===----------------------------------------------------------------------===// namespace { struct FunctionInfo { unsigned TypeInfoArgNr; int ArrSizeArgNr; bool SafeToDelete; FunctionInfo(unsigned typeInfoArgNr, int arrSizeArgNr, bool safeToDelete) : TypeInfoArgNr(typeInfoArgNr), ArrSizeArgNr(arrSizeArgNr), SafeToDelete(safeToDelete) {} }; /// This pass replaces GC calls with alloca's /// class VISIBILITY_HIDDEN GarbageCollect2Stack : public FunctionPass { StringMap KnownFunctions; Module* M; public: static char ID; // Pass identification GarbageCollect2Stack() : FunctionPass(&ID) {} bool doInitialization(Module &M); bool runOnFunction(Function &F); virtual void getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); AU.addRequired(); } private: const Type* getTypeFor(Value* typeinfo); }; char GarbageCollect2Stack::ID = 0; } // end anonymous namespace. static RegisterPass X("dgc2stack", "Promote (GC'ed) heap allocations to stack"); // Public interface to the pass. FunctionPass *createGarbageCollect2Stack() { return new GarbageCollect2Stack(); } bool GarbageCollect2Stack::doInitialization(Module &M) { this->M = &M; KnownFunctions["_d_allocmemoryT"] = new FunctionInfo(0, -1, true); KnownFunctions["_d_newarrayvT"] = new FunctionInfo(0, 1, true); } static void RemoveCall(Instruction* Inst) { if (InvokeInst* Invoke = dyn_cast(Inst)) { // If this was an invoke instruction, we need to do some extra // work to preserve the control flow. // First 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()); } // Remove the runtime call. Inst->eraseFromParent(); } /// runOnFunction - Top level algorithm. /// bool GarbageCollect2Stack::runOnFunction(Function &F) { DEBUG(DOUT << "Running -dgc2stack on function " << F.getName() << '\n'); TargetData &TD = getAnalysis(); const LoopInfo &LI = getAnalysis(); BasicBlock& Entry = F.getEntryBlock(); IRBuilder<> AllocaBuilder(&Entry, Entry.begin()); bool Changed = false; for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) { // We don't yet have sufficient analysis to properly determine if // allocations will be unreferenced when the loop returns to their // allocation point, so we're playing it safe by ignoring allocations // in loops. // TODO: Analyze loops too... if (LI.getLoopFor(BB)) { continue; } for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) { // Ignore non-calls. Instruction* Inst = I++; CallSite CS = CallSite::get(Inst); if (!CS.getInstruction()) continue; // Ignore indirect calls and calls to non-external functions. Function *Callee = CS.getCalledFunction(); if (Callee == 0 || !Callee->isDeclaration() || !(Callee->hasExternalLinkage() || Callee->hasDLLImportLinkage())) continue; // Ignore unknown calls. const char *CalleeName = Callee->getNameStart(); StringMap::iterator OMI = KnownFunctions.find(CalleeName, CalleeName+Callee->getNameLen()); if (OMI == KnownFunctions.end()) continue; assert(isa(Inst->getType()) && "GC function doesn't return a pointer?"); FunctionInfo* info = OMI->getValue(); if (Inst->use_empty() && info->SafeToDelete) { Changed = true; NumDeleted++; RemoveCall(Inst); continue; } DEBUG(DOUT << "GarbageCollect2Stack inspecting: " << *Inst); if (PointerMayBeCaptured(Inst, true)) { continue; } Value* TypeInfo = CS.getArgument(info->TypeInfoArgNr); const Type* Ty = getTypeFor(TypeInfo); if (!Ty) { 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; 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 = Builder.CreateBitCast(newVal, Inst->getType()); Inst->replaceAllUsesWith(newVal); RemoveCall(Inst); } } return Changed; } const Type* GarbageCollect2Stack::getTypeFor(Value* typeinfo) { GlobalVariable* ti_global = dyn_cast(typeinfo->stripPointerCasts()); if (!ti_global) return NULL; std::string metaname = TD_PREFIX; metaname.append(ti_global->getNameStart(), ti_global->getNameEnd()); GlobalVariable* global = M->getGlobalVariable(metaname); if (!global || !global->hasInitializer()) return NULL; MDNode* node = dyn_cast(global->getInitializer()); if (!node) return NULL; if (node->getNumOperands() != TD_NumFields || node->getOperand(TD_Confirm)->stripPointerCasts() != ti_global) return NULL; return node->getOperand(TD_Type)->getType(); } #endif //USE_METADATA