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