1212
1313import SIL
1414
15- extension LoadInst : OnoneSimplifyable {
15+ extension LoadInst : OnoneSimplifyable , SILCombineSimplifyable {
1616 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
1855 }
1956
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 {
21101 guard let globalInitVal = getGlobalInitValue ( address: address) else {
22- return
102+ return false
23103 }
24104 if !globalInitVal. canBeCopied ( into: parentFunction, context) {
25- return
105+ return false
26106 }
27107 var cloner = StaticInitCloner ( cloneBefore: self , context)
28108 defer { cloner. deinitialize ( ) }
@@ -31,6 +111,94 @@ extension LoadInst : OnoneSimplifyable {
31111
32112 uses. replaceAll ( with: initVal, context)
33113 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+ }
34202 }
35203}
36204
@@ -112,5 +280,26 @@ private extension Value {
112280 }
113281 return true
114282 }
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+ }
115294}
116295
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