@@ -14,148 +14,138 @@ import SIL
1414
1515extension DestructureTupleInst : OnoneSimplifiable , SILCombineSimplifiable {
1616 func simplify( _ context: SimplifyContext ) {
17+ foldWithAggregateConstruction ( context)
18+ }
19+ }
1720
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- }
21+ extension DestructureStructInst : OnoneSimplifiable , SILCombineSimplifiable {
22+ func simplify( _ context: SimplifyContext ) {
23+ foldWithAggregateConstruction ( context)
24+ }
25+ }
3226
33- // Eliminate the redundant instruction pair
34- // ```
35- // %t = tuple (%0, %1, %2)
36- // (%3, %4, %5) = destructure_tuple %t
37- // ```
38- // and replace the results %3, %4, %5 with %0, %1, %2, respectively
39- //
40- if let tuple = self . tuple as? TupleInst {
41- tryReplaceConstructDestructPair ( construct: tuple, destruct: self , context)
42- }
27+ private protocol DestructureInstruction : MultipleValueInstruction {
28+ func createExtract( of aggregate: Value , elementIndex: Int , using builder: Builder ) -> Value
29+ }
30+
31+ extension DestructureTupleInst : DestructureInstruction {
32+ func createExtract( of aggregate: Value , elementIndex: Int , using builder: Builder ) -> Value {
33+ return builder. createTupleExtract ( tuple: aggregate, elementIndex: elementIndex)
4334 }
35+ }
4436
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
37+ extension DestructureStructInst : DestructureInstruction {
38+ func createExtract( of aggregate: Value , elementIndex: Int , using builder: Builder ) -> Value {
39+ return builder. createStructExtract ( struct: aggregate, fieldIndex: elementIndex)
5640 }
5741}
5842
59- extension DestructureStructInst : OnoneSimplifiable , SILCombineSimplifiable {
60- func simplify( _ context: SimplifyContext ) {
43+ private protocol ConstructureInstruction : SingleValueInstruction { }
44+
45+ extension TupleInst : ConstructureInstruction { }
46+ extension StructInst : ConstructureInstruction { }
47+
48+ private extension DestructureInstruction {
49+ var aggregate : Value { operands [ 0 ] . value }
50+
51+ func foldWithAggregateConstruction( _ context: SimplifyContext ) {
6152
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) {
53+ if aggregate. type. isTrivial ( in: parentFunction) {
54+ // ```
55+ // (%1, %2) = destructure_tuple %t
56+ // ```
57+ // ->
58+ // ```
59+ // %1 = tuple_extract %t, 0
60+ // %2 = tuple_extract %t, 1
61+ // ```
62+ replaceWithAggregateExtract ( context)
7463 return
7564 }
7665
77- switch self . struct {
78- case let str as StructInst :
66+ switch aggregate {
67+ case let constructInst as ConstructureInstruction :
7968 // Eliminate the redundant instruction pair
8069 // ```
81- // %s = struct (%0, %1, %2)
82- // (%3, %4, %5) = destructure_struct %s
70+ // %t = tuple (%0, %1, %2)
71+ // (%3, %4, %5) = destructure_tuple %t
8372 // ```
8473 // and replace the results %3, %4, %5 with %0, %1, %2, respectively
8574 //
86- tryReplaceConstructDestructPair ( construct : str , destruct : self , context)
75+ tryFoldWithConstructure ( constructure : constructInst , context)
8776
8877 case let copy as CopyValueInst :
8978 // Similar to the pattern above, but with a copy_value:
9079 // Replace
9180 // ```
92- // %s = struct (%0, %1, %2)
93- // %c = copy_value %s // can also be a chain of multiple copies
94- // (%3, %4, %5) = destructure_struct %c
81+ // %t = tuple (%0, %1, %2)
82+ // %c = copy_value %t // can also be a chain of multiple copies
83+ // (%3, %4, %5) = destructure_tuple %c
9584 // ```
9685 // with
9786 // ```
9887 // %c0 = copy_value %0
9988 // %c1 = copy_value %1
10089 // %c2 = copy_value %2
101- // %s = struct (%0, %1, %2) // keep the original struct
90+ // %s = tuple (%0, %1, %2) // keep the original tuple/ struct instruction
10291 // ```
10392 // and replace the results %3, %4, %5 with %c0, %c1, %c2, respectively.
10493 //
105- // This transformation has the advantage that we can do it even if the `struct` instruction
94+ // This transformation has the advantage that we can do it even if the `tuple`/` struct`
10695 // has other uses than the `copy_value`.
10796 //
108- if copy. uses. singleUse? . instruction == self ,
109- let structInst = copy. fromValue. lookThroughCopy as? StructInst ,
110- structInst. parentBlock == self . parentBlock
111- {
112- for (result, operand) in zip ( self . results, structInst. operands) {
113- if operand. value. type. isTrivial ( in: parentFunction) {
114- result. uses. replaceAll ( with: operand. value, context)
115- } else {
116- let builder = Builder ( before: structInst, context)
117- let copiedOperand = builder. createCopyValue ( operand: operand. value)
118- result. uses. replaceAll ( with: copiedOperand, context)
119- }
120- }
121- context. erase ( instruction: self )
122- context. erase ( instruction: copy)
123- }
97+ tryFoldWithCopyOfConstructure ( copy: copy, context)
98+
12499 default :
125100 break
126101 }
127102 }
128103
129- private func replaceWithStructExtract( _ context: SimplifyContext ) -> Bool {
130- guard self . struct. type. isTrivial ( in: parentFunction) else {
131- return false
132- }
104+ private func replaceWithAggregateExtract( _ context: SimplifyContext ) {
133105 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)
106+ for (elementIdx , result) in results. enumerated ( ) {
107+ let elementValue = createExtract ( of : aggregate , elementIndex : elementIdx , using : builder )
108+ result. uses. replaceAll ( with: elementValue , context)
137109 }
138110 context. erase ( instruction: self )
139- return true
140111 }
141- }
142112
143- private func tryReplaceConstructDestructPair( construct: SingleValueInstruction ,
144- destruct: MultipleValueInstruction ,
145- _ context: SimplifyContext ) {
146- let singleUse = context. preserveDebugInfo ? construct. uses. singleUse : construct. uses. ignoreDebugUses. singleUse
147- let canEraseFirst = singleUse? . instruction == destruct
113+ private func tryFoldWithConstructure( constructure: ConstructureInstruction , _ context: SimplifyContext ) {
114+ let singleConstructureUse = context. preserveDebugInfo ? constructure. uses. singleUse : constructure. uses. ignoreDebugUses. singleUse
115+ let canEraseConstructure = singleConstructureUse? . instruction == self
148116
149- if !canEraseFirst && construct. parentFunction. hasOwnership && construct. ownership == . owned {
150- // We cannot add more uses to this tuple without inserting a copy.
151- return
152- }
117+ if !canEraseConstructure && constructure. ownership == . owned {
118+ // We cannot add more uses to this tuple/struct without inserting a copy.
119+ return
120+ }
121+
122+ for (result, operand) in zip ( self . results, constructure. operands) {
123+ result. uses. replaceAll ( with: operand. value, context)
124+ }
153125
154- for (result, operand) in zip ( destruct. results, construct. operands) {
155- result. uses. replaceAll ( with: operand. value, context)
126+ context. erase ( instruction: self )
127+ if canEraseConstructure {
128+ context. erase ( instructionIncludingDebugUses: constructure)
129+ }
156130 }
157131
158- if canEraseFirst {
159- context. erase ( instructionIncludingDebugUses: destruct)
132+ private func tryFoldWithCopyOfConstructure( copy: CopyValueInst , _ context: SimplifyContext ) {
133+ guard copy. uses. singleUse? . instruction == self ,
134+ let constructure = copy. fromValue. lookThroughCopy as? ConstructureInstruction ,
135+ constructure. parentBlock == self . parentBlock
136+ else {
137+ return
138+ }
139+ for (result, operand) in zip ( self . results, constructure. operands) {
140+ if operand. value. type. isTrivial ( in: parentFunction) {
141+ result. uses. replaceAll ( with: operand. value, context)
142+ } else {
143+ let builder = Builder ( before: constructure, context)
144+ let copiedOperand = builder. createCopyValue ( operand: operand. value)
145+ result. uses. replaceAll ( with: copiedOperand, context)
146+ }
147+ }
148+ context. erase ( instruction: self )
149+ context. erase ( instruction: copy)
160150 }
161151}
0 commit comments