@@ -2777,6 +2777,104 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {
27772777 return ControlVariableAddr;
27782778}
27792779
2780+ // / Move the end_borrow that guards an alloc_box's lifetime to before the
2781+ // / dealloc_box in the CFG diamond that is created for destruction when it is
2782+ // / not statically known whether the value is initialized.
2783+ // /
2784+ // / In the following context
2785+ // /
2786+ // / %box = alloc_box
2787+ // / %mark_uninit = mark_uninitialized %box
2788+ // / %lifetime = begin_borrow [lexical] %mark_uninit
2789+ // / %proj_box = project_box %lifetime
2790+ // /
2791+ // / We are replacing a
2792+ // /
2793+ // / destroy_value %mark_uninit
2794+ // /
2795+ // / with a
2796+ // /
2797+ // / destroy_addr %proj_box
2798+ // /
2799+ // / That's a problem, though, because by SILGen construction the
2800+ // / destroy_value is always preceded by an end_borrow
2801+ // /
2802+ // / end_borrow %lifetime
2803+ // / destroy_value %mark_uninit
2804+ // /
2805+ // / Consequently, it's not sufficient to just replace the destroy_value
2806+ // / %mark_uninit with a destroy_addr %proj_box (or to replace it with a diamond
2807+ // / where one branch has that destroy_addr) because the destroy_addr is a use
2808+ // / of %proj_box which must be within the lexical lifetime of the box.
2809+ // /
2810+ // / On the other side, we are hemmed in by the fact that the end_borrow must
2811+ // / precede the dealloc_box which will be created in the diamond. So we
2812+ // / couldn't simply start inserting before the end_borrow (because the bottom
2813+ // / of the diamond contains a dealloc_box, so we would have an end_borrow after
2814+ // / the dealloc_box).
2815+ // /
2816+ // / At this point, we have the following code:
2817+ // /
2818+ // / end_borrow %lifetime
2819+ // / %initialized = load %addr
2820+ // / cond_br %initialized, yes, no
2821+ // /
2822+ // / yes:
2823+ // / destroy_addr %proj_box
2824+ // / br bottom
2825+ // /
2826+ // / no:
2827+ // / br bottom
2828+ // /
2829+ // / bottom:
2830+ // / br keep_going
2831+ // /
2832+ // / keep_going:
2833+ // /
2834+ // / So just move the end_borrow to the right position, at the top of the bottom
2835+ // / block. The caller will then add the dealloc_box.
2836+ static bool adjustAllocBoxEndBorrow (SILInstruction *previous,
2837+ SILValue destroyedAddr,
2838+ SILBuilderWithScope &builder) {
2839+ // This fixup only applies if we're destroying a project_box.
2840+ auto *pbi = dyn_cast<ProjectBoxInst>(destroyedAddr);
2841+ if (!pbi)
2842+ return false ;
2843+
2844+ // This fixup only applies if we're destroying a project_box of the lexical
2845+ // lifetime of an alloc_box.
2846+ auto *lifetime = dyn_cast<BeginBorrowInst>(pbi->getOperand ());
2847+ if (!lifetime)
2848+ return false ;
2849+ assert (lifetime->isLexical ());
2850+ assert (isa<AllocBoxInst>(
2851+ cast<MarkUninitializedInst>(lifetime->getOperand ())->getOperand ()));
2852+
2853+ // Scan the block backwards from previous, looking for an end_borrow. SILGen
2854+ // will emit the sequence
2855+ //
2856+ // end_borrow %lifetime
2857+ // destroy_value %mark_uninit
2858+ //
2859+ // but other passes may have moved them apart.
2860+ EndBorrowInst *ebi = nullptr ;
2861+ for (auto *instruction = previous; instruction;
2862+ instruction = instruction->getPreviousInstruction ()) {
2863+ auto *candidate = dyn_cast<EndBorrowInst>(instruction);
2864+ if (!candidate)
2865+ continue ;
2866+ auto *bbi = dyn_cast<BeginBorrowInst>(candidate->getOperand ());
2867+ if (bbi != lifetime)
2868+ continue ;
2869+ ebi = candidate;
2870+ }
2871+ if (!ebi)
2872+ return false ;
2873+
2874+ ebi->moveBefore (&*builder.getInsertionPoint ());
2875+ return true ;
2876+ }
2877+
27802878// / Process any destroy_addr and strong_release instructions that are invoked on
27812879// / a partially initialized value. This generates code to destroy the elements
27822880// / that are known to be alive, ignore the ones that are known to be dead, and
@@ -2797,7 +2895,7 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
27972895
27982896 // Utilities.
27992897
2800- auto destroyMemoryElement = [&](SILLocation Loc, unsigned Elt) {
2898+ auto destroyMemoryElement = [&](SILLocation Loc, unsigned Elt) -> SILValue {
28012899 using EndScopeKind = DIMemoryObjectInfo::EndScopeKind;
28022900 SmallVector<std::pair<SILValue, EndScopeKind>, 4 > EndScopeList;
28032901 SILValue EltPtr =
@@ -2820,12 +2918,14 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
28202918 }
28212919 llvm_unreachable (" Covered switch isn't covered!" );
28222920 }
2921+ return EltPtr;
28232922 };
28242923
28252924 // Destroy all the allocation's fields, not including the allocation
28262925 // itself, if we have a class initializer.
2827- auto destroyMemoryElements = [&](SILLocation Loc,
2926+ auto destroyMemoryElements = [&](SILInstruction *Release, SILLocation Loc,
28282927 AvailabilitySet Availability) {
2928+ auto *Previous = Release->getPreviousInstruction ();
28292929 // Delegating initializers don't model the fields of the class.
28302930 if (TheMemory.isClassInitSelf () && TheMemory.isDelegatingInit ())
28312931 return ;
@@ -2865,9 +2965,10 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
28652965
28662966 // Set up the initialized release block.
28672967 B.setInsertionPoint (ReleaseBlock->begin ());
2868- destroyMemoryElement (Loc, Elt);
2968+ auto EltPtr = destroyMemoryElement (Loc, Elt);
28692969
28702970 B.setInsertionPoint (ContBlock->begin ());
2971+ adjustAllocBoxEndBorrow (Previous, EltPtr, B);
28712972 }
28722973 };
28732974
@@ -2944,10 +3045,10 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
29443045 // If false, self is uninitialized and must be freed.
29453046 B.setInsertionPoint (DeallocBlock->begin ());
29463047 B.setCurrentDebugScope (DeallocBlock->begin ()->getDebugScope ());
2947- destroyMemoryElements (Loc, Availability);
3048+ destroyMemoryElements (Release, Loc, Availability);
29483049 processUninitializedRelease (Release, false , B.getInsertionPoint ());
29493050 } else {
2950- destroyMemoryElements (Loc, Availability);
3051+ destroyMemoryElements (Release, Loc, Availability);
29513052 processUninitializedRelease (Release, false , B.getInsertionPoint ());
29523053
29533054 // The original strong_release or destroy_addr instruction is
@@ -2972,7 +3073,7 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
29723073
29733074 // self.init or super.init was not called. If we're in the super.init
29743075 // case, destroy any initialized fields.
2975- destroyMemoryElements (Loc, Availability);
3076+ destroyMemoryElements (Release, Loc, Availability);
29763077 processUninitializedRelease (Release, false , B.getInsertionPoint ());
29773078 break ;
29783079
@@ -3019,7 +3120,7 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
30193120 // If false, self is uninitialized and must be freed.
30203121 B.setInsertionPoint (DeallocBlock->begin ());
30213122 B.setCurrentDebugScope (DeallocBlock->begin ()->getDebugScope ());
3022- destroyMemoryElements (Loc, Availability);
3123+ destroyMemoryElements (Release, Loc, Availability);
30233124 processUninitializedRelease (Release, false , B.getInsertionPoint ());
30243125
30253126 break ;
@@ -3054,7 +3155,7 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
30543155 // If false, self is uninitialized and must be freed.
30553156 B.setInsertionPoint (DeallocBlock->begin ());
30563157 B.setCurrentDebugScope (DeallocBlock->begin ()->getDebugScope ());
3057- destroyMemoryElements (Loc, Availability);
3158+ destroyMemoryElements (Release, Loc, Availability);
30583159 processUninitializedRelease (Release, false , B.getInsertionPoint ());
30593160
30603161 break ;
0 commit comments