Skip to content

Commit a12e33e

Browse files
committed
Swift Optimizer: add the FunctionUses utility
Provides a list of instructions, which reference a function. A function "use" is an instruction in another (or the same) function which references the function. In most cases those are `function_ref` instructions, but can also be e.g. `keypath` instructions. 'FunctionUses' performs an analysis of all functions in the module and collects instructions which reference other functions. This utility can be used to do inter-procedural caller-analysis.
1 parent 2fe1ee5 commit a12e33e

File tree

7 files changed

+393
-0
lines changed

7 files changed

+393
-0
lines changed

SwiftCompilerSources/Sources/Optimizer/DataStructures/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
swift_compiler_sources(Optimizer
1010
BasicBlockRange.swift
1111
BasicBlockWorklist.swift
12+
FunctionUses.swift
1213
InstructionRange.swift
1314
Set.swift
1415
Stack.swift)
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//===--- FunctionUses.swift -----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
/// Provides a list of instructions, which reference a function.
16+
///
17+
/// A function "use" is an instruction in another (or the same) function which
18+
/// references the function. In most cases those are `function_ref` instructions,
19+
/// but can also be e.g. `keypath` instructions.
20+
///
21+
/// 'FunctionUses' performs an analysis of all functions in the module and collects
22+
/// instructions which reference other functions. This utility can be used to do
23+
/// inter-procedural caller-analysis.
24+
///
25+
/// In order to use `FunctionUses`, first call `collect()` and then get use-lists of
26+
/// functions with `getUses(of:)`.
27+
struct FunctionUses {
28+
29+
// Function uses are stored in a single linked list, whereas the "next" is not a pointer
30+
// but an index into `FunctionUses.useStorage`.
31+
fileprivate struct Use {
32+
// The index of the next use in `FunctionUses.useStorage`.
33+
let next: Int?
34+
35+
// The instruction which references the function.
36+
let usingInstruction: Instruction
37+
}
38+
39+
// The head of the single-linked list of function uses.
40+
fileprivate struct FirstUse {
41+
// The head of the use-list.
42+
var first: Int?
43+
44+
// True if the function has unknown uses
45+
var hasUnknownUses: Bool
46+
47+
init(of function: Function) {
48+
self.hasUnknownUses = function.isPossiblyUsedExternally || function.isAvailableExternally
49+
}
50+
51+
mutating func insert(_ inst: Instruction, _ uses: inout [Use]) {
52+
let newFirst = uses.count
53+
uses.append(Use(next: first, usingInstruction: inst))
54+
first = newFirst
55+
}
56+
}
57+
58+
/// The list of uses of a function.
59+
struct UseList : CollectionLikeSequence, CustomStringConvertible {
60+
struct Iterator : IteratorProtocol {
61+
fileprivate let useStorage: [Use]
62+
fileprivate var currentUseIdx: Int?
63+
64+
mutating func next() -> Instruction? {
65+
if let useIdx = currentUseIdx {
66+
let use = useStorage[useIdx]
67+
currentUseIdx = use.next
68+
return use.usingInstruction
69+
}
70+
return nil
71+
}
72+
}
73+
74+
// The "storage" for all function uses.
75+
fileprivate let useStorage: [Use]
76+
77+
// The head of the single-linked use list.
78+
fileprivate let firstUse: FirstUse
79+
80+
/// True if the function has unknown uses in addition to the list of referencing instructions.
81+
///
82+
/// This is the case, e.g. if the function has public linkage or if the function
83+
/// is referenced from a vtable or witness table.
84+
var hasUnknownUses: Bool { firstUse.hasUnknownUses }
85+
86+
func makeIterator() -> Iterator {
87+
return Iterator(useStorage: useStorage, currentUseIdx: firstUse.first)
88+
}
89+
90+
var description: String {
91+
var result = "[\n"
92+
if hasUnknownUses {
93+
result += "<unknown uses>\n"
94+
}
95+
for inst in self {
96+
result += "@\(inst.function.name): \(inst)\n"
97+
98+
}
99+
result += "]"
100+
return result
101+
}
102+
103+
var customMirror: Mirror { Mirror(self, children: []) }
104+
}
105+
106+
// The "storage" for all function uses.
107+
private var useStorage: [Use] = []
108+
109+
// The use-list head for each function.
110+
private var uses: [Function: FirstUse] = [:]
111+
112+
init() {
113+
// Already start with a reasonable big capacity to reduce the number of
114+
// re-allocations when appending to the data structures.
115+
useStorage.reserveCapacity(128)
116+
uses.reserveCapacity(64)
117+
}
118+
119+
/// Returns the use-list of `function`.
120+
///
121+
/// Note that `collect` must be called before `getUses` can be used.
122+
func getUses(of function: Function) -> UseList {
123+
UseList(useStorage: useStorage, firstUse: uses[function, default: FirstUse(of: function)])
124+
}
125+
126+
/// Collects all uses of all function in the module.
127+
mutating func collect(context: ModulePassContext) {
128+
129+
// Mark all functions, which are referenced from tables, to have "unknown" uses.
130+
131+
for vTable in context.vTables {
132+
for entry in vTable.entries {
133+
markUnknown(entry.function)
134+
}
135+
}
136+
137+
for witnessTable in context.witnessTables {
138+
for entry in witnessTable.entries {
139+
if entry.kind == .method, let f = entry.methodFunction {
140+
markUnknown(f)
141+
}
142+
}
143+
}
144+
145+
for witnessTable in context.defaultWitnessTables {
146+
for entry in witnessTable.entries {
147+
if entry.kind == .method, let f = entry.methodFunction {
148+
markUnknown(f)
149+
}
150+
}
151+
}
152+
153+
// Collect all instructions, which reference functions, in the module.
154+
for function in context.functions {
155+
for inst in function.instructions {
156+
inst.visitReferencedFunctions { referencedFunc in
157+
uses[referencedFunc, default: FirstUse(of: referencedFunc)].insert(inst, &useStorage)
158+
}
159+
}
160+
}
161+
}
162+
163+
private mutating func markUnknown(_ function: Function) {
164+
uses[function, default: FirstUse(of: function)].hasUnknownUses = true
165+
}
166+
}

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 registerSwiftPasses() {
6161
registerPass(simplifyStrongReleasePass, { simplifyStrongReleasePass.run($0) })
6262

6363
// Test passes
64+
registerPass(functionUsesDumper, { functionUsesDumper.run($0) })
6465
registerPass(silPrinterPass, { silPrinterPass.run($0) })
6566
registerPass(escapeInfoDumper, { escapeInfoDumper.run($0) })
6667
registerPass(addressEscapeInfoDumper, { addressEscapeInfoDumper.run($0) })

SwiftCompilerSources/Sources/Optimizer/TestPasses/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+
FunctionUsesDumper.swift
1011
AccessDumper.swift
1112
EscapeInfoDumper.swift
1213
SILPrinter.swift
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===--- FunctionUsesDumper.swift -----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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+
let functionUsesDumper = ModulePass(name: "dump-function-uses", {
16+
(context: ModulePassContext) in
17+
18+
var functionUses = FunctionUses()
19+
functionUses.collect(context: context)
20+
21+
for function in context.functions {
22+
let uses = functionUses.getUses(of: function)
23+
24+
print("Uses of \(function.name)")
25+
print(uses)
26+
print("End function \(function.name)\n")
27+
}
28+
})

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,8 @@ SWIFT_FUNCTION_PASS(RunUnitTests, "run-unit-tests",
399399
"Runs the compiler internal unit tests")
400400
SWIFT_FUNCTION_PASS(SILPrinter, "sil-printer",
401401
"Test pass which prints the SIL of a function")
402+
SWIFT_MODULE_PASS(FunctionUsesDumper, "dump-function-uses",
403+
"Dump the results of FunctionUses")
402404
PASS(SROA, "sroa",
403405
"Scalar Replacement of Aggregate Stack Objects")
404406
PASS(SROABBArgs, "sroa-bb-args",

0 commit comments

Comments
 (0)