Skip to content

Commit d716ba9

Browse files
authored
Merge pull request swiftlang#33743 from slavapestov/conditional-destroy-root-class-5.3
DI: Correctly handle conditional destroy of 'self' in root class [5.3]
2 parents 908f105 + ed0d939 commit d716ba9

File tree

4 files changed

+506
-35
lines changed

4 files changed

+506
-35
lines changed

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ class DIMemoryObjectInfo {
176176
MemoryInst->isDerivedClassSelfOnly();
177177
}
178178

179+
/// True if this memory object is the 'self' of a root class init method.
180+
bool isRootClassSelf() const {
181+
return isClassInitSelf() && MemoryInst->isRootSelf();
182+
}
183+
179184
/// True if this memory object is the 'self' of a non-root class init method.
180185
bool isNonRootClassSelf() const {
181186
return isClassInitSelf() && !MemoryInst->isRootSelf();

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 111 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,13 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
10711071
// it for later. Once we've collected all of the conditional init/assigns,
10721072
// we can insert a single control variable for the memory object for the
10731073
// whole function.
1074-
if (!Use.onlyTouchesTrivialElements(TheMemory))
1074+
//
1075+
// For root class initializers, we must keep track of initializations of
1076+
// trivial stored properties also, since we need to know when the object
1077+
// has been fully initialized when deciding if a strong_release should
1078+
// lower to a partial_dealloc_ref.
1079+
if (TheMemory.isRootClassSelf() ||
1080+
!Use.onlyTouchesTrivialElements(TheMemory))
10751081
HasConditionalInitAssign = true;
10761082
return;
10771083
}
@@ -2177,12 +2183,12 @@ static void updateControlVariable(SILLocation Loc,
21772183
}
21782184

