@@ -80,8 +80,11 @@ private indirect enum GlobalInitValue {
8080 // For example, a struct or vector which is initialized by storing its elements.
8181 case aggregate( [ GlobalInitValue ] )
8282
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 )
8588
8689 init ? ( of globalInitFunction: Function , _ context: FunctionPassContext ) {
8790 self = . undefined
@@ -133,20 +136,44 @@ private indirect enum GlobalInitValue {
133136 self = builder. initValue
134137 }
135138
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+
136155 // Sets an element in the constant tree.
137156 // Returns true if this was successful. One reason for being not successful is if a certain
138157 // 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 {
140159 let ( kind, index, subPath) = path. pop ( )
141160 switch kind {
142161 case . root:
143162 guard case . undefined = self else {
144163 // The element was set twice.
145164 return false
146165 }
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+ }
148172 return true
149173
174+ case . enumCase:
175+ return setEnumCase ( to: value, at: subPath, index: index, type: type)
176+
150177 case . structField:
151178 guard let structFields = type. getNominalFields ( in: value. parentFunction) else {
152179 return false
@@ -186,7 +213,7 @@ private indirect enum GlobalInitValue {
186213 }
187214
188215 private mutating func setField(
189- to value: Value , at path: SmallProjectionPath ,
216+ to value: InitValue , at path: SmallProjectionPath ,
190217 index: Int , type: Type , numFields: Int
191218 ) -> Bool {
192219 if case . undefined = self {
@@ -205,6 +232,43 @@ private indirect enum GlobalInitValue {
205232 return false
206233 }
207234
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+
208272 /// Creates SIL for this global init value in the initializer of the `global`.
209273 func materialize( into global: GlobalVariable , from function: Function , _ context: FunctionPassContext ) {
210274 var cloner = Cloner ( cloneToGlobal: global, context)
@@ -248,8 +312,11 @@ private indirect enum GlobalInitValue {
248312 }
249313 return builder. createVector ( type: type, arguments: elementValues)
250314
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) !
253320 let payloadValue = payload. materializeRecursively ( type: payloadType, & cloner, builder, function)
254321 return builder. createEnum ( caseIndex: caseIndex, payload: payloadValue, enumType: type)
255322 }
@@ -272,7 +339,7 @@ private indirect enum GlobalInitValue {
272339 _ context: FunctionPassContext
273340 ) {
274341 switch self {
275- case . undefined:
342+ case . undefined, . enumCase :
276343 break
277344 case . constant( let value) :
278345 if value. containsLoad ( context) {
@@ -281,7 +348,7 @@ private indirect enum GlobalInitValue {
281348 self = . aggregate( ( value as! Instruction ) . operands. lazy. map { . constant( $0. value) } )
282349 resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
283350 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!) )
285352 resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
286353 case let li as LoadInst :
287354 guard let allocStack = li. address as? AllocStackInst ,
@@ -306,10 +373,9 @@ private indirect enum GlobalInitValue {
306373 newFields [ i] . resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
307374 }
308375 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)
313379 }
314380 }
315381
@@ -321,7 +387,9 @@ private indirect enum GlobalInitValue {
321387 return value. isValidGlobalInitValue ( context)
322388 case . aggregate( let fields) :
323389 return fields. allSatisfy { $0. isValid ( context) }
324- case . enumCase( _, let payload) :
390+ case . enumCase:
391+ return true
392+ case . enumCaseWithPayload( _, let payload) :
325393 return payload. isValid ( context)
326394 }
327395 }
@@ -361,7 +429,7 @@ private struct InitValueBuilder: AddressDefUseWalker {
361429 let accessPath = store. destination. lookThroughRawLayoutAddress. constantAccessPath
362430 switch accessPath. base {
363431 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) {
365433 return . abortWalk
366434 }
367435 return . continueWalk
@@ -376,13 +444,35 @@ private struct InitValueBuilder: AddressDefUseWalker {
376444 return . abortWalk
377445 }
378446 // 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) {
380448 return . abortWalk
381449 }
382450 return . continueWalk
383451 default :
384452 fatalError ( " could not compute access path " )
385453 }
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+
386476 case is LoadInst , is DeallocStackInst :
387477 return . continueWalk
388478 case let bi as BuiltinInst :
@@ -477,6 +567,8 @@ private extension Function {
477567 return false
478568 case let store as StoreInst :
479569 return !store. destination. lookThroughRawLayoutAddress. isAddressOfStack ( orGlobal: global)
570+ case let injectEnum as InjectEnumAddrInst :
571+ return !injectEnum. enum. isAddressOfStack ( orGlobal: global)
480572 case let bi as BuiltinInst where bi. id == . PrepareInitialization:
481573 return false
482574 default :
@@ -533,3 +625,9 @@ private extension Value {
533625 return self
534626 }
535627}
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