@@ -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,41 @@ 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
+ if path. isEmpty, case . enumCaseWithoutPayload( let iea) = value {
237
+
238
+ guard case . undefined = self else {
239
+ // The enum was set twice.
240
+ return false
241
+ }
242
+ assert ( index == iea. caseIndex)
243
+ self = . enumCase( caseIndex: index)
244
+ } else {
245
+ guard let payloadType = type. getEnumCases ( in: value. parentFunction) !. getPayloadType ( ofCaseIndex: index) else {
246
+ return false
247
+ }
248
+ switch self {
249
+ case . undefined:
250
+ // It's the first time we set the payload or a sub-field of it.
251
+ var payload = GlobalInitValue . undefined
252
+ if !payload. setElement ( to: value, at: path, type: payloadType) {
253
+ return false
254
+ }
255
+ self = . enumCaseWithPayload( caseIndex: index, payload: payload)
256
+ case . enumCaseWithPayload( let existingIndex, var payload) where index == existingIndex:
257
+ // Some sub-field of the enum-payload was already set.
258
+ self = . undefined // avoid copy-on-write
259
+ if !payload. setElement ( to: value, at: path, type: payloadType) {
260
+ return false
261
+ }
262
+ self = . enumCaseWithPayload( caseIndex: index, payload: payload)
263
+ default :
264
+ return false
265
+ }
266
+ }
267
+ return true
268
+ }
269
+
208
270
/// Creates SIL for this global init value in the initializer of the `global`.
209
271
func materialize( into global: GlobalVariable , from function: Function , _ context: FunctionPassContext ) {
210
272
var cloner = Cloner ( cloneToGlobal: global, context)
@@ -248,8 +310,11 @@ private indirect enum GlobalInitValue {
248
310
}
249
311
return builder. createVector ( type: type, arguments: elementValues)
250
312
251
- case . enumCase( let caseIndex, let payload) :
252
- let payloadType = type. getEnumCases ( in: function) !. first ( where: { $0. index == caseIndex } ) !. payload!
313
+ case . enumCase( let caseIndex) :
314
+ return builder. createEnum ( caseIndex: caseIndex, payload: nil , enumType: type)
315
+
316
+ case . enumCaseWithPayload( let caseIndex, let payload) :
317
+ let payloadType = type. getEnumCases ( in: function) !. getPayloadType ( ofCaseIndex: caseIndex) !
253
318
let payloadValue = payload. materializeRecursively ( type: payloadType, & cloner, builder, function)
254
319
return builder. createEnum ( caseIndex: caseIndex, payload: payloadValue, enumType: type)
255
320
}
@@ -272,7 +337,7 @@ private indirect enum GlobalInitValue {
272
337
_ context: FunctionPassContext
273
338
) {
274
339
switch self {
275
- case . undefined:
340
+ case . undefined, . enumCase :
276
341
break
277
342
case . constant( let value) :
278
343
if value. containsLoad ( context) {
@@ -281,7 +346,7 @@ private indirect enum GlobalInitValue {
281
346
self = . aggregate( ( value as! Instruction ) . operands. lazy. map { . constant( $0. value) } )
282
347
resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
283
348
case let ei as EnumInst :
284
- self = . enumCase ( caseIndex: ei. caseIndex, payload: . constant( ei. payload!) )
349
+ self = . enumCaseWithPayload ( caseIndex: ei. caseIndex, payload: . constant( ei. payload!) )
285
350
resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
286
351
case let li as LoadInst :
287
352
guard let allocStack = li. address as? AllocStackInst ,
@@ -306,10 +371,9 @@ private indirect enum GlobalInitValue {
306
371
newFields [ i] . resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
307
372
}
308
373
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)
374
+ case . enumCaseWithPayload( let caseIndex, var payload) :
375
+ payload. resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
376
+ self = . enumCaseWithPayload( caseIndex: caseIndex, payload: payload)
313
377
}
314
378
}
315
379
@@ -321,7 +385,9 @@ private indirect enum GlobalInitValue {
321
385
return value. isValidGlobalInitValue ( context)
322
386
case . aggregate( let fields) :
323
387
return fields. allSatisfy { $0. isValid ( context) }
324
- case . enumCase( _, let payload) :
388
+ case . enumCase:
389
+ return true
390
+ case . enumCaseWithPayload( _, let payload) :
325
391
return payload. isValid ( context)
326
392
}
327
393
}
@@ -361,7 +427,7 @@ private struct InitValueBuilder: AddressDefUseWalker {
361
427
let accessPath = store. destination. lookThroughRawLayoutAddress. constantAccessPath
362
428
switch accessPath. base {
363
429
case . global, . stack:
364
- if !initValue. setElement ( to: store. source, at: accessPath. projectionPath, type: originalAddress. type) {
430
+ if !initValue. setElement ( to: . value ( store. source) , at: accessPath. projectionPath, type: originalAddress. type) {
365
431
return . abortWalk
366
432
}
367
433
return . continueWalk
@@ -376,13 +442,35 @@ private struct InitValueBuilder: AddressDefUseWalker {
376
442
return . abortWalk
377
443
}
378
444
// The `nonConstAccessPath` now contains a single `.anyIndexedElement`.
379
- if !initValue. setElement ( to: store. source, at: nonConstAccessPath. projectionPath, type: originalAddress. type) {
445
+ if !initValue. setElement ( to: . value ( store. source) , at: nonConstAccessPath. projectionPath, type: originalAddress. type) {
380
446
return . abortWalk
381
447
}
382
448
return . continueWalk
383
449
default :
384
450
fatalError ( " could not compute access path " )
385
451
}
452
+ case let injectEnum as InjectEnumAddrInst :
453
+ if injectEnum. element. hasAssociatedValues {
454
+ if !injectEnum. operand. value. type. isLoadable ( in: injectEnum. parentFunction) {
455
+ // TODO: we don't support non-loadable enum cases with payload yet, because IRGen support is missing.
456
+ // e.g. `var global: Atomic<Int>? = Atomic<Int>(0)`
457
+ // FixedTypeInfo (= used for non-loadable types) is missing the ability to pack a payload into an enum.
458
+ return . abortWalk
459
+ }
460
+ return . continueWalk
461
+ }
462
+ let accessPath = injectEnum. enum. getAccessPath ( fromInitialPath: SmallProjectionPath ( . enumCase,
463
+ index: injectEnum. caseIndex) )
464
+ switch accessPath. base {
465
+ case . global, . stack:
466
+ if !initValue. setElement ( to: . enumCaseWithoutPayload( injectEnum) , at: accessPath. projectionPath, type: originalAddress. type) {
467
+ return . abortWalk
468
+ }
469
+ return . continueWalk
470
+ default :
471
+ return . abortWalk
472
+ }
473
+
386
474
case is LoadInst , is DeallocStackInst :
387
475
return . continueWalk
388
476
case let bi as BuiltinInst :
@@ -477,6 +565,8 @@ private extension Function {
477
565
return false
478
566
case let store as StoreInst :
479
567
return !store. destination. lookThroughRawLayoutAddress. isAddressOfStack ( orGlobal: global)
568
+ case let injectEnum as InjectEnumAddrInst :
569
+ return !injectEnum. enum. isAddressOfStack ( orGlobal: global)
480
570
case let bi as BuiltinInst where bi. id == . PrepareInitialization:
481
571
return false
482
572
default :
@@ -533,3 +623,9 @@ private extension Value {
533
623
return self
534
624
}
535
625
}
626
+
627
+ private extension EnumCases {
628
+ func getPayloadType( ofCaseIndex caseIndex: Int ) -> Type ? {
629
+ return first ( where: { $0. index == caseIndex } ) !. payload
630
+ }
631
+ }
0 commit comments