Skip to content

Commit b73dd3b

Browse files
committed
DI: Lower AssignInst in a post-processing pass
Compiler passes that intermingle analysis with mutation of the CFG are fraught with danger. The bug here was that a single AssignInst could appear twice in DI's Uses list, once as a store use and once as a load use. When processing Uses, we would lower AssignInsts on the fly. We would take care to erase the instruction pointer from the current Use, but if a subsequent Use *also* referenced the now-deleted AssignInst, we would crash. Handle this in the standard way: instead of lowering assignments right away, just build a list of assignments that we're planning on lowering and process them all at the very end. This has the added benefit of simplifying the code, because we no longer have to work as hard to keep the state of the Uses list consistent while lowering AssignInsts. The rest of DI should not care that the lowering got postponed either, since it was already expected to handle any ordering of elements in the Uses list, so it could not assume that any particular AssignInst has been lowered. Fixes <https://bugs.swift.org/browse/SR-9451>.
1 parent eab1800 commit b73dd3b

File tree

3 files changed

+51
-41
lines changed

3 files changed

+51
-41
lines changed

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ namespace {
390390
SmallVectorImpl<DIMemoryUse> &Uses;
391391
TinyPtrVector<SILInstruction *> &StoresToSelf;
392392
SmallVectorImpl<SILInstruction *> &Destroys;
393+
SmallVector<unsigned, 8> NeedsUpdateForInitState;
393394
std::vector<ConditionalDestroy> ConditionalDestroys;
394395

395396
llvm::SmallDenseMap<SILBasicBlock*, LiveOutBlockState, 32> PerBlockInfo;
@@ -471,7 +472,8 @@ namespace {
471472
bool SuperInitDone,
472473
bool FailedSelfUse);
473474

474-
void handleSelfInitUse(DIMemoryUse &Use);
475+
void handleSelfInitUse(unsigned UseID);
476+
475477
void updateInstructionForInitState(DIMemoryUse &Use);
476478

477479

@@ -776,7 +778,7 @@ void LifetimeChecker::doIt() {
776778
handleEscapeUse(Use);
777779
break;
778780
case DIUseKind::SelfInit:
779-
handleSelfInitUse(Use);
781+
handleSelfInitUse(i);
780782
break;
781783
case DIUseKind::LoadForTypeOfSelf:
782784
handleLoadForTypeOfSelfUse(Use);
@@ -805,6 +807,12 @@ void LifetimeChecker::doIt() {
805807
ControlVariable = handleConditionalInitAssign();
806808
if (!ConditionalDestroys.empty())
807809
handleConditionalDestroys(ControlVariable);
810+
811+
// handleStoreUse(), handleSelfInitUse() and handleConditionalInitAssign()
812+
// postpone lowering of assignment instructions to avoid deleting
813+
// instructions that still appear in the Uses list.
814+
for (unsigned UseID : NeedsUpdateForInitState)
815+
updateInstructionForInitState(Uses[UseID]);
808816
}
809817

810818
void LifetimeChecker::handleLoadUse(const DIMemoryUse &Use) {
@@ -1015,7 +1023,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
10151023

10161024
// Otherwise, we have a definite init or assign. Make sure the instruction
10171025
// itself is tagged properly.
1018-
updateInstructionForInitState(Use);
1026+
NeedsUpdateForInitState.push_back(UseID);
10191027
}
10201028

10211029
/// Check whether the instruction is an application.
@@ -1756,7 +1764,8 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use,
17561764

17571765
/// handleSelfInitUse - When processing a 'self' argument on a class, this is
17581766
/// a call to self.init or super.init.
1759-
void LifetimeChecker::handleSelfInitUse(DIMemoryUse &Use) {
1767+
void LifetimeChecker::handleSelfInitUse(unsigned UseID) {
1768+
auto &Use = Uses[UseID];
17601769
auto *Inst = Use.Inst;
17611770

17621771
assert(TheMemory.isAnyInitSelf());
@@ -1791,7 +1800,7 @@ void LifetimeChecker::handleSelfInitUse(DIMemoryUse &Use) {
17911800

17921801
// Lower Assign instructions if needed.
17931802
if (isa<AssignInst>(Use.Inst))
1794-
updateInstructionForInitState(Use);
1803+
NeedsUpdateForInitState.push_back(UseID);
17951804
} else {
17961805
// super.init also requires that all ivars are initialized before the
17971806
// superclass initializer runs.
@@ -1851,14 +1860,13 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
18511860
if (auto *AI = dyn_cast<AssignInst>(Inst)) {
18521861
// Remove this instruction from our data structures, since we will be
18531862
// removing it.
1854-
auto Kind = Use.Kind;
18551863
Use.Inst = nullptr;
18561864
NonLoadUses.erase(Inst);
18571865

18581866
PartialInitializationKind PartialInitKind;
18591867

18601868
if (TheMemory.isClassInitSelf() &&
1861-
Kind == DIUseKind::SelfInit) {
1869+
Use.Kind == DIUseKind::SelfInit) {
18621870
assert(InitKind == IsInitialization);
18631871
PartialInitKind = PartialInitializationKind::IsReinitialization;
18641872
} else {
@@ -1867,27 +1875,8 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
18671875
: PartialInitializationKind::IsNotInitialization);
18681876
}
18691877

1870-
unsigned FirstElement = Use.FirstElement;
1871-
unsigned NumElements = Use.NumElements;
1872-
1873-
SmallVector<SILInstruction*, 4> InsertedInsts;
1874-
SILBuilderWithScope B(Inst, &InsertedInsts);
1878+
SILBuilderWithScope B(Inst);
18751879
lowerAssignInstruction(B, AI, PartialInitKind);
1876-
1877-
// If lowering of the assign introduced any new loads or stores, keep track
1878-
// of them.
1879-
for (auto I : InsertedInsts) {
1880-
if (isa<StoreInst>(I)) {
1881-
NonLoadUses[I] = Uses.size();
1882-
Uses.push_back(DIMemoryUse(I, Kind, FirstElement, NumElements));
1883-
} else if (isa<LoadInst>(I)) {
1884-
// If we have a re-initialization, the value must be a class,
1885-
// and the load is just there so we can free the uninitialized
1886-
// object husk; it's not an actual use of 'self'.
1887-
if (PartialInitKind != PartialInitializationKind::IsReinitialization)
1888-
Uses.push_back(DIMemoryUse(I, Load, FirstElement, NumElements));
1889-
}
1890-
}
18911880
return;
18921881
}
18931882

@@ -2256,15 +2245,12 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {
22562245
// Finally, now that we know the value is uninitialized on all paths, it is
22572246
// safe to do an unconditional initialization.
22582247
Use.Kind = DIUseKind::Initialization;
2259-
2260-
// Now that the instruction has a concrete "init" form, update it to reflect
2261-
// that. Note that this can invalidate the Uses vector and delete
2262-
// the instruction.
2263-
updateInstructionForInitState(Use);
2264-
2265-
// Revisit the instruction on the next pass through the loop, so that we
2266-
// emit a mask update as appropriate.
2267-
--i;
2248+
NeedsUpdateForInitState.push_back(i);
2249+
2250+
// Update the control variable.
2251+
APInt Bitmask = Use.getElementBitmask(NumMemoryElements);
2252+
SILBuilderWithScope SB(Use.Inst);
2253+
updateControlVariable(Loc, Bitmask, ControlVariableAddr, OrFn, SB);
22682254
}
22692255

22702256
// At each block that stores to self, mark the self value as having been

test/SILOptimizer/definite_init_diagnostics.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,3 +1551,31 @@ func testOptionalUnwrapNoError() -> Int? {
15511551
x = 0
15521552
return x!
15531553
}
1554+
1555+
// <https://bugs.swift.org/browse/SR-9451>
1556+
class StrongCycle {
1557+
var c: StrongCycle
1558+
var d: Int
1559+
init(first: ()) {
1560+
self.d = 10
1561+
self.c = self // expected-error {{variable 'self.c' used before being initialized}}
1562+
}
1563+
1564+
init(second: ()) {
1565+
self.c = self // expected-error {{variable 'self.c' used before being initialized}}
1566+
self.d = 10
1567+
}
1568+
}
1569+
1570+
class WeakCycle {
1571+
weak var c: WeakCycle?
1572+
var d: Int
1573+
init(first: ()) { // FIXME: This is inconsistent with the strong reference behavior above
1574+
self.d = 10
1575+
self.c = self
1576+
}
1577+
init(second: ()) {
1578+
self.c = self // expected-error {{variable 'self.d' used before being initialized}}
1579+
self.d = 10
1580+
}
1581+
}

test/SILOptimizer/definite_init_markuninitialized_var.sil

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,7 @@ bb0:
260260

261261
%b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_weak Optional<SomeClass>>
262262
%ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional<SomeClass>>, 0
263-
%c = mark_uninitialized [var] %ba : $*@sil_weak Optional<SomeClass> // expected-note {{variable defined here}}
264-
265-
// Invalid load to keep the alloc_box around so we can check init semantics.
266-
%c_loaded = load_weak %c : $*@sil_weak Optional<SomeClass> // expected-error {{used before being initialized}}
267-
destroy_value %c_loaded : $Optional<SomeClass>
263+
%c = mark_uninitialized [var] %ba : $*@sil_weak Optional<SomeClass>
268264

269265
%f = function_ref @getSomeOptionalClass : $@convention(thin) () -> @owned Optional<SomeClass>
270266

0 commit comments

Comments
 (0)