@@ -28,6 +28,15 @@ func devirtualizeDeinits(of destroy: DestroyAddrInst, _ context: some MutatingCo
28
28
return devirtualize ( destroy: destroy, context)
29
29
}
30
30
31
+ func devirtualizeDeinits( of builtin: BuiltinInst , _ context: some MutatingContext ) -> Bool {
32
+ switch builtin. id {
33
+ case . DestroyArray:
34
+ return devirtualize ( builtinDestroyArray: builtin, context)
35
+ default :
36
+ return true
37
+ }
38
+ }
39
+
31
40
private func devirtualize( destroy: some DevirtualizableDestroy , _ context: some MutatingContext ) -> Bool {
32
41
let type = destroy. type
33
42
if !type. isMoveOnly {
@@ -254,6 +263,59 @@ extension DestroyAddrInst : DevirtualizableDestroy {
254
263
}
255
264
}
256
265
266
+ private func devirtualize( builtinDestroyArray: BuiltinInst , _ context: some MutatingContext ) -> Bool {
267
+ let function = builtinDestroyArray. parentFunction
268
+ let elementType = builtinDestroyArray. substitutionMap. replacementType. loweredType ( in: function)
269
+ guard elementType. isMoveOnly,
270
+ // This avoids lowering the loop if the element is a non-copyable generic type.
271
+ ( elementType. isStruct || elementType. isEnum)
272
+ else {
273
+ return true
274
+ }
275
+
276
+ // Lower the `builtin "destroyArray" to a loop which destroys all elements
277
+
278
+ let basePointer = builtinDestroyArray. arguments [ 1 ]
279
+ let arrayCount = builtinDestroyArray. arguments [ 2 ]
280
+ let indexType = arrayCount. type
281
+ let boolType = context. getBuiltinIntegerType ( bitWidth: 1 )
282
+
283
+ let preheaderBlock = builtinDestroyArray. parentBlock
284
+ let exitBlock = context. splitBlock ( after: builtinDestroyArray)
285
+ let headerBlock = context. createBlock ( after: preheaderBlock)
286
+ let bodyBlock = context. createBlock ( after: headerBlock)
287
+
288
+ let preheaderBuilder = Builder ( atEndOf: preheaderBlock, location: builtinDestroyArray. location, context)
289
+ let zero = preheaderBuilder. createIntegerLiteral ( 0 , type: indexType)
290
+ let one = preheaderBuilder. createIntegerLiteral ( 1 , type: indexType)
291
+ let falseValue = preheaderBuilder. createIntegerLiteral ( 0 , type: boolType)
292
+ let baseAddress = preheaderBuilder. createPointerToAddress ( pointer: basePointer,
293
+ addressType: elementType. addressType,
294
+ isStrict: true , isInvariant: false )
295
+ preheaderBuilder. createBranch ( to: headerBlock, arguments: [ zero] )
296
+
297
+ let inductionVariable = headerBlock. addArgument ( type: indexType, ownership: . none, context)
298
+ let headerBuilder = Builder ( atEndOf: headerBlock, location: builtinDestroyArray. location, context)
299
+ let cmp = headerBuilder. createBuiltinBinaryFunction ( name: " cmp_slt " , operandType: indexType, resultType: boolType,
300
+ arguments: [ inductionVariable, arrayCount] )
301
+ headerBuilder. createCondBranch ( condition: cmp, trueBlock: bodyBlock, falseBlock: exitBlock)
302
+
303
+ let bodyBuilder = Builder ( atEndOf: bodyBlock, location: builtinDestroyArray. location, context)
304
+ let elementAddr = bodyBuilder. createIndexAddr ( base: baseAddress, index: inductionVariable,
305
+ needStackProtection: false )
306
+ let destroy = bodyBuilder. createDestroyAddr ( address: elementAddr)
307
+ let resultType = context. getTupleType ( elements: [ indexType, boolType] ) . loweredType ( in: function)
308
+ let increment = bodyBuilder. createBuiltinBinaryFunction ( name: " sadd_with_overflow " , operandType: indexType,
309
+ resultType: resultType,
310
+ arguments: [ inductionVariable, one, falseValue] )
311
+ let incrResult = bodyBuilder. createTupleExtract ( tuple: increment, elementIndex: 0 )
312
+ bodyBuilder. createBranch ( to: headerBlock, arguments: [ incrResult] )
313
+
314
+ context. erase ( instruction: builtinDestroyArray)
315
+
316
+ return devirtualize ( destroy: destroy, context)
317
+ }
318
+
257
319
private extension EnumCases {
258
320
func allPayloadsAreTrivial( in function: Function ) -> Bool {
259
321
allSatisfy ( { $0. payload? . isTrivial ( in: function) ?? true } )
0 commit comments