12
12
13
13
import SIL
14
14
15
- /// Performs mandatory optimizations for performance-annotated functions.
15
+ /// Performs mandatory optimizations for performance-annotated functions, and global
16
+ /// variable initializers that are required to be statically initialized.
16
17
///
17
18
/// Optimizations include:
18
19
/// * de-virtualization
@@ -22,14 +23,15 @@ import SIL
22
23
/// * dead alloc elimination
23
24
/// * instruction simplification
24
25
///
25
- /// The pass starts with performance-annotated functions and transitively handles
26
+ /// The pass starts with performance-annotated functions / globals and transitively handles
26
27
/// called functions.
27
28
///
28
29
let mandatoryPerformanceOptimizations = ModulePass ( name: " mandatory-performance-optimizations " ) {
29
30
( moduleContext: ModulePassContext ) in
30
31
31
32
var worklist = FunctionWorklist ( )
32
33
worklist. addAllPerformanceAnnotatedFunctions ( of: moduleContext)
34
+ worklist. addAllAnnotatedGlobalInitOnceFunctions ( of: moduleContext)
33
35
34
36
optimizeFunctionsTopDown ( using: & worklist, moduleContext)
35
37
}
@@ -47,34 +49,43 @@ private func optimizeFunctionsTopDown(using worklist: inout FunctionWorklist,
47
49
}
48
50
}
49
51
52
+ fileprivate struct PathFunctionTuple : Hashable {
53
+ var path : SmallProjectionPath
54
+ var function : Function
55
+ }
56
+
50
57
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
58
+ var alreadyInlinedFunctions : Set < PathFunctionTuple > = Set ( )
59
+
60
+ var changed = true
61
+ while changed {
62
+ changed = runSimplification ( on: function, context, preserveDebugInfo: true ) { instruction, simplifyCtxt in
63
+ if let i = instruction as? OnoneSimplifyable {
64
+ i. simplify ( simplifyCtxt)
65
+ if instruction. isDeleted {
66
+ return
67
+ }
68
+ }
69
+ switch instruction {
70
+ case let apply as FullApplySite :
71
+ inlineAndDevirtualize ( apply: apply, alreadyInlinedFunctions: & alreadyInlinedFunctions, context, simplifyCtxt)
72
+ default :
73
+ break
56
74
}
57
75
}
58
- switch instruction {
59
- case let apply as FullApplySite :
60
- inlineAndDevirtualize ( apply: apply, context, simplifyCtxt)
61
- default :
62
- break
63
- }
64
- }
65
76
66
- _ = context. specializeApplies ( in: function, isMandatory: true )
77
+ _ = context. specializeApplies ( in: function, isMandatory: true )
67
78
68
- removeUnusedMetatypeInstructions ( in: function, context)
79
+ removeUnusedMetatypeInstructions ( in: function, context)
69
80
70
- // If this is a just specialized function, try to optimize copy_addr, etc.
71
- if context. optimizeMemoryAccesses ( in: function) {
81
+ // If this is a just specialized function, try to optimize copy_addr, etc.
82
+ changed = context. optimizeMemoryAccesses ( in: function) || changed
72
83
_ = context. eliminateDeadAllocations ( in: function)
73
84
}
74
85
}
75
86
76
- private func inlineAndDevirtualize( apply: FullApplySite , _ context : FunctionPassContext , _ simplifyCtxt : SimplifyContext ) {
77
-
87
+ private func inlineAndDevirtualize( apply: FullApplySite , alreadyInlinedFunctions : inout Set < PathFunctionTuple > ,
88
+ _ context : FunctionPassContext , _ simplifyCtxt : SimplifyContext ) {
78
89
if simplifyCtxt. tryDevirtualize ( apply: apply, isMandatory: true ) != nil {
79
90
return
80
91
}
@@ -88,7 +99,7 @@ private func inlineAndDevirtualize(apply: FullApplySite, _ context: FunctionPass
88
99
return
89
100
}
90
101
91
- if shouldInline ( apply: apply, callee: callee) {
102
+ if shouldInline ( apply: apply, callee: callee, alreadyInlinedFunctions : & alreadyInlinedFunctions ) {
92
103
simplifyCtxt. inlineFunction ( apply: apply, mandatoryInline: true )
93
104
94
105
// In OSSA `partial_apply [on_stack]`s are represented as owned values rather than stack locations.
@@ -110,7 +121,7 @@ private func removeUnusedMetatypeInstructions(in function: Function, _ context:
110
121
}
111
122
}
112
123
113
- private func shouldInline( apply: FullApplySite , callee: Function ) -> Bool {
124
+ private func shouldInline( apply: FullApplySite , callee: Function , alreadyInlinedFunctions : inout Set < PathFunctionTuple > ) -> Bool {
114
125
if callee. isTransparent {
115
126
return true
116
127
}
@@ -123,9 +134,103 @@ private func shouldInline(apply: FullApplySite, callee: Function) -> Bool {
123
134
// Force inlining them in global initializers so that it's possible to statically initialize the global.
124
135
return true
125
136
}
137
+ if apply. parentFunction. isGlobalInitOnceFunction,
138
+ let global = apply. parentFunction. getInitializedGlobal ( ) ,
139
+ global. mustBeInitializedStatically,
140
+ let applyInst = apply as? ApplyInst ,
141
+ let projectionPath = applyInst. isStored ( to: global) ,
142
+ alreadyInlinedFunctions. insert ( PathFunctionTuple ( path: projectionPath, function: callee) ) . inserted {
143
+ return true
144
+ }
126
145
return false
127
146
}
128
147
148
+ private extension Value {
149
+ /// Analyzes the def-use chain of an apply instruction, and looks for a single chain that leads to a store instruction
150
+ /// that initializes a part of a global variable or the entire variable:
151
+ ///
152
+ /// Example:
153
+ /// %g = global_addr @global
154
+ /// ...
155
+ /// %f = function_ref @func
156
+ /// %apply = apply %f(...)
157
+ /// store %apply to %g <--- is a store to the global trivially (the apply result is immediately going into a store)
158
+ ///
159
+ /// Example:
160
+ /// %apply = apply %f(...)
161
+ /// %apply2 = apply %f2(%apply)
162
+ /// store %apply2 to %g <--- is a store to the global (the apply result has a single chain into the store)
163
+ ///
164
+ /// Example:
165
+ /// %a = apply %f(...)
166
+ /// %s = struct $MyStruct (%a, %b)
167
+ /// store %s to %g <--- is a partial store to the global (returned SmallProjectionPath is MyStruct.s0)
168
+ ///
169
+ /// Example:
170
+ /// %a = apply %f(...)
171
+ /// %as = struct $AStruct (%other, %a)
172
+ /// %bs = struct $BStruct (%as, %bother)
173
+ /// store %bs to %g <--- is a partial store to the global (returned SmallProjectionPath is MyStruct.s0.s1)
174
+ ///
175
+ /// Returns nil if we cannot find a singular def-use use chain (e.g. because a value has more than one user)
176
+ /// leading to a store to the specified global variable.
177
+ func isStored( to global: GlobalVariable ) -> SmallProjectionPath ? {
178
+ var singleUseValue : any Value = self
179
+ var path = SmallProjectionPath ( )
180
+ while true {
181
+ guard let use = singleUseValue. uses. singleNonDebugUse else {
182
+ return nil
183
+ }
184
+
185
+ switch use. instruction {
186
+ case is StructInst :
187
+ path = path. push ( . structField, index: use. index)
188
+ break
189
+ case is TupleInst :
190
+ path = path. push ( . tupleField, index: use. index)
191
+ break
192
+ case let ei as EnumInst :
193
+ path = path. push ( . enumCase, index: ei. caseIndex)
194
+ break
195
+ case let si as StoreInst :
196
+ guard let storeDestination = si. destination as? GlobalAddrInst else {
197
+ return nil
198
+ }
199
+
200
+ guard storeDestination. global == global else {
201
+ return nil
202
+ }
203
+
204
+ return path
205
+ default :
206
+ return nil
207
+ }
208
+
209
+ guard let nextInstruction = use. instruction as? SingleValueInstruction else {
210
+ return nil
211
+ }
212
+
213
+ singleUseValue = nextInstruction
214
+ }
215
+ }
216
+ }
217
+
218
+ private extension Function {
219
+ /// Analyzes the global initializer function and returns global it initializes (from `alloc_global` instruction).
220
+ func getInitializedGlobal( ) -> GlobalVariable ? {
221
+ for inst in self . entryBlock. instructions {
222
+ switch inst {
223
+ case let agi as AllocGlobalInst :
224
+ return agi. global
225
+ default :
226
+ break
227
+ }
228
+ }
229
+
230
+ return nil
231
+ }
232
+ }
233
+
129
234
fileprivate struct FunctionWorklist {
130
235
private( set) var functions = Array < Function > ( )
131
236
private var pushedFunctions = Set < Function > ( )
@@ -146,6 +251,15 @@ fileprivate struct FunctionWorklist {
146
251
}
147
252
}
148
253
254
+ mutating func addAllAnnotatedGlobalInitOnceFunctions( of moduleContext: ModulePassContext ) {
255
+ for f in moduleContext. functions where f. isGlobalInitOnceFunction {
256
+ if let global = f. getInitializedGlobal ( ) ,
257
+ global. mustBeInitializedStatically {
258
+ pushIfNotVisited ( f)
259
+ }
260
+ }
261
+ }
262
+
149
263
mutating func add( calleesOf function: Function ) {
150
264
for inst in function. instructions {
151
265
switch inst {
0 commit comments