Skip to content

Commit 49d3960

Browse files
committed
MandatoryPerformanceOptimization: de-virtualize deinits of builtin "destroyArray"
This is required for embedded swift. rdar://157131184
1 parent 3da2c14 commit 49d3960

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,14 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
153153
worklist.addWitnessMethods(of: conformance, moduleContext)
154154

155155
default:
156-
break
156+
if !devirtualizeDeinits(of: bi, simplifyCtxt) {
157+
// If invoked from SourceKit avoid reporting false positives when WMO is turned off for indexing purposes.
158+
if moduleContext.enableWMORequiredDiagnostics {
159+
context.diagnosticEngine.diagnose(.deinit_not_visible, at: bi.location)
160+
}
161+
}
157162
}
158163

159-
160164
// We need to de-virtualize deinits of non-copyable types to be able to specialize the deinitializers.
161165
case let destroyValue as DestroyValueInst:
162166
if !devirtualizeDeinits(of: destroyValue, simplifyCtxt) {

SwiftCompilerSources/Sources/Optimizer/Utilities/Devirtualization.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
3140
private 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+
257319
private extension EnumCases {
258320
func allPayloadsAreTrivial(in function: Function) -> Bool {
259321
allSatisfy({ $0.payload?.isTrivial(in: function) ?? true })

0 commit comments

Comments
 (0)