Skip to content

Commit bd7db67

Browse files
committed
MandatoryPerformanceOptimizations: perform function signature optimization to remove metatype arguments
Generic specialization already takes care of removing metatype arguments of generic functions. But sometimes non-generic functions have metatype arguments which must be removed. We need handle this case with a function signature optimization. This enables, for example, to use `OptionSet` in embedded swift. rdar://121206953
1 parent 80051fe commit bd7db67

File tree

10 files changed

+389
-10
lines changed

10 files changed

+389
-10
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ private func optimizeFunctionsTopDown(using worklist: inout FunctionWorklist,
5252

5353
optimize(function: f, context, &worklist)
5454
}
55+
56+
// Generic specialization takes care of removing metatype arguments of generic functions.
57+
// But sometimes non-generic functions have metatype arguments which must be removed.
58+
// We need handle this case with a function signature optimization.
59+
removeMetatypeArgumentsInCallees(of: f, moduleContext)
60+
61+
worklist.addCallees(of: f)
5562
}
5663
}
5764

@@ -100,6 +107,11 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ work
100107
context.diagnosticEngine.diagnose(destroyAddr.location.sourceLoc, .deinit_not_visible)
101108
}
102109

110+
case let iem as InitExistentialMetatypeInst:
111+
if iem.uses.ignoreDebugUses.isEmpty {
112+
context.erase(instructionIncludingDebugUses: iem)
113+
}
114+
103115
default:
104116
break
105117
}
@@ -113,8 +125,6 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ work
113125
changed = context.optimizeMemoryAccesses(in: function) || changed
114126
changed = context.eliminateDeadAllocations(in: function) || changed
115127
}
116-
117-
worklist.add(calleesOf: function)
118128
}
119129

120130
private func specializeVTableAndAddEntriesToWorklist(for type: Type, in function: Function, _ context: FunctionPassContext, _ worklist: inout FunctionWorklist) {
@@ -153,6 +163,14 @@ private func inlineAndDevirtualize(apply: FullApplySite, alreadyInlinedFunctions
153163
}
154164
}
155165

