//===- 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/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 GC calls promoted to stack allocations"); STATISTIC(NumDeleted, "Number of GC calls deleted because the return value was unused"); //===----------------------------------------------------------------------===// // GarbageCollect2Stack Pass Implementation //===----------------------------------------------------------------------===// namespace { struct FunctionInfo { unsigned TypeInfoArgNr; bool SafeToDelete; FunctionInfo(unsigned typeInfoArgNr, bool safeToDelete) : TypeInfoArgNr(typeInfoArgNr), SafeToDelete(safeToDelete) {} Value* getArraySize(CallSite CS) { return 0; } }; /// 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, true); } /// runOnFunction - Top level algorithm. /// bool GarbageCollect2Stack::runOnFunction(Function &F) { DEBUG(DOUT << "Running -dgc2stack on function " << F.getName() << '\n'); const 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++; Inst->eraseFromParent(); 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; } // Let's alloca this! Changed = true; NumGcToStack++; Value* arrSize = info->getArraySize(CS); Value* newVal = AllocaBuilder.CreateAlloca(Ty, arrSize, ".nongc_mem"); if (newVal->getType() != Inst->getType()) newVal = AllocaBuilder.CreateBitCast(newVal, Inst->getType()); Inst->replaceAllUsesWith(newVal); if (InvokeInst* Invoke = dyn_cast(Inst)) { Invoke->getUnwindDest()->removePredecessor(Invoke->getParent()); // Create a branch to the "normal" destination. BranchInst::Create(Invoke->getNormalDest(), Invoke->getParent()); } Inst->eraseFromParent(); } } 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