@@ -70,8 +70,8 @@ static SILInstruction *endOSSALifetime(SILValue value, SILBuilder &builder) {
7070 return builder.createEndBorrow (loc, value);
7171}
7272
73- static bool endLifetimeAtBoundary (SILValue value,
74- const SSAPrunedLiveness &liveness) {
73+ static bool endLifetimeAtLivenessBoundary (SILValue value,
74+ const SSAPrunedLiveness &liveness) {
7575 PrunedLivenessBoundary boundary;
7676 liveness.computeBoundary (boundary);
7777
@@ -149,7 +149,7 @@ class VisitUnreachableLifetimeEnds {
149149 llvm::function_ref<void (SILInstruction *)> visit);
150150
151151 struct State {
152- enum class Value : uint8_t {
152+ enum Value : uint8_t {
153153 Unavailable = 0 ,
154154 Available,
155155 Unknown,
@@ -161,10 +161,6 @@ class VisitUnreachableLifetimeEnds {
161161 State meet (State const other) const {
162162 return *this < other ? *this : other;
163163 }
164-
165- static State Unavailable () { return {Value::Unavailable}; }
166- static State Available () { return {Value::Available}; }
167- static State Unknown () { return {Value::Unknown}; }
168164 };
169165
170166 struct Result {
@@ -197,36 +193,47 @@ class VisitUnreachableLifetimeEnds {
197193
198194void VisitUnreachableLifetimeEnds::computeRegion (
199195 const SSAPrunedLiveness &liveness) {
200- // Find the non-lifetime-ending boundary of `value` .
196+ // (1) Compute the complete liveness boundary .
201197 PrunedLivenessBoundary boundary;
202198 liveness.computeBoundary (boundary);
203199
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+
204219 for (SILInstruction *lastUser : boundary.lastUsers ) {
205220 if (liveness.isInterestingUser (lastUser)
206221 != PrunedLiveness::LifetimeEndingUse) {
207- region.insert (lastUser->getParent ());
208- starts.insert (lastUser->getParent ());
222+ collect (lastUser->getParent ());
209223 }
210224 }
211225 for (SILBasicBlock *edge : boundary.boundaryEdges ) {
212- region.insert (edge);
213- starts.insert (edge);
226+ collect (edge);
214227 }
215228 for (SILNode *deadDef : boundary.deadDefs ) {
216- region.insert (deadDef->getParentBlock ());
217- starts.insert (deadDef->getParentBlock ());
229+ collect (deadDef->getParentBlock ());
218230 }
219231
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.
226233 while (auto *block = regionWorklist.pop ()) {
227234 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.
230237 assert (isa<UnreachableInst>(block->getTerminator ()));
231238 }
232239 for (auto *successor : block->getSuccessorBlocks ()) {
@@ -244,9 +251,9 @@ void VisitUnreachableLifetimeEnds::propagateAvailablity(Result &result) {
244251 // - start blocks are ::Available
245252 for (auto *block : region) {
246253 if (starts.contains (block))
247- result.setState (block, State::Available () );
254+ result.setState (block, State::Available);
248255 else
249- result.setState (block, State::Unknown () );
256+ result.setState (block, State::Unknown);
250257 }
251258
252259 BasicBlockWorklist worklist (value->getFunction ());
@@ -280,14 +287,14 @@ void VisitUnreachableLifetimeEnds::propagateAvailablity(Result &result) {
280287void VisitUnreachableLifetimeEnds::visitAvailabilityBoundary (
281288 Result const &result, llvm::function_ref<void (SILInstruction *)> visit) {
282289 for (auto *block : region) {
283- auto available = result.getState (block) == State::Available () ;
290+ auto available = result.getState (block) == State::Available;
284291 if (!available) {
285292 continue ;
286293 }
287294 auto hasUnreachableSuccessor = [&]() {
288295 // Use a lambda to avoid checking if possible.
289296 return llvm::any_of (block->getSuccessorBlocks (), [&result](auto *block) {
290- return result.getState (block) == State::Unavailable () ;
297+ return result.getState (block) == State::Unavailable;
291298 });
292299 };
293300 if (!block->succ_empty () && !hasUnreachableSuccessor ()) {
@@ -315,8 +322,9 @@ void OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
315322 visitor.visitAvailabilityBoundary (result, visit);
316323}
317324
318- static bool endLifetimeAtUnreachableBlocks (SILValue value,
319- const SSAPrunedLiveness &liveness) {
325+ static bool
326+ endLifetimeAtAvailabilityBoundary (SILValue value,
327+ const SSAPrunedLiveness &liveness) {
320328 bool changed = false ;
321329 OSSALifetimeCompletion::visitUnreachableLifetimeEnds (
322330 value, liveness, [&](auto *unreachable) {
@@ -330,12 +338,8 @@ static bool endLifetimeAtUnreachableBlocks(SILValue value,
330338// / End the lifetime of \p value at unreachable instructions.
331339// /
332340// / 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) {
339343 // Called for inner borrows, inner adjacent reborrows, inner reborrows, and
340344 // scoped addresses.
341345 auto handleInnerScope = [this ](SILValue innerBorrowedValue) {
@@ -345,10 +349,13 @@ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(
345349 liveness.compute (domInfo, handleInnerScope);
346350
347351 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 ;
352359 }
353360 // TODO: Rebuild outer adjacent phis on demand (SILGen does not currently
354361 // produce guaranteed phis). See FindEnclosingDefs &
@@ -367,9 +374,15 @@ static FunctionTest OSSALifetimeCompletionTest(
367374 " ossa-lifetime-completion" ,
368375 [](auto &function, auto &arguments, auto &test) {
369376 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+ }
370383 llvm::outs () << " OSSA lifetime completion: " << value;
371384 OSSALifetimeCompletion completion (&function, /* domInfo*/ nullptr );
372- completion.completeOSSALifetime (value);
385+ completion.completeOSSALifetime (value, kind );
373386 function.print (llvm::outs ());
374387 });
375388} // end namespace swift::test
0 commit comments