Skip to content

Commit 58b414d

Browse files
Merge pull request #41781 from nate-chandler/lexical_lifetimes/di-moves-end_borrow
[DI] Fixup alloc_box borrow scope ends.
2 parents b962b6f + 05e643b commit 58b414d

File tree

2 files changed

+130
-8
lines changed

2 files changed

+130
-8
lines changed

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 109 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-swift-frontend -emit-sil %s -o /dev/null
2+
3+
// For OSLogTestHelper.
4+
// REQUIRES: VENDOR=apple
5+
6+
import OSLogTestHelper
7+
8+
struct Thing {
9+
let guts: AnyObject
10+
}
11+
12+
func getThings() -> [Thing] { [] }
13+
14+
func run() {
15+
var things: [Thing]
16+
while(true) {
17+
things = getThings()
18+
OSLogMessage("count: \(things.count)")
19+
}
20+
}
21+

0 commit comments

Comments
 (0)