Skip to content

Commit a5f9619

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 38263ca commit a5f9619

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

@@ -777,7 +779,7 @@ void LifetimeChecker::doIt() {
777779
handleEscapeUse(Use);
778780
break;
779781
case DIUseKind::SelfInit:
780-
handleSelfInitUse(Use);
782+
handleSelfInitUse(i);
781783
break;
782784
case DIUseKind::LoadForTypeOfSelf:
783785
handleLoadForTypeOfSelfUse(Use);
@@ -806,6 +808,12 @@ void LifetimeChecker::doIt() {
806808
ControlVariable = handleConditionalInitAssign();
807809
if (!ConditionalDestroys.empty())
808810
handleConditionalDestroys(ControlVariable);
811+
812+
// handleStoreUse(), handleSelfInitUse() and handleConditionalInitAssign()
813+
// postpone lowering of assignment instructions to avoid deleting
814+
// instructions that still appear in the Uses list.
815+
for (unsigned UseID : NeedsUpdateForInitState)
816+
updateInstructionForInitState(Uses[UseID]);
809817
}
810818

811819
void LifetimeChecker::handleLoadUse(const DIMemoryUse &Use) {
@@ -1016,7 +1024,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
10161024

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

10221030
/// Check whether the instruction is an application.
@@ -1757,7 +1765,8 @@ void LifetimeChecker::handleLoadUseFailure(const DIMemoryUse &Use,
17571765

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

17631772
assert(TheMemory.isAnyInitSelf());
@@ -1792,7 +1801,7 @@ void LifetimeChecker::handleSelfInitUse(DIMemoryUse &Use) {
17921801

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

18591867
PartialInitializationKind PartialInitKind;
18601868

18611869
if (TheMemory.isClassInitSelf() &&
1862-
Kind == DIUseKind::SelfInit) {
1870+
Use.Kind == DIUseKind::SelfInit) {
18631871
assert(InitKind == IsInitialization);
18641872
PartialInitKind = PartialInitializationKind::IsReinitialization;
18651873
} else {
@@ -1868,27 +1876,8 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
18681876
: PartialInitializationKind::IsNotInitialization);
18691877
}
18701878

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

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

22712257
// 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)