@@ -101,19 +101,22 @@ static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
101101 Context.Diags .diagnose (loc, diag, std::forward<U>(args)...);
102102}
103103
104- static DestroyValueInst *dynCastToDestroyOf (SILInstruction *instruction,
105- SILValue def) {
104+ // / Is \p instruction a destroy_value whose operand is \p def, or its
105+ // / transitive copy.
106+ static bool isDestroyOfCopyOf (SILInstruction *instruction, SILValue def) {
106107 auto *destroy = dyn_cast<DestroyValueInst>(instruction);
107108 if (!destroy)
108- return nullptr ;
109- auto originalDestroyedDef = destroy->getOperand ();
110- if (originalDestroyedDef == def)
111- return destroy;
112- auto underlyingDestroyedDef =
113- CanonicalizeOSSALifetime::getCanonicalCopiedDef (originalDestroyedDef);
114- if (underlyingDestroyedDef != def)
115- return nullptr ;
116- return destroy;
109+ return false ;
110+ auto destroyed = destroy->getOperand ();
111+ while (true ) {
112+ if (destroyed == def)
113+ return true ;
114+ auto *copy = dyn_cast<CopyValueInst>(destroyed);
115+ if (!copy)
116+ break ;
117+ destroyed = copy->getOperand ();
118+ }
119+ return false ;
117120}
118121
119122// ===----------------------------------------------------------------------===//
@@ -172,11 +175,18 @@ bool CanonicalizeOSSALifetime::computeCanonicalLiveness() {
172175 liveness->updateForUse (user, /* lifetimeEnding*/ true );
173176 break ;
174177 case OperandOwnership::DestroyingConsume:
175- if (isa<DestroyValueInst> (user)) {
178+ if (isDestroyOfCopyOf (user, getCurrentDef () )) {
176179 destroys.insert (user);
177180 } else {
178- // destroy_value does not force pruned liveness (but store etc. does).
179- liveness->updateForUse (user, /* lifetimeEnding*/ true );
181+ // destroy_value of a transitive copy of the currentDef does not
182+ // force pruned liveness (but store etc. does).
183+
184+ // Even though this instruction is a DestroyingConsume of its operand,
185+ // if it's a destroy_value whose operand is not a transitive copy of
186+ // currentDef, then it's just ending an implicit borrow of currentDef,
187+ // not consuming it.
188+ auto lifetimeEnding = !isa<DestroyValueInst>(user);
189+ liveness->updateForUse (user, lifetimeEnding);
180190 }
181191 recordConsumingUse (use);
182192 break ;
@@ -511,7 +521,7 @@ void CanonicalizeOSSALifetime::extendUnconsumedLiveness(
511521 // destroys.
512522 BasicBlockWorklist worklist (currentDef->getFunction ());
513523 for (auto *instruction : boundary.lastUsers ) {
514- if (dynCastToDestroyOf (instruction, getCurrentDef () ))
524+ if (destroys. contains (instruction))
515525 continue ;
516526 if (liveness->isInterestingUser (instruction)
517527 != PrunedLiveness::IsInterestingUser::LifetimeEndingUse)
@@ -576,17 +586,20 @@ namespace {
576586// / values with overlapping live ranges and failing to find a fixed point
577587// / because their destroys are repeatedly hoisted over one another.
578588class ExtendBoundaryToDestroys final {
589+ using InstructionPredicate = llvm::function_ref<bool (SILInstruction *)>;
579590 SSAPrunedLiveness &liveness;
580591 PrunedLivenessBoundary const &originalBoundary;
581592 SILValue currentDef;
582593 BasicBlockSet seenMergePoints;
594+ InstructionPredicate isDestroy;
583595
584596public:
585597 ExtendBoundaryToDestroys (SSAPrunedLiveness &liveness,
586598 PrunedLivenessBoundary const &originalBoundary,
587- SILValue currentDef)
599+ SILValue currentDef, InstructionPredicate isDestroy )
588600 : liveness(liveness), originalBoundary(originalBoundary),
589- currentDef (currentDef), seenMergePoints(currentDef->getFunction ()){};
601+ currentDef (currentDef), seenMergePoints(currentDef->getFunction ()),
602+ isDestroy(isDestroy){};
590603 ExtendBoundaryToDestroys (ExtendBoundaryToDestroys const &) = delete;
591604 ExtendBoundaryToDestroys &
592605 operator =(ExtendBoundaryToDestroys const &) = delete;
@@ -610,34 +623,37 @@ class ExtendBoundaryToDestroys final {
610623 // / Look past ignoreable instructions to find the _last_ destroy after the
611624 // / specified instruction that destroys \p def.
612625 static DestroyValueInst *findDestroyAfter (SILInstruction *previous,
613- SILValue def) {
626+ SILValue def,
627+ InstructionPredicate isDestroy) {
614628 DestroyValueInst *retval = nullptr ;
615629 for (auto *instruction = previous->getNextInstruction (); instruction;
616630 instruction = instruction->getNextInstruction ()) {
617631 if (!CanonicalizeOSSALifetime::ignoredByDestroyHoisting (
618632 instruction->getKind ()))
619633 break ;
620- if (auto destroy = dynCastToDestroyOf (instruction, def ))
621- retval = destroy ;
634+ if (isDestroy (instruction))
635+ retval = cast<DestroyValueInst>(instruction) ;
622636 }
623637 return retval;
624638 }
625639
626640 // / Look past ignoreable instructions to find the _last_ destroy at or after
627641 // / the specified instruction that destroys \p def.
628- static DestroyValueInst *findDestroyAtOrAfter (SILInstruction *start,
629- SILValue def) {
630- if (auto *dvi = dynCastToDestroyOf (start, def))
631- return dvi;
632- return findDestroyAfter (start, def);
642+ static DestroyValueInst *
643+ findDestroyAtOrAfter (SILInstruction *start, SILValue def,
644+ InstructionPredicate isDestroy) {
645+ if (isDestroy (start))
646+ return cast<DestroyValueInst>(start);
647+ return findDestroyAfter (start, def, isDestroy);
633648 }
634649
635650 // / Look past ignoreable instructions to find the _first_ destroy in \p
636651 // / destination that destroys \p def and isn't separated from the beginning
637652 // / by "interesting" instructions.
638- static DestroyValueInst *findDestroyFromBlockBegin (SILBasicBlock *destination,
639- SILValue def) {
640- return findDestroyAtOrAfter (&*destination->begin (), def);
653+ static DestroyValueInst *
654+ findDestroyFromBlockBegin (SILBasicBlock *destination, SILValue def,
655+ InstructionPredicate isDestroy) {
656+ return findDestroyAtOrAfter (&*destination->begin (), def, isDestroy);
641657 }
642658
643659private:
@@ -651,12 +667,14 @@ class ExtendBoundaryToDestroys final {
651667 // / stays in place and \p def remains a dead def.
652668 void extendBoundaryFromDef (SILNode *def, PrunedLivenessBoundary &boundary) {
653669 if (auto *arg = dyn_cast<SILArgument>(def)) {
654- if (auto *dvi = findDestroyFromBlockBegin (arg->getParent (), currentDef)) {
670+ if (auto *dvi = findDestroyFromBlockBegin (arg->getParent (), currentDef,
671+ isDestroy)) {
655672 boundary.lastUsers .push_back (dvi);
656673 return ;
657674 }
658675 } else {
659- if (auto *dvi = findDestroyAfter (cast<SILInstruction>(def), currentDef)) {
676+ if (auto *dvi = findDestroyAfter (cast<SILInstruction>(def), currentDef,
677+ isDestroy)) {
660678 boundary.lastUsers .push_back (dvi);
661679 return ;
662680 }
@@ -673,7 +691,8 @@ class ExtendBoundaryToDestroys final {
673691 // / stays in place and \p destination remains a boundary edge.
674692 void extendBoundaryFromBoundaryEdge (SILBasicBlock *destination,
675693 PrunedLivenessBoundary &boundary) {
676- if (auto *dvi = findDestroyFromBlockBegin (destination, currentDef)) {
694+ if (auto *dvi =
695+ findDestroyFromBlockBegin (destination, currentDef, isDestroy)) {
677696 boundary.lastUsers .push_back (dvi);
678697 } else {
679698 boundary.boundaryEdges .push_back (destination);
@@ -694,8 +713,9 @@ class ExtendBoundaryToDestroys final {
694713 // / user remains a last user.
695714 void extendBoundaryFromUser (SILInstruction *user,
696715 PrunedLivenessBoundary &boundary) {
697- if (auto *dvi = dynCastToDestroyOf (user, currentDef)) {
698- auto *existingDestroy = findDestroyAtOrAfter (dvi, currentDef);
716+ if (isDestroy (user)) {
717+ auto *dvi = cast<DestroyValueInst>(user);
718+ auto *existingDestroy = findDestroyAtOrAfter (dvi, currentDef, isDestroy);
699719 assert (existingDestroy && " couldn't find a destroy at or after one!?" );
700720 boundary.lastUsers .push_back (existingDestroy);
701721 return ;
@@ -713,7 +733,8 @@ class ExtendBoundaryToDestroys final {
713733 extendBoundaryFromTerminator (terminator, boundary);
714734 return ;
715735 }
716- if (auto *existingDestroy = findDestroyAfter (user, currentDef)) {
736+ if (auto *existingDestroy =
737+ findDestroyAfter (user, currentDef, isDestroy)) {
717738 boundary.lastUsers .push_back (existingDestroy);
718739 return ;
719740 }
@@ -745,7 +766,8 @@ class ExtendBoundaryToDestroys final {
745766 assert (block->getSingleSuccessorBlock () == successor);
746767 continue ;
747768 }
748- if (auto *dvi = findDestroyFromBlockBegin (successor, currentDef)) {
769+ if (auto *dvi =
770+ findDestroyFromBlockBegin (successor, currentDef, isDestroy)) {
749771 boundary.lastUsers .push_back (dvi);
750772 foundDestroy = true ;
751773 } else {
@@ -770,8 +792,9 @@ void CanonicalizeOSSALifetime::findExtendedBoundary(
770792 PrunedLivenessBoundary &boundary) {
771793 assert (boundary.lastUsers .size () == 0 && boundary.boundaryEdges .size () == 0 &&
772794 boundary.deadDefs .size () == 0 );
795+ auto isDestroy = [&](auto *inst) { return destroys.contains (inst); };
773796 ExtendBoundaryToDestroys extender (*liveness, originalBoundary,
774- getCurrentDef ());
797+ getCurrentDef (), isDestroy );
775798 extender.extend (boundary);
776799}
777800
@@ -806,8 +829,8 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
806829 PrunedLivenessBoundary const &boundary) {
807830 BasicBlockSet seenMergePoints (getCurrentDef ()->getFunction ());
808831 for (auto *instruction : boundary.lastUsers ) {
809- if (auto *dvi = dynCastToDestroyOf (instruction, getCurrentDef () )) {
810- consumes.recordFinalConsume (dvi );
832+ if (destroys. contains (instruction)) {
833+ consumes.recordFinalConsume (instruction );
811834 continue ;
812835 }
813836 switch (liveness->isInterestingUser (instruction)) {
@@ -905,7 +928,8 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
905928 defUseWorklist.insert (copy);
906929 return true ;
907930 }
908- if (auto *destroy = dynCastToDestroyOf (user, getCurrentDef ())) {
931+ if (destroys.contains (user)) {
932+ auto *destroy = cast<DestroyValueInst>(user);
909933 // If this destroy was marked as a final destroy, ignore it; otherwise,
910934 // delete it.
911935 if (!consumes.claimConsume (destroy)) {
0 commit comments