Skip to content

Commit 8cf65c6

Browse files
committed
[CanonicalizeOSSALifetime] Handle live phis.
It is possible for phis to be marked live. With guaranteed phis, they will be the last uses and be non-consuming. In this case, the merge block will have multiple predecessors whose terminators are on the boundary. When inserting destroys, track whether a merge point has been visited previously. To facilitate this, restructure the boundary extension and destroy insertion code. Previously, the extender was building up a list of places at which to insert destroys. In particular it was using the "boundary edge" collection for all blocks at the beginning of which a destroy should be created. In particular, it would add merge blocks. Such blocks are not boundary blocks. Here, the extender produces a PrunedLivenessBoundary which doesn't violate that invariant. This required some changes to the destroy insertion code to find where to insert destroys. It is now similar to PrunedLivenessBoundary::visitInsertionPoints and could be used as a template for a PrunedLivenessBoundary::visitBoundaryPoints through which ::visitInsertionPoints might be factored.
1 parent 79c2f1e commit 8cf65c6

File tree

2 files changed

+142
-40
lines changed

2 files changed

+142
-40
lines changed

include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ class CanonicalizeOSSALifetime final {
349349

350350
void findExtendedBoundary(PrunedLivenessBoundary &boundary);
351351

352-
void insertDestroysOnBoundary(PrunedLivenessBoundary &boundary);
352+
void insertDestroysOnBoundary(PrunedLivenessBoundary const &boundary);
353353

354354
void rewriteCopies();
355355
};

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 141 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ void CanonicalizeOSSALifetime::extendLivenessThroughOverlappingAccess() {
414414
// liveness computed in Step 1.
415415
//===----------------------------------------------------------------------===//
416416

417-
namespace swift {
417+
namespace {
418418
/// Extends the boundary from PrunedLiveness down to preexisting destroys of the
419419
/// def which aren't separated from the original boundary by "interesting"
420420
/// instructions.
@@ -423,39 +423,38 @@ namespace swift {
423423
/// iterating to a fixed point by canonicalizing the lifetimes of several
424424
/// values with overlapping live ranges and failing to find a fixed point
425425
/// because their destroys are repeatedly hoisted over one another.
426-
class CanonicalizeOSSALifetimeBoundaryExtender {
426+
class ExtendBoundaryToDestroys final {
427427
SSAPrunedLiveness &liveness;
428-
PrunedLivenessBoundary &originalBoundary;
428+
PrunedLivenessBoundary const &originalBoundary;
429429
SILValue currentDef;
430+
BasicBlockSet seenMergePoints;
430431

431432
public:
432-
CanonicalizeOSSALifetimeBoundaryExtender(
433-
SSAPrunedLiveness &liveness, PrunedLivenessBoundary &originalBoundary,
434-
SILValue currentDef)
433+
ExtendBoundaryToDestroys(SSAPrunedLiveness &liveness,
434+
PrunedLivenessBoundary const &originalBoundary,
435+
SILValue currentDef)
435436
: liveness(liveness), originalBoundary(originalBoundary),
436-
currentDef(currentDef){};
437+
currentDef(currentDef), seenMergePoints(currentDef->getFunction()){};
438+
ExtendBoundaryToDestroys(ExtendBoundaryToDestroys const &) = delete;
439+
ExtendBoundaryToDestroys &
440+
operator=(ExtendBoundaryToDestroys const &) = delete;
437441

438442
/// Compute the extended boundary by walking out from the original boundary
439443
/// (from PrunedLiveness::computeBoundary) down to any destroys that appear
440444
/// later but which aren't separated from the original boundary by
441445
/// "interesting" users.
442446
void extend(PrunedLivenessBoundary &boundary) {
443447
for (auto *def : originalBoundary.deadDefs) {
444-
if (auto *argument = dyn_cast<SILArgument>(def)) {
445-
extendBoundaryFromCFGEdge(argument->getParent(), boundary);
446-
} else {
447-
extendBoundaryFromInstruction(cast<SILInstruction>(def), boundary);
448-
}
448+
extendBoundaryFromDef(def, boundary);
449449
}
450450
for (auto *destination : originalBoundary.boundaryEdges) {
451-
extendBoundaryFromCFGEdge(destination, boundary);
451+
extendBoundaryFromBoundaryEdge(destination, boundary);
452452
}
453453
for (auto *user : originalBoundary.lastUsers) {
454-
extendBoundaryFromInstruction(user, boundary);
454+
extendBoundaryFromUser(user, boundary);
455455
}
456456
}
457457

458-
private:
459458
/// Look past ignoreable instructions to find the _last_ destroy after the
460459
/// specified instruction that destroys \p def.
461460
static DestroyValueInst *findDestroyAfter(SILInstruction *previous,
@@ -481,21 +480,48 @@ class CanonicalizeOSSALifetimeBoundaryExtender {
481480
return findDestroyAfter(start, def);
482481
}
483482

484-
/// Look past ignoreable instructions to find the _last_ destroy "on" the edge
485-
/// that ends at \p destination (i.e. looking from the beginning of \p
486-
/// destination) that destroys \p def.
487-
static DestroyValueInst *findDestroyOnCFGEdge(SILBasicBlock *destination,
488-
SILValue def) {
483+
/// Look past ignoreable instructions to find the _first_ destroy in \p
484+
/// destination that destroys \p def and isn't separated from the beginning
485+
/// by "interesting" instructions.
486+
static DestroyValueInst *findDestroyFromBlockBegin(SILBasicBlock *destination,
487+
SILValue def) {
489488
return findDestroyAtOrAfter(&*destination->begin(), def);
490489
}
491490

491+
private:
492+
/// Compute the points on the extended boundary found by walking forward from
493+
/// the dead def (starting either with the top of the block in the case of a
494+
/// dead arg or the next instruction in the case of an instruction) down to
495+
/// any destroys that appear later but which aren't separated from the
496+
/// original boundary by "interesting" users.
497+
///
498+
/// If a destroy is found, it becomes a last user. Otherwise, the boundary
499+
/// stays in place and \p def remains a dead def.
500+
void extendBoundaryFromDef(SILNode *def, PrunedLivenessBoundary &boundary) {
501+
if (auto *arg = dyn_cast<SILArgument>(def)) {
502+
if (auto *dvi = findDestroyFromBlockBegin(arg->getParent(), currentDef)) {
503+
boundary.lastUsers.push_back(dvi);
504+
return;
505+
}
506+
} else {
507+
if (auto *dvi = findDestroyAfter(cast<SILInstruction>(def), currentDef)) {
508+
boundary.lastUsers.push_back(dvi);
509+
return;
510+
}
511+
}
512+
boundary.deadDefs.push_back(def);
513+
}
514+
492515
/// Compute the points on the extended boundary found by walking down from the
493516
/// boundary edge in the original boundary (uniquely determined by the
494517
/// specified destination edge) down to any destroys that appear later but
495518
/// which aren't separated from the original boundary by "interesting" users.
496-
void extendBoundaryFromCFGEdge(SILBasicBlock *destination,
497-
PrunedLivenessBoundary &boundary) {
498-
if (auto *dvi = findDestroyOnCFGEdge(destination, currentDef)) {
519+
///
520+
/// If a destroy is found, it becomes a last user. Otherwise, the boundary
521+
/// stays in place and \p destination remains a boundary edge.
522+
void extendBoundaryFromBoundaryEdge(SILBasicBlock *destination,
523+
PrunedLivenessBoundary &boundary) {
524+
if (auto *dvi = findDestroyFromBlockBegin(destination, currentDef)) {
499525
boundary.lastUsers.push_back(dvi);
500526
} else {
501527
boundary.boundaryEdges.push_back(destination);
@@ -506,8 +532,16 @@ class CanonicalizeOSSALifetimeBoundaryExtender {
506532
/// specified instruction in the original boundary down to any destroys that
507533
/// appear later but which aren't separated from the original boundary by
508534
/// "interesting" users.
509-
void extendBoundaryFromInstruction(SILInstruction *user,
510-
PrunedLivenessBoundary &boundary) {
535+
///
536+
/// If the user is consuming, the boundary remains in place.
537+
///
538+
/// If the user is a terminator, see extendBoundaryFromTerminator.
539+
///
540+
/// If a destroy is found after the (non-consuming, non-terminator) \p user,
541+
/// it becomes a last user. Otherwise, the boundary stays in place and \p
542+
/// user remains a last user.
543+
void extendBoundaryFromUser(SILInstruction *user,
544+
PrunedLivenessBoundary &boundary) {
511545
if (auto *dvi = dynCastToDestroyOf(user, currentDef)) {
512546
auto *existingDestroy = findDestroyAtOrAfter(dvi, currentDef);
513547
assert(existingDestroy && "couldn't find a destroy at or after one!?");
@@ -523,11 +557,8 @@ class CanonicalizeOSSALifetimeBoundaryExtender {
523557
return;
524558
case PrunedLiveness::IsInterestingUser::NonLifetimeEndingUse:
525559
case PrunedLiveness::IsInterestingUser::NonUser:
526-
if (isa<TermInst>(user)) {
527-
auto *block = user->getParent();
528-
for (auto *successor : block->getSuccessorBlocks()) {
529-
extendBoundaryFromCFGEdge(successor, boundary);
530-
}
560+
if (auto *terminator = dyn_cast<TermInst>(user)) {
561+
extendBoundaryFromTerminator(terminator, boundary);
531562
return;
532563
}
533564
if (auto *existingDestroy = findDestroyAfter(user, currentDef)) {
@@ -537,18 +568,59 @@ class CanonicalizeOSSALifetimeBoundaryExtender {
537568
boundary.lastUsers.push_back(user);
538569
}
539570
}
571+
572+
/// Compute the points on the extended boundary by walking into \p user's
573+
/// parent's successors and looking for destroys.
574+
///
575+
/// If any destroys are found, they become last users and all other successors
576+
/// (which lack destroys) become boundary edges. If no destroys are found,
577+
/// the boundary stays in place and \p user remains a last user.
578+
void extendBoundaryFromTerminator(TermInst *user,
579+
PrunedLivenessBoundary &boundary) {
580+
auto *block = user->getParent();
581+
// Record the successors at the beginning of which we didn't find destroys.
582+
// If we found a destroy at the beginning of any other successor, then all
583+
// the other edges become boundary edges.
584+
SmallVector<SILBasicBlock *, 4> successorsWithoutDestroys;
585+
bool foundDestroy = false;
586+
for (auto *successor : block->getSuccessorBlocks()) {
587+
// If multiple terminators were live and had the same successor, only
588+
// record the boundary corresponding to that destination block once.
589+
if (!seenMergePoints.insert(successor)) {
590+
// Thanks to the lack of critical edges, having seen this successor
591+
// before means it has multiple predecessors, so this must be \p block's
592+
// unique successor.
593+
assert(block->getSingleSuccessorBlock() == successor);
594+
continue;
595+
}
596+
if (auto *dvi = findDestroyFromBlockBegin(successor, currentDef)) {
597+
boundary.lastUsers.push_back(dvi);
598+
foundDestroy = true;
599+
} else {
600+
successorsWithoutDestroys.push_back(successor);
601+
}
602+
}
603+
if (foundDestroy) {
604+
// If we found a destroy in any successor, then every block at the
605+
// beginning of which we didn't find a destroy becomes a boundary edge.
606+
for (auto *successor : successorsWithoutDestroys) {
607+
boundary.boundaryEdges.push_back(successor);
608+
}
609+
} else {
610+
boundary.lastUsers.push_back(user);
611+
}
612+
}
540613
};
541-
} // namespace swift
614+
} // anonymous namespace
542615

543616
void CanonicalizeOSSALifetime::findExtendedBoundary(
544617
PrunedLivenessBoundary &boundary) {
545618
PrunedLivenessBoundary originalBoundary;
546619
liveness.computeBoundary(originalBoundary, consumingBlocks.getArrayRef());
547620

548-
CanonicalizeOSSALifetimeBoundaryExtender extender(liveness, originalBoundary,
549-
getCurrentDef());
621+
ExtendBoundaryToDestroys extender(liveness, originalBoundary,
622+
getCurrentDef());
550623
extender.extend(boundary);
551-
assert(boundary.deadDefs.empty() && "dead defs in extended boundary!?");
552624
}
553625

554626
//===----------------------------------------------------------------------===//
@@ -579,7 +651,8 @@ static void insertDestroyBeforeInstruction(SILInstruction *nextInstruction,
579651
/// - The postdominating consumes cannot be within nested loops.
580652
/// - Any blocks in nested loops are now marked LiveOut.
581653
void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
582-
PrunedLivenessBoundary &boundary) {
654+
PrunedLivenessBoundary const &boundary) {
655+
BasicBlockSet seenMergePoints(getCurrentDef()->getFunction());
583656
for (auto *instruction : boundary.lastUsers) {
584657
if (auto *dvi = dynCastToDestroyOf(instruction, getCurrentDef())) {
585658
consumes.recordFinalConsume(dvi);
@@ -591,11 +664,23 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
591664
continue;
592665
case PrunedLiveness::IsInterestingUser::NonLifetimeEndingUse:
593666
case PrunedLiveness::IsInterestingUser::NonUser:
667+
if (isa<TermInst>(instruction)) {
668+
auto *block = instruction->getParent();
669+
for (auto *successor : block->getSuccessorBlocks()) {
670+
if (!seenMergePoints.insert(successor)) {
671+
assert(block->getSingleSuccessorBlock() == successor);
672+
continue;
673+
}
674+
auto *insertionPoint = &*successor->begin();
675+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
676+
consumes, getCallbacks());
677+
LLVM_DEBUG(llvm::dbgs()
678+
<< " Destroy after terminator " << instruction
679+
<< " at beginning of " << successor << "\n");
680+
}
681+
continue;
682+
}
594683
auto *insertionPoint = instruction->getNextInstruction();
595-
// If there were no next instruction, it would be a TermInst and we would
596-
// have added the successors as boundary edge destinations (or found
597-
// destroys in their bodies).
598-
assert(insertionPoint);
599684
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
600685
getCallbacks());
601686
LLVM_DEBUG(llvm::dbgs()
@@ -609,6 +694,23 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
609694
getCallbacks());
610695
LLVM_DEBUG(llvm::dbgs() << " Destroy on edge " << edgeDestination << "\n");
611696
}
697+
for (auto *def : boundary.deadDefs) {
698+
if (auto *arg = dyn_cast<SILArgument>(def)) {
699+
auto *insertionPoint = &*arg->getParent()->begin();
700+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
701+
getCallbacks());
702+
LLVM_DEBUG(llvm::dbgs()
703+
<< " Destroy after dead def arg " << arg << "\n");
704+
} else {
705+
auto *instruction = cast<SILInstruction>(def);
706+
auto *insertionPoint = instruction->getNextInstruction();
707+
assert(insertionPoint && "def instruction was a terminator?!");
708+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
709+
getCallbacks());
710+
LLVM_DEBUG(llvm::dbgs()
711+
<< " Destroy after dead def inst " << instruction << "\n");
712+
}
713+
}
612714
}
613715

614716
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)