@@ -122,14 +122,21 @@ class VisitUnreachableLifetimeEnds {
122
122
// / The value whose dead-end block lifetime ends are to be visited.
123
123
SILValue value;
124
124
125
+ // / Whether to allow leaks.
126
+ // /
127
+ // / Here, that entails allowing walks to reach non-unreachable terminators and
128
+ // / not creating lifetime ends before them.
129
+ OSSALifetimeCompletion::AllowLeaks_t allowLeaks;
130
+
125
131
// / The non-lifetime-ending boundary of `value`.
126
132
BasicBlockSet starts;
127
133
// / The region between (inclusive) the `starts` and the unreachable blocks.
128
134
BasicBlockSetVector region;
129
135
130
136
public:
131
- VisitUnreachableLifetimeEnds (SILValue value)
132
- : value(value), starts(value->getFunction ()),
137
+ VisitUnreachableLifetimeEnds (SILValue value,
138
+ OSSALifetimeCompletion::AllowLeaks_t allowLeaks)
139
+ : value(value), allowLeaks(allowLeaks), starts(value->getFunction ()),
133
140
region(value->getFunction ()) {}
134
141
135
142
// / Region discovery.
@@ -232,9 +239,17 @@ void VisitUnreachableLifetimeEnds::computeRegion(
232
239
// (3) Forward walk to find the region in which `value` might be available.
233
240
while (auto *block = regionWorklist.pop ()) {
234
241
if (block->succ_empty ()) {
235
- // This assert will fail unless there is already a lifetime-ending
236
- // instruction on each path to normal function exits.
237
- assert (isa<UnreachableInst>(block->getTerminator ()));
242
+ // This is a function-exiting block.
243
+ //
244
+ // In valid-but-lifetime-incomplete OSSA there must be a lifetime-ending
245
+ // instruction on each path from the def that exits the function normally.
246
+ // Thus finding a value available at the end of such a block means that
247
+ // the block does _not_ must not exits the function normally; in other
248
+ // words its terminator must be an UnreachableInst.
249
+ //
250
+ // In invalid OSSA, indicated by the `allowLeaks` flag, no such guarantee
251
+ // exists.
252
+ assert (isa<UnreachableInst>(block->getTerminator ()) || allowLeaks);
238
253
}
239
254
for (auto *successor : block->getSuccessorBlocks ()) {
240
255
regionWorklist.pushIfNotVisited (successor);
@@ -291,27 +306,33 @@ void VisitUnreachableLifetimeEnds::visitAvailabilityBoundary(
291
306
if (!available) {
292
307
continue ;
293
308
}
294
- auto hasUnreachableSuccessor = [&]() {
309
+ auto hasUnavailableSuccessor = [&]() {
295
310
// Use a lambda to avoid checking if possible.
296
311
return llvm::any_of (block->getSuccessorBlocks (), [&result](auto *block) {
297
312
return result.getState (block) == State::Unavailable;
298
313
});
299
314
};
300
- if (!block->succ_empty () && !hasUnreachableSuccessor ()) {
315
+ if (!block->succ_empty () && !hasUnavailableSuccessor ()) {
316
+ continue ;
317
+ }
318
+ if (allowLeaks && block->succ_empty () &&
319
+ !isa<UnreachableInst>(block->getTerminator ())) {
320
+ // Availability extends to the end of a function-exiting-normally block.
321
+ // If leaks are allowed, don't visit.
301
322
continue ;
302
323
}
303
- assert (hasUnreachableSuccessor () ||
324
+ assert (hasUnavailableSuccessor () ||
304
325
isa<UnreachableInst>(block->getTerminator ()));
305
326
visit (block->getTerminator ());
306
327
}
307
328
}
308
329
} // end anonymous namespace
309
330
310
331
void OSSALifetimeCompletion::visitUnreachableLifetimeEnds (
311
- SILValue value, const SSAPrunedLiveness &liveness,
332
+ SILValue value, AllowLeaks_t allowLeaks, const SSAPrunedLiveness &liveness,
312
333
llvm::function_ref<void (SILInstruction *)> visit) {
313
334
314
- VisitUnreachableLifetimeEnds visitor (value);
335
+ VisitUnreachableLifetimeEnds visitor (value, allowLeaks );
315
336
316
337
visitor.computeRegion (liveness);
317
338
@@ -322,12 +343,12 @@ void OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
322
343
visitor.visitAvailabilityBoundary (result, visit);
323
344
}
324
345
325
- static bool
326
- endLifetimeAtAvailabilityBoundary ( SILValue value,
327
- const SSAPrunedLiveness &liveness) {
346
+ static bool endLifetimeAtAvailabilityBoundary (
347
+ SILValue value, OSSALifetimeCompletion::AllowLeaks_t allowLeaks ,
348
+ const SSAPrunedLiveness &liveness) {
328
349
bool changed = false ;
329
350
OSSALifetimeCompletion::visitUnreachableLifetimeEnds (
330
- value, liveness, [&](auto *unreachable) {
351
+ value, allowLeaks, liveness, [&](auto *unreachable) {
331
352
SILBuilderWithScope builder (unreachable);
332
353
endOSSALifetime (value, builder);
333
354
changed = true ;
@@ -342,20 +363,25 @@ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(SILValue value,
342
363
Boundary boundary) {
343
364
// Called for inner borrows, inner adjacent reborrows, inner reborrows, and
344
365
// scoped addresses.
345
- auto handleInnerScope = [this ](SILValue innerBorrowedValue) {
346
- completeOSSALifetime (innerBorrowedValue);
366
+ auto handleInnerScope = [this , boundary ](SILValue innerBorrowedValue) {
367
+ completeOSSALifetime (innerBorrowedValue, boundary );
347
368
};
348
369
InteriorLiveness liveness (value);
349
370
liveness.compute (domInfo, handleInnerScope);
350
371
351
372
bool changed = false ;
352
373
switch (boundary) {
353
- case Boundary::Availability:
354
- changed |= endLifetimeAtAvailabilityBoundary (value, liveness.getLiveness ());
355
- break ;
356
374
case Boundary::Liveness:
357
375
changed |= endLifetimeAtLivenessBoundary (value, liveness.getLiveness ());
358
376
break ;
377
+ case Boundary::Availability:
378
+ changed |= endLifetimeAtAvailabilityBoundary (value, DoNotAllowLeaks,
379
+ liveness.getLiveness ());
380
+ break ;
381
+ case Boundary::AvailabilityWithLeaks:
382
+ changed |= endLifetimeAtAvailabilityBoundary (value, AllowLeaks,
383
+ liveness.getLiveness ());
384
+ break ;
359
385
}
360
386
// TODO: Rebuild outer adjacent phis on demand (SILGen does not currently
361
387
// produce guaranteed phis). See FindEnclosingDefs &
@@ -371,16 +397,19 @@ namespace swift::test {
371
397
// Dumps:
372
398
// - function
373
399
static FunctionTest OSSALifetimeCompletionTest (
374
- " ossa-lifetime-completion " ,
400
+ " ossa_lifetime_completion " ,
375
401
[](auto &function, auto &arguments, auto &test) {
376
402
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
- }
383
- llvm::outs () << " OSSA lifetime completion: " << value;
403
+ OSSALifetimeCompletion::Boundary kind =
404
+ llvm::StringSwitch<OSSALifetimeCompletion::Boundary>(
405
+ arguments.takeString ())
406
+ .Case (" liveness" , OSSALifetimeCompletion::Boundary::Liveness)
407
+ .Case (" availability" ,
408
+ OSSALifetimeCompletion::Boundary::Availability)
409
+ .Case (" availability_with_leaks" ,
410
+ OSSALifetimeCompletion::Boundary::AvailabilityWithLeaks);
411
+ llvm::outs () << " OSSA lifetime completion on " << kind
412
+ << " boundary: " << value;
384
413
OSSALifetimeCompletion completion (&function, /* domInfo*/ nullptr );
385
414
completion.completeOSSALifetime (value, kind);
386
415
function.print (llvm::outs ());
@@ -462,8 +491,9 @@ bool UnreachableLifetimeCompletion::completeLifetimes() {
462
491
463
492
bool changed = false ;
464
493
for (auto value : incompleteValues) {
465
- if (completion.completeOSSALifetime (value)
466
- == LifetimeCompletion::WasCompleted) {
494
+ if (completion.completeOSSALifetime (
495
+ value, OSSALifetimeCompletion::Boundary::Availability) ==
496
+ LifetimeCompletion::WasCompleted) {
467
497
changed = true ;
468
498
}
469
499
}
0 commit comments