21792185
/// Test a bit in the control variable at the current insertion point.
2180-
static SILValue testControlVariable(SILLocation Loc,
2181-
unsigned Elt,
2182-
SILValue ControlVariableAddr,
2183-
Identifier &ShiftRightFn,
2184-
Identifier &TruncateFn,
2185-
SILBuilder &B) {
2186+
static SILValue testControlVariableBit(SILLocation Loc,
2187+
unsigned Elt,
2188+
SILValue ControlVariableAddr,
2189+
Identifier &ShiftRightFn,
2190+
Identifier &TruncateFn,
2191+
SILBuilder &B) {
21862192
SILValue ControlVariable =
21872193
B.createLoad(Loc, ControlVariableAddr, LoadOwnershipQualifier::Trivial);
21882194

@@ -2215,6 +2221,32 @@ static SILValue testControlVariable(SILLocation Loc,
22152221
{}, CondVal);
22162222
}
22172223

2224+
/// Test if all bits in the control variable are set at the current
2225+
/// insertion point.
2226+
static SILValue testAllControlVariableBits(SILLocation Loc,
2227+
SILValue ControlVariableAddr,
2228+
Identifier &CmpEqFn,
2229+
SILBuilder &B) {
2230+
SILValue ControlVariable =
2231+
B.createLoad(Loc, ControlVariableAddr, LoadOwnershipQualifier::Trivial);
2232+
2233+
SILValue CondVal = ControlVariable;
2234+
CanBuiltinIntegerType IVType = CondVal->getType().castTo<BuiltinIntegerType>();
2235+
2236+
if (IVType->getFixedWidth() == 1)
2237+
return CondVal;
2238+
2239+
SILValue AllBitsSet = B.createIntegerLiteral(Loc, CondVal->getType(), -1);
2240+
if (!CmpEqFn.get())
2241+
CmpEqFn = getBinaryFunction("cmp_eq", CondVal->getType(),
2242+
B.getASTContext());
2243+
SILValue Args[] = { CondVal, AllBitsSet };
2244+
2245+
return B.createBuiltin(Loc, CmpEqFn,
2246+
SILType::getBuiltinIntegerType(1, B.getASTContext()),
2247+
{}, Args);
2248+
}
2249+
22182250
/// handleConditionalInitAssign - This memory object has some stores
22192251
/// into (some element of) it that is either an init or an assign based on the
22202252
/// control flow path through the function, or have a destroy event that happens
@@ -2276,7 +2308,13 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {
22762308
// If this ambiguous store is only of trivial types, then we don't need to
22772309
// do anything special. We don't even need keep the init bit for the
22782310
// element precise.
2279-
if (Use.onlyTouchesTrivialElements(TheMemory))
2311+
//
2312+
// For root class initializers, we must keep track of initializations of
2313+
// trivial stored properties also, since we need to know when the object
2314+
// has been fully initialized when deciding if a strong_release should
2315+
// lower to a partial_dealloc_ref.
2316+
if (!TheMemory.isRootClassSelf() &&
2317+
Use.onlyTouchesTrivialElements(TheMemory))
22802318
continue;
22812319

22822320
B.setInsertionPoint(Use.Inst);
@@ -2315,9 +2353,9 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {
23152353
// initialization.
23162354
for (unsigned Elt = Use.FirstElement, e = Elt+Use.NumElements;
23172355
Elt != e; ++Elt) {
2318-
auto CondVal = testControlVariable(Loc, Elt, ControlVariableAddr,
2319-
ShiftRightFn, TruncateFn,
2320-
B);
2356+
auto CondVal = testControlVariableBit(Loc, Elt, ControlVariableAddr,
2357+
ShiftRightFn, TruncateFn,
2358+
B);
23212359

23222360
SILBasicBlock *TrueBB, *FalseBB, *ContBB;
23232361
InsertCFGDiamond(CondVal, Loc, B,
@@ -2386,7 +2424,7 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {
23862424
void LifetimeChecker::
23872425
handleConditionalDestroys(SILValue ControlVariableAddr) {
23882426
SILBuilderWithScope B(TheMemory.getUninitializedValue());
2389-
Identifier ShiftRightFn, TruncateFn;
2427+
Identifier ShiftRightFn, TruncateFn, CmpEqFn;
23902428

23912429
unsigned NumMemoryElements = TheMemory.getNumElements();
23922430

@@ -2456,9 +2494,9 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
24562494

24572495
// Insert a load of the liveness bitmask and split the CFG into a diamond
24582496
// right before the destroy_addr, if we haven't already loaded it.
2459-
auto CondVal = testControlVariable(Loc, Elt, ControlVariableAddr,
2460-
ShiftRightFn, TruncateFn,
2461-
B);
2497+
auto CondVal = testControlVariableBit(Loc, Elt, ControlVariableAddr,
2498+
ShiftRightFn, TruncateFn,
2499+
B);
24622500

24632501
SILBasicBlock *ReleaseBlock, *DeallocBlock, *ContBlock;
24642502

@@ -2477,11 +2515,11 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
24772515
// depending on if the self box was initialized or not.
24782516
auto emitReleaseOfSelfWhenNotConsumed = [&](SILLocation Loc,
24792517
SILInstruction *Release) {
2480-
auto CondVal = testControlVariable(Loc, SelfInitializedElt,
2481-
ControlVariableAddr,
2482-
ShiftRightFn,
2483-
TruncateFn,
2484-
B);
2518+
auto CondVal = testControlVariableBit(Loc, SelfInitializedElt,
2519+
ControlVariableAddr,
2520+
ShiftRightFn,
2521+
TruncateFn,
2522+
B);
24852523

24862524
SILBasicBlock *ReleaseBlock, *ConsumedBlock, *ContBlock;
24872525

@@ -2513,12 +2551,50 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
25132551
// Just conditionally destroy each memory element, and for classes,
25142552
// also free the partially initialized object.
25152553
if (!TheMemory.isNonRootClassSelf()) {
2516-
destroyMemoryElements(Loc, Availability);
2517-
processUninitializedRelease(Release, false, B.getInsertionPoint());
2554+
assert(!Availability.isAllYes() &&
2555+
"Should not end up here if fully initialized");
2556+
2557+
// For root class initializers, we check if all proeprties were
2558+
// dynamically initialized, and if so, treat this as a release of
2559+
// an initialized 'self', instead of tearing down the fields
2560+
// one by one and deallocating memory.
2561+
//
2562+
// This is required for correctness, since the condition that
2563+
// allows 'self' to escape is that all stored properties were
2564+
// initialized. So we cannot deallocate the memory if 'self' may
2565+
// have escaped.
2566+
//
2567+
// This also means the deinitializer will run if all stored
2568+
// properties were initialized.
2569+
if (TheMemory.isClassInitSelf() &&
2570+
Availability.hasAny(DIKind::Partial)) {
2571+
auto CondVal = testAllControlVariableBits(Loc, ControlVariableAddr,
2572+
CmpEqFn, B);
2573+
2574+
SILBasicBlock *ReleaseBlock, *DeallocBlock, *ContBlock;
2575+
2576+
InsertCFGDiamond(CondVal, Loc, B,
2577+
ReleaseBlock, DeallocBlock, ContBlock);
2578+
2579+
// If true, self was fully initialized and must be released.
2580+
B.setInsertionPoint(ReleaseBlock->begin());
2581+
B.setCurrentDebugScope(ReleaseBlock->begin()->getDebugScope());
2582+
Release->moveBefore(&*B.getInsertionPoint());
2583+
2584+
// If false, self is uninitialized and must be freed.
2585+
B.setInsertionPoint(DeallocBlock->begin());
2586+
B.setCurrentDebugScope(DeallocBlock->begin()->getDebugScope());
2587+
destroyMemoryElements(Loc, Availability);
2588+
processUninitializedRelease(Release, false, B.getInsertionPoint());
2589+
} else {
2590+
destroyMemoryElements(Loc, Availability);
2591+
processUninitializedRelease(Release, false, B.getInsertionPoint());
2592+
2593+
// The original strong_release or destroy_addr instruction is
2594+
// always dead at this point.
2595+
deleteDeadRelease(CDElt.ReleaseID);
2596+
}
25182597

2519-
// The original strong_release or destroy_addr instruction is
2520-
// always dead at this point.
2521-
deleteDeadRelease(CDElt.ReleaseID);
25222598
continue;
25232599
}
25242600

@@ -2564,11 +2640,11 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
25642640
// self.init or super.init may or may not have been called.
25652641
// We have not yet stored 'self' into the box.
25662642

2567-
auto CondVal = testControlVariable(Loc, SuperInitElt,
2568-
ControlVariableAddr,
2569-
ShiftRightFn,
2570-
TruncateFn,
2571-
B);
2643+
auto CondVal = testControlVariableBit(Loc, SuperInitElt,
2644+
ControlVariableAddr,
2645+
ShiftRightFn,
2646+
TruncateFn,
2647+
B);
25722648

25732649
SILBasicBlock *ConsumedBlock, *DeallocBlock, *ContBlock;
25742650

@@ -2598,11 +2674,11 @@ handleConditionalDestroys(SILValue ControlVariableAddr) {
25982674
// self.init or super.init may or may not have been called.
25992675
// We may or may have stored 'self' into the box.
26002676

2601-
auto CondVal = testControlVariable(Loc, SuperInitElt,
2602-
ControlVariableAddr,
2603-
ShiftRightFn,
2604-
TruncateFn,
2605-
B);
2677+
auto CondVal = testControlVariableBit(Loc, SuperInitElt,
2678+
ControlVariableAddr,
2679+
ShiftRightFn,
2680+
TruncateFn,
2681+
B);
26062682

26072683
SILBasicBlock *LiveBlock, *DeallocBlock, *ContBlock;
26082684

0 commit comments

Comments
 (0)