Skip to content

Commit 047c55c

Browse files
authored
Merge pull request swiftlang#21160 from slavapestov/di-untangle-analysis-from-mutation
DI: Lower AssignInst in a post-processing pass
2 parents c84d29e + a5f9619 commit 047c55c

File tree

3 files changed

+61
-64
lines changed

3 files changed

+61
-64
lines changed

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 32 additions & 59 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;
@@ -459,7 +460,7 @@ namespace {
459460

460461

461462
void handleStoreUse(unsigned UseID);
462-
void handleLoadUse(unsigned UseID);
463+
void handleLoadUse(const DIMemoryUse &Use);
463464
void handleLoadForTypeOfSelfUse(const DIMemoryUse &Use);
464465
void handleInOutUse(const DIMemoryUse &Use);
465466
void handleEscapeUse(const DIMemoryUse &Use);
@@ -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

@@ -765,15 +767,9 @@ void LifetimeChecker::doIt() {
765767
handleStoreUse(i);
766768
break;
767769

768-
case DIUseKind::IndirectIn: {
769-
bool IsSuperInitComplete, FailedSelfUse;
770-
// If the value is not definitively initialized, emit an error.
771-
if (!isInitializedAtUse(Use, &IsSuperInitComplete, &FailedSelfUse))
772-
handleLoadUseFailure(Use, IsSuperInitComplete, FailedSelfUse);
773-
break;
774-
}
770+
case DIUseKind::IndirectIn:
775771
case DIUseKind::Load:
776-
handleLoadUse(i);
772+
handleLoadUse(Use);
777773
break;
778774
case DIUseKind::InOutArgument:
779775
case DIUseKind::InOutSelfArgument:
@@ -783,7 +779,7 @@ void LifetimeChecker::doIt() {
783779
handleEscapeUse(Use);
784780
break;
785781
case DIUseKind::SelfInit:
786-
handleSelfInitUse(Use);
782+
handleSelfInitUse(i);
787783
break;
788784
case DIUseKind::LoadForTypeOfSelf:
789785
handleLoadForTypeOfSelfUse(Use);
@@ -812,11 +808,15 @@ void LifetimeChecker::doIt() {
812808
ControlVariable = handleConditionalInitAssign();
813809
if (!ConditionalDestroys.empty())
814810
handleConditionalDestroys(ControlVariable);
815-
}
816811

817-
void LifetimeChecker::handleLoadUse(unsigned UseID) {
818-
DIMemoryUse &Use = Uses[UseID];
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]);
817+
}
819818

819+
void LifetimeChecker::handleLoadUse(const DIMemoryUse &Use) {
820820
bool IsSuperInitComplete, FailedSelfUse;
821821
// If the value is not definitively initialized, emit an error.
822822
if (!isInitializedAtUse(Use, &IsSuperInitComplete, &FailedSelfUse))
@@ -1024,7 +1024,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {
10241024

10251025
// Otherwise, we have a definite init or assign. Make sure the instruction
10261026
// itself is tagged properly.
1027-
updateInstructionForInitState(Use);
1027+
NeedsUpdateForInitState.push_back(UseID);
10281028
}
10291029

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

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

17711772
assert(TheMemory.isAnyInitSelf());
@@ -1800,7 +1801,7 @@ void LifetimeChecker::handleSelfInitUse(DIMemoryUse &Use) {
18001801

18011802
// Lower Assign instructions if needed.
18021803
if (isa<AssignInst>(Use.Inst))
1803-
updateInstructionForInitState(Use);
1804+
NeedsUpdateForInitState.push_back(UseID);
18041805
} else {
18051806
// super.init also requires that all ivars are initialized before the
18061807
// superclass initializer runs.
@@ -1860,14 +1861,13 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
18601861
if (auto *AI = dyn_cast<AssignInst>(Inst)) {
18611862
// Remove this instruction from our data structures, since we will be
18621863
// removing it.
1863-
auto Kind = Use.Kind;
18641864
Use.Inst = nullptr;
18651865
NonLoadUses.erase(Inst);
18661866

18671867
PartialInitializationKind PartialInitKind;
18681868

18691869
if (TheMemory.isClassInitSelf() &&
1870-
Kind == DIUseKind::SelfInit) {
1870+
Use.Kind == DIUseKind::SelfInit) {
18711871
assert(InitKind == IsInitialization);
18721872
PartialInitKind = PartialInitializationKind::IsReinitialization;
18731873
} else {
@@ -1876,27 +1876,8 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
18761876
: PartialInitializationKind::IsNotInitialization);
18771877
}
18781878

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

@@ -2194,6 +2175,12 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {
21942175

21952176
// Ignore deleted uses.
21962177
if (Use.Inst == nullptr) continue;
2178+
2179+
// If this ambiguous store is only of trivial types, then we don't need to
2180+
// do anything special. We don't even need keep the init bit for the
2181+
// element precise.
2182+
if (Use.onlyTouchesTrivialElements(TheMemory))
2183+
continue;
21972184

21982185
B.setInsertionPoint(Use.Inst);
21992186

@@ -2209,23 +2196,12 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {
22092196

22102197
case DIUseKind::SelfInit:
22112198
case DIUseKind::Initialization:
2212-
// If this is an initialization of only trivial elements, then we don't
2213-
// need to update the bitvector.
2214-
if (Use.onlyTouchesTrivialElements(TheMemory))
2215-
continue;
2216-
22172199
APInt Bitmask = Use.getElementBitmask(NumMemoryElements);
22182200
SILBuilderWithScope SB(Use.Inst);
22192201
updateControlVariable(Loc, Bitmask, ControlVariableAddr, OrFn, SB);
22202202
continue;
22212203
}
22222204

2223-
// If this ambiguous store is only of trivial types, then we don't need to
2224-
// do anything special. We don't even need keep the init bit for the
2225-
// element precise.
2226-
if (Use.onlyTouchesTrivialElements(TheMemory))
2227-
continue;
2228-
22292205
// If this is the interesting case, we need to generate a CFG diamond for
22302206
// each element touched, destroying any live elements so that the resulting
22312207
// store is always an initialize. This disambiguates the dynamic
@@ -2270,15 +2246,12 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {
22702246
// Finally, now that we know the value is uninitialized on all paths, it is
22712247
// safe to do an unconditional initialization.
22722248
Use.Kind = DIUseKind::Initialization;
2273-
2274-
// Now that the instruction has a concrete "init" form, update it to reflect
2275-
// that. Note that this can invalidate the Uses vector and delete
2276-
// the instruction.
2277-
updateInstructionForInitState(Use);
2278-
2279-
// Revisit the instruction on the next pass through the loop, so that we
2280-
// emit a mask update as appropriate.
2281-
--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);
22822255
}
22832256

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