Skip to content

Commit ab751d5

Browse files
committed
[Autodiff] Adds logic to generate specialized functions in the closure-spec pass
1 parent 15cab3a commit ab751d5

File tree

21 files changed

+1279
-268
lines changed

21 files changed

+1279
-268
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift

Lines changed: 552 additions & 35 deletions
Large diffs are not rendered by default.

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,12 +353,41 @@ 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+
/// Utility function that should be used by optimizations that generate new functions or specialized versions of
376+
/// existing functions.
377+
func createAndBuildSpecializedFunction(createFn: (FunctionPassContext) -> Function,
378+
buildFn: (Function, FunctionPassContext) -> ()) -> Function
379+
{
380+
let specializedFunction = createFn(self)
381+
382+
let nestedFunctionPassContext =
383+
FunctionPassContext(_bridged: _bridged.initializeNestedPassContext(specializedFunction.bridged))
384+
385+
defer { _bridged.deinitializedNestedPassContext() }
386+
387+
buildFn(specializedFunction, nestedFunctionPassContext)
388+
389+
return specializedFunction
390+
}
362391
}
363392

364393
struct SimplifyContext : MutatingContext {
@@ -445,6 +474,13 @@ extension Builder {
445474
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
446475
}
447476

477+
/// Creates a builder which inserts instructions into an empty function, using the location of the function itself.
478+
init(atStartOf function: Function, _ context: some MutatingContext) {
479+
context.verifyIsTransforming(function: function)
480+
self.init(insertAt: .atStartOf(function), context.notifyInstructionChanged,
481+
context._bridged.asNotificationHandler())
482+
}
483+
448484
init(staticInitializerOf global: GlobalVariable, _ context: some MutatingContext) {
449485
self.init(insertAt: .staticInitializer(global),
450486
location: Location.artificialUnreachableLocation,
@@ -629,7 +665,6 @@ extension Function {
629665
bridged.setIsPerformanceConstraint(isPerformanceConstraint)
630666
}
631667

632-
633668
func fixStackNesting(_ context: FunctionPassContext) {
634669
context._bridged.fixStackNesting(bridged)
635670
}

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: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//===--- SpecializationCloner.swift --------------------------------------------==//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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 var _context: FunctionPassContext
20+
private var _bridged: BridgedSpecializationCloner
21+
22+
init(emptySpecializedFunction: Function, _ context: FunctionPassContext) {
23+
self._context = context
24+
self._bridged = BridgedSpecializationCloner(emptySpecializedFunction.bridged)
25+
}
26+
27+
public var context: FunctionPassContext {
28+
self._context
29+
}
30+
31+
public var bridged: BridgedSpecializationCloner {
32+
self._bridged
33+
}
34+
35+
public var cloned: Function {
36+
bridged.getCloned().function
37+
}
38+
39+
public var entryBlock: BasicBlock {
40+
if cloned.blocks.isEmpty {
41+
cloned.appendNewBlock(context)
42+
} else {
43+
cloned.entryBlock
44+
}
45+
}
46+
47+
public func getClonedBlock(for originalBlock: BasicBlock) -> BasicBlock {
48+
bridged.getClonedBasicBlock(originalBlock.bridged).block
49+
}
50+
51+
public func cloneFunctionBody(from originalFunction: Function, entryBlockArgs: [Value]) {
52+
entryBlockArgs.withBridgedValues { bridgedEntryBlockArgs in
53+
bridged.cloneFunctionBody(originalFunction.bridged, self.entryBlock.bridged, bridgedEntryBlockArgs)
54+
}
55+
}
56+
57+
}

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: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,28 @@ 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

2526
let insertAt: InsertionPoint
26-
let location: Location
27+
let location: Location?
2728
private let notificationHandler: BridgedChangeNotificationHandler
2829
private let notifyNewInstruction: (Instruction) -> ()
2930

3031
public var bridged: BridgedBuilder {
3132
switch insertAt {
3233
case .before(let inst):
3334
return BridgedBuilder(insertAt: .beforeInst, insertionObj: inst.bridged.obj,
34-
loc: location.bridged)
35+
loc: location!.bridged)
3536
case .atEndOf(let block):
3637
return BridgedBuilder(insertAt: .endOfBlock, insertionObj: block.bridged.obj,
37-
loc: location.bridged)
38+
loc: location!.bridged)
39+
case .atStartOf(let function):
40+
return BridgedBuilder(insertAt: .startOfFunction, function: function.bridged)
3841
case .staticInitializer(let global):
3942
return BridgedBuilder(insertAt: .intoGlobal, insertionObj: global.bridged.obj,
40-
loc: location.bridged)
43+
loc: location!.bridged)
4144
}
4245
}
4346

@@ -53,9 +56,26 @@ public struct Builder {
5356
return instruction
5457
}
5558

