@@ -28,6 +28,15 @@ func devirtualizeDeinits(of destroy: DestroyAddrInst, _ context: some MutatingCo
2828 return devirtualize ( destroy: destroy, context)
2929}
3030
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+
3140private func devirtualize( destroy: some DevirtualizableDestroy , _ context: some MutatingContext ) -> Bool {
3241 let type = destroy. type
3342 if !type. isMoveOnly {
@@ -254,6 +263,59 @@ extension DestroyAddrInst : DevirtualizableDestroy {
254263 }
255264}
256265
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+
257319private extension EnumCases {
258320 func allPayloadsAreTrivial( in function: Function ) -> Bool {
259321 allSatisfy ( { $0. payload? . isTrivial ( in: function) ?? true } )
0 commit comments