Skip to content

Commit 73e36e2

Browse files
committed
SimplifyDestructure: canonicalize destructure_tuple and destructure_struct for trivial tuples/structs
Replace destructure_tuple with tuple_extract instructions and destructure_struct with struct_extract instructions. This canonicalization helps other optimizations to e.g. CSE tuple_extract/struct_extract.
1 parent 1bd74d1 commit 73e36e2

File tree

3 files changed

+91
-1
lines changed

3 files changed

+91
-1
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyDestructure.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ import SIL
1515
extension DestructureTupleInst : OnoneSimplifyable, SILCombineSimplifyable {
1616
func simplify(_ context: SimplifyContext) {
1717

18+
// If the tuple is trivial, replace
19+
// ```
20+
// (%1, %2) = destructure_tuple %t
21+
// ```
22+
// ->
23+
// ```
24+
// %1 = tuple_extract %t, 0
25+
// %2 = tuple_extract %t, 1
26+
// ```
27+
// This canonicalization helps other optimizations to e.g. CSE tuple_extracts.
28+
//
29+
if replaceWithTupleExtract(context) {
30+
return
31+
}
32+
1833
// Eliminate the redundant instruction pair
1934
// ```
2035
// %t = tuple (%0, %1, %2)
@@ -26,11 +41,39 @@ extension DestructureTupleInst : OnoneSimplifyable, SILCombineSimplifyable {
2641
tryReplaceConstructDestructPair(construct: tuple, destruct: self, context)
2742
}
2843
}
44+
45+
private func replaceWithTupleExtract(_ context: SimplifyContext) -> Bool {
46+
guard self.tuple.type.isTrivial(in: parentFunction) else {
47+
return false
48+
}
49+
let builder = Builder(before: self, context)
50+
for (elementIdx, result) in results.enumerated() {
51+
let elementValue = builder.createTupleExtract(tuple: self.tuple, elementIndex: elementIdx)
52+
result.uses.replaceAll(with: elementValue, context)
53+
}
54+
context.erase(instruction: self)
55+
return true
56+
}
2957
}
3058

3159
extension DestructureStructInst : OnoneSimplifyable, SILCombineSimplifyable {
3260
func simplify(_ context: SimplifyContext) {
3361

62+
// If the struct is trivial, replace
63+
// ```
64+
// (%1, %2) = destructure_struct %s
65+
// ```
66+
// ->
67+
// ```
68+
// %1 = struct_extract %s, #S.field0
69+
// %2 = struct_extract %s, #S.field1
70+
// ```
71+
// This canonicalization helps other optimizations to e.g. CSE tuple_extracts.
72+
//
73+
if replaceWithStructExtract(context) {
74+
return
75+
}
76+
3477
switch self.struct {
3578
case let str as StructInst:
3679
// Eliminate the redundant instruction pair
@@ -82,6 +125,19 @@ extension DestructureStructInst : OnoneSimplifyable, SILCombineSimplifyable {
82125
break
83126
}
84127
}
128+
129+
private func replaceWithStructExtract(_ context: SimplifyContext) -> Bool {
130+
guard self.struct.type.isTrivial(in: parentFunction) else {
131+
return false
132+
}
133+
let builder = Builder(before: self, context)
134+
for (fieldIdx, result) in results.enumerated() {
135+
let fieldValue = builder.createStructExtract(struct: self.struct, fieldIndex: fieldIdx)
136+
result.uses.replaceAll(with: fieldValue, context)
137+
}
138+
context.erase(instruction: self)
139+
return true
140+
}
85141
}
86142

87143
private func tryReplaceConstructDestructPair(construct: SingleValueInstruction,

test/SILOptimizer/simplify_destructure_struct.sil

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,13 @@ struct S {
1111
@_hasStorage let b: Int
1212
}
1313

14+
struct TrivialS {
15+
@_hasStorage let a: Int
16+
@_hasStorage let b: Int
17+
}
18+
1419
// CHECK-LABEL: sil [ossa] @forward_owned :
15-
// CHECK-NOT: destructure_tuple
20+
// CHECK-NOT: destructure_struct
1621
// CHECK: fix_lifetime %1
1722
// CHECK: return %0
1823
// CHECK: } // end sil function 'forward_owned'
@@ -141,3 +146,15 @@ bb2:
141146
return %4 : $String
142147
}
143148

149+
// CHECK-LABEL: sil [ossa] @to_struct_extract :
150+
// CHECK: %1 = struct_extract %0 : $TrivialS, #TrivialS.a
151+
// CHECK: %2 = struct_extract %0 : $TrivialS, #TrivialS.b
152+
// CHECK: %3 = tuple (%1 : $Int, %2 : $Int)
153+
// CHECK: } // end sil function 'to_struct_extract'
154+
sil [ossa] @to_struct_extract : $@convention(thin) (TrivialS) -> (Int, Int) {
155+
bb0(%0 : $TrivialS):
156+
(%1, %2) = destructure_struct %0
157+
%3 = tuple (%1, %2)
158+
return %3
159+
}
160+

test/SILOptimizer/simplify_destructure_tuple.sil

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
import Swift
77
import Builtin
88

9+
struct TrivialS {
10+
@_hasStorage let a: Int
11+
@_hasStorage let b: Int
12+
}
13+
914
// CHECK-LABEL: sil [ossa] @forward_owned :
1015
// CHECK-NOT: destructure_tuple
1116
// CHECK: fix_lifetime %1
@@ -65,3 +70,15 @@ bb0(%0 : @owned $String, %1 : $Int):
6570
return %4 : $String
6671
}
6772

73+
// CHECK-LABEL: sil [ossa] @to_tuple_extract :
74+
// CHECK: %1 = tuple_extract %0 : $(Int, Int), 0
75+
// CHECK: %2 = tuple_extract %0 : $(Int, Int), 1
76+
// CHECK: %3 = struct $TrivialS (%1 : $Int, %2 : $Int)
77+
// CHECK: } // end sil function 'to_tuple_extract'
78+
sil [ossa] @to_tuple_extract : $@convention(thin) ((Int, Int)) -> TrivialS {
79+
bb0(%0 : $(Int, Int)):
80+
(%1, %2) = destructure_tuple %0
81+
%3 = struct $TrivialS (%1, %2)
82+
return %3
83+
}
84+

0 commit comments

Comments
 (0)