Skip to content

Commit b688a1f

Browse files
committed
[SILOpt] experimental async demotion pass
For chains of async functions where suspensions can be statically proven to never be required, this pass removes all suspensions and turns the functions into synchronous functions. For example, this function does not actually require any suspensions, once the correct executor is acquired upon initial entry: ``` func fib(_ n: Int) async -> Int { if n <= 1 { return n } return await fib(n-1) + fib(n-2) } ``` So we can turn the above into this for better performance: ``` func fib() async -> Int { return fib_sync() } func fib_sync(_ n: Int) -> Int { if n <= 1 { return n } return fib(n-1) + fib(n-2) } ``` while rewriting callers of `fib` to use the `sync` entry-point when we can prove that it will be invoked on a compatible executor. This pass is currently experimental and under development. Thus, it is disabled by default and you must use `-enable-experimental-async-demotion` to try it.
1 parent d0a9e78 commit b688a1f

File tree

21 files changed

+879
-2
lines changed

21 files changed

+879
-2
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AsyncDemotion.swift

Lines changed: 723 additions & 0 deletions
Large diffs are not rendered by default.

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
swift_compiler_sources(Optimizer
1010
AssumeSingleThreaded.swift
11+
AsyncDemotion.swift
1112
CleanupDebugSteps.swift
1213
ComputeEscapeEffects.swift
1314
ComputeSideEffects.swift

SwiftCompilerSources/Sources/Optimizer/PassManager/ModulePassContext.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ struct ModulePassContext : Context, CustomStringConvertible {
123123
runOnFunction(FunctionPassContext(_bridged: _bridged))
124124
_bridged.endTransformFunction();
125125
}
126+
127+
func mangleAsyncRemoved(from function: Function) -> String {
128+
let stdString = _bridged.mangleAsyncRemoved(function.bridged)
129+
return String(_cxxString: stdString)
130+
}
126131
}
127132

128133
extension GlobalVariable {

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ private func registerSwiftPasses() {
6666
registerPass(stackProtection, { stackProtection.run($0) })
6767

6868
// Function passes
69+
registerPass(asyncDemotion, { asyncDemotion.run($0) })
6970
registerPass(letPropertyLowering, { letPropertyLowering.run($0) })
7071
registerPass(mergeCondFailsPass, { mergeCondFailsPass.run($0) })
7172
registerPass(computeEscapeEffects, { computeEscapeEffects.run($0) })

SwiftCompilerSources/Sources/SIL/ApplySite.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ public protocol ApplySite : Instruction {
6767
extension ApplySite {
6868
public var callee: Value { operands[ApplyOperands.calleeOperandIndex].value }
6969

70+
public var isAsync: Bool {
71+
return callee.type.isAsyncFunction
72+
}
73+
7074
/// Returns the subset of operands which are argument operands.
7175
///
7276
/// This does not include the callee function operand.

SwiftCompilerSources/Sources/SIL/Argument.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ final public class FunctionArgument : Argument {
4343
bridged.getConvention().convention
4444
}
4545

46+
public var isSelf: Bool {
47+
return bridged.isSelf()
48+
}
49+
4650
public var isIndirectResult: Bool {
4751
return index < parentFunction.numIndirectResultArguments
4852
}

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ public class Instruction : CustomStringConvertible, Hashable {
9292
final public var mayWriteToMemory: Bool { memoryEffects.write }
9393
final public var mayReadOrWriteMemory: Bool { memoryEffects.read || memoryEffects.write }
9494

95+
public final var maySuspend: Bool {
96+
return bridged.maySuspend()
97+
}
98+
9599
public final var mayRelease: Bool {
96100
return bridged.mayRelease()
97101
}
@@ -338,6 +342,8 @@ final public class CondFailInst : Instruction, UnaryInstruction {
338342
public var message: String { bridged.CondFailInst_getMessage().string }
339343
}
340344

345+
final public class HopToExecutorInst : Instruction, UnaryInstruction {}
346+
341347
final public class FixLifetimeInst : Instruction, UnaryInstruction {}
342348

343349
final public class DebugValueInst : Instruction, UnaryInstruction {}
@@ -745,6 +751,13 @@ class ValueToBridgeObjectInst : SingleValueInstruction, UnaryInstruction {
745751
public var value: Value { return operand.value }
746752
}
747753

754+
final public
755+
class GetAsyncContinuationInst : SingleValueInstruction {}
756+
757+
final public
758+
class GetAsyncContinuationAddrInst : SingleValueInstruction, UnaryInstruction {}
759+
760+
748761
final public
749762
class MarkDependenceInst : SingleValueInstruction {
750763
public var value: Value { return operands[0].value }

SwiftCompilerSources/Sources/SIL/Registration.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public func registerSILClasses() {
4646
register(EndAccessInst.self)
4747
register(EndBorrowInst.self)
4848
register(CondFailInst.self)
49+
register(HopToExecutorInst.self)
4950
register(MarkUninitializedInst.self)
5051
register(FixLifetimeInst.self)
5152
register(DebugValueInst.self)
@@ -133,6 +134,8 @@ public func registerSILClasses() {
133134
register(RefToBridgeObjectInst.self)
134135
register(BridgeObjectToRefInst.self)
135136
register(BridgeObjectToWordInst.self)
137+
register(GetAsyncContinuationInst.self)
138+
register(GetAsyncContinuationAddrInst.self)
136139
register(BeginAccessInst.self)
137140
register(BeginBorrowInst.self)
138141
register(ProjectBoxInst.self)

SwiftCompilerSources/Sources/SIL/Type.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public struct Type : CustomStringConvertible, NoReflectionChildren {
5757
public var isFunction: Bool { bridged.isFunction() }
5858
public var isMetatype: Bool { bridged.isMetatype() }
5959
public var isNoEscapeFunction: Bool { bridged.isNoEscapeFunction() }
60+
public var isAsyncFunction: Bool { bridged.isAsyncFunction() }
6061

6162
public var canBeClass: swift.TypeTraitResult { bridged.canBeClass() }
6263

@@ -198,7 +199,7 @@ extension swift.SILType {
198199
}
199200

200201
// TODO: use an AST type for this once we have it
201-
public struct NominalTypeDecl : Equatable {
202+
public struct NominalTypeDecl : Equatable, Hashable {
202203
private let bridged: BridgedNominalTypeDecl
203204

204205
public init(_bridged: BridgedNominalTypeDecl) {
@@ -211,7 +212,15 @@ public struct NominalTypeDecl : Equatable {
211212
lhs.bridged.decl == rhs.bridged.decl
212213
}
213214

215+
public func hash(into hasher: inout Hasher) {
216+
hasher.combine(bridged.decl)
217+
}
218+
214219
public var isStructWithUnreferenceableStorage: Bool {
215220
bridged.isStructWithUnreferenceableStorage()
216221
}
222+
223+
public var isGlobalActor: Bool {
224+
return bridged.isGlobalActor()
225+
}
217226
}

include/swift/AST/SILOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ class SILOptions {
117117
/// Controls whether to emit actor data-race checks.
118118
bool EnableActorDataRaceChecks = false;
119119

120+
/// Controls whether to run async demotion pass.
121+
bool EnableAsyncDemotion = false;
122+
120123
/// Should we run any SIL performance optimizations
121124
///
122125
/// Useful when you want to enable -O LLVM opts but not -O SIL opts.

0 commit comments

Comments
 (0)