@@ -48,6 +48,10 @@ let initializeStaticGlobalsPass = FunctionPass(name: "initialize-static-globals"
48
48
return
49
49
}
50
50
51
+ // Sometimes structs are not stored in one piece, but as individual elements.
52
+ // Merge such individual stores to a single store of the whole struct.
53
+ mergeStores ( in: function, context)
54
+
51
55
guard let ( allocInst, storeToGlobal) = getGlobalInitialization ( of: function) else {
52
56
return
53
57
}
@@ -137,3 +141,80 @@ private extension Function {
137
141
return block
138
142
}
139
143
}
144
+
145
+ /// Merges stores to individual struct fields to a single store of the whole struct.
146
+ ///
147
+ /// store %element1 to %element1Addr
148
+ /// store %element2 to %element2Addr
149
+ /// ->
150
+ /// %s = struct $S (%element1, %element2)
151
+ /// store %s to @structAddr
152
+ private func mergeStores( in function: Function , _ context: FunctionPassContext ) {
153
+ for inst in function. instructions {
154
+ if let store = inst as? StoreInst {
155
+ if let elementStores = getSequenceOfElementStores ( firstStore: store) {
156
+ merge ( elementStores: elementStores, context)
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ /// Returns a sequence of individual stores to elements of a struct.
163
+ ///
164
+ /// %addr1 = struct_element_addr %structAddr, #field1
165
+ /// store %element1 to %addr1
166
+ /// // ...
167
+ /// %addr_n = struct_element_addr %structAddr, #field_n
168
+ /// store %element_n to %addr_n
169
+ ///
170
+ private func getSequenceOfElementStores( firstStore: StoreInst ) -> [ StoreInst ] ? {
171
+ guard let elementAddr = firstStore. destination as? StructElementAddrInst else {
172
+ return nil
173
+ }
174
+ let structAddr = elementAddr. struct
175
+ let numElements = structAddr. type. getNominalFields ( in: firstStore. parentFunction) . count
176
+ var elementStores = Array < StoreInst ? > ( repeating: nil , count: numElements)
177
+ var numStoresFound = 0
178
+
179
+ for inst in InstructionList ( first: firstStore) {
180
+ switch inst {
181
+ case let store as StoreInst :
182
+ guard store. storeOwnership == . trivial,
183
+ let sea = store. destination as? StructElementAddrInst ,
184
+ sea. struct == structAddr,
185
+ // Multiple stores to the same element?
186
+ elementStores [ sea. fieldIndex] == nil else {
187
+ return nil
188
+ }
189
+
190
+ elementStores [ sea. fieldIndex] = store
191
+ numStoresFound += 1
192
+ if numStoresFound == numElements {
193
+ // If we saw `numElements` distinct stores, it implies that all elements in `elementStores` are not nil.
194
+ return elementStores. map { $0! }
195
+ }
196
+ default :
197
+ if inst. mayReadOrWriteMemory {
198
+ return nil
199
+ }
200
+ }
201
+ }
202
+ return nil
203
+ }
204
+
205
+ private func merge( elementStores: [ StoreInst ] , _ context: FunctionPassContext ) {
206
+ let lastStore = elementStores. last!
207
+ let builder = Builder ( after: lastStore, context)
208
+
209
+ let structAddr = ( lastStore. destination as! StructElementAddrInst ) . struct
210
+ let str = builder. createStruct ( type: structAddr. type. objectType, elements: elementStores. map { $0. source } )
211
+ builder. createStore ( source: str, destination: structAddr, ownership: lastStore. storeOwnership)
212
+
213
+ for store in elementStores {
214
+ let destAddr = store. destination as! StructElementAddrInst
215
+ context. erase ( instruction: store)
216
+ if destAddr. uses. isEmpty {
217
+ context. erase ( instruction: destAddr)
218
+ }
219
+ }
220
+ }
0 commit comments