Skip to content

Update SimplifyCFG's replacement of phi with its incoming value #72792

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 6 commits into from
Apr 3, 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
2 changes: 1 addition & 1 deletion docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8401,7 +8401,7 @@ switch_value
// FIXME: All destination labels currently must take no arguments

Conditionally branches to one of several destination basic blocks based on a
value of builtin integer or function type. If the operand value matches one of the ``case``
value of builtin integer. If the operand value matches one of the ``case``
values of the instruction, control is transferred to the corresponding basic
block. If there is a ``default`` basic block, control is transferred to it if
the value does not match any of the ``case`` values. It is undefined behavior
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -10224,7 +10224,7 @@ class SwitchValueInst final
std::optional<unsigned> getUniqueCaseForDestination(SILBasicBlock *bb) const {
for (unsigned i = 0; i < getNumCases(); ++i) {
if (getCase(i).second == bb) {
return i + 1;
return i;
}
}
return std::nullopt;
Expand Down
7 changes: 7 additions & 0 deletions lib/SIL/Verifier/MemoryLifetimeVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,13 @@ bool MemoryLifetimeVerifier::isTrivialEnumSuccessor(SILBasicBlock *block,
} else if (auto *switchEnumAddr = dyn_cast<SwitchEnumAddrInst>(term)) {
elem = switchEnumAddr->getUniqueCaseForDestination(succ);
enumTy = switchEnumAddr->getOperand()->getType();
} else if (auto *switchValue = dyn_cast<SwitchValueInst>(term)) {
auto destCase = switchValue->getUniqueCaseForDestination(succ);
assert(destCase.has_value());
auto caseValue =
cast<IntegerLiteralInst>(switchValue->getCase(*destCase).first);
auto testValue = dyn_cast<IntegerLiteralInst>(switchValue->getOperand());
return testValue ? testValue->getValue() != caseValue->getValue() : true;
} else {
return false;
}
Expand Down
5 changes: 2 additions & 3 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5227,9 +5227,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
void checkSwitchValueInst(SwitchValueInst *SVI) {
// TODO: Type should be either integer or function
auto Ty = SVI->getOperand()->getType();
require(Ty.is<BuiltinIntegerType>() || Ty.is<SILFunctionType>(),
"switch_value operand should be either of an integer "
"or function type");
require(Ty.is<BuiltinIntegerType>(),
"switch_value operand should be an integer");

auto ult = [](const SILValue &a, const SILValue &b) {
return a == b || a < b;
Expand Down
2 changes: 1 addition & 1 deletion lib/SILOptimizer/Mandatory/YieldOnceCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ class YieldOnceCheck : public SILFunctionTransform {
switchValue->getUniqueCaseForDestination(noYieldTarget);
assert(caseNumberOpt.has_value());

auto caseNumber = caseNumberOpt.value();
auto caseNumber = caseNumberOpt.value() + 1;
diagnose(
astCtx, enumCaseLoc, diag::switch_value_case_doesnt_yield,
(Twine(caseNumber) + llvm::getOrdinalSuffix(caseNumber)).str());
Expand Down
31 changes: 22 additions & 9 deletions lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -945,9 +945,7 @@ static SILValue createValueFromAddr(SILValue addr, SILBuilder *builder,
/// We leave the cleaning up to mem2reg.
SILInstruction *
SILCombiner::visitInjectEnumAddrInst(InjectEnumAddrInst *IEAI) {
if (IEAI->getFunction()->hasOwnership())
return nullptr;

auto *func = IEAI->getFunction();
// Given an inject_enum_addr of a concrete type without payload, promote it to
// a store of an enum. Mem2reg/load forwarding will clean things up for us. We
// can't handle the payload case here due to the flow problems caused by the
Expand Down Expand Up @@ -1076,8 +1074,10 @@ SILCombiner::visitInjectEnumAddrInst(InjectEnumAddrInst *IEAI) {
EnumInst *E =
Builder.createEnum(IEAI->getLoc(), SILValue(), IEAI->getElement(),
IEAI->getOperand()->getType().getObjectType());
Builder.createStore(IEAI->getLoc(), E, IEAI->getOperand(),
StoreOwnershipQualifier::Unqualified);
auto storeQual = !func->hasOwnership()
? StoreOwnershipQualifier::Unqualified
: StoreOwnershipQualifier::Trivial;
Builder.createStore(IEAI->getLoc(), E, IEAI->getOperand(), storeQual);
return eraseInstFromFunction(*IEAI);
}

Expand Down Expand Up @@ -1194,8 +1194,13 @@ SILCombiner::visitInjectEnumAddrInst(InjectEnumAddrInst *IEAI) {
EnumInst *E = Builder.createEnum(
DataAddrInst->getLoc(), en, DataAddrInst->getElement(),
DataAddrInst->getOperand()->getType().getObjectType());
auto storeQual = !func->hasOwnership()
? StoreOwnershipQualifier::Unqualified
: DataAddrInst->getOperand()->getType().isTrivial(*func)
? StoreOwnershipQualifier::Trivial
: StoreOwnershipQualifier::Init;
Builder.createStore(DataAddrInst->getLoc(), E, DataAddrInst->getOperand(),
StoreOwnershipQualifier::Unqualified);
storeQual);
// Cleanup.
getInstModCallbacks().notifyWillBeDeleted(DataAddrInst);
deleter.forceDeleteWithUsers(DataAddrInst);
Expand Down Expand Up @@ -1265,14 +1270,22 @@ SILCombiner::visitInjectEnumAddrInst(InjectEnumAddrInst *IEAI) {
elemType.getObjectType(), &*Builder.getInsertionPoint(),
Builder.getBuilderContext(), /*noUndef*/ true);
} else {
enumValue = Builder.createLoad(DataAddrInst->getLoc(), AllocStack,
LoadOwnershipQualifier::Unqualified);
auto loadQual = !func->hasOwnership() ? LoadOwnershipQualifier::Unqualified
: DataAddrInst->getOperand()->getType().isTrivial(*func)
? LoadOwnershipQualifier::Trivial
: LoadOwnershipQualifier::Take;
enumValue =
Builder.createLoad(DataAddrInst->getLoc(), AllocStack, loadQual);
}
EnumInst *E = Builder.createEnum(
DataAddrInst->getLoc(), enumValue, DataAddrInst->getElement(),
DataAddrInst->getOperand()->getType().getObjectType());
auto storeQual = !func->hasOwnership() ? StoreOwnershipQualifier::Unqualified
: DataAddrInst->getOperand()->getType().isTrivial(*func)
? StoreOwnershipQualifier::Trivial
: StoreOwnershipQualifier::Init;
Builder.createStore(DataAddrInst->getLoc(), E, DataAddrInst->getOperand(),
StoreOwnershipQualifier::Unqualified);
storeQual);
Builder.createDeallocStack(DataAddrInst->getLoc(), AllocStack);
eraseInstFromFunction(*DataAddrInst);
return eraseInstFromFunction(*IEAI);
Expand Down
18 changes: 15 additions & 3 deletions lib/SILOptimizer/Transforms/SimplifyCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3794,12 +3794,24 @@ static void tryToReplaceArgWithIncomingValue(SILBasicBlock *BB, unsigned i,
if (!A->getIncomingPhiValues(Incoming) || Incoming.empty())
return;

SILValue V = Incoming[0];
for (size_t Idx = 1, Size = Incoming.size(); Idx < Size; ++Idx) {
if (Incoming[Idx] != V)
SILValue V;
for (size_t Idx = 0; Idx < Incoming.size(); ++Idx) {
if (Incoming[Idx] == A) {
continue;
}
if (!V) {
V = Incoming[Idx];
continue;
}
if (Incoming[Idx] != V) {
return;
}
}

if (!V) {
return;
}

// If the incoming values of all predecessors are equal usually this means
// that the common incoming value dominates the BB. But: this might be not
// the case if BB is unreachable. Therefore we still have to check it.
Expand Down
45 changes: 0 additions & 45 deletions test/SIL/Parser/basic.sil
Original file line number Diff line number Diff line change
Expand Up @@ -973,51 +973,6 @@ bb3:
return %t : $()
}

// CHECK-LABEL: sil @test_switch_value : $@convention(thin) (Builtin.Word) -> ()
sil @test_switch_value : $@convention(thin) (Builtin.Word) -> () {
bb0(%0 : $Builtin.Word):
// CHECK: switch_value %{{.*}} : $Builtin.Word, case %1: bb1, case %2: bb2
%1 = integer_literal $Builtin.Word, 1
%2 = integer_literal $Builtin.Word, 2
switch_value %0 : $Builtin.Word, case %1: bb1, case %2: bb2

bb1:
%7 = function_ref @_T6switch1aFT_T_ : $@convention(thin) () -> () // CHECK: function_ref
%8 = apply %7() : $@convention(thin) () -> ()
br bb3

bb2:
%12 = function_ref @_T6switch1bFT_T_ : $@convention(thin) () -> () // CHECK: function_ref
%13 = apply %12() : $@convention(thin) () -> ()
br bb3

bb3:
// CHECK: [[FUNC1:%.*]] = function_ref @_T6switch1aFT_T_
// CHECK: [[FUNC2:%.*]] = function_ref @_T6switch1bFT_T_
// CHECK: switch_value %{{.*}} : $@convention(thin) () -> (), case [[FUNC1]]: bb4, case [[FUNC2]]: bb5
%20 = function_ref @_T6switch1bFT_T_ : $@convention(thin) () -> ()
%21 = function_ref @_T6switch1aFT_T_ : $@convention(thin) () -> ()
%22 = function_ref @_T6switch1bFT_T_ : $@convention(thin) () -> ()
switch_value %20 : $@convention(thin) () -> (), case %21: bb4, case %22: bb5

bb4:
// CHECK: function_ref
%37 = function_ref @_T6switch1aFT_T_ : $@convention(thin) () -> ()
%38 = apply %37() : $@convention(thin) () -> ()
br bb6

bb5:
// CHECK: function_ref
%42 = function_ref @_T6switch1bFT_T_ : $@convention(thin) () -> ()
%43 = apply %42() : $@convention(thin) () -> ()
br bb6

bb6:
%18 = tuple ()
// CHECK: return
return %18 : $()
}

class ConcreteClass : ClassP {
}
struct Spoon : Bendable {
Expand Down
11 changes: 0 additions & 11 deletions test/SIL/Parser/undef.sil
Original file line number Diff line number Diff line change
Expand Up @@ -287,17 +287,6 @@ bb2:
return undef : $()
}

sil @switch_value_test : $() -> () {
bb0:
// CHECK: switch_value undef : $Builtin.Int1, case undef: bb1
switch_value undef : $Builtin.Int1, case undef: bb1
bb1:
// CHECK: switch_value undef : $() -> (), case undef: bb2
switch_value undef : $() -> (), case undef: bb2
bb2:
return undef : $()
}

sil @switch_enum_test : $() -> () {
bb0:
// CHECK: switch_enum undef : $E, case #E.Case!enumelt: bb1, default bb2
Expand Down
25 changes: 25 additions & 0 deletions test/SIL/memory_lifetime.sil
Original file line number Diff line number Diff line change
Expand Up @@ -781,3 +781,28 @@ bb0:
%7 = tuple ()
return %7 : $()
}

sil [ossa] @test_init_enum_switch_value : $@convention(thin) (@in_guaranteed Optional<T>, @in_guaranteed T) -> () {
bb0(%0 : $*Optional<T>, %1 : $*T):
%stk = alloc_stack $Optional<T>
%2 = init_enum_data_addr %stk : $*Optional<T>, #Optional.some!enumelt
copy_addr %1 to [init] %2 : $*T
inject_enum_addr %stk : $*Optional<T>, #Optional.some!enumelt
%one = integer_literal $Builtin.Word, 1
%two = integer_literal $Builtin.Word, 2
switch_value %one : $Builtin.Word, case %one: bb1, case %two: bb2

bb1:
destroy_addr %stk : $*Optional<T>
dealloc_stack %stk : $*Optional<T>
br bb3

bb2:
dealloc_stack %stk : $*Optional<T>
br bb3

bb3:
%r = tuple ()
return %r : $()
}

104 changes: 52 additions & 52 deletions test/SILOptimizer/sil_combine_ossa.sil
Original file line number Diff line number Diff line change
Expand Up @@ -1347,20 +1347,20 @@ bb3(%16 : $Int32): // Preds: bb1 bb2
}

// CHECK-LABEL: sil [ossa] @enum_promotion_of_concrete_types
// XHECK: bb0([[INT_PTR:%[0-9]+]]
// XHECK-NEXT: [[ALLOCA1:%[0-9]+]] = alloc_stack $FakeOptional<Builtin.Int1>
// XHECK-NEXT: [[ENUM1:%[0-9]+]] = enum $FakeOptional<Builtin.Int1>, #FakeOptional.none!enumelt
// XHECK-NEXT: store [[ENUM1]] to [trivial] [[ALLOCA1]]
// XHECK-NEXT: [[ALLOCA2:%[0-9]+]] = alloc_stack $FakeOptional<Builtin.Int1>
// XHECK-NEXT: [[INT:%[0-9]+]] = load [trivial] [[INT_PTR]] : $*Builtin.Int1
// XHECK-NEXT: [[ENUM2:%[0-9]+]] = enum $FakeOptional<Builtin.Int1>, #FakeOptional.some!enumelt, [[INT]] : $Builtin.Int1
// XHECK-NEXT: store [[ENUM2]] to [trivial] [[ALLOCA2]] : $*FakeOptional<Builtin.Int1>
// XHECK-NEXT: [[RESULT1:%[0-9]+]] = load [trivial] [[ALLOCA1]]
// XHECK-NEXT: [[RESULT2:%[0-9]+]] = load [trivial] [[ALLOCA2]]
// XHECK-NEXT: [[RESULT:%[0-9]+]] = tuple ([[RESULT1]] : $FakeOptional<Builtin.Int1>, [[RESULT2]] : $FakeOptional<Builtin.Int1>)
// XHECK-NEXT: dealloc_stack
// XHECK-NEXT: dealloc_stack
// XHECK-NEXT: return [[RESULT]]
// CHECK: bb0([[INT_PTR:%[0-9]+]]
// CHECK-NEXT: [[ALLOCA1:%[0-9]+]] = alloc_stack $FakeOptional<Builtin.Int1>
// CHECK-NEXT: [[ENUM1:%[0-9]+]] = enum $FakeOptional<Builtin.Int1>, #FakeOptional.none!enumelt
// CHECK-NEXT: store [[ENUM1]] to [trivial] [[ALLOCA1]]
// CHECK-NEXT: [[ALLOCA2:%[0-9]+]] = alloc_stack $FakeOptional<Builtin.Int1>
// CHECK-NEXT: [[INT:%[0-9]+]] = load [trivial] [[INT_PTR]] : $*Builtin.Int1
// CHECK-NEXT: [[ENUM2:%[0-9]+]] = enum $FakeOptional<Builtin.Int1>, #FakeOptional.some!enumelt, [[INT]] : $Builtin.Int1
// CHECK-NEXT: store [[ENUM2]] to [trivial] [[ALLOCA2]] : $*FakeOptional<Builtin.Int1>
// CHECK-NEXT: [[RESULT1:%[0-9]+]] = load [trivial] [[ALLOCA1]]
// CHECK-NEXT: [[RESULT2:%[0-9]+]] = load [trivial] [[ALLOCA2]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = tuple ([[RESULT1]] : $FakeOptional<Builtin.Int1>, [[RESULT2]] : $FakeOptional<Builtin.Int1>)
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: return [[RESULT]]
sil [ossa] @enum_promotion_of_concrete_types : $@convention(thin) (@in Builtin.Int1) -> (FakeOptional<Builtin.Int1>, FakeOptional<Builtin.Int1>) {
bb0(%0 : $*Builtin.Int1):
%1 = alloc_stack $FakeOptional<Builtin.Int1>
Expand All @@ -1384,24 +1384,24 @@ bb0(%0 : $*Builtin.Int1):
}

// CHECK-LABEL: sil [ossa] @enum_promotion_case2
// XHECK: bb0([[B_PTR:%[0-9]+]]
// XHECK-NEXT: [[ALLOCA:%[0-9]+]] = alloc_stack $FakeOptional<B>
// XHECK-NEXT: [[B_PTR_COPY_FOR_ENUM:%.*]] = copy_value [[B_PTR]]
// CHECK: bb0([[B_PTR:%[0-9]+]]
// CHECK-NEXT: [[ALLOCA:%[0-9]+]] = alloc_stack $FakeOptional<B>
// CHECK-NEXT: [[B_PTR_COPY_FOR_ENUM:%.*]] = copy_value [[B_PTR]]
// This copy is the copy that was between the init_enum_data_addr/inject_enum_addr
// XHECK-NEXT: [[B_PTR_COPY_NOT_OBSTRUCTING:%.*]] = copy_value [[B_PTR]]
// XHECK-NEXT: [[ENUM:%[0-9]+]] = enum $FakeOptional<B>, #FakeOptional.some!enumelt, [[B_PTR_COPY_FOR_ENUM]] : $B
// XHECK-NEXT: store [[ENUM]] to [init] [[ALLOCA]]
// XHECK-NEXT: [[RESULT:%[0-9]+]] = load [take] [[ALLOCA]]
// XHECK-NEXT: dealloc_stack
// XHECK-NEXT: destroy_value [[B_PTR]]
// XHECK-NEXT: br bb1
// XHECK: bb1:
// XHECK-NEXT: // function_ref
// XHECK-NEXT: function_ref
// XHECK-NEXT: apply
// XHECK-NEXT: destroy_value [[B_PTR_COPY_NOT_OBSTRUCTING]]
// XHECK-NEXT: return [[RESULT]]
// XHECK: } // end sil function 'enum_promotion_case2'
// CHECK-NEXT: [[B_PTR_COPY_NOT_OBSTRUCTING:%.*]] = copy_value [[B_PTR]]
// CHECK-NEXT: [[ENUM:%[0-9]+]] = enum $FakeOptional<B>, #FakeOptional.some!enumelt, [[B_PTR_COPY_FOR_ENUM]] : $B
// CHECK-NEXT: store [[ENUM]] to [init] [[ALLOCA]]
// CHECK-NEXT: [[RESULT:%[0-9]+]] = load [take] [[ALLOCA]]
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: destroy_value [[B_PTR]]
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: // function_ref
// CHECK-NEXT: function_ref
// CHECK-NEXT: apply
// CHECK-NEXT: destroy_value [[B_PTR_COPY_NOT_OBSTRUCTING]]
// CHECK-NEXT: return [[RESULT]]
// CHECK: } // end sil function 'enum_promotion_case2'
sil [ossa] @enum_promotion_case2 : $@convention(thin) (@owned B) -> @owned FakeOptional<B> {
bb0(%0 : @owned $B):
%2 = alloc_stack $FakeOptional<B>
Expand All @@ -1428,27 +1428,27 @@ bb1:

// Negative test corresponding to the previous test.
// CHECK-LABEL: sil [ossa] @no_enum_promotion_of_non_concrete_types
// XHECK: bb0
// XHECK-NEXT: alloc_stack $FakeOptional<T>
// XHECK-NEXT: inject_enum_addr {{%[0-9]+}} : $*FakeOptional<T>, #FakeOptional.none!enumelt
// XHECK-NEXT: alloc_stack $FakeOptional<T>
// XHECK-NEXT: init_enum_data_addr {{%[0-9]+}} : $*FakeOptional<T>, #FakeOptional.some!enumelt
// XHECK-NEXT: copy_addr
// XHECK-NEXT: inject_enum_addr
// XHECK-NEXT: cond_br
// XHECK: bb1:
// XHECK-NEXT: copy_addr
// XHECK-NEXT: destroy_addr
// XHECK-NEXT: br bb3
// XHECK: bb2:
// XHECK-NEXT: copy_addr
// XHECK-NEXT: destroy_addr
// XHECK-NEXT: br bb3
// XHECK: bb3
// XHECK-NEXT: tuple
// XHECK-NEXT: dealloc_stack
// XHECK-NEXT: dealloc_stack
// XHECK-NEXT: return
// CHECK: bb0
// CHECK-NEXT: alloc_stack $FakeOptional<T>
// CHECK-NEXT: inject_enum_addr {{%[0-9]+}} : $*FakeOptional<T>, #FakeOptional.none!enumelt
// CHECK-NEXT: alloc_stack $FakeOptional<T>
// CHECK-NEXT: init_enum_data_addr {{%[0-9]+}} : $*FakeOptional<T>, #FakeOptional.some!enumelt
// CHECK-NEXT: copy_addr
// CHECK-NEXT: inject_enum_addr
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: copy_addr
// CHECK-NEXT: destroy_addr
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: copy_addr
// CHECK-NEXT: destroy_addr
// CHECK-NEXT: br bb3
// CHECK: bb3
// CHECK-NEXT: tuple
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: return
sil [ossa] @no_enum_promotion_of_non_concrete_types : $@convention(thin) <T> (@inout T, Builtin.Int1) -> @out FakeOptional<T> {
bb0(%0 : $*FakeOptional<T>, %1 : $*T, %2 : $Builtin.Int1):
%3 = alloc_stack $FakeOptional<T>
Expand Down
Loading