Skip to content

Commit c37d31c

Browse files
authored
Merge pull request swiftlang#73596 from jkshtj/specialize
[Autodiff] Adds logic to generate specialized functions in the closure-spec pass
2 parents 552815e + ce2161e commit c37d31c

File tree

21 files changed

+1190
-287
lines changed

21 files changed

+1190
-287
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift

Lines changed: 485 additions & 36 deletions
Large diffs are not rendered by default.

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,12 +353,49 @@ struct FunctionPassContext : MutatingContext {
353353
return String(taking: _bridged.mangleOutlinedVariable(function.bridged))
354354
}
355355

356+
func mangle(withClosureArgs closureArgs: [Value], closureArgIndices: [Int], from applySiteCallee: Function) -> String {
357+
closureArgs.withBridgedValues { bridgedClosureArgsRef in
358+
closureArgIndices.withBridgedArrayRef{bridgedClosureArgIndicesRef in
359+
String(taking: _bridged.mangleWithClosureArgs(
360+
bridgedClosureArgsRef,
361+
bridgedClosureArgIndicesRef,
362+
applySiteCallee.bridged
363+
))
364+
}
365+
}
366+
}
367+
356368
func createGlobalVariable(name: String, type: Type, isPrivate: Bool) -> GlobalVariable {
357369
let gv = name._withBridgedStringRef {
358370
_bridged.createGlobalVariable($0, type.bridged, isPrivate)
359371
}
360372
return gv.globalVar
361373
}
374+
375+
func createFunctionForClosureSpecialization(from applySiteCallee: Function, withName specializedFunctionName: String,
376+
withParams specializedParameters: [ParameterInfo],
377+
withSerialization isSerialized: Bool) -> Function
378+
{
379+
return specializedFunctionName._withBridgedStringRef { nameRef in
380+
let bridgedParamInfos = specializedParameters.map { $0._bridged }
381+
382+
return bridgedParamInfos.withUnsafeBufferPointer { paramBuf in
383+
_bridged.ClosureSpecializer_createEmptyFunctionWithSpecializedSignature(nameRef, paramBuf.baseAddress,
384+
paramBuf.count,
385+
applySiteCallee.bridged,
386+
isSerialized).function
387+
}
388+
}
389+
}
390+
391+
func buildSpecializedFunction(specializedFunction: Function, buildFn: (Function, FunctionPassContext) -> ()) {
392+
let nestedFunctionPassContext =
393+
FunctionPassContext(_bridged: _bridged.initializeNestedPassContext(specializedFunction.bridged))
394+
395+
defer { _bridged.deinitializedNestedPassContext() }
396+
397+
buildFn(specializedFunction, nestedFunctionPassContext)
398+
}
362399
}
363400

