Skip to content

Commit d6ffddc

Browse files
committed
[sil-combine] Add ownership support for switch_enum_addr transforms.
I implemented specifically the promotion from switch_enum_addr -> switch_enum for loadable types. NOTE: I did not add support for the inject_enum_addr based conversion to br since that optimization deletes an edge from the CFG, potentially invalidating CFG properties. Since OSSA needs to use DeadEndBlocks, we want to avoid changing reachability in any way with our SILCombine transforms.
1 parent ba23002 commit d6ffddc

File tree

3 files changed

+66
-31
lines changed

3 files changed

+66
-31
lines changed

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -147,20 +147,24 @@ static EnumElementDecl *getInjectEnumCaseTo(SILValue Addr) {
147147
}
148148

149149
SILInstruction *SILCombiner::visitSwitchEnumAddrInst(SwitchEnumAddrInst *SEAI) {
150-
if (SEAI->getFunction()->hasOwnership())
151-
return nullptr;
150+
SILValue Addr = SEAI->getOperand();
152151

153152
// Convert switch_enum_addr -> br
154-
// if the only thing which writes to the address is an inject_enum_addr.
155-
SILValue Addr = SEAI->getOperand();
156-
if (EnumElementDecl *EnumCase = getInjectEnumCaseTo(Addr)) {
157-
SILBasicBlock *Dest = SEAI->getCaseDestination(EnumCase);
158-
// If the only instruction which writes to Addr is an inject_enum_addr we
159-
// know that there cannot be an enum payload.
160-
assert(Dest->getNumArguments() == 0 &&
161-
"didn't expect a payload argument");
162-
Builder.createBranch(SEAI->getLoc(), Dest);
163-
return eraseInstFromFunction(*SEAI);
153+
//
154+
// If the only thing which writes to the address is an inject_enum_addr. We
155+
// only perform these optimizations when we are not in OSSA since this
156+
// eliminates an edge from the CFG and we want SILCombine in OSSA to never do
157+
// that, so in the future we can invalidate less.
158+
if (!SEAI->getFunction()->hasOwnership()) {
159+
if (EnumElementDecl *EnumCase = getInjectEnumCaseTo(Addr)) {
160+
SILBasicBlock *Dest = SEAI->getCaseDestination(EnumCase);
161+
// If the only instruction which writes to Addr is an inject_enum_addr we
162+
// know that there cannot be an enum payload.
163+
assert(Dest->getNumArguments() == 0 &&
164+
"didn't expect a payload argument");
165+
Builder.createBranch(SEAI->getLoc(), Dest);
166+
return eraseInstFromFunction(*SEAI);
167+
}
164168
}
165169

166170
SILType Ty = Addr->getType();
@@ -172,15 +176,44 @@ SILInstruction *SILCombiner::visitSwitchEnumAddrInst(SwitchEnumAddrInst *SEAI) {
172176
// ->
173177
// %value = load %ptr
174178
// switch_enum %value
175-
SmallVector<std::pair<EnumElementDecl*, SILBasicBlock*>, 8> Cases;
176-
for (int i = 0, e = SEAI->getNumCases(); i < e; ++i)
179+
//
180+
// If we are using ownership, we perform a load_borrow right before the new
181+
// switch_enum and end the borrow scope right afterwards.
182+
Builder.setCurrentDebugScope(SEAI->getDebugScope());
183+
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 8> Cases;
184+
for (int i : range(SEAI->getNumCases())) {
177185
Cases.push_back(SEAI->getCase(i));
186+
}
178187

179-
Builder.setCurrentDebugScope(SEAI->getDebugScope());
180188
SILBasicBlock *Default = SEAI->hasDefault() ? SEAI->getDefaultBB() : nullptr;
181-
LoadInst *EnumVal = Builder.createLoad(SEAI->getLoc(), Addr,
182-
LoadOwnershipQualifier::Unqualified);
183-
Builder.createSwitchEnum(SEAI->getLoc(), EnumVal, Default, Cases);
189+
SILValue EnumVal = Builder.emitLoadBorrowOperation(SEAI->getLoc(), Addr);
190+
auto *sei = Builder.createSwitchEnum(SEAI->getLoc(), EnumVal, Default, Cases);
191+
192+
if (Builder.hasOwnership()) {
193+
for (int i : range(sei->getNumCases())) {
194+
auto c = sei->getCase(i);
195+
if (c.first->hasAssociatedValues()) {
196+
auto eltType = Addr->getType().getEnumElementType(
197+
c.first, Builder.getModule(), Builder.getTypeExpansionContext());
198+
eltType = eltType.getObjectType();
199+
if (eltType.isTrivial(Builder.getFunction())) {
200+
c.second->createPhiArgument(eltType, OwnershipKind::None);
201+
} else {
202+
c.second->createPhiArgument(eltType, OwnershipKind::Guaranteed);
203+
}
204+
}
205+
Builder.setInsertionPoint(c.second->front().getIterator());
206+
Builder.emitEndBorrowOperation(SEAI->getLoc(), EnumVal);
207+
}
208+
209+
if (auto defaultBlock = sei->getDefaultBBOrNull()) {
210+
defaultBlock.get()->createPhiArgument(EnumVal->getType(),
211+
OwnershipKind::Guaranteed);
212+
Builder.setInsertionPoint(defaultBlock.get()->front().getIterator());
213+
Builder.emitEndBorrowOperation(SEAI->getLoc(), EnumVal);
214+
}
215+
}
216+
184217
return eraseInstFromFunction(*SEAI);
185218
}
186219

test/SILOptimizer/sil_combine_enums_ossa.sil

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ enum Numerals {
2020

2121
sil [ossa] @external_func: $@convention(thin) () -> ()
2222

23-
// CHECK-LABEL: eliminate_sw_enum_addr
24-
// XHECK-NOT: switch_enum_addr
25-
// XHECK: switch_enum
26-
// XHECK: return
23+
// CHECK-LABEL: sil [ossa] @eliminate_sw_enum_addr : $@convention(thin) () -> Int {
24+
// CHECK-NOT: switch_enum_addr
25+
// CHECK: switch_enum {{%.*}} :
26+
// CHECK: } // end sil function 'eliminate_sw_enum_addr'
2727
sil [ossa] @eliminate_sw_enum_addr : $@convention(thin) () -> Int {
2828
bb0:
2929
%0 = alloc_stack $Optional<SomeClass>, var, name "x" // users: %2, %4, %5, %17, %19

test/SILOptimizer/sil_combine_ossa.sil

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,14 +1531,15 @@ bb0(%0 : @owned $E):
15311531
}
15321532

15331533
// CHECK-LABEL: sil [ossa] @unchecked_take_enum_data_addr_promotion : $@convention(thin) (@inout FakeOptional<B>) -> @owned B {
1534-
// XHECK: bb0(%0 : $*FakeOptional<B>):
1535-
// XHECK-NEXT: [[BORROW:%.*]] = load_borrow
1536-
// XHECK-NEXT: switch_enum [[BORROW]]
1537-
// XHECK: bb1([[GUARANTEED:%.*]] : @guaranteed
1538-
// XHECK-NEXT: end_borrow [[BORROW]]
1534+
// CHECK: bb0(%0 : $*FakeOptional<B>):
1535+
// CHECK-NEXT: [[BORROW:%.*]] = load_borrow
1536+
// CHECK-NEXT: switch_enum [[BORROW]]
1537+
// CHECK: bb1([[GUARANTEED:%.*]] : @guaranteed
1538+
// CHECK-NEXT: end_borrow [[BORROW]]
15391539
// XHECK-NEXT: load
15401540
// XHECK-NEXT: unchecked_enum_data
15411541
// XHECK-NEXT: return
1542+
// CHECK: } /
15421543
sil [ossa] @unchecked_take_enum_data_addr_promotion : $@convention(thin) (@inout FakeOptional<B>) -> @owned B {
15431544
bb0(%0 : $*FakeOptional<B>):
15441545
switch_enum_addr %0 : $*FakeOptional<B>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
@@ -2477,10 +2478,11 @@ struct FakeInt32 {
24772478
}
24782479

24792480
// CHECK-LABEL: sil [ossa] @loadable_switchenumaddr_promotion : $@convention(thin) (@in FakeOptional<Builtin.RawPointer>) -> () {
2480-
// XHECK-NOT: switch_enum_addr
2481-
// XHECK: load
2482-
// XHECK-NEXT: switch_enum
2483-
// XHECK-NOT: switch_enum_addr
2481+
// CHECK-NOT: switch_enum_addr
2482+
// CHECK: load
2483+
// CHECK-NEXT: switch_enum
2484+
// CHECK-NOT: switch_enum_addr
2485+
// CHECK: } // end sil function 'loadable_switchenumaddr_promotion'
24842486
sil [ossa] @loadable_switchenumaddr_promotion : $@convention(thin) (@in FakeOptional<Builtin.RawPointer>) -> () {
24852487
bb0(%0 : $*FakeOptional<Builtin.RawPointer>):
24862488
switch_enum_addr %0 : $*FakeOptional<Builtin.RawPointer>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2

0 commit comments

Comments
 (0)