@@ -2777,6 +2777,104 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {
2777
2777
return ControlVariableAddr;
2778
2778
}
2779
2779
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
+
2780
2878
// / Process any destroy_addr and strong_release instructions that are invoked on
2781
2879
// / a partially initialized value. This generates code to destroy the elements
2782
2880
// / that are known to be alive, ignore the ones that are known to be dead, and
@@ -2797,7 +2895,7 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
2797
2895
2798
2896
// Utilities.
2799
2897
2800
- auto destroyMemoryElement = [&](SILLocation Loc, unsigned Elt) {
2898
+ auto destroyMemoryElement = [&](SILLocation Loc, unsigned Elt) -> SILValue {
2801
2899
using EndScopeKind = DIMemoryObjectInfo::EndScopeKind;
2802
2900
SmallVector<std::pair<SILValue, EndScopeKind>, 4 > EndScopeList;
2803
2901
SILValue EltPtr =
@@ -2820,12 +2918,14 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
2820
2918
}
2821
2919
llvm_unreachable (" Covered switch isn't covered!" );
2822
2920
}
2921
+ return EltPtr;
2823
2922
};
2824
2923
2825
2924
// Destroy all the allocation's fields, not including the allocation
2826
2925
// itself, if we have a class initializer.
2827
- auto destroyMemoryElements = [&](SILLocation Loc,
2926
+ auto destroyMemoryElements = [&](SILInstruction *Release, SILLocation Loc,
2828
2927
AvailabilitySet Availability) {
2928
+ auto *Previous = Release->getPreviousInstruction ();
2829
2929
// Delegating initializers don't model the fields of the class.
2830
2930
if (TheMemory.isClassInitSelf () && TheMemory.isDelegatingInit ())
2831
2931
return ;
@@ -2865,9 +2965,10 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
2865
2965
2866
2966
// Set up the initialized release block.
2867
2967
B.setInsertionPoint (ReleaseBlock->begin ());
2868
- destroyMemoryElement (Loc, Elt);
2968
+ auto EltPtr = destroyMemoryElement (Loc, Elt);
2869
2969
2870
2970
B.setInsertionPoint (ContBlock->begin ());
2971
+ adjustAllocBoxEndBorrow (Previous, EltPtr, B);
2871
2972
}
2872
2973
};
2873
2974
@@ -2944,10 +3045,10 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
2944
3045
// If false, self is uninitialized and must be freed.
2945
3046
B.setInsertionPoint (DeallocBlock->begin ());
2946
3047
B.setCurrentDebugScope (DeallocBlock->begin ()->getDebugScope ());
2947
- destroyMemoryElements (Loc, Availability);
3048
+ destroyMemoryElements (Release, Loc, Availability);
2948
3049
processUninitializedRelease (Release, false , B.getInsertionPoint ());
2949
3050
} else {
2950
- destroyMemoryElements (Loc, Availability);
3051
+ destroyMemoryElements (Release, Loc, Availability);
2951
3052
processUninitializedRelease (Release, false , B.getInsertionPoint ());
2952
3053
2953
3054
// The original strong_release or destroy_addr instruction is
@@ -2972,7 +3073,7 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
2972
3073
2973
3074
// self.init or super.init was not called. If we're in the super.init
2974
3075
// case, destroy any initialized fields.
2975
- destroyMemoryElements (Loc, Availability);
3076
+ destroyMemoryElements (Release, Loc, Availability);
2976
3077
processUninitializedRelease (Release, false , B.getInsertionPoint ());
2977
3078
break ;
2978
3079
@@ -3019,7 +3120,7 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
3019
3120
// If false, self is uninitialized and must be freed.
3020
3121
B.setInsertionPoint (DeallocBlock->begin ());
3021
3122
B.setCurrentDebugScope (DeallocBlock->begin ()->getDebugScope ());
3022
- destroyMemoryElements (Loc, Availability);
3123
+ destroyMemoryElements (Release, Loc, Availability);
3023
3124
processUninitializedRelease (Release, false , B.getInsertionPoint ());
3024
3125
3025
3126
break ;
@@ -3054,7 +3155,7 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
3054
3155
// If false, self is uninitialized and must be freed.
3055
3156
B.setInsertionPoint (DeallocBlock->begin ());
3056
3157
B.setCurrentDebugScope (DeallocBlock->begin ()->getDebugScope ());
3057
- destroyMemoryElements (Loc, Availability);
3158
+ destroyMemoryElements (Release, Loc, Availability);
3058
3159
processUninitializedRelease (Release, false , B.getInsertionPoint ());
3059
3160
3060
3161
break ;
0 commit comments