Skip to content

DI: Lower AssignInst in a post-processing pass [5.0] #21163

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 32 additions & 59 deletions lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ namespace {
SmallVectorImpl<DIMemoryUse> &Uses;
TinyPtrVector<SILInstruction *> &StoresToSelf;
SmallVectorImpl<SILInstruction *> &Destroys;
SmallVector<unsigned, 8> NeedsUpdateForInitState;
std::vector<ConditionalDestroy> ConditionalDestroys;

llvm::SmallDenseMap<SILBasicBlock*, LiveOutBlockState, 32> PerBlockInfo;
Expand Down Expand Up @@ -459,7 +460,7 @@ namespace {


void handleStoreUse(unsigned UseID);
void handleLoadUse(unsigned UseID);
void handleLoadUse(const DIMemoryUse &Use);
void handleLoadForTypeOfSelfUse(const DIMemoryUse &Use);
void handleInOutUse(const DIMemoryUse &Use);
void handleEscapeUse(const DIMemoryUse &Use);
Expand All @@ -471,7 +472,8 @@ namespace {
bool SuperInitDone,
bool FailedSelfUse);

void handleSelfInitUse(DIMemoryUse &Use);
void handleSelfInitUse(unsigned UseID);

void updateInstructionForInitState(DIMemoryUse &Use);


Expand Down Expand Up @@ -764,15 +766,9 @@ void LifetimeChecker::doIt() {
handleStoreUse(i);
break;

case DIUseKind::IndirectIn: {
bool IsSuperInitComplete, FailedSelfUse;
// If the value is not definitively initialized, emit an error.
if (!isInitializedAtUse(Use, &IsSuperInitComplete, &FailedSelfUse))
handleLoadUseFailure(Use, IsSuperInitComplete, FailedSelfUse);
break;
}
case DIUseKind::IndirectIn:
case DIUseKind::Load:
handleLoadUse(i);
handleLoadUse(Use);
break;
case DIUseKind::InOutArgument:
case DIUseKind::InOutSelfArgument:
Expand All @@ -782,7 +778,7 @@ void LifetimeChecker::doIt() {
handleEscapeUse(Use);
break;
case DIUseKind::SelfInit:
handleSelfInitUse(Use);
handleSelfInitUse(i);
break;
case DIUseKind::LoadForTypeOfSelf:
handleLoadForTypeOfSelfUse(Use);
Expand Down Expand Up @@ -811,11 +807,15 @@ void LifetimeChecker::doIt() {
ControlVariable = handleConditionalInitAssign();
if (!ConditionalDestroys.empty())
handleConditionalDestroys(ControlVariable);
}

void LifetimeChecker::handleLoadUse(unsigned UseID) {
DIMemoryUse &Use = Uses[UseID];
// handleStoreUse(), handleSelfInitUse() and handleConditionalInitAssign()
// postpone lowering of assignment instructions to avoid deleting
// instructions that still appear in the Uses list.
for (unsigned UseID : NeedsUpdateForInitState)
updateInstructionForInitState(Uses[UseID]);
}

void LifetimeChecker::handleLoadUse(const DIMemoryUse &Use) {
bool IsSuperInitComplete, FailedSelfUse;
// If the value is not definitively initialized, emit an error.
if (!isInitializedAtUse(Use, &IsSuperInitComplete, &FailedSelfUse))
Expand Down Expand Up @@ -1023,7 +1023,7 @@ void LifetimeChecker::handleStoreUse(unsigned UseID) {

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

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

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

assert(TheMemory.isAnyInitSelf());
Expand Down Expand Up @@ -1799,7 +1800,7 @@ void LifetimeChecker::handleSelfInitUse(DIMemoryUse &Use) {

// Lower Assign instructions if needed.
if (isa<AssignInst>(Use.Inst))
updateInstructionForInitState(Use);
NeedsUpdateForInitState.push_back(UseID);
} else {
// super.init also requires that all ivars are initialized before the
// superclass initializer runs.
Expand Down Expand Up @@ -1859,14 +1860,13 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
if (auto *AI = dyn_cast<AssignInst>(Inst)) {
// Remove this instruction from our data structures, since we will be
// removing it.
auto Kind = Use.Kind;
Use.Inst = nullptr;
NonLoadUses.erase(Inst);

PartialInitializationKind PartialInitKind;

if (TheMemory.isClassInitSelf() &&
Kind == DIUseKind::SelfInit) {
Use.Kind == DIUseKind::SelfInit) {
assert(InitKind == IsInitialization);
PartialInitKind = PartialInitializationKind::IsReinitialization;
} else {
Expand All @@ -1875,27 +1875,8 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
: PartialInitializationKind::IsNotInitialization);
}

unsigned FirstElement = Use.FirstElement;
unsigned NumElements = Use.NumElements;

SmallVector<SILInstruction*, 4> InsertedInsts;
SILBuilderWithScope B(Inst, &InsertedInsts);
SILBuilderWithScope B(Inst);
lowerAssignInstruction(B, AI, PartialInitKind);

// If lowering of the assign introduced any new loads or stores, keep track
// of them.
for (auto I : InsertedInsts) {
if (isa<StoreInst>(I)) {
NonLoadUses[I] = Uses.size();
Uses.push_back(DIMemoryUse(I, Kind, FirstElement, NumElements));
} else if (isa<LoadInst>(I)) {
// If we have a re-initialization, the value must be a class,
// and the load is just there so we can free the uninitialized
// object husk; it's not an actual use of 'self'.
if (PartialInitKind != PartialInitializationKind::IsReinitialization)
Uses.push_back(DIMemoryUse(I, Load, FirstElement, NumElements));
}
}
return;
}

Expand Down Expand Up @@ -2193,6 +2174,12 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {

// Ignore deleted uses.
if (Use.Inst == nullptr) continue;

// If this ambiguous store is only of trivial types, then we don't need to
// do anything special. We don't even need keep the init bit for the
// element precise.
if (Use.onlyTouchesTrivialElements(TheMemory))
continue;

B.setInsertionPoint(Use.Inst);

Expand All @@ -2208,23 +2195,12 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {

case DIUseKind::SelfInit:
case DIUseKind::Initialization:
// If this is an initialization of only trivial elements, then we don't
// need to update the bitvector.
if (Use.onlyTouchesTrivialElements(TheMemory))
continue;

APInt Bitmask = Use.getElementBitmask(NumMemoryElements);
SILBuilderWithScope SB(Use.Inst);
updateControlVariable(Loc, Bitmask, ControlVariableAddr, OrFn, SB);
continue;
}

// If this ambiguous store is only of trivial types, then we don't need to
// do anything special. We don't even need keep the init bit for the
// element precise.
if (Use.onlyTouchesTrivialElements(TheMemory))
continue;

// If this is the interesting case, we need to generate a CFG diamond for
// each element touched, destroying any live elements so that the resulting
// store is always an initialize. This disambiguates the dynamic
Expand Down Expand Up @@ -2269,15 +2245,12 @@ SILValue LifetimeChecker::handleConditionalInitAssign() {
// Finally, now that we know the value is uninitialized on all paths, it is
// safe to do an unconditional initialization.
Use.Kind = DIUseKind::Initialization;

// Now that the instruction has a concrete "init" form, update it to reflect
// that. Note that this can invalidate the Uses vector and delete
// the instruction.
updateInstructionForInitState(Use);

// Revisit the instruction on the next pass through the loop, so that we
// emit a mask update as appropriate.
--i;
NeedsUpdateForInitState.push_back(i);

// Update the control variable.
APInt Bitmask = Use.getElementBitmask(NumMemoryElements);
SILBuilderWithScope SB(Use.Inst);
updateControlVariable(Loc, Bitmask, ControlVariableAddr, OrFn, SB);
}

// At each block that stores to self, mark the self value as having been
Expand Down
28 changes: 28 additions & 0 deletions test/SILOptimizer/definite_init_diagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1551,3 +1551,31 @@ func testOptionalUnwrapNoError() -> Int? {
x = 0
return x!
}

// <https://bugs.swift.org/browse/SR-9451>
class StrongCycle {
var c: StrongCycle
var d: Int
init(first: ()) {
self.d = 10
self.c = self // expected-error {{variable 'self.c' used before being initialized}}
}

init(second: ()) {
self.c = self // expected-error {{variable 'self.c' used before being initialized}}
self.d = 10
}
}

class WeakCycle {
weak var c: WeakCycle?
var d: Int
init(first: ()) { // FIXME: This is inconsistent with the strong reference behavior above
self.d = 10
self.c = self
}
init(second: ()) {
self.c = self // expected-error {{variable 'self.d' used before being initialized}}
self.d = 10
}
}
6 changes: 1 addition & 5 deletions test/SILOptimizer/definite_init_markuninitialized_var.sil
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,7 @@ bb0:

%b = alloc_box $<τ_0_0> { var τ_0_0 } <@sil_weak Optional<SomeClass>>
%ba = project_box %b : $<τ_0_0> { var τ_0_0 } <@sil_weak Optional<SomeClass>>, 0
%c = mark_uninitialized [var] %ba : $*@sil_weak Optional<SomeClass> // expected-note {{variable defined here}}

// Invalid load to keep the alloc_box around so we can check init semantics.
%c_loaded = load_weak %c : $*@sil_weak Optional<SomeClass> // expected-error {{used before being initialized}}
destroy_value %c_loaded : $Optional<SomeClass>
%c = mark_uninitialized [var] %ba : $*@sil_weak Optional<SomeClass>

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

Expand Down