Mercurial > projects > ldc
comparison gen/passes/GarbageCollect2Stack.cpp @ 1285:91d9386d4a5a
Implement another D-specific pass: -dgc2stack
This one promotes GC allocations to stack memory when it can determine it's safe
to do so.
Not all GC calls are recognized yet (in fact only one *is* recognized for now).
Needs metadata, so disabled for LLVM versions that don't support it.
author | Frits van Bommel <fvbommel wxs.nl> |
---|---|
date | Sat, 02 May 2009 11:58:50 +0200 |
parents | |
children | 875afb7a93b6 |
comparison
equal
deleted
inserted
replaced
1284:5851c18e4c6d | 1285:91d9386d4a5a |
---|---|
1 //===- GarbageCollect2Stack - Optimize calls to the D garbage collector ---===// | |
2 // | |
3 // The LLVM D Compiler | |
4 // | |
5 // This file is distributed under the University of Illinois Open Source | |
6 // License. See LICENSE.TXT for details. | |
7 // | |
8 //===----------------------------------------------------------------------===// | |
9 // | |
10 // This file attempts to turn allocations on the garbage-collected heap into | |
11 // stack allocations. | |
12 // | |
13 //===----------------------------------------------------------------------===// | |
14 | |
15 #include "gen/metadata.h" | |
16 | |
17 // This pass doesn't work without metadata, so #ifdef it out entirely if the | |
18 // LLVM version in use doesn't support it. | |
19 #ifdef USE_METADATA | |
20 | |
21 | |
22 #define DEBUG_TYPE "dgc2stack" | |
23 | |
24 #include "Passes.h" | |
25 | |
26 #include "llvm/Pass.h" | |
27 #include "llvm/Module.h" | |
28 #include "llvm/Support/CallSite.h" | |
29 #include "llvm/Support/CommandLine.h" | |
30 #include "llvm/Support/IRBuilder.h" | |
31 #include "llvm/Analysis/CaptureTracking.h" | |
32 #include "llvm/Analysis/LoopInfo.h" | |
33 #include "llvm/Target/TargetData.h" | |
34 #include "llvm/ADT/StringMap.h" | |
35 #include "llvm/ADT/Statistic.h" | |
36 #include "llvm/Support/Compiler.h" | |
37 #include "llvm/Support/Debug.h" | |
38 using namespace llvm; | |
39 | |
40 STATISTIC(NumGcToStack, "Number of GC calls promoted to stack allocations"); | |
41 STATISTIC(NumDeleted, "Number of GC calls deleted because the return value was unused"); | |
42 | |
43 //===----------------------------------------------------------------------===// | |
44 // GarbageCollect2Stack Pass Implementation | |
45 //===----------------------------------------------------------------------===// | |
46 | |
47 namespace { | |
48 struct FunctionInfo { | |
49 unsigned TypeInfoArgNr; | |
50 bool SafeToDelete; | |
51 | |
52 FunctionInfo(unsigned typeInfoArgNr, bool safeToDelete) | |
53 : TypeInfoArgNr(typeInfoArgNr), SafeToDelete(safeToDelete) {} | |
54 | |
55 Value* getArraySize(CallSite CS) { | |
56 return 0; | |
57 } | |
58 }; | |
59 | |
60 /// This pass replaces GC calls with alloca's | |
61 /// | |
62 class VISIBILITY_HIDDEN GarbageCollect2Stack : public FunctionPass { | |
63 StringMap<FunctionInfo*> KnownFunctions; | |
64 Module* M; | |
65 | |
66 public: | |
67 static char ID; // Pass identification | |
68 GarbageCollect2Stack() : FunctionPass(&ID) {} | |
69 | |
70 bool doInitialization(Module &M); | |
71 | |
72 bool runOnFunction(Function &F); | |
73 | |
74 virtual void getAnalysisUsage(AnalysisUsage &AU) const { | |
75 AU.addRequired<TargetData>(); | |
76 AU.addRequired<LoopInfo>(); | |
77 } | |
78 | |
79 private: | |
80 const Type* getTypeFor(Value* typeinfo); | |
81 }; | |
82 char GarbageCollect2Stack::ID = 0; | |
83 } // end anonymous namespace. | |
84 | |
85 static RegisterPass<GarbageCollect2Stack> | |
86 X("dgc2stack", "Promote (GC'ed) heap allocations to stack"); | |
87 | |
88 // Public interface to the pass. | |
89 FunctionPass *createGarbageCollect2Stack() { | |
90 return new GarbageCollect2Stack(); | |
91 } | |
92 | |
93 bool GarbageCollect2Stack::doInitialization(Module &M) { | |
94 this->M = &M; | |
95 KnownFunctions["_d_allocmemoryT"] = new FunctionInfo(0, true); | |
96 } | |
97 | |
98 /// runOnFunction - Top level algorithm. | |
99 /// | |
100 bool GarbageCollect2Stack::runOnFunction(Function &F) { | |
101 DEBUG(DOUT << "Running on function " << F.getName() << '\n'); | |
102 | |
103 const TargetData &TD = getAnalysis<TargetData>(); | |
104 const LoopInfo &LI = getAnalysis<LoopInfo>(); | |
105 | |
106 BasicBlock& Entry = F.getEntryBlock(); | |
107 | |
108 IRBuilder<> AllocaBuilder(&Entry, Entry.begin()); | |
109 | |
110 bool Changed = false; | |
111 for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) { | |
112 // We don't yet have sufficient analysis to properly determine if | |
113 // allocations will be unreferenced when the loop returns to their | |
114 // allocation point, so we're playing it safe by ignoring allocations | |
115 // in loops. | |
116 // TODO: Analyze loops too... | |
117 if (LI.getLoopFor(BB)) { | |
118 DEBUG(DOUT << "Skipping loop block " << *BB << '\n'); | |
119 continue; | |
120 } | |
121 | |
122 for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) { | |
123 // Ignore non-calls. | |
124 Instruction* Inst = I++; | |
125 CallSite CS = CallSite::get(Inst); | |
126 if (!CS.getInstruction()) | |
127 continue; | |
128 | |
129 // Ignore indirect calls and calls to non-external functions. | |
130 Function *Callee = CS.getCalledFunction(); | |
131 if (Callee == 0 || !Callee->isDeclaration() || | |
132 !(Callee->hasExternalLinkage() || Callee->hasDLLImportLinkage())) | |
133 continue; | |
134 | |
135 // Ignore unknown calls. | |
136 const char *CalleeName = Callee->getNameStart(); | |
137 StringMap<FunctionInfo*>::iterator OMI = | |
138 KnownFunctions.find(CalleeName, CalleeName+Callee->getNameLen()); | |
139 if (OMI == KnownFunctions.end()) continue; | |
140 | |
141 assert(isa<PointerType>(Inst->getType()) | |
142 && "GC function doesn't return a pointer?"); | |
143 | |
144 FunctionInfo* info = OMI->getValue(); | |
145 | |
146 if (Inst->use_empty() && info->SafeToDelete) { | |
147 Changed = true; | |
148 NumDeleted++; | |
149 Inst->eraseFromParent(); | |
150 continue; | |
151 } | |
152 | |
153 DEBUG(DOUT << "GarbageCollect2Stack inspecting: " << *Inst); | |
154 | |
155 if (PointerMayBeCaptured(Inst, true)) { | |
156 DEBUG(DOUT << ">> is captured :(\n"); | |
157 continue; | |
158 } | |
159 DEBUG(DOUT << ">> is not captured :)\n"); | |
160 | |
161 Value* TypeInfo = CS.getArgument(info->TypeInfoArgNr); | |
162 const Type* Ty = getTypeFor(TypeInfo); | |
163 if (!Ty) { | |
164 DEBUG(DOUT << ">> Couldn't find valid TypeInfo metadata :(\n"); | |
165 continue; | |
166 } | |
167 | |
168 // Let's alloca this! | |
169 Changed = true; | |
170 NumGcToStack++; | |
171 | |
172 Value* arrSize = info->getArraySize(CS); | |
173 Value* newVal = AllocaBuilder.CreateAlloca(Ty, arrSize, ".nongc_mem"); | |
174 | |
175 if (newVal->getType() != Inst->getType()) | |
176 newVal = AllocaBuilder.CreateBitCast(newVal, Inst->getType()); | |
177 | |
178 Inst->replaceAllUsesWith(newVal); | |
179 | |
180 if (InvokeInst* Invoke = dyn_cast<InvokeInst>(Inst)) { | |
181 Invoke->getUnwindDest()->removePredecessor(Invoke->getParent()); | |
182 // Create a branch to the "normal" destination. | |
183 BranchInst::Create(Invoke->getNormalDest(), Invoke->getParent()); | |
184 } | |
185 Inst->eraseFromParent(); | |
186 } | |
187 } | |
188 return Changed; | |
189 } | |
190 | |
191 const Type* GarbageCollect2Stack::getTypeFor(Value* typeinfo) { | |
192 GlobalVariable* ti_global = dyn_cast<GlobalVariable>(typeinfo->stripPointerCasts()); | |
193 if (!ti_global) | |
194 return NULL; | |
195 | |
196 DEBUG(DOUT << ">> Found typeinfo init\n"; | |
197 DOUT << ">> Value: " << *ti_global << "\n"; | |
198 DOUT << ">> Name: " << ti_global->getNameStr() << "\n"); | |
199 | |
200 std::string metaname = "llvm.ldc.typeinfo."; | |
201 metaname.append(ti_global->getNameStart(), ti_global->getNameEnd()); | |
202 | |
203 DEBUG(DOUT << ">> Looking for global named " << metaname << "\n"); | |
204 | |
205 GlobalVariable* global = M->getGlobalVariable(metaname); | |
206 DEBUG(DOUT << ">> global: " << global->getInitializer() << "\n"); | |
207 if (!global || !global->hasInitializer()) | |
208 return NULL; | |
209 | |
210 DEBUG(DOUT << ">> Found metadata global\n"); | |
211 | |
212 MDNode* node = dyn_cast<MDNode>(global->getInitializer()); | |
213 if (!node) | |
214 return NULL; | |
215 | |
216 DEBUG(DOUT << ">> Found metadata node\n"); | |
217 | |
218 if (node->getNumOperands() != 2 || | |
219 node->getOperand(0)->stripPointerCasts() != ti_global) | |
220 return NULL; | |
221 | |
222 DEBUG(DOUT << ">> Validated metadata node\n"); | |
223 | |
224 return node->getOperand(1)->getType(); | |
225 } | |
226 | |
227 | |
228 #endif //USE_METADATA |