@@ -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,41 @@ 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+ 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+
208270 /// Creates SIL for this global init value in the initializer of the `global`.
209271 func materialize( into global: GlobalVariable , from function: Function , _ context: FunctionPassContext ) {
210272 var cloner = Cloner ( cloneToGlobal: global, context)
@@ -248,8 +310,11 @@ private indirect enum GlobalInitValue {
248310 }
249311 return builder. createVector ( type: type, arguments: elementValues)
250312
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) !
253318 let payloadValue = payload. materializeRecursively ( type: payloadType, & cloner, builder, function)
254319 return builder. createEnum ( caseIndex: caseIndex, payload: payloadValue, enumType: type)
255320 }
@@ -272,7 +337,7 @@ private indirect enum GlobalInitValue {
272337 _ context: FunctionPassContext
273338 ) {
274339 switch self {
275- case . undefined:
340+ case . undefined, . enumCase :
276341 break
277342 case . constant( let value) :
278343 if value. containsLoad ( context) {
@@ -281,7 +346,7 @@ private indirect enum GlobalInitValue {
281346 self = . aggregate( ( value as! Instruction ) . operands. lazy. map { . constant( $0. value) } )
282347 resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
283348 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!) )
285350 resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
286351 case let li as LoadInst :
287352 guard let allocStack = li. address as? AllocStackInst ,
@@ -306,10 +371,9 @@ private indirect enum GlobalInitValue {
306371 newFields [ i] . resolveLoadsRecursively ( from: & stackValues, & resolvedAllocStacks, context)
307372 }
308373 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)
313377 }
314378 }
315379
@@ -321,7 +385,9 @@ private indirect enum GlobalInitValue {
321385 return value. isValidGlobalInitValue ( context)
322386 case . aggregate( let fields) :
323387 return fields. allSatisfy { $0. isValid ( context) }
324- case . enumCase( _, let payload) :
388+ case . enumCase:
389+ return true
390+ case . enumCaseWithPayload( _, let payload) :
325391 return payload. isValid ( context)
326392 }
327393 }
@@ -361,7 +427,7 @@ private struct InitValueBuilder: AddressDefUseWalker {
361427 let accessPath = store. destination. lookThroughRawLayoutAddress. constantAccessPath
362428 switch accessPath. base {
363429 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) {
365431 return . abortWalk
366432 }
367433 return . continueWalk
@@ -376,13 +442,35 @@ private struct InitValueBuilder: AddressDefUseWalker {
376442 return . abortWalk
377443 }
378444 // 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) {
380446 return . abortWalk
381447 }
382448 return . continueWalk
383449 default :
384450 fatalError ( " could not compute access path " )
385451 }
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+
386474 case is LoadInst , is DeallocStackInst :
387475 return . continueWalk
388476 case let bi as BuiltinInst :
@@ -477,6 +565,8 @@ private extension Function {
477565 return false
478566 case let store as StoreInst :
479567 return !store. destination. lookThroughRawLayoutAddress. isAddressOfStack ( orGlobal: global)
568+ case let injectEnum as InjectEnumAddrInst :
569+ return !injectEnum. enum. isAddressOfStack ( orGlobal: global)
480570 case let bi as BuiltinInst where bi. id == . PrepareInitialization:
481571 return false
482572 default :
@@ -533,3 +623,9 @@ private extension Value {
533623 return self
534624 }
535625}
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