Skip to content

Commit dc3cb18

Browse files
committed
Swift Optimizer: add the MandatoryPerformanceOptimizations pass
As a replacement for the old MandatoryGenericSpecializer The pass it not enabled yet in the pass pipeline
1 parent 1355e94 commit dc3cb18

File tree

15 files changed

+416
-124
lines changed

15 files changed

+416
-124
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ let lateOnoneSimplificationPass = FunctionPass(name: "late-onone-simplification"
7171
//===--------------------------------------------------------------------===//
7272

7373

74-
private func runSimplification(on function: Function, _ context: FunctionPassContext,
75-
preserveDebugInfo: Bool,
76-
_ simplify: (Instruction, SimplifyContext) -> ()) {
74+
func runSimplification(on function: Function, _ context: FunctionPassContext,
75+
preserveDebugInfo: Bool,
76+
_ simplify: (Instruction, SimplifyContext) -> ()) {
7777
var worklist = InstructionWorklist(context)
7878
defer { worklist.deinitialize() }
7979

SwiftCompilerSources/Sources/Optimizer/ModulePasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
swift_compiler_sources(Optimizer
10+
MandatoryPerformanceOptimizations.swift
1011
ReadOnlyGlobalVariables.swift
1112
StackProtection.swift
1213
)
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//===--- MandatoryPerformanceOptimizations.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 SIL
14+
15+
/// Performs mandatory optimizations for performance-annotated functions.
16+
///
17+
/// Optimizations include:
18+
/// * de-virtualization
19+
/// * mandatory inlining
20+
/// * generic specialization
21+
/// * mandatory memory optimizations
22+
/// * dead alloc elimination
23+
/// * instruction simplification
24+
///
25+
/// The pass starts with performance-annotated functions and transitively handles
26+
/// called functions.
27+
///
28+
let mandatoryPerformanceOptimizations = ModulePass(name: "mandatory-performance-optimizations") {
29+
(moduleContext: ModulePassContext) in
30+
31+
var worklist = FunctionWorklist()
32+
worklist.addAllPerformanceAnnotatedFunctions(of: moduleContext)
33+
34+
optimizeFunctionsTopDown(using: &worklist, moduleContext)
35+
}
36+
37+
private func optimizeFunctionsTopDown(using worklist: inout FunctionWorklist,
38+
_ moduleContext: ModulePassContext) {
39+
while let f = worklist.pop() {
40+
moduleContext.transform(function: f) { context in
41+
if !context.loadFunction(function: f, loadCalleesRecursively: true) {
42+
return
43+
}
44+
optimize(function: f, context)
45+
worklist.add(calleesOf: f)
46+
}
47+
}
48+
}
49+
50+
private func optimize(function: Function, _ context: FunctionPassContext) {
51+
runSimplification(on: function, context, preserveDebugInfo: true) { instruction, simplifyCtxt in
52+
if let i = instruction as? OnoneSimplifyable {
53+
i.simplify(simplifyCtxt)
54+
if instruction.isDeleted {
55+
return
56+
}
57+
}
58+
switch instruction {
59+
case let apply as FullApplySite:
60+
inlineAndDevirtualize(apply: apply, context, simplifyCtxt)
61+
case let mt as MetatypeInst:
62+
if mt.isTriviallyDeadIgnoringDebugUses {
63+
simplifyCtxt.erase(instructionIncludingDebugUses: mt)
64+
}
65+
default:
66+
break
67+
}
68+
}
69+
70+
_ = context.specializeApplies(in: function, isMandatory: true)
71+
72+
// If this is a just specialized function, try to optimize copy_addr, etc.
73+
if context.optimizeMemoryAccesses(in: function) {
74+
_ = context.eliminateDeadAllocations(in: function)
75+
}
76+
}
77+
78+
private func inlineAndDevirtualize(apply: FullApplySite, _ context: FunctionPassContext, _ simplifyCtxt: SimplifyContext) {
79+
80+
if simplifyCtxt.tryDevirtualize(apply: apply, isMandatory: true) != nil {
81+
return
82+
}
83+
84+
guard let callee = apply.referencedFunction else {
85+
return
86+
}
87+
88+
if !context.loadFunction(function: callee, loadCalleesRecursively: true) {
89+
// We don't have the funcion body of the callee.
90+
return
91+
}
92+
93+
if shouldInline(apply: apply, callee: callee) {
94+
simplifyCtxt.inlineFunction(apply: apply, mandatoryInline: true)
95+
96+
// In OSSA `partial_apply [on_stack]`s are represented as owned values rather than stack locations.
97+
// It is possible for their destroys to violate stack discipline.
98+
// When inlining into non-OSSA, those destroys are lowered to dealloc_stacks.
99+
// This can result in invalid stack nesting.
100+
if callee.hasOwnership && !apply.parentFunction.hasOwnership {
101+
simplifyCtxt.notifyInvalidatedStackNesting()
102+
}
103+
}
104+
}
105+
106+
private func shouldInline(apply: FullApplySite, callee: Function) -> Bool {
107+
if callee.isTransparent {
108+
return true
109+
}
110+
if apply is BeginApplyInst {
111+
// Avoid co-routines because they might allocate (their context).
112+
return true
113+
}
114+
if apply.parentFunction.isGlobalInitOnceFunction && callee.inlineStrategy == .always {
115+
// Some arithmetic operations, like integer conversions, are not transparent but `inline(__always)`.
116+
// Force inlining them in global initializers so that it's possible to statically initialize the global.
117+
return true
118+
}
119+
return false
120+
}
121+
122+
fileprivate struct FunctionWorklist {
123+
private(set) var functions = Array<Function>()
124+
private var pushedFunctions = Set<Function>()
125+
private var currentIndex = 0
126+
127+
mutating func pop() -> Function? {
128+
if currentIndex < functions.count {
129+
let f = functions[currentIndex]
130+
currentIndex += 1
131+
return f
132+
}
133+
return nil
134+
}
135+
136+
mutating func addAllPerformanceAnnotatedFunctions(of moduleContext: ModulePassContext) {
137+
for f in moduleContext.functions where f.performanceConstraints != .none {
138+
pushIfNotVisited(f)
139+
}
140+
}
141+
142+
mutating func add(calleesOf function: Function) {
143+
for inst in function.instructions {
144+
switch inst {
145+
case let apply as ApplySite:
146+
if let callee = apply.referencedFunction {
147+
pushIfNotVisited(callee)
148+
}
149+
case let bi as BuiltinInst:
150+
switch bi.id {
151+
case .Once, .OnceWithContext:
152+
if let fri = bi.operands[1].value as? FunctionRefInst {
153+
pushIfNotVisited(fri.referencedFunction)
154+
}
155+
break;
156+
default:
157+
break
158+
}
159+
default:
160+
break
161+
}
162+
}
163+
}
164+
165+
mutating func pushIfNotVisited(_ element: Function) {
166+
if pushedFunctions.insert(element).inserted {
167+
functions.append(element)
168+
}
169+
}
170+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,24 @@ extension MutatingContext {
109109
return nil
110110
}
111111

112+
func inlineFunction(apply: FullApplySite, mandatoryInline: Bool) {
113+
let instAfterInling: Instruction?
114+
switch apply {
115+
case is ApplyInst, is BeginApplyInst:
116+
instAfterInling = apply.next
117+
case is TryApplyInst:
118+
instAfterInling = apply.parentBlock.next?.instructions.first
119+
default:
120+
instAfterInling = nil
121+
}
122+
123+
_bridged.inlineFunction(apply.bridged, mandatoryInline)
124+
125+
if let instAfterInling = instAfterInling {
126+
notifyNewInstructions(from: apply, to: instAfterInling)
127+
}
128+
}
129+
112130
/// Copies all instructions of a static init value of a global to the insertion point of `builder`.
113131
func copyStaticInitializer(fromInitValue: Value, to builder: Builder) -> Value? {
114132
let range = _bridged.copyStaticInitializer(fromInitValue.bridged, builder.bridged)
@@ -191,12 +209,21 @@ struct FunctionPassContext : MutatingContext {
191209
return PostDominatorTree(bridged: bridgedPDT)
192210
}
193211

194-
func loadFunction(name: StaticString) -> Function? {
212+
func loadFunction(name: StaticString, loadCalleesRecursively: Bool) -> Function? {
195213
return name.withUTF8Buffer { (nameBuffer: UnsafeBufferPointer<UInt8>) in
196-
_bridged.loadFunction(llvm.StringRef(nameBuffer.baseAddress, nameBuffer.count)).function
214+
let nameStr = llvm.StringRef(nameBuffer.baseAddress, nameBuffer.count)
215+
return _bridged.loadFunction(nameStr, loadCalleesRecursively).function
197216
}
198217
}
199218

219+
func loadFunction(function: Function, loadCalleesRecursively: Bool) -> Bool {
220+
if function.isDefinition {
221+
return true
222+
}
223+
_bridged.loadFunction(function.bridged, loadCalleesRecursively)
224+
return function.isDefinition
225+
}
226+
200227
func erase(block: BasicBlock) {
201228
_bridged.eraseBlock(block.bridged)
202229
}
@@ -210,6 +237,31 @@ struct FunctionPassContext : MutatingContext {
210237
_bridged.asNotificationHandler().notifyChanges(.effectsChanged)
211238
}
212239

240+
func optimizeMemoryAccesses(in function: Function) -> Bool {
241+
if swift.optimizeMemoryAccesses(function.bridged.getFunction()) {
242+
notifyInstructionsChanged()
243+
return true
244+
}
245+
return false
246+
}
247+
248+
func eliminateDeadAllocations(in function: Function) -> Bool {
249+
if swift.eliminateDeadAllocations(function.bridged.getFunction()) {
250+
notifyInstructionsChanged()
251+
return true
252+
}
253+
return false
254+
}
255+
256+
func specializeApplies(in function: Function, isMandatory: Bool) -> Bool {
257+
if _bridged.specializeAppliesInFunction(function.bridged, isMandatory) {
258+
notifyInstructionsChanged()
259+
notifyCallsChanged()
260+
return true
261+
}
262+
return false
263+
}
264+
213265
/// Copies `initValue` (including all operand instructions, transitively) to the
214266
/// static init value of `global`.
215267
func createStaticInitializer(for global: GlobalVariable, initValue: SingleValueInstruction) {

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ private func registerForSILCombine<InstType: SILCombineSimplifyable>(
6161

6262
private func registerSwiftPasses() {
6363
// Module passes
64+
registerPass(mandatoryPerformanceOptimizations, { mandatoryPerformanceOptimizations.run($0) })
6465
registerPass(readOnlyGlobalVariablesPass, { readOnlyGlobalVariablesPass.run($0) })
6566
registerPass(stackProtection, { stackProtection.run($0) })
6667

SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ extension Value {
7878
}
7979
}
8080

81-
private extension Instruction {
81+
extension Instruction {
8282
var isTriviallyDead: Bool {
8383
if results.contains(where: { !$0.uses.isEmpty }) {
8484
return false

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
2020
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
2121
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
22+
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
2223

2324
SWIFT_BEGIN_NULLABILITY_ANNOTATIONS
2425

@@ -217,6 +218,8 @@ struct BridgedPassContext {
217218
SWIFT_IMPORT_UNSAFE
218219
OptionalBridgedValue constantFoldBuiltin(BridgedInstruction builtin) const;
219220

221+
bool specializeAppliesInFunction(BridgedFunction function, bool isMandatory) const;
222+
220223
void createStaticInitializer(BridgedGlobalVar global, BridgedInstruction initValue) const;
221224

222225
struct StaticInitCloneResult {
@@ -227,6 +230,8 @@ struct BridgedPassContext {
227230
SWIFT_IMPORT_UNSAFE
228231
StaticInitCloneResult copyStaticInitializer(BridgedValue initValue, BridgedBuilder b) const;
229232

233+
void inlineFunction(BridgedInstruction apply, bool mandatoryInline) const;
234+
230235
SWIFT_IMPORT_UNSAFE
231236
BridgedValue getSILUndef(swift::SILType type) const {
232237
return {swift::SILUndef::get(type, *invocation->getFunction())};
@@ -390,9 +395,18 @@ struct BridgedPassContext {
390395
}
391396

392397
SWIFT_IMPORT_UNSAFE
393-
OptionalBridgedFunction loadFunction(llvm::StringRef name) const {
398+
OptionalBridgedFunction loadFunction(llvm::StringRef name, bool loadCalleesRecursively) const {
399+
swift::SILModule *mod = invocation->getPassManager()->getModule();
400+
return {mod->loadFunction(name, loadCalleesRecursively ? swift::SILModule::LinkingMode::LinkAll
401+
: swift::SILModule::LinkingMode::LinkNormal)};
402+
}
403+
404+
SWIFT_IMPORT_UNSAFE
405+
void loadFunction(BridgedFunction function, bool loadCalleesRecursively) const {
394406
swift::SILModule *mod = invocation->getPassManager()->getModule();
395-
return {mod->loadFunction(name, swift::SILModule::LinkingMode::LinkNormal)};
407+
mod->loadFunction(function.getFunction(),
408+
loadCalleesRecursively ? swift::SILModule::LinkingMode::LinkAll
409+
: swift::SILModule::LinkingMode::LinkNormal);
396410
}
397411

398412
SWIFT_IMPORT_UNSAFE

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,8 @@ SWIFT_FUNCTION_PASS(SILPrinter, "sil-printer",
382382
"Test pass which prints the SIL of a function")
383383
SWIFT_MODULE_PASS(FunctionUsesDumper, "dump-function-uses",
384384
"Dump the results of FunctionUses")
385+
SWIFT_MODULE_PASS(MandatoryPerformanceOptimizations, "mandatory-performance-optimizations",
386+
"Performs optimizations for performance-annotated functions")
385387
SWIFT_MODULE_PASS(ReadOnlyGlobalVariablesPass, "read-only-global-variables",
386388
"Converts read-only var-globals to let-globals")
387389
SWIFT_MODULE_PASS(StackProtection, "stack-protection",

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -569,13 +569,16 @@ IntegerLiteralInst *optimizeBuiltinCanBeObjCClass(BuiltinInst *bi,
569569
/// Performs "predictable" memory access optimizations.
570570
///
571571
/// See the PredictableMemoryAccessOptimizations pass.
572-
bool optimizeMemoryAccesses(SILFunction &fn);
572+
bool optimizeMemoryAccesses(SILFunction *fn);
573573

574574
/// Performs "predictable" dead allocation optimizations.
575575
///
576576
/// See the PredictableDeadAllocationElimination pass.
577-
bool eliminateDeadAllocations(SILFunction &fn);
577+
bool eliminateDeadAllocations(SILFunction *fn);
578578

579+
bool specializeAppliesInFunction(SILFunction &F,
580+
SILTransform *transform,
581+
bool isMandatory);
579582
} // end namespace swift
580583

581584
#endif // SWIFT_SILOPTIMIZER_UTILS_INSTOPTUTILS_H

0 commit comments

Comments
 (0)