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