12
12
13
13
import SIL
14
14
15
- /// Outlines COW objects from functions into statically initialized global variables.
16
- /// This is currently only done for Arrays.
15
+ /// Outlines class objects from functions into statically initialized global variables.
16
+ /// This is currently done for Arrays and for global let variables.
17
+ ///
17
18
/// If a function constructs an Array literal with constant elements (done by storing
18
19
/// the element values into the array buffer), a new global variable is created which
19
20
/// contains the constant elements in its static initializer.
@@ -26,14 +27,25 @@ import SIL
26
27
/// ```
27
28
/// is turned into
28
29
/// ```
29
- /// private let outlinedVariable_from_arrayLookup = [10, 11, 12] // statically initialized
30
+ /// private let outlinedVariable = [10, 11, 12] // statically initialized and allocated in the data section
30
31
///
31
32
/// public func arrayLookup(_ i: Int) -> Int {
32
- /// return outlinedVariable_from_arrayLookup [i]
33
+ /// return outlinedVariable [i]
33
34
/// }
34
35
/// ```
35
36
///
36
- /// As a second optimization, if the array is a string literal which is a parameter to the
37
+ /// Similar with global let variables:
38
+ /// ```
39
+ /// let c = SomeClass()
40
+ /// ```
41
+ /// is turned into
42
+ /// ```
43
+ /// private let outlinedVariable = SomeClass() // statically initialized and allocated in the data section
44
+ ///
45
+ /// let c = outlinedVariable
46
+ /// ```
47
+ ///
48
+ /// As a second optimization, if an array is a string literal which is a parameter to the
37
49
/// `_findStringSwitchCase` library function and the array has many elements (> 16), the
38
50
/// call is redirected to `_findStringSwitchCaseWithCache`. This function builds a cache
39
51
/// (e.g. a Dictionary) and stores it into a global variable.
@@ -55,14 +67,11 @@ private func optimizeObjectAllocation(allocRef: AllocRefInstBase, _ context: Fun
55
67
return nil
56
68
}
57
69
58
- // The presence of an end_cow_mutation guarantees that the originally initialized
59
- // object is not mutated (because it must be copied before mutation).
60
- guard let endCOW = findEndCOWMutation ( of: allocRef) ,
61
- !endCOW. doKeepUnique else {
70
+ guard let endOfInitInst = findEndOfInitialization ( of: allocRef) else {
62
71
return nil
63
72
}
64
73
65
- guard let ( storesToClassFields, storesToTailElements) = getInitialization ( of: allocRef) else {
74
+ guard let ( storesToClassFields, storesToTailElements) = getInitialization ( of: allocRef, ignore : endOfInitInst ) else {
66
75
return nil
67
76
}
68
77
@@ -77,32 +86,39 @@ private func optimizeObjectAllocation(allocRef: AllocRefInstBase, _ context: Fun
77
86
return replace ( object: allocRef, with: outlinedGlobal, context)
78
87
}
79
88
80
- private func findEndCOWMutation( of object: Value ) -> EndCOWMutationInst ? {
89
+ // The end-of-initialization is either an end_cow_mutation, because it guarantees that the originally initialized
90
+ // object is not mutated (it must be copied before mutation).
91
+ // Or it is the store to a global let variable in the global's initializer function.
92
+ private func findEndOfInitialization( of object: Value ) -> Instruction ? {
81
93
for use in object. uses {
82
- switch use. instruction {
83
- case let uci as UpcastInst :
84
- if let ecm = findEndCOWMutation ( of: uci) {
85
- return ecm
86
- }
87
- case let urci as UncheckedRefCastInst :
88
- if let ecm = findEndCOWMutation ( of: urci) {
89
- return ecm
90
- }
91
- case let mv as MoveValueInst :
92
- if let ecm = findEndCOWMutation ( of: mv) {
94
+ let user = use. instruction
95
+ switch user {
96
+ case is UpcastInst ,
97
+ is UncheckedRefCastInst ,
98
+ is MoveValueInst ,
99
+ is EndInitLetRefInst :
100
+ if let ecm = findEndOfInitialization ( of: user as! SingleValueInstruction ) {
93
101
return ecm
94
102
}
95
103
case let ecm as EndCOWMutationInst :
96
104
return ecm
105
+ case let store as StoreInst :
106
+ if let ga = store. destination as? GlobalAddrInst ,
107
+ ga. global. isLet,
108
+ ga. parentFunction. initializedGlobal == ga. global
109
+ {
110
+ return store
111
+ }
97
112
default :
98
113
break
99
114
}
100
115
}
101
116
return nil
102
117
}
103
118
104
- private func getInitialization( of allocRef: AllocRefInstBase ) -> ( storesToClassFields: [ StoreInst ] ,
105
- storesToTailElements: [ StoreInst ] ) ? {
119
+ private func getInitialization( of allocRef: AllocRefInstBase , ignore ignoreInst: Instruction )
120
+ -> ( storesToClassFields: [ StoreInst ] , storesToTailElements: [ StoreInst ] ) ?
121
+ {
106
122
guard let numTailElements = allocRef. numTailElements else {
107
123
return nil
108
124
}
@@ -115,9 +131,10 @@ private func getInitialization(of allocRef: AllocRefInstBase) -> (storesToClassF
115
131
// store %0 to %3
116
132
// %4 = tuple_element_addr %2, 1
117
133
// store %1 to %4
118
- var tailStores = Array < StoreInst ? > ( repeating: nil , count: numTailElements * allocRef. numStoresPerTailElement)
134
+ let tailCount = numTailElements != 0 ? numTailElements * allocRef. numStoresPerTailElement : 0
135
+ var tailStores = Array < StoreInst ? > ( repeating: nil , count: tailCount)
119
136
120
- if !findInitStores( of: allocRef, & fieldStores, & tailStores) {
137
+ if !findInitStores( of: allocRef, & fieldStores, & tailStores, ignore : ignoreInst ) {
121
138
return nil
122
139
}
123
140
@@ -130,19 +147,16 @@ private func getInitialization(of allocRef: AllocRefInstBase) -> (storesToClassF
130
147
131
148
private func findInitStores( of object: Value ,
132
149
_ fieldStores: inout [ StoreInst ? ] ,
133
- _ tailStores: inout [ StoreInst ? ] ) -> Bool {
150
+ _ tailStores: inout [ StoreInst ? ] ,
151
+ ignore ignoreInst: Instruction ) -> Bool {
134
152
for use in object. uses {
135
- switch use. instruction {
136
- case let uci as UpcastInst :
137
- if !findInitStores( of: uci, & fieldStores, & tailStores) {
138
- return false
139
- }
140
- case let urci as UncheckedRefCastInst :
141
- if !findInitStores( of: urci, & fieldStores, & tailStores) {
142
- return false
143
- }
144
- case let mvi as MoveValueInst :
145
- if !findInitStores( of: mvi, & fieldStores, & tailStores) {
153
+ let user = use. instruction
154
+ switch user {
155
+ case is UpcastInst ,
156
+ is UncheckedRefCastInst ,
157
+ is MoveValueInst ,
158
+ is EndInitLetRefInst :
159
+ if !findInitStores( of: user as! SingleValueInstruction , & fieldStores, & tailStores, ignore: ignoreInst) {
146
160
return false
147
161
}
148
162
case let rea as RefElementAddrInst :
@@ -153,6 +167,8 @@ private func findInitStores(of object: Value,
153
167
if !findStores( toTailAddress: rta, tailElementIndex: 0 , stores: & tailStores) {
154
168
return false
155
169
}
170
+ case ignoreInst:
171
+ break
156
172
default :
157
173
if !isValidUseOfObject( use) {
158
174
return false
@@ -243,8 +259,7 @@ private func isValidUseOfObject(_ use: Operand) -> Bool {
243
259
is DeallocStackRefInst ,
244
260
is StrongRetainInst ,
245
261
is StrongReleaseInst ,
246
- is FixLifetimeInst ,
247
- is EndCOWMutationInst :
262
+ is FixLifetimeInst :
248
263
return true
249
264
250
265
case let mdi as MarkDependenceInst :
@@ -314,23 +329,24 @@ private func constructObject(of allocRef: AllocRefInstBase,
314
329
}
315
330
let globalBuilder = Builder ( staticInitializerOf: global, context)
316
331
317
- // Create the initializers for the tail elements.
318
- let numTailTupleElems = allocRef. numStoresPerTailElement
319
- if numTailTupleElems > 1 {
320
- // The elements are tuples: combine numTailTupleElems elements to a single tuple instruction.
321
- for elementIdx in 0 ..< allocRef. numTailElements! {
322
- var tupleElems = [ Value] ( )
323
- for tupleIdx in 0 ..< numTailTupleElems {
324
- let store = storesToTailElements [ elementIdx * numTailTupleElems + tupleIdx]
325
- tupleElems. append ( cloner. clone ( store. source as! SingleValueInstruction ) )
332
+ if !storesToTailElements. isEmpty {
333
+ // Create the initializers for the tail elements.
334
+ let numTailTupleElems = allocRef. numStoresPerTailElement
335
+ if numTailTupleElems > 1 {
336
+ // The elements are tuples: combine numTailTupleElems elements to a single tuple instruction.
337
+ for elementIdx in 0 ..< allocRef. numTailElements! {
338
+ let tupleElems = ( 0 ..< numTailTupleElems) . map { tupleIdx in
339
+ let store = storesToTailElements [ elementIdx * numTailTupleElems + tupleIdx]
340
+ return cloner. clone ( store. source as! SingleValueInstruction )
341
+ }
342
+ let tuple = globalBuilder. createTuple ( type: allocRef. tailAllocatedTypes [ 0 ] , elements: tupleElems)
343
+ objectArgs. append ( tuple)
344
+ }
345
+ } else {
346
+ // The non-tuple element case.
347
+ for store in storesToTailElements {
348
+ objectArgs. append ( cloner. clone ( store. source as! SingleValueInstruction ) )
326
349
}
327
- let tuple = globalBuilder. createTuple ( type: allocRef. tailAllocatedTypes [ 0 ] , elements: tupleElems)
328
- objectArgs. append ( tuple)
329
- }
330
- } else {
331
- // The non-tuple element case.
332
- for store in storesToTailElements {
333
- objectArgs. append ( cloner. clone ( store. source as! SingleValueInstruction ) )
334
350
}
335
351
}
336
352
globalBuilder. createObject ( type: allocRef. type, arguments: objectArgs, numBaseElements: storesToClassFields. count)
@@ -374,6 +390,10 @@ private func rewriteUses(of startValue: Value, _ context: FunctionPassContext) {
374
390
worklist. pushIfNotVisited ( usersOf: endMutation)
375
391
endMutation. uses. replaceAll ( with: endMutation. instance, context)
376
392
context. erase ( instruction: endMutation)
393
+ case let eilr as EndInitLetRefInst :
394
+ worklist. pushIfNotVisited ( usersOf: eilr)
395
+ eilr. uses. replaceAll ( with: eilr. operand. value, context)
396
+ context. erase ( instruction: eilr)
377
397
case let upCast as UpcastInst :
378
398
worklist. pushIfNotVisited ( usersOf: upCast)
379
399
case let refCast as UncheckedRefCastInst :
@@ -407,6 +427,11 @@ private extension AllocRefInstBase {
407
427
}
408
428
409
429
var numTailElements : Int ? {
430
+
431
+ if tailAllocatedCounts. count == 0 {
432
+ return 0
433
+ }
434
+
410
435
// We only support a single tail allocated array.
411
436
// Stdlib's tail allocated arrays don't have any side-effects in the constructor if the element type is trivial.
412
437
// TODO: also exclude custom tail allocated arrays which might have side-effects in the destructor.
0 commit comments