364401
struct SimplifyContext : MutatingContext {
@@ -445,6 +482,13 @@ extension Builder {
445482
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
446483
}
447484

485+
/// Creates a builder which inserts instructions into an empty function, using the location of the function itself.
486+
init(atStartOf function: Function, _ context: some MutatingContext) {
487+
context.verifyIsTransforming(function: function)
488+
self.init(insertAt: .atStartOf(function), location: function.location,
489+
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
490+
}
491+
448492
init(staticInitializerOf global: GlobalVariable, _ context: some MutatingContext) {
449493
self.init(insertAt: .staticInitializer(global),
450494
location: Location.artificialUnreachableLocation,
@@ -629,7 +673,6 @@ extension Function {
629673
bridged.setIsPerformanceConstraint(isPerformanceConstraint)
630674
}
631675

632-
633676
func fixStackNesting(_ context: FunctionPassContext) {
634677
context._bridged.fixStackNesting(bridged)
635678
}

SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ swift_compiler_sources(Optimizer
1010
AddressUtils.swift
1111
BorrowedFromUpdater.swift
1212
BorrowUtils.swift
13+
SpecializationCloner.swift
1314
DiagnosticEngine.swift
1415
Devirtualization.swift
1516
EscapeUtils.swift

SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ extension Builder {
178178
insertFunc(builder)
179179
}
180180
}
181+
182+
func destroyCapturedArgs(for paiOnStack: PartialApplyInst) {
183+
precondition(paiOnStack.isOnStack, "Function must only be called for `partial_apply`s on stack!")
184+
self.bridged.destroyCapturedArgs(paiOnStack.bridged)
185+
}
181186
}
182187

183188
extension Value {
@@ -398,23 +403,6 @@ extension LoadInst {
398403
}
399404
}
400405

401-
extension PartialApplyInst {
402-
var isPartialApplyOfReabstractionThunk: Bool {
403-
// A partial_apply of a reabstraction thunk either has a single capture
404-
// (a function) or two captures (function and dynamic Self type).
405-
if self.numArguments == 1 || self.numArguments == 2,
406-
let fun = self.referencedFunction,
407-
fun.isReabstractionThunk,
408-
self.arguments[0].type.isFunction,
409-
self.arguments[0].type.isReferenceCounted(in: self.parentFunction) || self.callee.type.isThickFunction
410-
{
411-
return true
412-
}
413-
414-
return false
415-
}
416-
}
417-
418406
extension FunctionPassContext {
419407
/// Returns true if any blocks were removed.
420408
func removeDeadBlocks(in function: Function) -> Bool {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===--- SpecializationCloner.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 OptimizerBridging
14+
import SIL
15+
16+
/// Utility cloner type that can be used by optimizations that generate new functions or specialized versions of
17+
/// existing functions.
18+
struct SpecializationCloner {
19+
private let bridged: BridgedSpecializationCloner
20+
let context: FunctionPassContext
21+
22+
init(emptySpecializedFunction: Function, _ context: FunctionPassContext) {
23+
self.bridged = BridgedSpecializationCloner(emptySpecializedFunction.bridged)
24+
self.context = context
25+
}
26+
27+
var cloned: Function {
28+
bridged.getCloned().function
29+
}
30+
31+
var entryBlock: BasicBlock {
32+
if cloned.blocks.isEmpty {
33+
return cloned.appendNewBlock(context)
34+
} else {
35+
return cloned.entryBlock
36+
}
37+
}
38+
39+
func getClonedBlock(for originalBlock: BasicBlock) -> BasicBlock {
40+
bridged.getClonedBasicBlock(originalBlock.bridged).block
41+
}
42+
43+
func cloneFunctionBody(from originalFunction: Function, entryBlockArguments: [Value]) {
44+
entryBlockArguments.withBridgedValues { bridgedEntryBlockArgs in
45+
bridged.cloneFunctionBody(originalFunction.bridged, self.entryBlock.bridged, bridgedEntryBlockArgs)
46+
}
47+
}
48+
49+
}

SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ public func registerOptimizerTests() {
165165
linearLivenessTest,
166166
parseTestSpecificationTest,
167167
variableIntroducerTest,
168-
gatherCallSitesTest
168+
gatherCallSitesTest,
169+
specializedFunctionSignatureAndBodyTest
169170
)
170171

171172
// Finally register the thunk they all call through.

SwiftCompilerSources/Sources/SIL/Argument.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ final public class FunctionArgument : Argument {
6969
public var resultDependence: LifetimeDependenceConvention? {
7070
parentFunction.argumentConventions[resultDependsOn: index]
7171
}
72+
73+
/// Copies the following flags from `arg`:
74+
/// 1. noImplicitCopy
75+
/// 2. lifetimeAnnotation
76+
/// 3. closureCapture
77+
/// 4. parameterPack
78+
public func copyFlags(from arg: FunctionArgument) {
79+
bridged.copyFlags(arg.bridged)
80+
}
7281
}
7382

7483
public struct Phi {
@@ -445,6 +454,17 @@ public enum ArgumentConvention : CustomStringConvertible {
445454
}
446455
}
447456

457+
public var isConsumed: Bool {
458+
switch self {
459+
case .indirectIn, .directOwned, .packOwned:
460+
return true
461+
case .indirectInGuaranteed, .directGuaranteed, .packGuaranteed,
462+
.indirectInout, .indirectInoutAliasable, .indirectOut,
463+
.packOut, .packInout, .directUnowned:
464+
return false
465+
}
466+
}
467+
448468
public var isExclusiveIndirect: Bool {
449469
switch self {
450470
case .indirectIn,

SwiftCompilerSources/Sources/SIL/BasicBlock.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ public struct InstructionList : CollectionLikeSequence, IteratorProtocol {
122122

123123
public var first: Instruction? { currentInstruction }
124124

125+
public var last: Instruction? { reversed().first }
126+
125127
public func reversed() -> ReverseInstructionList {
126128
if let inst = currentInstruction {
127129
let lastInst = inst.bridged.getLastInstOfParent().instruction

SwiftCompilerSources/Sources/SIL/Builder.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public struct Builder {
1919
public enum InsertionPoint {
2020
case before(Instruction)
2121
case atEndOf(BasicBlock)
22+
case atStartOf(Function)
2223
case staticInitializer(GlobalVariable)
2324
}
2425

@@ -35,6 +36,9 @@ public struct Builder {
3536
case .atEndOf(let block):
3637
return BridgedBuilder(insertAt: .endOfBlock, insertionObj: block.bridged.obj,
3738
loc: location.bridged)
39+
case .atStartOf(let function):
40+
return BridgedBuilder(insertAt: .startOfFunction, insertionObj: function.bridged.obj,
41+
loc: location.bridged)
3842
case .staticInitializer(let global):
3943
return BridgedBuilder(insertAt: .intoGlobal, insertionObj: global.bridged.obj,
4044
loc: location.bridged)
@@ -290,6 +294,20 @@ public struct Builder {
290294
return notifyNew(tttf.getAs(ThinToThickFunctionInst.self))
291295
}
292296

297+
public func createPartialApply(
298+
function: Value,
299+
substitutionMap: SubstitutionMap,
300+
capturedArguments: [Value],
301+
calleeConvention: ArgumentConvention,
302+
hasUnknownResultIsolation: Bool,
303+
isOnStack: Bool
304+
) -> PartialApplyInst {
305+
return capturedArguments.withBridgedValues { capturedArgsRef in
306+
let pai = bridged.createPartialApply(function.bridged, capturedArgsRef, calleeConvention.bridged, substitutionMap.bridged, hasUnknownResultIsolation, isOnStack)
307+
return notifyNew(pai.getAs(PartialApplyInst.self))
308+
}
309+
}
310+
293311
@discardableResult
294312
public func createSwitchEnum(enum enumVal: Value,
295313
cases: [(Int, BasicBlock)],
@@ -434,4 +452,14 @@ public struct Builder {
434452
let endAccess = bridged.createEndAccess(beginAccess.bridged)
435453
return notifyNew(endAccess.getAs(EndAccessInst.self))
436454
}
455+
456+
public func createConvertFunction(originalFunction: Value, resultType: Type, withoutActuallyEscaping: Bool) -> ConvertFunctionInst {
457+
let convertFunction = bridged.createConvertFunction(originalFunction.bridged, resultType.bridged, withoutActuallyEscaping)
458+
return notifyNew(convertFunction.getAs(ConvertFunctionInst.self))
459+
}
460+
461+
public func createConvertEscapeToNoEscape(originalFunction: Value, resultType: Type, isLifetimeGuaranteed: Bool) -> ConvertEscapeToNoEscapeInst {
462+
let convertFunction = bridged.createConvertEscapeToNoEscape(originalFunction.bridged, resultType.bridged, isLifetimeGuaranteed)
463+
return notifyNew(convertFunction.getAs(ConvertEscapeToNoEscapeInst.self))
464+
}
437465
}

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
2121
return StringRef(bridged: bridged.getName())
2222
}
2323

24+
public var location: Location {
25+
return Location(bridged: bridged.getLocation())
26+
}
27+
2428
final public var description: String {
2529
return String(taking: bridged.getDebugDescription())
2630
}
@@ -93,8 +97,6 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
9397

9498
public var isAsync: Bool { bridged.isAsync() }
9599

96-
public var isReabstractionThunk: Bool { bridged.isReabstractionThunk() }
97-
98100
/// True if this is a `[global_init]` function.
99101
///
100102
/// Such a function is typically a global addressor which calls the global's
@@ -140,7 +142,7 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
140142
case noThunk, thunk, reabstractionThunk, signatureOptimizedThunk
141143
}
142144

143-
var thunkKind: ThunkKind {
145+
public var thunkKind: ThunkKind {
144146
switch bridged.isThunk() {
145147
case .IsNotThunk: return .noThunk
146148
case .IsThunk: return .thunk

0 commit comments

Comments
 (0)