Skip to content

Commit a7bf654

Browse files
authored
Merge pull request #73623 from eeckstein/statically-initialized-array-variables
Optimizer: support static initialization of global arrays
2 parents b3ea46a + af68435 commit a7bf654

File tree

18 files changed

+225
-131
lines changed

18 files changed

+225
-131
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocVectorLowering.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ private func getInitStores(to allocVectorBuiltin: BuiltinInst, count: Int,
283283

284284
for use in allocVectorBuiltin.uses {
285285
if let ptrToAddr = use.instruction as? PointerToAddressInst {
286-
if !findInitStores(of: ptrToAddr, atIndex: 0, &stores) {
286+
if !findInitStores(of: ptrToAddr, atIndex: 0, &stores, context) {
287287
return nil
288288
}
289289
} else {
@@ -307,18 +307,20 @@ private func getInitStores(to allocVectorBuiltin: BuiltinInst, count: Int,
307307
return stores.map { $0! }
308308
}
309309

310-
private func findInitStores(of address: Value, atIndex: Int, _ initStores: inout [StoreInst?]) -> Bool {
310+
private func findInitStores(of address: Value, atIndex: Int, _ initStores: inout [StoreInst?],
311+
_ context: FunctionPassContext) -> Bool
312+
{
311313
for use in address.uses {
312314
switch use.instruction {
313315
case let indexAddr as IndexAddrInst:
314316
guard let indexLiteral = indexAddr.index as? IntegerLiteralInst,
315317
let index = indexLiteral.value,
316-
findInitStores(of: indexAddr, atIndex: atIndex + index, &initStores) else
318+
findInitStores(of: indexAddr, atIndex: atIndex + index, &initStores, context) else
317319
{
318320
return false
319321
}
320322
case let store as StoreInst where store.destinationOperand == use:
321-
if !store.source.isValidGlobalInitValue {
323+
if !store.source.isValidGlobalInitValue(context) {
322324
return false
323325
}
324326
if atIndex >= initStores.count ||

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ let initializeStaticGlobalsPass = FunctionPass(name: "initialize-static-globals"
5959

6060
// The initializer must not contain a `global_value` because `global_value` needs to
6161
// initialize the class metadata at runtime.
62-
guard let (allocInst, storeToGlobal) = getGlobalInitialization(of: function, allowGlobalValue: false) else {
62+
guard let (allocInst, storeToGlobal) = getGlobalInitialization(of: function,
63+
forStaticInitializer: true,
64+
context) else
65+
{
6366
return
6467
}
6568

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjectOutliner.swift

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ private func optimizeObjectAllocation(allocRef: AllocRefInstBase, _ context: Fun
8787
return nil
8888
}
8989

90-
guard let (storesToClassFields, storesToTailElements) = getInitialization(of: allocRef, ignore: endOfInitInst) else {
90+
guard let (storesToClassFields, storesToTailElements) = getInitialization(of: allocRef,
91+
ignore: endOfInitInst,
92+
context) else
93+
{
9194
return nil
9295
}
9396

@@ -136,7 +139,8 @@ private func findEndOfInitialization(of object: Value, canStoreToGlobal: Bool) -
136139
return nil
137140
}
138141

139-
private func getInitialization(of allocRef: AllocRefInstBase, ignore ignoreInst: Instruction)
142+
private func getInitialization(of allocRef: AllocRefInstBase, ignore ignoreInst: Instruction,
143+
_ context: FunctionPassContext)
140144
-> (storesToClassFields: [StoreInst], storesToTailElements: [StoreInst])?
141145
{
142146
guard let numTailElements = allocRef.numTailElements else {
@@ -154,7 +158,7 @@ private func getInitialization(of allocRef: AllocRefInstBase, ignore ignoreInst:
154158
let tailCount = numTailElements != 0 ? numTailElements * allocRef.numStoresPerTailElement : 0
155159
var tailStores = Array<StoreInst?>(repeating: nil, count: tailCount)
156160

157-
if !findInitStores(of: allocRef, &fieldStores, &tailStores, ignore: ignoreInst) {
161+
if !findInitStores(of: allocRef, &fieldStores, &tailStores, ignore: ignoreInst, context) {
158162
return nil
159163
}
160164

@@ -168,7 +172,9 @@ private func getInitialization(of allocRef: AllocRefInstBase, ignore ignoreInst:
168172
private func findInitStores(of object: Value,
169173
_ fieldStores: inout [StoreInst?],
170174
_ tailStores: inout [StoreInst?],
171-
ignore ignoreInst: Instruction) -> Bool {
175+
ignore ignoreInst: Instruction,
176+
_ context: FunctionPassContext) -> Bool
177+
{
172178
for use in object.uses {
173179
let user = use.instruction
174180
switch user {
@@ -177,15 +183,15 @@ private func findInitStores(of object: Value,
177183
is MoveValueInst,
178184
is EndInitLetRefInst,
179185
is BeginBorrowInst:
180-
if !findInitStores(of: user as! SingleValueInstruction, &fieldStores, &tailStores, ignore: ignoreInst) {
186+
if !findInitStores(of: user as! SingleValueInstruction, &fieldStores, &tailStores, ignore: ignoreInst, context) {
181187
return false
182188
}
183189
case let rea as RefElementAddrInst:
184-
if !findStores(inUsesOf: rea, index: rea.fieldIndex, stores: &fieldStores) {
190+
if !findStores(inUsesOf: rea, index: rea.fieldIndex, stores: &fieldStores, context) {
185191
return false
186192
}
187193
case let rta as RefTailAddrInst:
188-
if !findStores(toTailAddress: rta, tailElementIndex: 0, stores: &tailStores) {
194+
if !findStores(toTailAddress: rta, tailElementIndex: 0, stores: &tailStores, context) {
189195
return false
190196
}
191197
case ignoreInst,
@@ -200,7 +206,8 @@ private func findInitStores(of object: Value,
200206
return true
201207
}
202208

203-
private func findStores(toTailAddress tailAddr: Value, tailElementIndex: Int, stores: inout [StoreInst?]) -> Bool {
209+
private func findStores(toTailAddress tailAddr: Value, tailElementIndex: Int, stores: inout [StoreInst?],
210+
_ context: FunctionPassContext) -> Bool {
204211
for use in tailAddr.uses {
205212
switch use.instruction {
206213
case let indexAddr as IndexAddrInst:
@@ -209,26 +216,26 @@ private func findStores(toTailAddress tailAddr: Value, tailElementIndex: Int, st
209216
{
210217
return false
211218
}
212-
if !findStores(toTailAddress: indexAddr, tailElementIndex: tailElementIndex + tailIdx, stores: &stores) {
219+
if !findStores(toTailAddress: indexAddr, tailElementIndex: tailElementIndex + tailIdx, stores: &stores, context) {
213220
return false
214221
}
215222
case let tea as TupleElementAddrInst:
216223
// The tail elements are tuples. There is a separate store for each tuple element.
217224
let numTupleElements = tea.tuple.type.tupleElements.count
218225
let tupleIdx = tea.fieldIndex
219-
if !findStores(inUsesOf: tea, index: tailElementIndex * numTupleElements + tupleIdx, stores: &stores) {
226+
if !findStores(inUsesOf: tea, index: tailElementIndex * numTupleElements + tupleIdx, stores: &stores, context) {
220227
return false
221228
}
222229
case let atp as AddressToPointerInst:
223-
if !findStores(toTailAddress: atp, tailElementIndex: tailElementIndex, stores: &stores) {
230+
if !findStores(toTailAddress: atp, tailElementIndex: tailElementIndex, stores: &stores, context) {
224231
return false
225232
}
226233
case let mdi as MarkDependenceInst:
227-
if !findStores(toTailAddress: mdi, tailElementIndex: tailElementIndex, stores: &stores) {
234+
if !findStores(toTailAddress: mdi, tailElementIndex: tailElementIndex, stores: &stores, context) {
228235
return false
229236
}
230237
case let pta as PointerToAddressInst:
231-
if !findStores(toTailAddress: pta, tailElementIndex: tailElementIndex, stores: &stores) {
238+
if !findStores(toTailAddress: pta, tailElementIndex: tailElementIndex, stores: &stores, context) {
232239
return false
233240
}
234241
case let store as StoreInst:
@@ -237,7 +244,7 @@ private func findStores(toTailAddress tailAddr: Value, tailElementIndex: Int, st
237244
// Just to be on the safe side..
238245
return false
239246
}
240-
if !handleStore(store, index: tailElementIndex, stores: &stores) {
247+
if !handleStore(store, index: tailElementIndex, stores: &stores, context) {
241248
return false
242249
}
243250
default:
@@ -249,10 +256,12 @@ private func findStores(toTailAddress tailAddr: Value, tailElementIndex: Int, st
249256
return true
250257
}
251258

252-
private func findStores(inUsesOf address: Value, index: Int, stores: inout [StoreInst?]) -> Bool {
259+
private func findStores(inUsesOf address: Value, index: Int, stores: inout [StoreInst?],
260+
_ context: FunctionPassContext) -> Bool
261+
{
253262
for use in address.uses {
254263
if let store = use.instruction as? StoreInst {
255-
if !handleStore(store, index: index, stores: &stores) {
264+
if !handleStore(store, index: index, stores: &stores, context) {
256265
return false
257266
}
258267
} else if !isValidUseOfObject(use) {
@@ -262,9 +271,11 @@ private func findStores(inUsesOf address: Value, index: Int, stores: inout [Stor
262271
return true
263272
}
264273

265-
private func handleStore(_ store: StoreInst, index: Int, stores: inout [StoreInst?]) -> Bool {
274+
private func handleStore(_ store: StoreInst, index: Int, stores: inout [StoreInst?],
275+
_ context: FunctionPassContext) -> Bool
276+
{
266277
if index >= 0 && index < stores.count,
267-
store.source.isValidGlobalInitValue,
278+
store.source.isValidGlobalInitValue(context),
268279
stores[index] == nil {
269280
stores[index] = store
270281
return true

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ private func getInitializerFromInitFunction(of globalAddr: GlobalAddrInst, _ con
250250
}
251251
let initFn = initFnRef.referencedFunction
252252
context.notifyDependency(onBodyOf: initFn)
253-
guard let (_, storeToGlobal) = getGlobalInitialization(of: initFn, allowGlobalValue: true) else {
253+
guard let (_, storeToGlobal) = getGlobalInitialization(of: initFn, forStaticInitializer: false, context) else {
254254
return nil
255255
}
256256
return storeToGlobal.source

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ extension Context {
4545

4646
var moduleIsSerialized: Bool { _bridged.moduleIsSerialized() }
4747

48+
func canMakeStaticObjectReadOnly(objectType: Type) -> Bool {
49+
_bridged.canMakeStaticObjectReadOnly(objectType.bridged)
50+
}
51+
4852
func lookupDeinit(ofNominal: NominalTypeDecl) -> Function? {
4953
_bridged.lookUpNominalDeinitFunction(ofNominal.bridged).function
5054
}

SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,18 +131,18 @@ extension Value {
131131
}
132132

133133
/// True if this value is a valid in a static initializer, including all its operands.
134-
var isValidGlobalInitValue: Bool {
134+
func isValidGlobalInitValue(_ context: some Context) -> Bool {
135135
guard let svi = self as? SingleValueInstruction else {
136136
return false
137137
}
138138
if let beginAccess = svi as? BeginAccessInst {
139-
return beginAccess.address.isValidGlobalInitValue
139+
return beginAccess.address.isValidGlobalInitValue(context)
140140
}
141-
if !svi.isValidInStaticInitializerOfGlobal {
141+
if !svi.isValidInStaticInitializerOfGlobal(context) {
142142
return false
143143
}
144144
for op in svi.operands {
145-
if !op.value.isValidGlobalInitValue {
145+
if !op.value.isValidGlobalInitValue(context) {
146146
return false
147147
}
148148
}
@@ -303,6 +303,102 @@ extension Instruction {
303303
}
304304
return !mayReadOrWriteMemory && !hasUnspecifiedSideEffects
305305
}
306+
307+
func isValidInStaticInitializerOfGlobal(_ context: some Context) -> Bool {
308+
// Rule out SILUndef and SILArgument.
309+
if operands.contains(where: { $0.value.definingInstruction == nil }) {
310+
return false
311+
}
312+
switch self {
313+
case let bi as BuiltinInst:
314+
switch bi.id {
315+
case .ZeroInitializer:
316+
let type = bi.type.isBuiltinVector ? bi.type.builtinVectorElementType : bi.type
317+
return type.isBuiltinInteger || type.isBuiltinFloat
318+
case .PtrToInt:
319+
return bi.operands[0].value is StringLiteralInst
320+
case .IntToPtr:
321+
return bi.operands[0].value is IntegerLiteralInst
322+
case .StringObjectOr:
323+
// The first operand can be a string literal (i.e. a pointer), but the
324+
// second operand must be a constant. This enables creating a
325+
// a pointer+offset relocation.
326+
// Note that StringObjectOr requires the or'd bits in the first
327+
// operand to be 0, so the operation is equivalent to an addition.
328+
return bi.operands[1].value is IntegerLiteralInst
329+
case .ZExtOrBitCast:
330+
return true;
331+
case .USubOver:
332+
// Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits)
333+
// This pattern appears in UTF8 String literal construction.
334+
if let tei = bi.uses.getSingleUser(ofType: TupleExtractInst.self),
335+
tei.isResultOfOffsetSubtract {
336+
return true
337+
}
338+
return false
339+
case .OnFastPath:
340+
return true
341+
default:
342+
return false
343+
}
344+
case let tei as TupleExtractInst:
345+
// Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits)
346+
// This pattern appears in UTF8 String literal construction.
347+
if tei.isResultOfOffsetSubtract,
348+
let bi = tei.uses.getSingleUser(ofType: BuiltinInst.self),
349+
bi.id == .StringObjectOr {
350+
return true
351+
}
352+
return false
353+
case let sli as StringLiteralInst:
354+
switch sli.encoding {
355+
case .Bytes, .UTF8, .UTF8_OSLOG:
356+
return true
357+
case .ObjCSelector:
358+
// Objective-C selector string literals cannot be used in static
359+
// initializers.
360+
return false
361+
}
362+
case let gvi as GlobalValueInst:
363+
return context.canMakeStaticObjectReadOnly(objectType: gvi.type)
364+
case is StructInst,
365+
is TupleInst,
366+
is EnumInst,
367+
is IntegerLiteralInst,
368+
is FloatLiteralInst,
369+
is ObjectInst,
370+
is VectorInst,
371+
is AllocVectorInst,
372+
is UncheckedRefCastInst,
373+
is ValueToBridgeObjectInst,
374+
is ConvertFunctionInst,
375+
is ThinToThickFunctionInst,
376+
is AddressToPointerInst,
377+
is GlobalAddrInst,
378+
is FunctionRefInst:
379+
return true
380+
default:
381+
return false
382+
}
383+
}
384+
}
385+
386+
// Match the pattern:
387+
// tuple_extract(usub_with_overflow(x, integer_literal, integer_literal 0), 0)
388+
private extension TupleExtractInst {
389+
var isResultOfOffsetSubtract: Bool {
390+
if fieldIndex == 0,
391+
let bi = tuple as? BuiltinInst,
392+
bi.id == .USubOver,
393+
bi.operands[1].value is IntegerLiteralInst,
394+
let overflowLiteral = bi.operands[2].value as? IntegerLiteralInst,
395+
let overflowValue = overflowLiteral.value,
396+
overflowValue == 0
397+
{
398+
return true
399+
}
400+
return false
401+
}
306402
}
307403

308404
extension StoreInst {
@@ -657,7 +753,8 @@ extension InstructionRange {
657753
/// ```
658754
func getGlobalInitialization(
659755
of function: Function,
660-
allowGlobalValue: Bool
756+
forStaticInitializer: Bool,
757+
_ context: some Context
661758
) -> (allocInst: AllocGlobalInst, storeToGlobal: StoreInst)? {
662759
guard let block = function.blocks.singleElement else {
663760
return nil
@@ -695,10 +792,10 @@ func getGlobalInitialization(
695792
return nil
696793
}
697794
store = si
698-
case is GlobalValueInst where allowGlobalValue:
795+
case is GlobalValueInst where !forStaticInitializer:
699796
break
700797
default:
701-
if !inst.isValidInStaticInitializerOfGlobal {
798+
if !inst.isValidInStaticInitializerOfGlobal(context) {
702799
return nil
703800
}
704801
}

0 commit comments

Comments
 (0)