@@ -80,8 +80,11 @@ private indirect enum GlobalInitValue {
80
80
// For example, a struct or vector which is initialized by storing its elements.
81
81
case aggregate( [ GlobalInitValue ] )
82
82
83
- // An enum with a payload which is not a SIL "constant".
84
- case enumCase( caseIndex: Int , payload: GlobalInitValue )
83
+ // An enum case without a payload of an address-only enum.
84
+ case enumCase( caseIndex: Int )
85
+
86
+ // An enum case which is not a SIL "constant", e.g. because it's address-only
87
+ case enumCaseWithPayload( caseIndex: Int , payload: GlobalInitValue )
85
88
86
89
init ? ( of globalInitFunction: Function , _ context: FunctionPassContext ) {
87
90
self = . undefined
@@ -133,20 +136,44 @@ private indirect enum GlobalInitValue {
133
136
self = builder. initValue
134
137
}
135
138
139
+ enum InitValue {
140
+ // The common case
141
+ case value( Value )
142
+
143
+ // For payload-less cases of address-only enums. Such cases are initialized purely with an `inject_enum_addr`,
144
+ // and we don't have a `Value` which represents the resulting enum(-case).
145
+ case enumCaseWithoutPayload( InjectEnumAddrInst )
146
+
147
+ var parentFunction : Function {
148
+ switch self {
149
+ case . value( let value) : return value. parentFunction
150
+ case . enumCaseWithoutPayload( let iea) : return iea. parentFunction
151
+ }
152
+ }
153
+ }
154
+
136
155
// Sets an element in the constant tree.
137
156
// Returns true if this was successful. One reason for being not successful is if a certain
138
157
// element is set twice, i.e. does not have a single defined value.
139
- mutating func setElement( to value: Value , at path: SmallProjectionPath , type: Type ) -> Bool {
158
+ mutating func setElement( to value: InitValue , at path: SmallProjectionPath , type: Type ) -> Bool {
140
159
let ( kind, index, subPath) = path. pop ( )
141
160
switch kind {
142
161
case . root:
143
162
guard case . undefined = self else {
144
163
// The element was set twice.
145
164
return false
146
165
}
147
- self = . constant( value)
166
+ switch value {
167
+ case . value( let value) :
168
+ self = . constant( value)
169
+ case . enumCaseWithoutPayload:
170
+ fatalError ( " should have been handled in the .enumCase of the SmallProjectionPath below " )
171
+ }
148
172
return true
149
173
174
+ case . enumCase:
175
+ return setEnumCase ( to: value, at: subPath, index: index, type: type)
176
+
150
177
case . structField:
151
178
guard let structFields = type. getNominalFields ( in: value. parentFunction) else {
152
179
return false
@@ -186,7 +213,7 @@ private indirect enum GlobalInitValue {
186
213
}
187
214
188
215
private mutating func setField(
189
- to value: Value , at path: SmallProjectionPath ,
216
+ to value: InitValue , at path: SmallProjectionPath ,
190
217
index: Int , type: Type , numFields: Int
191
218
) -> Bool {
192
219
if case . undefined = self {
@@ -205,6 +232,43 @@ private indirect enum GlobalInitValue {
205
232
return false
206
233
}
207
234
235
+ private mutating func setEnumCase( to value: InitValue , at path: SmallProjectionPath , index: Int , type: Type ) -> Bool {
236
+ switch value {
237
+
238
+ case . enumCaseWithoutPayload( let iea) :
239
+ guard case . undefined = self else {
240
+ // The enum was set twice.
241
+ return false
242
+ }
243
+ assert ( index == iea. caseIndex)
244
+ self = . enumCase( caseIndex: index)
245
+
246
+ case . value:
247
+ guard let payloadType = type. getEnumCases ( in: value. parentFunction) !. getPayloadType ( ofCaseIndex: index) else {
248
+ return false
249
+ }
250
+ switch self {
251
+ case . undefined:
252
+ // It's the first time we set the payload or a sub-field of it.
253
+ var payload = GlobalInitValue . undefined
254
+ if !payload. setElement ( to: value, at: path, type: payloadType) {
255
+ return false
256
+ }
257
+ self = . enumCaseWithPayload( caseIndex: index, payload: payload)
258
+ case . enumCaseWithPayload( let existingIndex, var payload) where index == existingIndex:
259
+ // Some sub-field of the enum-payload was already set.
260
+ self = . undefined // avoid copy-on-write
261
+ if !payload. setElement ( to: value, at: path, type: payloadType) {
262
+ return false
263
+ }
264
+ self = . enumCaseWithPayload( caseIndex: index, payload: payload)
265
+ default :
266
+ return false
267
+ }
268
+ }
269
+ return true
270
+ }
271
+
208
272
/// Creates SIL for this global init value in the initializer of the `global`.
209
273
func materialize( into global: GlobalVariable , from function: Function , _ context: FunctionPassContext ) {
210
274
var cloner = StaticInitCloner ( cloneTo: global, context)
@@ -248,8 +312,11 @@ private indirect enum GlobalInitValue {
248
312
}
249
313
return builder. createVector ( type: type, arguments: elementValues)
250
314
251
- case . enumCase( let caseIndex, let payload) :
252
- let payloadType = type. getEnumCases ( in: function) !. first ( where: { $0. index == caseIndex } ) !. payload!
315
+ case . enumCase( let caseIndex) :
316
+ return builder. createEnum ( caseIndex: caseIndex, payload: nil , enumType: type)
317
+
318
+ case . enumCaseWithPayload( let caseIndex, let payload) :
319
+ let payloadType = type. getEnumCases ( in: function) !. getPayloadType ( ofCaseIndex: caseIndex) !
253
320
let payloadValue = payload. materializeRecursively ( type: payloadType, & cloner, builder, function)
254
321
return builder. createEnum ( caseIndex: caseIndex, payload: payloadValue, enumType: type)
255
322
}
@@ -272,7 +339,7 @@ private indirect enum GlobalInitValue {
272
339
_ context: FunctionPassContext
273
340
) {
274
341
switch self {
275
- case . undefined:
342
+ case . undefined, . enumCase :
276
343
break
277
344
case . constant( let value) :
278
345
if value. containsLoad ( context) {
@@ -281,7 +348,7 @@ private indirect enum GlobalInitValue {
281
348
self = . aggregate( ( value as! Instruction ) . operands. lazy. map { . constant( $0. value) } )
282
349
resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
283
350
case let ei as EnumInst :
284
- self = . enumCase ( caseIndex: ei. caseIndex, payload: . constant( ei. payload!) )
351
+ self = . enumCaseWithPayload ( caseIndex: ei. caseIndex, payload: . constant( ei. payload!) )
285
352
resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
286
353
case let li as LoadInst :
287
354
guard let allocStack = li. address as? AllocStackInst ,
@@ -306,10 +373,9 @@ private indirect enum GlobalInitValue {
306
373
newFields [ i] . resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
307
374
}
308
375
self = . aggregate( newFields)
309
- case . enumCase( let caseIndex, let payload) :
310
- var newPayload = payload
311
- newPayload. resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
312
- self = . enumCase( caseIndex: caseIndex, payload: newPayload)
376
+ case . enumCaseWithPayload( let caseIndex, var payload) :
377
+ payload. resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
378
+ self = . enumCaseWithPayload( caseIndex: caseIndex, payload: payload)
313
379
}
314
380
}
315
381
@@ -321,7 +387,9 @@ private indirect enum GlobalInitValue {
321
387
return value. isValidGlobalInitValue ( context)
322
388
case . aggregate( let fields) :
323
389
return fields. allSatisfy { $0. isValid ( context) }
324
- case . enumCase( _, let payload) :
390
+ case . enumCase:
391
+ return true
392
+ case . enumCaseWithPayload( _, let payload) :
325
393
return payload. isValid ( context)
326
394
}
327
395
}
@@ -358,10 +426,10 @@ private struct InitValueBuilder: AddressDefUseWalker {
358
426
mutating func leafUse( address: Operand , path: UnusedWalkingPath ) -> WalkResult {
359
427
switch address. instruction {
360
428
case let store as StoreInst :
361
- let accessPath = store. destination. constantAccessPath
429
+ let accessPath = store. destination. lookThroughRawLayoutAddress . constantAccessPath
362
430
switch accessPath. base {
363
431
case . global, . stack:
364
- if !initValue. setElement ( to: store. source, at: accessPath. projectionPath, type: originalAddress. type) {
432
+ if !initValue. setElement ( to: . value ( store. source) , at: accessPath. projectionPath, type: originalAddress. type) {
365
433
return . abortWalk
366
434
}
367
435
return . continueWalk
@@ -376,17 +444,49 @@ private struct InitValueBuilder: AddressDefUseWalker {
376
444
return . abortWalk
377
445
}
378
446
// The `nonConstAccessPath` now contains a single `.anyIndexedElement`.
379
- if !initValue. setElement ( to: store. source, at: nonConstAccessPath. projectionPath, type: originalAddress. type) {
447
+ if !initValue. setElement ( to: . value ( store. source) , at: nonConstAccessPath. projectionPath, type: originalAddress. type) {
380
448
return . abortWalk
381
449
}
382
450
return . continueWalk
383
451
default :
384
452
fatalError ( " could not compute access path " )
385
453
}
454
+ case let injectEnum as InjectEnumAddrInst :
455
+ if injectEnum. element. hasAssociatedValues {
456
+ if !injectEnum. operand. value. type. isLoadable ( in: injectEnum. parentFunction) {
457
+ // TODO: we don't support non-loadable enum cases with payload yet, because IRGen support is missing.
458
+ // e.g. `var global: Atomic<Int>? = Atomic<Int>(0)`
459
+ // FixedTypeInfo (= used for non-loadable types) is missing the ability to pack a payload into an enum.
460
+ return . abortWalk
461
+ }
462
+ return . continueWalk
463
+ }
464
+ let accessPath = injectEnum. enum. getAccessPath ( fromInitialPath: SmallProjectionPath ( . enumCase,
465
+ index: injectEnum. caseIndex) )
466
+ switch accessPath. base {
467
+ case . global, . stack:
468
+ if !initValue. setElement ( to: . enumCaseWithoutPayload( injectEnum) , at: accessPath. projectionPath, type: originalAddress. type) {
469
+ return . abortWalk
470
+ }
471
+ return . continueWalk
472
+ default :
473
+ return . abortWalk
474
+ }
475
+
386
476
case is LoadInst , is DeallocStackInst :
387
477
return . continueWalk
388
- case let bi as BuiltinInst where bi. id == . PrepareInitialization:
389
- return . continueWalk
478
+ case let bi as BuiltinInst :
479
+ switch bi. id {
480
+ case . PrepareInitialization:
481
+ return . continueWalk
482
+ case . AddressOfRawLayout:
483
+ if let addr2Ptr = bi. uses. getSingleUser ( ofType: PointerToAddressInst . self) {
484
+ return walkDownUses ( ofAddress: addr2Ptr, path: path)
485
+ }
486
+ return . abortWalk
487
+ default :
488
+ return . abortWalk
489
+ }
390
490
default :
391
491
return . abortWalk
392
492
}
@@ -466,14 +566,9 @@ private extension Function {
466
566
case let alloc as AllocGlobalInst where alloc. global == global:
467
567
return false
468
568
case let store as StoreInst :
469
- switch store. destination. accessBase {
470
- case . global( let g) where g == global:
471
- return false
472
- case . stack:
473
- return false
474
- default :
475
- return true
476
- }
569
+ return !store. destination. lookThroughRawLayoutAddress. isAddressOfStack ( orGlobal: global)
570
+ case let injectEnum as InjectEnumAddrInst :
571
+ return !injectEnum. enum. isAddressOfStack ( orGlobal: global)
477
572
case let bi as BuiltinInst where bi. id == . PrepareInitialization:
478
573
return false
479
574
default :
@@ -496,3 +591,43 @@ private extension Function {
496
591
context. removeTriviallyDeadInstructionsIgnoringDebugUses ( in: self )
497
592
}
498
593
}
594
+
595
+ private extension Value {
596
+ func isAddressOfStack( orGlobal global: GlobalVariable ) -> Bool {
597
+ switch accessBase {
598
+ case . global( let g) where g == global:
599
+ return true
600
+ case . stack:
601
+ return true
602
+ default :
603
+ return false
604
+ }
605
+ }
606
+
607
+ /// Looks through a `@_rawLayout` projection, which "type casts" a raw-layout struct to it's content, which
608
+ /// must match the like-type of the raw-layout, e.g.
609
+ /// ```
610
+ /// @_rawLayout(like: T)
611
+ /// struct S {}
612
+ ///
613
+ /// %2 = builtin "addressOfRawLayout"<S>(%1 : $*S) : $Builtin.RawPointer
614
+ /// %3 = pointer_to_address %2 to $*T
615
+ /// ```
616
+ var lookThroughRawLayoutAddress : Value {
617
+ if let ptr2Addr = self as? PointerToAddressInst ,
618
+ let builtin = ptr2Addr. pointer as? BuiltinInst , builtin. id == . AddressOfRawLayout,
619
+ let likeType = builtin. arguments [ 0 ] . type. rawLayoutSubstitutedLikeType,
620
+ builtin. arguments [ 0 ] . type. rawLayoutSubstitutedCountType == nil ,
621
+ likeType. canonical == type. canonicalType
622
+ {
623
+ return builtin. arguments [ 0 ]
624
+ }
625
+ return self
626
+ }
627
+ }
628
+
629
+ private extension EnumCases {
630
+ func getPayloadType( ofCaseIndex caseIndex: Int ) -> Type ? {
631
+ return first ( where: { $0. index == caseIndex } ) !. payload
632
+ }
633
+ }
0 commit comments