@@ -95,7 +95,7 @@ AllocationBasedLivenessAnalysis::LivenessData* AllocationBasedLivenessAnalysis::
95
95
}
96
96
97
97
// figure out the potential accesses to the memory via GEP and bitcasts
98
- while (!worklist.empty () && !hasNoLifetimeEnd )
98
+ while (!worklist.empty ())
99
99
{
100
100
auto * use = worklist.pop_back_val ();
101
101
auto * II = cast<Instruction>(use->getUser ());
@@ -139,22 +139,44 @@ AllocationBasedLivenessAnalysis::LivenessData* AllocationBasedLivenessAnalysis::
139
139
}
140
140
}
141
141
142
- // we add the return instructions to the list of users to express the infinite lifetime
143
- if (hasNoLifetimeEnd)
142
+ return new LivenessData (I, allUsers, *LI, *DT, commonDominator, hasNoLifetimeEnd);
143
+ }
144
+
145
+ template <typename range>
146
+ static inline void doWorkLoop (
147
+ SmallVector<BasicBlock*>& worklist,
148
+ DenseSet<BasicBlock*>& bbSet1,
149
+ DenseSet<BasicBlock*>& bbSet2,
150
+ std::function<range(BasicBlock*)> iterate,
151
+ std::function<bool(BasicBlock*)> continueCondition
152
+ ) {
153
+ // perform data flow analysis
154
+ while (!worklist.empty ())
144
155
{
145
- for_each (instructions (*I->getFunction ()),
146
- [&](auto & II)
147
- {
148
- if (isa<ReturnInst>(&II))
149
- allUsers.insert (&II);
150
- }
151
- );
156
+ auto * currbb = worklist.pop_back_val ();
157
+
158
+ if (continueCondition (currbb))
159
+ continue ;
160
+
161
+ bool addToSet1 = false ;
162
+
163
+ for (auto * pbb : iterate(currbb))
164
+ {
165
+ addToSet1 = true ;
166
+
167
+ bool inserted = bbSet2.insert (pbb).second ;
168
+
169
+ if (inserted)
170
+ worklist.push_back (pbb);
171
+ }
172
+
173
+ if (addToSet1)
174
+ bbSet1.insert (currbb);
152
175
}
153
176
154
- return new LivenessData (I, allUsers, *LI, commonDominator);
155
177
}
156
178
157
- AllocationBasedLivenessAnalysis::LivenessData::LivenessData (Instruction* allocationInstruction, const SetVector<Instruction*>& usersOfAllocation, const LoopInfo& LI, BasicBlock* userDominatorBlock)
179
+ AllocationBasedLivenessAnalysis::LivenessData::LivenessData (Instruction* allocationInstruction, const SetVector<Instruction*>& usersOfAllocation, const LoopInfo& LI, const DominatorTree& DT, BasicBlock* userDominatorBlock, bool isLifetimeInfinite )
158
180
{
159
181
if (!userDominatorBlock)
160
182
userDominatorBlock = allocationInstruction->getParent ();
@@ -171,38 +193,44 @@ AllocationBasedLivenessAnalysis::LivenessData::LivenessData(Instruction* allocat
171
193
// Keep track of loop header of blocks that contain allocation instruction
172
194
auto * allocationParent = allocationInstruction->getParent ();
173
195
llvm::SmallPtrSet<llvm::BasicBlock*, 4 > containedLoopHeaders;
174
- if (const auto * parentLoop = LI.getLoopFor (allocationParent);
175
- parentLoop != nullptr ) {
196
+ if (const auto * parentLoop = LI.getLoopFor (allocationParent))
197
+ {
176
198
containedLoopHeaders.insert (parentLoop->getHeader ());
177
- while (parentLoop->getParentLoop () != nullptr ) {
199
+ while (parentLoop->getParentLoop ()) {
178
200
parentLoop = parentLoop->getParentLoop ();
179
201
containedLoopHeaders.insert (parentLoop->getHeader ());
180
202
}
181
203
}
204
+
182
205
// perform data flow analysis
183
- while (!worklist.empty ())
206
+ doWorkLoop<llvm::pred_range>(
207
+ worklist,
208
+ bbIn,
209
+ bbOut,
210
+ [&](auto * currbb) { return llvm::predecessors (currbb); },
211
+ [&](auto * currbb) { return bbIn.contains (currbb) || currbb == userDominatorBlock || containedLoopHeaders.contains (currbb); }
212
+ );
213
+
214
+ // handle infinite lifetime
215
+ if (isLifetimeInfinite)
184
216
{
185
- auto * currbb = worklist.pop_back_val ();
186
-
187
- if (bbIn.contains (currbb) || currbb == userDominatorBlock)
188
- continue ;
189
-
190
- // If alloca is defined in the loop, we skip loop header
191
- // so that we don't escape loop scope.
192
- if (containedLoopHeaders.count (currbb) != 0 )
193
- {
194
- continue ;
195
- }
196
-
197
- if (currbb != allocationParent)
198
- {
199
- bbIn.insert (currbb);
200
- }
201
- for (auto * pbb : llvm::predecessors (currbb))
202
- {
203
- bbOut.insert (pbb);
204
- worklist.push_back (pbb);
205
- }
217
+ // traverse all the successors until there are no left.
218
+ auto bbInOnly = bbIn;
219
+ set_subtract (bbInOnly, bbOut);
220
+
221
+ for (auto * bb : bbInOnly)
222
+ worklist.push_back (bb);
223
+
224
+ // in case the only use is the one that causes lifetime escape
225
+ worklist.push_back (userDominatorBlock);
226
+
227
+ doWorkLoop<llvm::succ_range>(
228
+ worklist,
229
+ bbOut,
230
+ bbIn,
231
+ [&](auto * currbb) { return llvm::successors (currbb); },
232
+ [&](auto * currbb) { return false ; }
233
+ );
206
234
}
207
235
208
236
// if the lifetime escapes any loop, we should make sure all the loops blocks are included
@@ -215,12 +243,38 @@ AllocationBasedLivenessAnalysis::LivenessData::LivenessData(Instruction* allocat
215
243
{
216
244
llvm::for_each (
217
245
loop->blocks (),
218
- [&](auto * block) {
219
- bbOut.insert (block);
220
- if (block != loop->getHeader ())
221
- bbIn.insert (block);
222
- }
246
+ [&](auto * block) { bbOut.insert (block); bbIn.insert (block); }
223
247
);
248
+
249
+ if (loop->getLoopPreheader ())
250
+ {
251
+ bbOut.insert (loop->getLoopPreheader ());
252
+ }
253
+ else
254
+ {
255
+ // if the header has multiple predecessors, we need to find the common dominator of all of these
256
+ auto * commonDominator = loop->getHeader ();
257
+ for (auto * bb : llvm::predecessors (loop->getHeader ()))
258
+ {
259
+ if (loop->contains (bb))
260
+ continue ;
261
+
262
+ commonDominator = DT.findNearestCommonDominator (commonDominator, bb);
263
+ worklist.push_back (bb);
264
+ }
265
+
266
+ // acknowledge lifetime flow out of the common dominator block
267
+ bbOut.insert (commonDominator);
268
+
269
+ // add all blocks inbetween
270
+ doWorkLoop<llvm::pred_range>(
271
+ worklist,
272
+ bbIn,
273
+ bbOut,
274
+ [&](auto * currbb) { return llvm::predecessors (currbb); },
275
+ [&](auto * currbb) { return bbOut.contains (currbb) || currbb == commonDominator; }
276
+ );
277
+ }
224
278
}
225
279
}
226
280
@@ -264,7 +318,7 @@ AllocationBasedLivenessAnalysis::LivenessData::LivenessData(Instruction* allocat
264
318
{
265
319
for (auto & I : llvm::reverse (*bb))
266
320
{
267
- if (usersOfAllocation.contains (&I))
321
+ if (usersOfAllocation.contains (&I) || isLifetimeInfinite )
268
322
{
269
323
lifetimeEnds.push_back (&I);
270
324
break ;
@@ -289,12 +343,34 @@ bool AllocationBasedLivenessAnalysis::LivenessData::OverlapsWith(const LivenessD
289
343
// check lifetime boundaries
290
344
for (auto & [LD1, LD2] : { std::make_pair (*this , LD), std::make_pair (LD, *this ) })
291
345
{
292
- if (LD1.lifetimeEnds .size () == 1 && *LD1.lifetimeEnds .begin () == LD1.lifetimeStart )
293
- continue ;
294
-
295
346
for (auto * I : LD1.lifetimeEnds )
296
347
{
297
- if (I->getParent () == LD2.lifetimeStart ->getParent ())
348
+ // what if LD1 is contained in a single block
349
+ if (I->getParent () == LD1.lifetimeStart ->getParent ())
350
+ {
351
+ auto * bb = I->getParent ();
352
+ bool inflow = LD2.bbIn .contains (bb);
353
+ bool outflow = LD2.bbOut .contains (bb);
354
+ bool lifetimeStart = LD2.lifetimeStart ->getParent () == bb && LD2.lifetimeStart ->comesBefore (I);
355
+
356
+ auto * LD1_lifetimeStart = LD1.lifetimeStart ; // we have to copy LD1.lifetimeStart to avoid clang complaining about LD1 being captured by the lambda
357
+ bool lifetimeEnd = any_of (LD2.lifetimeEnds , [&](auto * lifetimeEnd) {
358
+ return lifetimeEnd->getParent () == bb && LD1_lifetimeStart->comesBefore (lifetimeEnd);
359
+ });
360
+
361
+ if (inflow && outflow)
362
+ return true ;
363
+
364
+ if (inflow && lifetimeEnd)
365
+ return true ;
366
+
367
+ if (outflow && lifetimeStart)
368
+ return true ;
369
+
370
+ if (lifetimeEnd && lifetimeStart)
371
+ return true ;
372
+ }
373
+ else if (I->getParent () == LD2.lifetimeStart ->getParent ())
298
374
{
299
375
if (LD2.lifetimeStart ->comesBefore (I))
300
376
return true ;
0 commit comments