@@ -122,14 +122,21 @@ class VisitUnreachableLifetimeEnds {
122122 // / The value whose dead-end block lifetime ends are to be visited.
123123 SILValue value;
124124
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+
125131 // / The non-lifetime-ending boundary of `value`.
126132 BasicBlockSet starts;
127133 // / The region between (inclusive) the `starts` and the unreachable blocks.
128134 BasicBlockSetVector region;
129135
130136public:
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 ()),
133140 region(value->getFunction ()) {}
134141
135142 // / Region discovery.
@@ -232,9 +239,17 @@ void VisitUnreachableLifetimeEnds::computeRegion(
232239 // (3) Forward walk to find the region in which `value` might be available.
233240 while (auto *block = regionWorklist.pop ()) {
234241 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);
238253 }
239254 for (auto *successor : block->getSuccessorBlocks ()) {
240255 regionWorklist.pushIfNotVisited (successor);
@@ -291,27 +306,33 @@ void VisitUnreachableLifetimeEnds::visitAvailabilityBoundary(
291306 if (!available) {
292307 continue ;
293308 }
294- auto hasUnreachableSuccessor = [&]() {
309+ auto hasUnavailableSuccessor = [&]() {
295310 // Use a lambda to avoid checking if possible.
296311 return llvm::any_of (block->getSuccessorBlocks (), [&result](auto *block) {
297312 return result.getState (block) == State::Unavailable;
298313 });
299314 };
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.
301322 continue ;
302323 }
303- assert (hasUnreachableSuccessor () ||
324+ assert (hasUnavailableSuccessor () ||
304325 isa<UnreachableInst>(block->getTerminator ()));
305326 visit (block->getTerminator ());
306327 }
307328}
308329} // end anonymous namespace
309330
310331void OSSALifetimeCompletion::visitUnreachableLifetimeEnds (
311- SILValue value, const SSAPrunedLiveness &liveness,
332+ SILValue value, AllowLeaks_t allowLeaks, const SSAPrunedLiveness &liveness,
312333 llvm::function_ref<void (SILInstruction *)> visit) {
313334
314- VisitUnreachableLifetimeEnds visitor (value);
335+ VisitUnreachableLifetimeEnds visitor (value, allowLeaks );
315336
316337 visitor.computeRegion (liveness);
317338
@@ -322,12 +343,12 @@ void OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
322343 visitor.visitAvailabilityBoundary (result, visit);
323344}
324345
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) {
328349 bool changed = false ;
329350 OSSALifetimeCompletion::visitUnreachableLifetimeEnds (
330- value, liveness, [&](auto *unreachable) {
351+ value, allowLeaks, liveness, [&](auto *unreachable) {
331352 SILBuilderWithScope builder (unreachable);
332353 endOSSALifetime (value, builder);
333354 changed = true ;
@@ -342,20 +363,25 @@ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(SILValue value,
342363 Boundary boundary) {
343364 // Called for inner borrows, inner adjacent reborrows, inner reborrows, and
344365 // scoped addresses.
345- auto handleInnerScope = [this ](SILValue innerBorrowedValue) {
346- completeOSSALifetime (innerBorrowedValue);
366+ auto handleInnerScope = [this , boundary ](SILValue innerBorrowedValue) {
367+ completeOSSALifetime (innerBorrowedValue, boundary );
347368 };
348369 InteriorLiveness liveness (value);
349370 liveness.compute (domInfo, handleInnerScope);
350371
351372 bool changed = false ;
352373 switch (boundary) {
353- case Boundary::Availability:
354- changed |= endLifetimeAtAvailabilityBoundary (value, liveness.getLiveness ());
355- break ;
356374 case Boundary::Liveness:
357375 changed |= endLifetimeAtLivenessBoundary (value, liveness.getLiveness ());
358376 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 ;
359385 }
360386 // TODO: Rebuild outer adjacent phis on demand (SILGen does not currently
361387 // produce guaranteed phis). See FindEnclosingDefs &
@@ -371,16 +397,19 @@ namespace swift::test {
371397// Dumps:
372398// - function
373399static FunctionTest OSSALifetimeCompletionTest (
374- " ossa-lifetime-completion " ,
400+ " ossa_lifetime_completion " ,
375401 [](auto &function, auto &arguments, auto &test) {
376402 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;
384413 OSSALifetimeCompletion completion (&function, /* domInfo*/ nullptr );
385414 completion.completeOSSALifetime (value, kind);
386415 function.print (llvm::outs ());
@@ -462,8 +491,9 @@ bool UnreachableLifetimeCompletion::completeLifetimes() {
462491
463492 bool changed = false ;
464493 for (auto value : incompleteValues) {
465- if (completion.completeOSSALifetime (value)
466- == LifetimeCompletion::WasCompleted) {
494+ if (completion.completeOSSALifetime (
495+ value, OSSALifetimeCompletion::Boundary::Availability) ==
496+ LifetimeCompletion::WasCompleted) {
467497 changed = true ;
468498 }
469499 }
0 commit comments