Skip to content

Optimizer: when propagating the concrete type of an existential, make sure to not violate dominance order #77560

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
merged 1 commit into from
Nov 13, 2024
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
34 changes: 19 additions & 15 deletions lib/SILOptimizer/Utils/InstOptUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -904,30 +904,34 @@ swift::findInitAddressForTrivialEnum(UncheckedTakeEnumDataAddrInst *utedai) {
if (!asi)
return nullptr;

SILInstruction *singleUser = nullptr;
InjectEnumAddrInst *singleInject = nullptr;
InitEnumDataAddrInst *singleInit = nullptr;
for (auto use : asi->getUses()) {
auto *user = use->getUser();
if (user == utedai)
continue;

// As long as there's only one UncheckedTakeEnumDataAddrInst and one
// InitEnumDataAddrInst, we don't care how many InjectEnumAddr and
// DeallocStack users there are.
if (isa<InjectEnumAddrInst>(user) || isa<DeallocStackInst>(user))
// If there is a single init_enum_data_addr and a single inject_enum_addr,
// those instructions must dominate the unchecked_take_enum_data_addr.
// Otherwise the enum wouldn't be initialized on all control flow paths.
if (auto *inj = dyn_cast<InjectEnumAddrInst>(user)) {
if (singleInject)
return nullptr;
singleInject = inj;
continue;
}

if (singleUser)
return nullptr;
if (auto *init = dyn_cast<InitEnumDataAddrInst>(user)) {
if (singleInit)
return nullptr;
singleInit = init;
continue;
}

singleUser = user;
if (isa<DeallocStackInst>(user) || isa<DebugValueInst>(user))
continue;
}
if (!singleUser)
return nullptr;

// Assume, without checking, that the returned InitEnumDataAddr dominates the
// given UncheckedTakeEnumDataAddrInst, because that's how SIL is defined. I
// don't know where this is actually verified.
return dyn_cast<InitEnumDataAddrInst>(singleUser);
return singleInit;
}

//===----------------------------------------------------------------------===//
Expand Down
40 changes: 40 additions & 0 deletions test/SILOptimizer/sil_combine_enums.sil
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ enum Numerals {
case Four
}

protocol P {
mutating func foo()
}

sil @createit : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@thick τ_0_0.Type) -> @out τ_0_0
sil @external_func: $@convention(thin) () -> ()

//CHECK-LABEL: eliminate_sw_enum_addr
Expand Down Expand Up @@ -663,3 +668,38 @@ bb0(%0 : $S):
return %11 : $()
}

// CHECK-LABEL: sil @dont_promote_existential_type_from_non_dominating_block :
// Just check that this doesn't crash
// CHECK: } // end sil function 'dont_promote_existential_type_from_non_dominating_block'
sil @dont_promote_existential_type_from_non_dominating_block : $@convention(thin) (@thick any P.Type) -> () {
bb0(%0 : $@thick any P.Type):
%1 = alloc_stack $Optional<any P>
cond_br undef, bb1, bb2

bb1:
inject_enum_addr %1 : $*Optional<any P>, #Optional.none!enumelt
br bb3

bb2:
%5 = init_enum_data_addr %1 : $*Optional<any P>, #Optional.some!enumelt
%6 = open_existential_metatype %0 : $@thick any P.Type to $@thick (@opened("B0100688-9C78-11EF-8A3C-4EA2A866E4C4", any P) Self).Type
%7 = init_existential_addr %5 : $*any P, $@opened("B0100688-9C78-11EF-8A3C-4EA2A866E4C4", any P) Self
%f = function_ref @createit : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@thick τ_0_0.Type) -> @out τ_0_0
%a = apply %f<@opened("B0100688-9C78-11EF-8A3C-4EA2A866E4C4", any P) Self>(%7, %6) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@thick τ_0_0.Type) -> @out τ_0_0
inject_enum_addr %1 : $*Optional<any P>, #Optional.some!enumelt
br bb3

bb3:
%9 = unchecked_take_enum_data_addr %1 : $*Optional<any P>, #Optional.some!enumelt
dealloc_stack %1 : $*Optional<any P>
%11 = alloc_stack [lexical] [var_decl] $any P, var, name "comp"
copy_addr [take] %9 to [init] %11 : $*any P
%13 = open_existential_addr mutable_access %11 : $*any P to $*@opened("36EE10D8-9C78-11EF-8A3C-4EA2A866E4C4", any P) Self
%14 = witness_method $@opened("36EE10D8-9C78-11EF-8A3C-4EA2A866E4C4", any P) Self, #P.foo : <Self where Self : P> (inout Self) -> () -> (), %13 : $*@opened("36EE10D8-9C78-11EF-8A3C-4EA2A866E4C4", any P) Self : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@inout τ_0_0) -> ()
%15 = apply %14<@opened("36EE10D8-9C78-11EF-8A3C-4EA2A866E4C4", any P) Self>(%13) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@inout τ_0_0) -> ()
destroy_addr %11 : $*any P
dealloc_stack %11 : $*any P
%18 = tuple ()
return %18 : $()
}