12
12
13
13
import SIL
14
14
15
- extension LoadInst : OnoneSimplifyable {
15
+ extension LoadInst : OnoneSimplifyable , SILCombineSimplifyable {
16
16
func simplify( _ context: SimplifyContext ) {
17
- replaceLoadOfGlobalLet ( context)
17
+ if optimizeLoadOfAddrUpcast ( context) {
18
+ return
19
+ }
20
+ if optimizeLoadFromStringLiteral ( context) {
21
+ return
22
+ }
23
+ if optmizeLoadFromEmptyCollection ( context) {
24
+ return
25
+ }
26
+ if replaceLoadOfGlobalLet ( context) {
27
+ return
28
+ }
29
+ removeIfDead ( context)
30
+ }
31
+
32
+ /// ```
33
+ /// %1 = unchecked_addr_cast %0 : $*DerivedClass to $*BaseClass
34
+ /// %2 = load %1
35
+ /// ```
36
+ /// is transformed to
37
+ /// ```
38
+ /// %1 = load %0 : $*BaseClass
39
+ /// %2 = upcast %1 : $DerivedClass to $BaseClass
40
+ /// ```
41
+ private func optimizeLoadOfAddrUpcast( _ context: SimplifyContext ) -> Bool {
42
+ if let uac = address as? UncheckedAddrCastInst ,
43
+ uac. type. isExactSuperclass ( of: uac. fromAddress. type) ,
44
+ uac. type != uac. fromAddress. type {
45
+
46
+ operand. set ( to: uac. fromAddress, context)
47
+ let builder = Builder ( before: self , context)
48
+ let newLoad = builder. createLoad ( fromAddress: uac. fromAddress, ownership: ownership)
49
+ let cast = builder. createUpcast ( from: newLoad, to: type)
50
+ uses. replaceAll ( with: cast, context)
51
+ context. erase ( instruction: self )
52
+ return true
53
+ }
54
+ return false
18
55
}
19
56
20
- private func replaceLoadOfGlobalLet( _ context: SimplifyContext ) {
57
+ /// The load from a string literal element, e.g.
58
+ /// ```
59
+ /// %0 = string_literal utf8 "abc"
60
+ /// %1 = integer_literal $Builtin.Word, 1
61
+ /// %2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*UInt8
62
+ /// %3 = index_addr %2 : $*UInt8, %1 : $Builtin.Word
63
+ /// %4 = struct_element_addr %3 : $*UInt8, #UInt8._value
64
+ /// %5 = load %4 : $*Builtin.Int8
65
+ /// ```
66
+ /// is replaced by the character value, e.g. `98` in this example.
67
+ ///
68
+ private func optimizeLoadFromStringLiteral( _ context: SimplifyContext ) -> Bool {
69
+ if self . type. isBuiltinInteger ( withFixedWidth: 8 ) ,
70
+ let sea = self . address as? StructElementAddrInst ,
71
+ let ( baseAddr, index) = sea. struct. getBaseAddressAndOffset ( ) ,
72
+ let pta = baseAddr as? PointerToAddressInst ,
73
+ let stringLiteral = pta. pointer as? StringLiteralInst ,
74
+ stringLiteral. encoding == . UTF8,
75
+ index < stringLiteral. value. count {
76
+
77
+ let builder = Builder ( before: self , context)
78
+ let charLiteral = builder. createIntegerLiteral ( Int ( stringLiteral. value [ index] ) , type: type)
79
+ uses. replaceAll ( with: charLiteral, context)
80
+ context. erase ( instruction: self )
81
+ return true
82
+ }
83
+ return false
84
+ }
85
+
86
+ /// Loading `count` or `capacity` from the empty `Array`, `Set` or `Dictionary` singleton
87
+ /// is replaced by a 0 literal.
88
+ private func optmizeLoadFromEmptyCollection( _ context: SimplifyContext ) -> Bool {
89
+ if self . isZeroLoadFromEmptyCollection ( ) {
90
+ let builder = Builder ( before: self , context)
91
+ let zeroLiteral = builder. createIntegerLiteral ( 0 , type: type)
92
+ uses. replaceAll ( with: zeroLiteral, context)
93
+ context. erase ( instruction: self )
94
+ return true
95
+ }
96
+ return false
97
+ }
98
+
99
+ /// The load of a global let variable is replaced by its static initializer value.
100
+ private func replaceLoadOfGlobalLet( _ context: SimplifyContext ) -> Bool {
21
101
guard let globalInitVal = getGlobalInitValue ( address: address) else {
22
- return
102
+ return false
23
103
}
24
104
if !globalInitVal. canBeCopied ( into: parentFunction, context) {
25
- return
105
+ return false
26
106
}
27
107
var cloner = StaticInitCloner ( cloneBefore: self , context)
28
108
defer { cloner. deinitialize ( ) }
@@ -31,6 +111,94 @@ extension LoadInst : OnoneSimplifyable {
31
111
32
112
uses. replaceAll ( with: initVal, context)
33
113
transitivelyErase ( load: self , context)
114
+ return true
115
+ }
116
+
117
+ private func isZeroLoadFromEmptyCollection( ) -> Bool {
118
+ if !type. isBuiltinInteger {
119
+ return false
120
+ }
121
+ var addr = address
122
+
123
+ // Find the root object of the load-address.
124
+ while true {
125
+ switch addr {
126
+ case let ga as GlobalAddrInst :
127
+ switch ga. global. name {
128
+ case " _swiftEmptyArrayStorage " ,
129
+ " _swiftEmptyDictionarySingleton " ,
130
+ " _swiftEmptySetSingleton " :
131
+ return true
132
+ default :
133
+ return false
134
+ }
135
+ case let sea as StructElementAddrInst :
136
+ let structType = sea. struct. type
137
+ if structType. nominal. name == " _SwiftArrayBodyStorage " {
138
+ switch structType. getNominalFields ( in: parentFunction) . getNameOfField ( withIndex: sea. fieldIndex) {
139
+ case " count " :
140
+ break
141
+ case " _capacityAndFlags " :
142
+ if uses. contains ( where: { !$0. instruction. isShiftRightByAtLeastOne } ) {
143
+ return false
144
+ }
145
+ default :
146
+ return false
147
+ }
148
+ }
149
+ addr = sea. struct
150
+ case let rea as RefElementAddrInst :
151
+ let classType = rea. instance. type
152
+ switch classType. nominal. name {
153
+ case " __RawDictionaryStorage " ,
154
+ " __RawSetStorage " :
155
+ // For Dictionary and Set we support "count" and "capacity".
156
+ switch classType. getNominalFields ( in: parentFunction) . getNameOfField ( withIndex: rea. fieldIndex) {
157
+ case " _count " , " _capacity " :
158
+ break
159
+ default :
160
+ return false
161
+ }
162
+ default :
163
+ break
164
+ }
165
+ addr = rea. instance
166
+ case is UncheckedRefCastInst ,
167
+ is UpcastInst ,
168
+ is RawPointerToRefInst ,
169
+ is AddressToPointerInst ,
170
+ is BeginBorrowInst ,
171
+ is CopyValueInst ,
172
+ is EndCOWMutationInst :
173
+ addr = ( addr as! UnaryInstruction ) . operand. value
174
+ case let mviResult as MultipleValueInstructionResult :
175
+ guard let bci = mviResult. parentInstruction as? BeginCOWMutationInst else {
176
+ return false
177
+ }
178
+ addr = bci. instance
179
+ default :
180
+ return false
181
+ }
182
+ }
183
+ }
184
+
185
+ /// Removes the `load [copy]` if the loaded value is just destroyed.
186
+ private func removeIfDead( _ context: SimplifyContext ) {
187
+ if ownership == . copy,
188
+ loadedValueIsDead ( context) {
189
+ for use in uses {
190
+ context. erase ( instruction: use. instruction)
191
+ }
192
+ context. erase ( instruction: self )
193
+ }
194
+ }
195
+
196
+ private func loadedValueIsDead( _ context: SimplifyContext ) -> Bool {
197
+ if context. preserveDebugInfo {
198
+ return !uses. contains { !( $0. instruction is DestroyValueInst ) }
199
+ } else {
200
+ return !nonDebugUses. contains { !( $0. instruction is DestroyValueInst ) }
201
+ }
34
202
}
35
203
}
36
204
@@ -112,5 +280,26 @@ private extension Value {
112
280
}
113
281
return true
114
282
}
283
+
284
+ func getBaseAddressAndOffset( ) -> ( baseAddress: Value , offset: Int ) ? {
285
+ if let indexAddr = self as? IndexAddrInst {
286
+ guard let indexLiteral = indexAddr. index as? IntegerLiteralInst ,
287
+ indexLiteral. value. getActiveBits ( ) <= 32 else {
288
+ return nil
289
+ }
290
+ return ( baseAddress: indexAddr. base, offset: Int ( indexLiteral. value. getZExtValue ( ) ) )
291
+ }
292
+ return ( baseAddress: self , offset: 0 )
293
+ }
115
294
}
116
295
296
+ private extension Instruction {
297
+ var isShiftRightByAtLeastOne : Bool {
298
+ guard let bi = self as? BuiltinInst ,
299
+ bi. id == . LShr,
300
+ let shiftLiteral = bi. operands [ 1 ] . value as? IntegerLiteralInst else {
301
+ return false
302
+ }
303
+ return shiftLiteral. value. isStrictlyPositive ( )
304
+ }
305
+ }
0 commit comments