Skip to content

Commit eb6c353

Browse files
committed
[irgen] Use temporary initializer for switch address subject.
If the subject of a switch statement is an address, use an initializer to initialize it. This produces better codegen in some cases.
1 parent 456775f commit eb6c353

12 files changed

+207
-53
lines changed

lib/SILGen/SILGenPattern.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,8 +2126,12 @@ void PatternMatchEmission::emitEnumElementDispatch(
21262126
llvm_unreachable("not allowed");
21272127
case CastConsumptionKind::CopyOnSuccess: {
21282128
auto copy = SGF.emitTemporaryAllocation(loc, srcValue->getType());
2129+
// If the element is trivial it won't be destroyed so we need to make
2130+
// sure we destroy the enum here.
2131+
if (eltTL->isTrivial())
2132+
copy = SGF.emitManagedBufferWithCleanup(copy).getValue();
21292133
SGF.B.createCopyAddr(loc, srcValue, copy, IsNotTake, IsInitialization);
2130-
// We can always take from the copy.
2134+
// We can always take from the managedCopy.
21312135
eltConsumption = CastConsumptionKind::TakeAlways;
21322136
eltValue = SGF.B.createUncheckedTakeEnumDataAddr(loc, copy, elt, eltTy);
21332137
break;
@@ -2758,8 +2762,17 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
27582762

27592763
// Emit the subject value. If at +1, dispatching will consume it. If it is at
27602764
// +0, we just forward down borrows.
2761-
ManagedValue subjectMV = emitRValueAsSingleValue(
2762-
S->getSubjectExpr(), SGFContext::AllowGuaranteedPlusZero);
2765+
// If this is an address type, use an initializer.
2766+
ManagedValue subjectMV;
2767+
if (getTypeLowering(S->getSubjectExpr()->getType()).isAddress()) {
2768+
auto init = emitTemporary(S->getSubjectExpr(), getTypeLowering(S->getSubjectExpr()->getType()));
2769+
emitExprInto(S->getSubjectExpr(), init.get());
2770+
subjectMV = emitManagedRValueWithCleanup(init->getAddressForInPlaceInitialization(*this, S->getSubjectExpr()));
2771+
subjectMV = ManagedValue::forUnmanaged(subjectMV.forward(*this));
2772+
} else {
2773+
subjectMV = emitRValueAsSingleValue(S->getSubjectExpr(),
2774+
SGFContext::AllowGuaranteedPlusZero);
2775+
}
27632776

27642777
// Inline constructor for subject.
27652778
auto subject = ([&]() -> ConsumableManagedValue {
@@ -2787,7 +2800,7 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
27872800
// If we have an address only type returned without a cleanup, we
27882801
// need to do a copy just to be safe. So for efficiency we pass it
27892802
// down take_always.
2790-
return {subjectMV.copy(*this, S), CastConsumptionKind::TakeAlways};
2803+
return {subjectMV, CastConsumptionKind::CopyOnSuccess};
27912804
}());
27922805

27932806
// If we need to diagnose an unexpected enum case or unexpected enum case

test/Interpreter/switch.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,24 @@ SwitchTestSuite.test("Enum Initialization Leaks") {
282282
}
283283
}
284284

285+
SwitchTestSuite.test("Destroy all elements: protocol and trivial") {
286+
enum ProtocolAndTrivial {
287+
case a(P)
288+
case b(Int)
289+
case c(LifetimeTracked)
290+
}
291+
292+
func testEnumWithProtocolAndTrivial(_ e: ProtocolAndTrivial) -> Bool {
293+
switch (e) {
294+
case .a(_): return true
295+
case .b(_): return true
296+
case .c(_): return true
297+
}
298+
}
299+
300+
do {
301+
testEnumWithProtocolAndTrivial(.c(LifetimeTracked(0)))
302+
}
303+
}
304+
285305
runAllTests()
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %target-swift-emit-silgen -module-name switch %s > %t.sil
2+
// RUN: %target-sil-opt %t.sil -sil-combine -enable-sil-verify-all | %FileCheck %s
3+
4+
protocol P { }
5+
6+
enum ProtocolAndTrivial {
7+
case a(P)
8+
case b(Int)
9+
}
10+
11+
// This test is mainly checking the verifier doesn't error but, just in case,
12+
// there's also a filecheck test.
13+
// CHECK-LABEL: @$s6switch4testyyAA18ProtocolAndTrivialO_ADtF
14+
// CHECK: [[C1:%[0-9]+]] = alloc_stack $(ProtocolAndTrivial, ProtocolAndTrivial)
15+
16+
// CHECK: [[CA:%[0-9]+]] = tuple_element_addr [[C1]] : $*(ProtocolAndTrivial, ProtocolAndTrivial), 0
17+
// CHECK: [[CB:%[0-9]+]] = tuple_element_addr [[C1]] : $*(ProtocolAndTrivial, ProtocolAndTrivial), 1
18+
19+
// CHECK: copy_addr %0 to [initialization] [[CA]]
20+
// CHECK: copy_addr %1 to [initialization] [[CB]]
21+
22+
// CHECK: [[CA:%[0-9]+]] = tuple_element_addr [[C1]] : $*(ProtocolAndTrivial, ProtocolAndTrivial), 0
23+
// CHECK: [[CB:%[0-9]+]] = tuple_element_addr [[C1]] : $*(ProtocolAndTrivial, ProtocolAndTrivial), 1
24+
25+
// CHECK: switch_enum_addr [[CA]] : $*ProtocolAndTrivial, case #ProtocolAndTrivial.a!enumelt: [[BBA:bb[0-9]+]], case #ProtocolAndTrivial.b!enumelt: [[BBB:bb[0-9]+]]
26+
27+
// We're only testing the trivial case.
28+
// CHECK: [[BBB]]:
29+
// CHECK: [[TMP:%[0-9]+]] = alloc_stack $ProtocolAndTrivial
30+
// CHECK: switch_enum_addr [[CB]] : $*ProtocolAndTrivial, case #ProtocolAndTrivial.b!enumelt: [[BOTH_B:bb[0-9]+]], default [[DEFAULT:bb[0-9]+]]
31+
32+
// Make sure that we destroy everything.
33+
// CHECK: [[BOTH_B]]:
34+
// CHECK: [[TMP2:%[0-9]+]] = alloc_stack $ProtocolAndTrivial
35+
// CHECK: destroy_addr [[TMP2]]
36+
// CHECK: destroy_addr [[TMP]]
37+
// CHECK: destroy_addr [[C1]]
38+
39+
// CHECK-LABEL: end sil function '$s6switch4testyyAA18ProtocolAndTrivialO_ADtF'
40+
func test(_ a : ProtocolAndTrivial, _ b : ProtocolAndTrivial) {
41+
switch ((a, b)) {
42+
case (.a(_), .a(_)): _ = ()
43+
case (.b(_), .b(_)): _ = ()
44+
default: _ = ()
45+
}
46+
}

test/SILGen/enum_resilience.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,36 @@ import resilient_enum
1010
// a default case
1111

1212
// CHECK-LABEL: sil hidden [ossa] @$s15enum_resilience15resilientSwitchyy0c1_A06MediumOF : $@convention(thin) (@in_guaranteed Medium) -> ()
13-
// CHECK: [[BOX:%.*]] = alloc_stack $Medium
13+
// CHECK: [[BOX:%.*]] = alloc_stack $Medium
1414
// CHECK-NEXT: copy_addr %0 to [initialization] [[BOX]]
1515
// CHECK-NEXT: [[METATYPE:%.+]] = value_metatype $@thick Medium.Type, [[BOX]] : $*Medium
1616
// CHECK-NEXT: switch_enum_addr [[BOX]] : $*Medium, case #Medium.Paper!enumelt: bb1, case #Medium.Canvas!enumelt: bb2, case #Medium.Pamphlet!enumelt: bb3, case #Medium.Postcard!enumelt: bb4, default bb5
1717
// CHECK: bb1:
18+
// CHECK-NEXT: destroy_addr [[BOX]]
1819
// CHECK-NEXT: dealloc_stack [[BOX]]
1920
// CHECK-NEXT: br bb6
2021
// CHECK: bb2:
22+
// CHECK-NEXT: destroy_addr [[BOX]]
2123
// CHECK-NEXT: dealloc_stack [[BOX]]
2224
// CHECK-NEXT: br bb6
2325
// CHECK: bb3:
24-
// CHECK-NEXT: [[INDIRECT_ADDR:%.*]] = unchecked_take_enum_data_addr [[BOX]]
26+
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $Medium
27+
// CHECK-NEXT: copy_addr
28+
// CHECK-NEXT: [[INDIRECT_ADDR:%.*]] = unchecked_take_enum_data_addr [[TMP]]
2529
// CHECK-NEXT: [[INDIRECT:%.*]] = load [take] [[INDIRECT_ADDR]]
2630
// CHECK-NEXT: [[PAYLOAD:%.*]] = project_box [[INDIRECT]]
2731
// CHECK-NEXT: destroy_value [[INDIRECT]]
32+
// CHECK-NEXT: dealloc_stack [[TMP]]
33+
// CHECK-NEXT: destroy_addr [[BOX]]
2834
// CHECK-NEXT: dealloc_stack [[BOX]]
2935
// CHECK-NEXT: br bb6
3036
// CHECK: bb4:
31-
// CHECK-NEXT: [[PAYLOAD_ADDR:%.*]] = unchecked_take_enum_data_addr [[BOX]]
37+
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $Medium
38+
// CHECK-NEXT: copy_addr
39+
// CHECK-NEXT: [[PAYLOAD_ADDR:%.*]] = unchecked_take_enum_data_addr [[TMP]]
3240
// CHECK-NEXT: destroy_addr [[PAYLOAD_ADDR]]
41+
// CHECK-NEXT: dealloc_stack [[TMP]]
42+
// CHECK-NEXT: destroy_addr [[BOX]]
3343
// CHECK-NEXT: dealloc_stack [[BOX]]
3444
// CHECK-NEXT: br bb6
3545
// CHECK: bb5:

test/SILGen/enum_resilience_testable.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,31 @@
2020
// CHECK-NEXT: [[METATYPE:%.+]] = value_metatype $@thick Medium.Type, [[BOX]] : $*Medium
2121
// CHECK-NEXT: switch_enum_addr [[BOX]] : $*Medium, case #Medium.Paper!enumelt: bb1, case #Medium.Canvas!enumelt: bb2, case #Medium.Pamphlet!enumelt: bb3, case #Medium.Postcard!enumelt: bb4, default bb5
2222
// CHECK: bb1:
23+
// CHECK-NEXT: destroy_addr [[BOX]]
2324
// CHECK-NEXT: dealloc_stack [[BOX]]
2425
// CHECK-NEXT: br bb6
2526
// CHECK: bb2:
27+
// CHECK-NEXT: destroy_addr [[BOX]]
2628
// CHECK-NEXT: dealloc_stack [[BOX]]
2729
// CHECK-NEXT: br bb6
2830
// CHECK: bb3:
29-
// CHECK-NEXT: [[INDIRECT_ADDR:%.*]] = unchecked_take_enum_data_addr [[BOX]]
31+
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $Medium
32+
// CHECK-NEXT: copy_addr
33+
// CHECK-NEXT: [[INDIRECT_ADDR:%.*]] = unchecked_take_enum_data_addr [[TMP]]
3034
// CHECK-NEXT: [[INDIRECT:%.*]] = load [take] [[INDIRECT_ADDR]]
3135
// CHECK-NEXT: [[PAYLOAD:%.*]] = project_box [[INDIRECT]]
3236
// CHECK-NEXT: destroy_value [[INDIRECT]]
37+
// CHECK-NEXT: dealloc_stack [[TMP]]
38+
// CHECK-NEXT: destroy_addr [[BOX]]
3339
// CHECK-NEXT: dealloc_stack [[BOX]]
3440
// CHECK-NEXT: br bb6
3541
// CHECK: bb4:
36-
// CHECK-NEXT: [[PAYLOAD_ADDR:%.*]] = unchecked_take_enum_data_addr [[BOX]]
42+
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $Medium
43+
// CHECK-NEXT: copy_addr
44+
// CHECK-NEXT: [[PAYLOAD_ADDR:%.*]] = unchecked_take_enum_data_addr [[TMP]]
3745
// CHECK-NEXT: destroy_addr [[PAYLOAD_ADDR]]
46+
// CHECK-NEXT: dealloc_stack [[TMP]]
47+
// CHECK-NEXT: destroy_addr [[BOX]]
3848
// CHECK-NEXT: dealloc_stack [[BOX]]
3949
// CHECK-NEXT: br bb6
4050
// CHECK: bb5:

test/SILGen/indirect_enum.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ func switchTreeA<T>(_ x: TreeA<T>) {
225225

226226
// CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum11switchTreeB{{[_0-9a-zA-Z]*}}F
227227
func switchTreeB<T>(_ x: TreeB<T>) {
228-
// CHECK: copy_addr %0 to [initialization] [[SCRATCH:%.*]] :
228+
// CHECK: [[SCRATCH:%.*]] = alloc_stack
229+
// CHECK: copy_addr %0 to [initialization] [[SCRATCH]]
229230
// CHECK: switch_enum_addr [[SCRATCH]]
230231
switch x {
231232

0 commit comments

Comments
 (0)