Skip to content

Commit 8139450

Browse files
authored
Merge pull request #76032 from eeckstein/mandatory-perfopt-linkage
MandatoryPerformanceOptimizations: force inlining of transparent functions and de-virtualization
2 parents 5e2df2b + b7e5ec8 commit 8139450

File tree

16 files changed

+302
-42
lines changed

16 files changed

+302
-42
lines changed

SwiftCompilerSources/Sources/Optimizer/DataStructures/FunctionUses.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ struct FunctionUses {
4545
var hasUnknownUses: Bool
4646

4747
init(of function: Function) {
48-
self.hasUnknownUses = function.isPossiblyUsedExternally || function.isAvailableExternally
48+
self.hasUnknownUses = function.isPossiblyUsedExternally || function.isDefinedExternally
4949
}
5050

5151
mutating func insert(_ inst: Instruction, _ uses: inout [Use]) {

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ let generalClosureSpecialization = FunctionPass(name: "experimental-swift-based-
119119
let autodiffClosureSpecialization = FunctionPass(name: "autodiff-closure-specialization") {
120120
(function: Function, context: FunctionPassContext) in
121121

122-
guard !function.isAvailableExternally,
122+
guard !function.isDefinedExternally,
123123
function.isAutodiffVJP,
124124
function.blocks.singleElement != nil else {
125125
return
@@ -502,7 +502,7 @@ private func handleApplies(for rootClosure: SingleValueInstruction, callSiteMap:
502502
continue
503503
}
504504

505-
if callee.isAvailableExternally {
505+
if callee.isDefinedExternally {
506506
continue
507507
}
508508

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import SIL
2727
let computeSideEffects = FunctionPass(name: "compute-side-effects") {
2828
(function: Function, context: FunctionPassContext) in
2929

30-
if function.isAvailableExternally {
30+
if function.isDefinedExternally {
3131
// We cannot assume anything about function, which are defined in another module,
3232
// even if the serialized SIL of its body is available in the current module.
3333
// If the other module was compiled with library evolution, the implementation

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,16 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
118118
context.erase(instructionIncludingDebugUses: iem)
119119
}
120120

121+
case let fri as FunctionRefInst:
122+
// Mandatory de-virtualization and mandatory inlining might leave referenced functions in "serialized"
123+
// functions with wrong linkage. Fix this by making the referenced function public.
124+
// It's not great, because it can prevent dead code elimination. But it's only a rare case.
125+
if function.serializedKind != .notSerialized,
126+
!fri.referencedFunction.hasValidLinkageForFragileRef(function.serializedKind)
127+
{
128+
fri.referencedFunction.set(linkage: .public, moduleContext)
129+
}
130+
121131
default:
122132
break
123133
}
@@ -153,6 +163,8 @@ private func specializeVTableAndAddEntriesToWorklist(for type: Type, in function
153163

154164
private func inlineAndDevirtualize(apply: FullApplySite, alreadyInlinedFunctions: inout Set<PathFunctionTuple>,
155165
_ context: FunctionPassContext, _ simplifyCtxt: SimplifyContext) {
166+
// De-virtualization and inlining in/into a "serialized" function might create function references to functions
167+
// with wrong linkage. We need to fix this later (see handling of FunctionRefInst in `optimize`).
156168
if simplifyCtxt.tryDevirtualize(apply: apply, isMandatory: true) != nil {
157169
return
158170
}
@@ -166,9 +178,7 @@ private func inlineAndDevirtualize(apply: FullApplySite, alreadyInlinedFunctions
166178
return
167179
}
168180

169-
if apply.canInline &&
170-
shouldInline(apply: apply, callee: callee, alreadyInlinedFunctions: &alreadyInlinedFunctions)
171-
{
181+
if shouldInline(apply: apply, callee: callee, alreadyInlinedFunctions: &alreadyInlinedFunctions) {
172182
if apply.inliningCanInvalidateStackNesting {
173183
simplifyCtxt.notifyInvalidatedStackNesting()
174184
}
@@ -196,9 +206,14 @@ private func removeUnusedMetatypeInstructions(in function: Function, _ context:
196206

197207
private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlinedFunctions: inout Set<PathFunctionTuple>) -> Bool {
198208
if callee.isTransparent {
209+
precondition(callee.hasOwnership, "transparent functions should have ownership at this stage of the pipeline")
199210
return true
200211
}
201212

213+
if !apply.canInline {
214+
return false
215+
}
216+
202217
if apply is BeginApplyInst {
203218
// Avoid co-routines because they might allocate (their context).
204219
return true

SwiftCompilerSources/Sources/Optimizer/ModulePasses/ReadOnlyGlobalVariables.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ let readOnlyGlobalVariablesPass = ModulePass(name: "read-only-global-variables")
3535
}
3636

3737
for g in moduleContext.globalVariables {
38-
if !g.isAvailableExternally,
38+
if !g.isDefinedExternally,
3939
!g.isPossiblyUsedExternally,
4040
!g.isLet,
4141
!writtenGlobals.contains(g) {

SwiftCompilerSources/Sources/Optimizer/PassManager/ModulePassContext.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,9 @@ extension GlobalVariable {
171171
bridged.setLet(value)
172172
}
173173
}
174+
175+
extension Function {
176+
func set(linkage: Linkage, _ context: ModulePassContext) {
177+
bridged.setLinkage(linkage.bridged)
178+
}
179+
}

SwiftCompilerSources/Sources/SIL/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_swift_compiler_module(SIL
2020
FunctionConvention.swift
2121
GlobalVariable.swift
2222
Instruction.swift
23+
Linkage.swift
2324
Location.swift
2425
Operand.swift
2526
Registration.swift

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
113113

114114
public var isGeneric: Bool { bridged.isGeneric() }
115115

116+
public var linkage: Linkage { bridged.getLinkage().linkage }
117+
116118
/// True, if the linkage of the function indicates that it is visible outside the current
117119
/// compilation unit and therefore not all of its uses are known.
118120
///
@@ -125,9 +127,7 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
125127
/// current compilation unit.
126128
///
127129
/// For example, `public_external` linkage.
128-
public var isAvailableExternally: Bool {
129-
return bridged.isAvailableExternally()
130-
}
130+
public var isDefinedExternally: Bool { linkage.isExternal }
131131

132132
public func hasSemanticsAttribute(_ attr: StaticString) -> Bool {
133133
attr.withUTF8Buffer { (buffer: UnsafeBufferPointer<UInt8>) in

SwiftCompilerSources/Sources/SIL/GlobalVariable.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ final public class GlobalVariable : CustomStringConvertible, HasShortDescription
3030

3131
public var isLet: Bool { bridged.isLet() }
3232

33+
public var linkage: Linkage { bridged.getLinkage().linkage }
34+
3335
/// True, if the linkage of the global variable indicates that it is visible outside the current
3436
/// compilation unit and therefore not all of its uses are known.
3537
///
@@ -42,9 +44,7 @@ final public class GlobalVariable : CustomStringConvertible, HasShortDescription
4244
/// current compilation unit.
4345
///
4446
/// For example, `public_external` linkage.
45-
public var isAvailableExternally: Bool {
46-
return bridged.isAvailableExternally()
47-
}
47+
public var isDefinedExternally: Bool { linkage.isExternal }
4848

4949
public var staticInitializerInstructions: InstructionList? {
5050
if let firstStaticInitInst = bridged.getFirstStaticInitInst().instruction {
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//===--- Linkage.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 SILBridging
14+
15+
public enum Linkage: CustomStringConvertible {
16+
/// This object definition is visible to multiple Swift modules (and
17+
/// thus potentially across linkage-unit boundaries). There are no
18+
/// other object definitions with this name in the program.
19+
///
20+
/// Public functions must be definitions, i.e. must have a body, except the
21+
/// body is emitted by clang.
22+
case `public`
23+
24+
/// This is a special linkage used for symbols which are treated
25+
/// as public for the purposes of SIL serialization and optimization,
26+
/// but do not have public entry points in the generated binary.
27+
///
28+
/// This linkage is used for @_alwaysEmitIntoClient functions.
29+
///
30+
/// There is no external variant of this linkage, because from other
31+
/// translation units in the same module, this behaves identically
32+
/// to the HiddenExternal linkage.
33+
///
34+
/// When deserialized, such declarations receive Shared linkage.
35+
///
36+
/// PublicNonABI functions must be definitions.
37+
case publicNonABI
38+
39+
/// Same as \c Public, except the definition is visible within a package
40+
/// of modules.
41+
case package
42+
43+
/// Similar to \c PublicNonABI, this definition is used for symbols treated
44+
/// as package but do not have package entry points in the generated binary.
45+
/// It's used for default argument expressions and `@_alwaysEmitIntoClient`.
46+
/// When deserialized, this will become \c Shared linkage.
47+
case packageNonABI
48+
49+
/// This object definition is visible only to the current Swift
50+
/// module (and thus should not be visible across linkage-unit
51+
/// boundaries). There are no other object definitions with this
52+
/// name in the module.
53+
///
54+
/// Hidden functions must be definitions, i.e. must have a body, except the
55+
/// body is emitted by clang.
56+
case hidden
57+
58+
/// This object definition is visible only within a single Swift
59+
/// module. There may be other object definitions with this name in
60+
/// the module; those definitions are all guaranteed to be
61+
/// semantically equivalent to this one.
62+
///
63+
/// This linkage is used e.g. for thunks and for specialized functions.
64+
///
65+
/// Shared functions must be definitions, i.e. must have a body, except the
66+
/// body is emitted by clang.
67+
case shared
68+
69+
/// This object definition is visible only within a single Swift
70+
/// file.
71+
///
72+
/// Private functions must be definitions, i.e. must have a body, except the
73+
/// body is emitted by clang.
74+
case `private`
75+
76+
/// A Public definition with the same name as this object will be
77+
/// available to the current Swift module at runtime. If this
78+
/// object is a definition, it is semantically equivalent to that
79+
/// definition.
80+
case publicExternal
81+
82+
/// Similar to \c PublicExternal.
83+
/// Used to reference a \c Package definition in a different module
84+
/// within a package.
85+
case packageExternal
86+
87+
/// A Public or Hidden definition with the same name as this object
88+
/// will be defined by the current Swift module at runtime.
89+
///
90+
/// This linkage is only used for non-whole-module compilations to refer to
91+
/// functions in other files of the same module.
92+
case hiddenExternal
93+
94+
public var isExternal: Bool {
95+
switch self {
96+
case .public,
97+
.publicNonABI,
98+
.package,
99+
.packageNonABI,
100+
.hidden,
101+
.shared,
102+
.private:
103+
return false
104+
case .packageExternal,
105+
.publicExternal,
106+
.hiddenExternal:
107+
return true
108+
}
109+
110+
}
111+
112+
public var description: String {
113+
switch self {
114+
case .public: return "public"
115+
case .publicNonABI: return "publicNonABI"
116+
case .package: return "package"
117+
case .packageNonABI: return "packageNonABI"
118+
case .hidden: return "hidden"
119+
case .shared: return "shared"
120+
case .private: return "private"
121+
case .packageExternal: return "packageExternal"
122+
case .publicExternal: return "publicExternal"
123+
case .hiddenExternal: return "hiddenExternal"
124+
}
125+
}
126+
}
127+
128+
// Bridging utilities
129+
130+
extension BridgedLinkage {
131+
var linkage: Linkage {
132+
switch self {
133+
case .Public: return .public
134+
case .PublicNonABI: return .publicNonABI
135+
case .Package: return .package
136+
case .PackageNonABI: return .packageNonABI
137+
case .Hidden: return .hidden
138+
case .Shared: return .shared
139+
case .Private: return .private
140+
case .PublicExternal: return .publicExternal
141+
case .PackageExternal: return .packageExternal
142+
case .HiddenExternal: return .hiddenExternal
143+
default:
144+
fatalError("unsupported argument convention")
145+
}
146+
}
147+
}
148+
149+
extension Linkage {
150+
public var bridged: BridgedLinkage {
151+
switch self {
152+
case .public: return .Public
153+
case .publicNonABI: return .PublicNonABI
154+
case .package: return .Package
155+
case .packageNonABI: return .PackageNonABI
156+
case .hidden: return .Hidden
157+
case .shared: return .Shared
158+
case .private: return .Private
159+
case .publicExternal: return .PublicExternal
160+
case .packageExternal: return .PackageExternal
161+
case .hiddenExternal: return .HiddenExternal
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)