59+
public init(insertAt: InsertionPoint,
60+
_ notifyNewInstruction: @escaping (Instruction) -> (),
61+
_ notificationHandler: BridgedChangeNotificationHandler) {
62+
guard case let .atStartOf(_) = insertAt else {
63+
fatalError("Initializer must only be used to initialize builders that insert at the start of functions.")
64+
}
65+
66+
self.insertAt = insertAt
67+
self.location = nil;
68+
self.notifyNewInstruction = notifyNewInstruction
69+
self.notificationHandler = notificationHandler
70+
}
71+
5672
public init(insertAt: InsertionPoint, location: Location,
5773
_ notifyNewInstruction: @escaping (Instruction) -> (),
5874
_ notificationHandler: BridgedChangeNotificationHandler) {
75+
if case let .atStartOf(_) = insertAt {
76+
fatalError("Initializer must not be used to initialize builders that insert at the start of functions.")
77+
}
78+
5979
self.insertAt = insertAt
6080
self.location = location;
6181
self.notifyNewInstruction = notifyNewInstruction
@@ -147,6 +167,18 @@ public struct Builder {
147167
return notifyNew(endInit.getAs(EndInitLetRefInst.self))
148168
}
149169

170+
@discardableResult
171+
public func createRetainValue(operand: Value) -> RetainValueInst {
172+
let retain = bridged.createRetainValue(operand.bridged)
173+
return notifyNew(retain.getAs(RetainValueInst.self))
174+
}
175+
176+
@discardableResult
177+
public func createReleaseValue(operand: Value) -> ReleaseValueInst {
178+
let release = bridged.createReleaseValue(operand.bridged)
179+
return notifyNew(release.getAs(ReleaseValueInst.self))
180+
}
181+
150182
@discardableResult
151183
public func createStrongRetain(operand: Value) -> StrongRetainInst {
152184
let retain = bridged.createStrongRetain(operand.bridged)
@@ -290,6 +322,20 @@ public struct Builder {
290322
return notifyNew(tttf.getAs(ThinToThickFunctionInst.self))
291323
}
292324

325+
public func createPartialApply(
326+
forFunction function: Value,
327+
substitutionMap: SubstitutionMap,
328+
capturedArgs: [Value],
329+
calleeConvention: ArgumentConvention,
330+
hasUnknownResultIsolation: Bool,
331+
isOnStack: Bool
332+
) -> PartialApplyInst {
333+
return capturedArgs.withBridgedValues { capturedArgsRef in
334+
let pai = bridged.createPartialApply(function.bridged, capturedArgsRef, calleeConvention.bridged, substitutionMap.bridged, hasUnknownResultIsolation, isOnStack)
335+
return notifyNew(pai.getAs(PartialApplyInst.self))
336+
}
337+
}
338+
293339
@discardableResult
294340
public func createSwitchEnum(enum enumVal: Value,
295341
cases: [(Int, BasicBlock)],
@@ -434,4 +480,14 @@ public struct Builder {
434480
let endAccess = bridged.createEndAccess(beginAccess.bridged)
435481
return notifyNew(endAccess.getAs(EndAccessInst.self))
436482
}
483+
484+
public func createConvertFunction(originalFunction: Value, resultType: Type, withoutActuallyEscaping: Bool) -> ConvertFunctionInst {
485+
let convertFunction = bridged.createConvertFunction(originalFunction.bridged, resultType.bridged, withoutActuallyEscaping)
486+
return notifyNew(convertFunction.getAs(ConvertFunctionInst.self))
487+
}
488+
489+
public func createConvertEscapeToNoEscape(originalFunction: Value, resultType: Type, isLifetimeGuaranteed: Bool) -> ConvertEscapeToNoEscapeInst {
490+
let convertFunction = bridged.createConvertEscapeToNoEscape(originalFunction.bridged, resultType.bridged, isLifetimeGuaranteed)
491+
return notifyNew(convertFunction.getAs(ConvertEscapeToNoEscapeInst.self))
492+
}
437493
}

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
9393

9494
public var isAsync: Bool { bridged.isAsync() }
9595

96-
public var isReabstractionThunk: Bool { bridged.isReabstractionThunk() }
97-
9896
/// True if this is a `[global_init]` function.
9997
///
10098
/// Such a function is typically a global addressor which calls the global's
@@ -140,7 +138,7 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
140138
case noThunk, thunk, reabstractionThunk, signatureOptimizedThunk
141139
}
142140

143-
var thunkKind: ThunkKind {
141+
public var thunkKind: ThunkKind {
144142
switch bridged.isThunk() {
145143
case .IsNotThunk: return .noThunk
146144
case .IsThunk: return .thunk

0 commit comments

Comments
 (0)