@@ -70,8 +70,8 @@ static SILInstruction *endOSSALifetime(SILValue value, SILBuilder &builder) {
70
70
return builder.createEndBorrow (loc, value);
71
71
}
72
72
73
- static bool endLifetimeAtBoundary (SILValue value,
74
- const SSAPrunedLiveness &liveness) {
73
+ static bool endLifetimeAtLivenessBoundary (SILValue value,
74
+ const SSAPrunedLiveness &liveness) {
75
75
PrunedLivenessBoundary boundary;
76
76
liveness.computeBoundary (boundary);
77
77
@@ -149,7 +149,7 @@ class VisitUnreachableLifetimeEnds {
149
149
llvm::function_ref<void (SILInstruction *)> visit);
150
150
151
151
struct State {
152
- enum class Value : uint8_t {
152
+ enum Value : uint8_t {
153
153
Unavailable = 0 ,
154
154
Available,
155
155
Unknown,
@@ -161,10 +161,6 @@ class VisitUnreachableLifetimeEnds {
161
161
State meet (State const other) const {
162
162
return *this < other ? *this : other;
163
163
}
164
-
165
- static State Unavailable () { return {Value::Unavailable}; }
166
- static State Available () { return {Value::Available}; }
167
- static State Unknown () { return {Value::Unknown}; }
168
164
};
169
165
170
166
struct Result {
@@ -197,36 +193,47 @@ class VisitUnreachableLifetimeEnds {
197
193
198
194
void VisitUnreachableLifetimeEnds::computeRegion (
199
195
const SSAPrunedLiveness &liveness) {
200
- // Find the non-lifetime-ending boundary of `value` .
196
+ // (1) Compute the complete liveness boundary .
201
197
PrunedLivenessBoundary boundary;
202
198
liveness.computeBoundary (boundary);
203
199
200
+ // Used in the forward walk below (3).
201
+ BasicBlockWorklist regionWorklist (value->getFunction ());
202
+
203
+ // (2) Collect the non-lifetime-ending liveness boundary. This is the
204
+ // portion of `boundary` consisting of:
205
+ // - non-lifetime-ending instructions (their parent blocks)
206
+ // - boundary edges
207
+ // - dead defs (their parent blocks)
208
+ auto collect = [&](SILBasicBlock *block) {
209
+ // `region` consists of the non-lifetime-ending boundary and all its
210
+ // iterative successors.
211
+ region.insert (block);
212
+ // `starts` just consists of the blocks in the non-lifetime-ending
213
+ // boundary.
214
+ starts.insert (block);
215
+ // The forward walk begins from the non-lifetime-ending boundary.
216
+ regionWorklist.push (block);
217
+ };
218
+
204
219
for (SILInstruction *lastUser : boundary.lastUsers ) {
205
220
if (liveness.isInterestingUser (lastUser)
206
221
!= PrunedLiveness::LifetimeEndingUse) {
207
- region.insert (lastUser->getParent ());
208
- starts.insert (lastUser->getParent ());
222
+ collect (lastUser->getParent ());
209
223
}
210
224
}
211
225
for (SILBasicBlock *edge : boundary.boundaryEdges ) {
212
- region.insert (edge);
213
- starts.insert (edge);
226
+ collect (edge);
214
227
}
215
228
for (SILNode *deadDef : boundary.deadDefs ) {
216
- region.insert (deadDef->getParentBlock ());
217
- starts.insert (deadDef->getParentBlock ());
229
+ collect (deadDef->getParentBlock ());
218
230
}
219
231
220
- // Forward walk to find the region in which `value` might be available.
221
- BasicBlockWorklist regionWorklist (value->getFunction ());
222
- // Start the forward walk from the non-lifetime-ending boundary.
223
- for (auto *start : region) {
224
- regionWorklist.push (start);
225
- }
232
+ // (3) Forward walk to find the region in which `value` might be available.
226
233
while (auto *block = regionWorklist.pop ()) {
227
234
if (block->succ_empty ()) {
228
- // This assert will fail unless there are already lifetime-ending
229
- // instruction on all paths to normal function exits.
235
+ // This assert will fail unless there is already a lifetime-ending
236
+ // instruction on each path to normal function exits.
230
237
assert (isa<UnreachableInst>(block->getTerminator ()));
231
238
}
232
239
for (auto *successor : block->getSuccessorBlocks ()) {
@@ -244,9 +251,9 @@ void VisitUnreachableLifetimeEnds::propagateAvailablity(Result &result) {
244
251
// - start blocks are ::Available
245
252
for (auto *block : region) {
246
253
if (starts.contains (block))
247
- result.setState (block, State::Available () );
254
+ result.setState (block, State::Available);
248
255
else
249
- result.setState (block, State::Unknown () );
256
+ result.setState (block, State::Unknown);
250
257
}
251
258
252
259
BasicBlockWorklist worklist (value->getFunction ());
@@ -280,14 +287,14 @@ void VisitUnreachableLifetimeEnds::propagateAvailablity(Result &result) {
280
287
void VisitUnreachableLifetimeEnds::visitAvailabilityBoundary (
281
288
Result const &result, llvm::function_ref<void (SILInstruction *)> visit) {
282
289
for (auto *block : region) {
283
- auto available = result.getState (block) == State::Available () ;
290
+ auto available = result.getState (block) == State::Available;
284
291
if (!available) {
285
292
continue ;
286
293
}
287
294
auto hasUnreachableSuccessor = [&]() {
288
295
// Use a lambda to avoid checking if possible.
289
296
return llvm::any_of (block->getSuccessorBlocks (), [&result](auto *block) {
290
- return result.getState (block) == State::Unavailable () ;
297
+ return result.getState (block) == State::Unavailable;
291
298
});
292
299
};
293
300
if (!block->succ_empty () && !hasUnreachableSuccessor ()) {
@@ -315,8 +322,9 @@ void OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
315
322
visitor.visitAvailabilityBoundary (result, visit);
316
323
}
317
324
318
- static bool endLifetimeAtUnreachableBlocks (SILValue value,
319
- const SSAPrunedLiveness &liveness) {
325
+ static bool
326
+ endLifetimeAtAvailabilityBoundary (SILValue value,
327
+ const SSAPrunedLiveness &liveness) {
320
328
bool changed = false ;
321
329
OSSALifetimeCompletion::visitUnreachableLifetimeEnds (
322
330
value, liveness, [&](auto *unreachable) {
@@ -330,12 +338,8 @@ static bool endLifetimeAtUnreachableBlocks(SILValue value,
330
338
// / End the lifetime of \p value at unreachable instructions.
331
339
// /
332
340
// / Returns true if any new instructions were created to complete the lifetime.
333
- // /
334
- // / This is only meant to cleanup lifetimes that lead to dead-end blocks. After
335
- // / recursively completing all nested scopes, it then simply ends the lifetime
336
- // / at the Unreachable instruction.
337
- bool OSSALifetimeCompletion::analyzeAndUpdateLifetime (
338
- SILValue value, bool forceBoundaryCompletion) {
341
+ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime (SILValue value,
342
+ Boundary boundary) {
339
343
// Called for inner borrows, inner adjacent reborrows, inner reborrows, and
340
344
// scoped addresses.
341
345
auto handleInnerScope = [this ](SILValue innerBorrowedValue) {
@@ -345,10 +349,13 @@ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(
345
349
liveness.compute (domInfo, handleInnerScope);
346
350
347
351
bool changed = false ;
348
- if (value->isLexical () && !forceBoundaryCompletion) {
349
- changed |= endLifetimeAtUnreachableBlocks (value, liveness.getLiveness ());
350
- } else {
351
- changed |= endLifetimeAtBoundary (value, liveness.getLiveness ());
352
+ switch (boundary) {
353
+ case Boundary::Availability:
354
+ changed |= endLifetimeAtAvailabilityBoundary (value, liveness.getLiveness ());
355
+ break ;
356
+ case Boundary::Liveness:
357
+ changed |= endLifetimeAtLivenessBoundary (value, liveness.getLiveness ());
358
+ break ;
352
359
}
353
360
// TODO: Rebuild outer adjacent phis on demand (SILGen does not currently
354
361
// produce guaranteed phis). See FindEnclosingDefs &
@@ -367,9 +374,15 @@ static FunctionTest OSSALifetimeCompletionTest(
367
374
" ossa-lifetime-completion" ,
368
375
[](auto &function, auto &arguments, auto &test) {
369
376
SILValue value = arguments.takeValue ();
377
+ std::optional<OSSALifetimeCompletion::Boundary> kind = std::nullopt;
378
+ if (arguments.hasUntaken ()) {
379
+ kind = arguments.takeBool ()
380
+ ? OSSALifetimeCompletion::Boundary::Liveness
381
+ : OSSALifetimeCompletion::Boundary::Availability;
382
+ }
370
383
llvm::outs () << " OSSA lifetime completion: " << value;
371
384
OSSALifetimeCompletion completion (&function, /* domInfo*/ nullptr );
372
- completion.completeOSSALifetime (value);
385
+ completion.completeOSSALifetime (value, kind );
373
386
function.print (llvm::outs ());
374
387
});
375
388
} // end namespace swift::test
0 commit comments