166+
private func removeMetatypeArgumentsInCallees(of function: Function, _ context: ModulePassContext) {
167+
for inst in function.instructions {
168+
if let apply = inst as? FullApplySite {
169+
specializeByRemovingMetatypeArguments(apply: apply, context)
170+
}
171+
}
172+
}
173+
156174
private func removeUnusedMetatypeInstructions(in function: Function, _ context: FunctionPassContext) {
157175
for inst in function.instructions {
158176
if let mt = inst as? MetatypeInst,
@@ -366,7 +384,7 @@ fileprivate struct FunctionWorklist {
366384
}
367385
}
368386

369-
mutating func add(calleesOf function: Function) {
387+
mutating func addCallees(of function: Function) {
370388
for inst in function.instructions {
371389
switch inst {
372390
case let apply as ApplySite:

SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ swift_compiler_sources(Optimizer
1313
Devirtualization.swift
1414
EscapeUtils.swift
1515
ForwardingUtils.swift
16+
FunctionSignatureTransforms.swift
1617
LifetimeDependenceUtils.swift
1718
OptUtils.swift
1819
OwnershipLiveness.swift
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
//===--- FunctionSignatureTransforms.swift ---------------------------------==//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
/// Replace an apply with metatype arguments with an apply to a specialized function, where the
16+
/// metatype values are not passed, but rematerialized in the entry block of the specialized function
17+
///
18+
/// ```
19+
/// func caller() {
20+
/// callee(Int.self)
21+
/// }
22+
/// func callee(_ t: Int.Type) { // a thick metatype
23+
/// // ...
24+
/// }
25+
/// ```
26+
/// ->
27+
/// ```
28+
/// func caller() {
29+
/// specialized_callee()
30+
/// }
31+
/// func specialized_callee() {
32+
/// let t: Int.Type = Int.self
33+
/// // ...
34+
/// }
35+
/// // remains a thunk
36+
/// func callee(_ t: Int.Type) {
37+
/// specialized_callee()
38+
/// }
39+
/// ```
40+
///
41+
func specializeByRemovingMetatypeArguments(apply: FullApplySite, _ context: ModulePassContext) {
42+
guard let callee = apply.referencedFunction,
43+
!callee.isGeneric
44+
else {
45+
return
46+
}
47+
48+
let deadArgIndices = callee.argumentTypes.enumerated()
49+
.filter { $0.element.isRemovableMetatype(in: callee) }
50+
.map { $0.offset }
51+
if deadArgIndices.isEmpty {
52+
return
53+
}
54+
55+
let specializedFuncName = context.mangle(withDeadArguments: deadArgIndices, from: callee)
56+
57+
let specializedCallee: Function
58+
if let existingSpecialization = context.lookupFunction(name: specializedFuncName) {
59+
specializedCallee = existingSpecialization
60+
} else {
61+
if !context.loadFunction(function: callee, loadCalleesRecursively: true) {
62+
return
63+
}
64+
specializedCallee = createSpecializedFunction(withName: specializedFuncName,
65+
withRemovedMetatypeArgumentsOf: apply,
66+
originalFunction: callee,
67+
context)
68+
}
69+
70+
context.transform(function: apply.parentFunction) { funcContext in
71+
replace(apply: apply, to: specializedCallee, funcContext)
72+
}
73+
}
74+
75+
/// Creates a specialized function by moving the whole function body of `originalFunction` to the new specialized
76+
/// function and calling the specialized function in the original function (which is now a thunk).
77+
private func createSpecializedFunction(
78+
withName name: String,
79+
withRemovedMetatypeArgumentsOf apply: FullApplySite,
80+
originalFunction: Function,
81+
_ context: ModulePassContext
82+
) -> Function {
83+
let (aliveParameters, hasSelfParameter) = getAliveParameters(of: originalFunction)
84+
85+
let specializedFunction = context.createSpecializedFunctionDeclaration(
86+
name: name,
87+
parameters: aliveParameters,
88+
hasSelfParameter: hasSelfParameter,
89+
fromOriginal: originalFunction)
90+
91+
let thunkLoc = originalFunction.entryBlock.instructions.first!.location.autoGenerated
92+
93+
context.moveFunctionBody(from: originalFunction, to: specializedFunction)
94+
// originalFunction is now empty and used as the thunk.
95+
let thunk = originalFunction
96+
97+
context.transform(function: thunk) { funcContext in
98+
thunk.set(thunkKind: .signatureOptimizedThunk, funcContext)
99+
createEntryBlock(in: thunk, usingArguments: specializedFunction.arguments, funcContext)
100+
}
101+
102+
context.transform(function: specializedFunction) { funcContext in
103+
removeMetatypArguments(in: specializedFunction, funcContext)
104+
}
105+
106+
context.transform(function: thunk) { funcContext in
107+
createForwardingApply(to: specializedFunction,
108+
in: thunk,
109+
originalApply: apply,
110+
debugLocation: thunkLoc,
111+
funcContext)
112+
}
113+
114+
return specializedFunction
115+
}
116+
117+
private func getAliveParameters(of originalFunction: Function) -> ([ParameterInfo], hasSelfParameter: Bool) {
118+
let convention = originalFunction.convention
119+
var aliveParams = [ParameterInfo]()
120+
var hasSelfParameter = originalFunction.hasSelfArgument
121+
for (paramIdx, origParam) in convention.parameters.enumerated() {
122+
let argIdx = paramIdx + convention.indirectSILResultCount
123+
if !originalFunction.argumentTypes[argIdx].isRemovableMetatype(in: originalFunction) {
124+
aliveParams.append(origParam)
125+
} else if hasSelfParameter && originalFunction.selfArgumentIndex == argIdx {
126+
hasSelfParameter = false
127+
}
128+
}
129+
return (aliveParams, hasSelfParameter)
130+
}
131+
132+
private func createEntryBlock(
133+
in function: Function,
134+
usingArguments: some Sequence<FunctionArgument>,
135+
_ context: FunctionPassContext
136+
) {
137+
let entryBlock = function.appendNewBlock(context)
138+
for arg in usingArguments {
139+
_ = entryBlock.addFunctionArgument(type: arg.type, context)
140+
}
141+
}
142+
143+
private func removeMetatypArguments(in specializedFunction: Function, _ context: FunctionPassContext) {
144+
let entryBlock = specializedFunction.entryBlock
145+
var funcArgIdx = 0
146+
while funcArgIdx < specializedFunction.entryBlock.arguments.count {
147+
let funcArg = specializedFunction.arguments[funcArgIdx]
148+
if funcArg.type.isRemovableMetatype(in: specializedFunction) {
149+
// Rematerialize the metatype value in the entry block.
150+
let builder = Builder(atBeginOf: entryBlock, context)
151+
let instanceType = funcArg.type.instanceTypeOfMetatype(in: specializedFunction)
152+
let metatype = builder.createMetatype(of: instanceType, representation: .Thick)
153+
funcArg.uses.replaceAll(with: metatype, context)
154+
entryBlock.eraseArgument(at: funcArgIdx, context)
155+
} else {
156+
funcArgIdx += 1
157+
}
158+
}
159+
}
160+
161+
private func createForwardingApply(
162+
to specializedFunction: Function,
163+
in thunk: Function,
164+
originalApply: FullApplySite,
165+
debugLocation: Location,
166+
_ context: FunctionPassContext
167+
) {
168+
let applyArgs = Array(thunk.arguments.filter { !$0.type.isRemovableMetatype(in: thunk) })
169+
170+
let builder = Builder(atEndOf: thunk.entryBlock, location: debugLocation, context)
171+
let callee = builder.createFunctionRef(specializedFunction)
172+
173+
// Use the original apply as template to create the forwarding apply
174+
switch originalApply {
175+
case let ai as ApplyInst:
176+
let newApply = builder.createApply(function: callee,
177+
ai.substitutionMap,
178+
arguments: applyArgs,
179+
isNonThrowing: ai.isNonThrowing,
180+
isNonAsync: ai.isNonAsync,
181+
specializationInfo: ai.specializationInfo)
182+
let builder = Builder(after: newApply, context)
183+
builder.createReturn(of: newApply)
184+
case let tai as TryApplyInst:
185+
let normalBlock = thunk.appendNewBlock(context)
186+
let errorBlock = thunk.appendNewBlock(context)
187+
builder.createTryApply(function: callee,
188+
tai.substitutionMap,
189+
arguments: applyArgs,
190+
normalBlock: normalBlock,
191+
errorBlock: errorBlock,
192+
specializationInfo: tai.specializationInfo)
193+
let originalArg = tai.normalBlock.arguments[0]
194+
let returnVal = normalBlock.addArgument(type: originalArg.type, ownership: originalArg.ownership, context)
195+
let returnBuilder = Builder(atEndOf: normalBlock, location: debugLocation, context)
196+
returnBuilder.createReturn(of: returnVal)
197+
198+
let errorArg = tai.errorBlock.arguments[0]
199+
let errorVal = errorBlock.addArgument(type: errorArg.type, ownership: errorArg.ownership, context)
200+
let errorBuilder = Builder(atEndOf: errorBlock, location: debugLocation, context)
201+
errorBuilder.createThrow(of: errorVal)
202+
default:
203+
fatalError("unknown full apply instruction \(originalApply)")
204+
}
205+
}
206+
207+
private func replace(apply: FullApplySite, to specializedCallee: Function, _ context: FunctionPassContext) {
208+
let builder = Builder(before: apply, context)
209+
let callee = builder.createFunctionRef(specializedCallee)
210+
let args = Array(apply.arguments.filter { !$0.type.isRemovableMetatype(in: apply.parentFunction) })
211+
switch apply {
212+
case let ai as ApplyInst:
213+
let newApply = builder.createApply(function: callee,
214+
ai.substitutionMap,
215+
arguments: args,
216+
isNonThrowing: ai.isNonThrowing,
217+
isNonAsync: ai.isNonAsync,
218+
specializationInfo: ai.specializationInfo)
219+
ai.uses.replaceAll(with: newApply, context)
220+
case let tai as TryApplyInst:
221+
builder.createTryApply(function: callee,
222+
tai.substitutionMap,
223+
arguments: args,
224+
normalBlock: tai.normalBlock,
225+
errorBlock: tai.errorBlock,
226+
specializationInfo: tai.specializationInfo)
227+
default:
228+
fatalError("unknown full apply instruction \(apply)")
229+
}
230+
context.erase(instruction: apply)
231+
}
232+
233+
private extension Type {
234+
func isRemovableMetatype(in function: Function) -> Bool {
235+
if isMetatype {
236+
if representationOfMetatype(in: function) == .Thick {
237+
let instanceTy = instanceTypeOfMetatype(in: function)
238+
// For structs and enums we know the metatype statically.
239+
return instanceTy.isStruct || instanceTy.isEnum
240+
}
241+
}
242+
return false
243+
}
244+
}

test/IRGen/objc_implementation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ public func fn(impl: ImplClass, swiftSub: SwiftSubclass) {
202202
// Swift calling convention -[ImplClass copyWithZone:]
203203
// CHECK-LABEL: define hidden swiftcc void @"$sSo9ImplClassC19objc_implementationE4copy4withypSg10ObjectiveC6NSZoneVSg_tF"
204204

205+
// FIXME: Do we actually want a public type metadata accessor? I'm guessing no.
206+
// CHECK-LABEL: define swiftcc %swift.metadata_response @"$sSo9ImplClassCMa"
207+
205208
// ObjC calling convention -[ImplClass copyWithZone:]
206209
// CHECK-LABEL: define internal ptr @"$sSo9ImplClassC19objc_implementationE4copy4withypSg10ObjectiveC6NSZoneVSg_tFTo"
207210

@@ -267,9 +270,6 @@ public func fn(impl: ImplClass, swiftSub: SwiftSubclass) {
267270
// CHECK: ret void
268271
// CHECK: }
269272

270-
// FIXME: Do we actually want a public type metadata accessor? I'm guessing no.
271-
// CHECK-LABEL: define swiftcc %swift.metadata_response @"$sSo9ImplClassCMa"
272-
273273
//
274274
// Not implemented in Swift
275275
//

test/IRGen/weak_import_deployment_target.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ public func callsNew() {
2323
}
2424

2525
// CHECK-OLD-LABEL: declare swiftcc void @"$s36weak_import_deployment_target_helper13hasDefaultArgyySiF"(i64)
26-
// CHECK-OLD-LABEL: declare swiftcc i64 @"$s36weak_import_deployment_target_helper8functionSiyF"()
2726
// CHECK-OLD-LABEL: declare extern_weak swiftcc void @"$s36weak_import_deployment_target_helper22hasAvailableDefaultArgyySiF"(i64)
2827
// CHECK-OLD-LABEL: declare extern_weak swiftcc i64 @"$s36weak_import_deployment_target_helper17availableFunctionSiyF"()
28+
// CHECK-OLD-LABEL: declare swiftcc i64 @"$s36weak_import_deployment_target_helper8functionSiyF"()
2929

3030
// CHECK-NEW-LABEL: declare swiftcc void @"$s36weak_import_deployment_target_helper13hasDefaultArgyySiF"(i64)
31-
// CHECK-NEW-LABEL: declare swiftcc i64 @"$s36weak_import_deployment_target_helper8functionSiyF"()
3231
// CHECK-NEW-LABEL: declare swiftcc void @"$s36weak_import_deployment_target_helper22hasAvailableDefaultArgyySiF"(i64)
3332
// CHECK-NEW-LABEL: declare swiftcc i64 @"$s36weak_import_deployment_target_helper17availableFunctionSiyF"()
33+
// CHECK-NEW-LABEL: declare swiftcc i64 @"$s36weak_import_deployment_target_helper8functionSiyF"()

0 commit comments

Comments
 (0)