Skip to content

Commit 6d6b94e

Browse files
committed
Swift Optimizer: add the InitializeStaticGlobals function pass
It converts a lazily initialized global to a statically initialized global variable. When this pass runs on a global initializer `[global_init_once_fn]` it tries to create a static initializer for the initialized global. ``` sil [global_init_once_fn] @globalinit { alloc_global @the_global %a = global_addr @the_global %i = some_const_initializer_insts store %i to %a } ``` The pass creates a static initializer for the global: ``` sil_global @the_global = { %initval = some_const_initializer_insts } ``` and removes the allocation and store instructions from the initializer function: ``` sil [global_init_once_fn] @globalinit { %a = global_addr @the_global %i = some_const_initializer_insts } ``` The initializer then becomes a side-effect free function which let's the builtin-simplification remove the `builtin "once"` which calls the initializer.
1 parent 2b117fd commit 6d6b94e

File tree

5 files changed

+292
-0
lines changed

5 files changed

+292
-0
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ swift_compiler_sources(Optimizer
1111
CleanupDebugSteps.swift
1212
ComputeEscapeEffects.swift
1313
ComputeSideEffects.swift
14+
InitializeStaticGlobals.swift
1415
ObjCBridgingOptimization.swift
1516
MergeCondFails.swift
1617
ReleaseDevirtualizer.swift
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
//===--- InitializeStaticGlobals.swift -------------------------------------==//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
/// Converts a lazily initialized global to a statically initialized global variable.
16+
///
17+
/// When this pass runs on a global initializer `[global_init_once_fn]` it tries to
18+
/// create a static initializer for the initialized global.
19+
///
20+
/// ```
21+
/// sil [global_init_once_fn] @globalinit {
22+
/// alloc_global @the_global
23+
/// %a = global_addr @the_global
24+
/// %i = some_const_initializer_insts
25+
/// store %i to %a
26+
/// }
27+
/// ```
28+
/// The pass creates a static initializer for the global:
29+
/// ```
30+
/// sil_global @the_global = {
31+
/// %initval = some_const_initializer_insts
32+
/// }
33+
/// ```
34+
/// and removes the allocation and store instructions from the initializer function:
35+
/// ```
36+
/// sil [global_init_once_fn] @globalinit {
37+
/// %a = global_addr @the_global
38+
/// %i = some_const_initializer_insts
39+
/// }
40+
/// ```
41+
/// The initializer then becomes a side-effect free function which let's the builtin-
42+
/// simplification remove the `builtin "once"` which calls the initializer.
43+
///
44+
let initializeStaticGlobalsPass = FunctionPass(name: "initialize-static-globals") {
45+
(function: Function, context: FunctionPassContext) in
46+
47+
if !function.isGlobalInitOnceFunction {
48+
return
49+
}
50+
51+
guard let (allocInst, storeToGlobal) = function.getGlobalInitialization() else {
52+
return
53+
}
54+
55+
if !allocInst.global.canBeInitializedStatically {
56+
return
57+
}
58+
59+
context.createStaticInitializer(for: allocInst.global,
60+
initValue: storeToGlobal.source as! SingleValueInstruction)
61+
context.erase(instruction: allocInst)
62+
context.erase(instruction: storeToGlobal)
63+
}
64+
65+
private extension Function {
66+
/// Analyses the global initializer function and returns the `alloc_global` and `store`
67+
/// instructions which initialize the global.
68+
///
69+
/// The function's single basic block must contain following code pattern:
70+
/// ```
71+
/// alloc_global @the_global
72+
/// %a = global_addr @the_global
73+
/// %i = some_const_initializer_insts
74+
/// store %i to %a
75+
/// ```
76+
func getGlobalInitialization() -> (allocInst: AllocGlobalInst, storeToGlobal: StoreInst)? {
77+
78+
guard let block = singleBlock else {
79+
return nil
80+
}
81+
82+
var allocInst: AllocGlobalInst? = nil
83+
var globalAddr: GlobalAddrInst? = nil
84+
var store: StoreInst? = nil
85+
86+
for inst in block.instructions {
87+
switch inst {
88+
case is ReturnInst,
89+
is DebugValueInst,
90+
is DebugStepInst:
91+
break
92+
case let agi as AllocGlobalInst:
93+
if allocInst != nil {
94+
return nil
95+
}
96+
allocInst = agi
97+
case let ga as GlobalAddrInst:
98+
if globalAddr != nil {
99+
return nil
100+
}
101+
guard let agi = allocInst, agi.global == ga.global else {
102+
return nil
103+
}
104+
globalAddr = ga
105+
case let si as StoreInst:
106+
if store != nil {
107+
return nil
108+
}
109+
guard let ga = globalAddr else {
110+
return nil
111+
}
112+
if si.destination != ga {
113+
return nil
114+
}
115+
store = si
116+
default:
117+
if !inst.isValidInStaticInitializerOfGlobal {
118+
return nil
119+
}
120+
}
121+
}
122+
if let store = store {
123+
return (allocInst: allocInst!, storeToGlobal: store)
124+
}
125+
return nil
126+
}
127+
128+
var singleBlock: BasicBlock? {
129+
let block = entryBlock
130+
if block.next != nil {
131+
return nil
132+
}
133+
return block
134+
}
135+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ private func registerSwiftPasses() {
6767
registerPass(mergeCondFailsPass, { mergeCondFailsPass.run($0) })
6868
registerPass(computeEscapeEffects, { computeEscapeEffects.run($0) })
6969
registerPass(computeSideEffects, { computeSideEffects.run($0) })
70+
registerPass(initializeStaticGlobalsPass, { initializeStaticGlobalsPass.run($0) })
7071
registerPass(objCBridgingOptimization, { objCBridgingOptimization.run($0) })
7172
registerPass(stackPromotion, { stackPromotion.run($0) })
7273
registerPass(functionStackProtection, { functionStackProtection.run($0) })

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ SWIFT_FUNCTION_PASS(ComputeSideEffects, "compute-side-effects",
228228
"Computes function side effects")
229229
SWIFT_FUNCTION_PASS(TestInstructionIteration, "test-instruction-iteration",
230230
"Tests instruction iteration")
231+
SWIFT_FUNCTION_PASS(InitializeStaticGlobals, "initialize-static-globals",
232+
"Initializes static global variables")
231233
PASS(FlowIsolation, "flow-isolation",
232234
"Enforces flow-sensitive actor isolation rules")
233235
PASS(FunctionOrderPrinter, "function-order-printer",
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -initialize-static-globals | %FileCheck %s
2+
3+
// REQUIRES: swift_in_compiler
4+
5+
sil_stage canonical
6+
7+
import Builtin
8+
import Swift
9+
import SwiftShims
10+
11+
public struct TStruct {
12+
let x: Int32
13+
init(x: Int32)
14+
}
15+
16+
struct Outer {
17+
let a: Int32
18+
let b: TStruct
19+
let c: Int32
20+
}
21+
22+
let trivialglobal: TStruct
23+
24+
public class TClass {
25+
final let x: Int32
26+
init(x: Int32)
27+
deinit
28+
}
29+
30+
struct GenericStruct<T> {
31+
var x: T
32+
}
33+
34+
let nontrivialglobal: TClass
35+
36+
// CHECK-LABEL: sil_global hidden [let] @$trivialglobal : $TStruct = {
37+
// CHECK: [[CONST:%.*]] = integer_literal $Builtin.Int32, 10
38+
// CHECK: [[INT:%.*]] = struct $Int32 ([[CONST]] : $Builtin.Int32)
39+
// CHECK: %initval = struct $TStruct ([[INT]] : $Int32)
40+
sil_global hidden [let] @$trivialglobal : $TStruct
41+
sil_global private @globalinit_trivialglobal_token : $Builtin.Word
42+
43+
44+
// CHECK-LABEL: sil_global hidden [let] @$nontrivialglobal : $TClass{{$}}
45+
sil_global hidden [let] @$nontrivialglobal : $TClass
46+
sil_global private @globalinit_nontrivialglobal_token : $Builtin.Word
47+
48+
// CHECK-LABEL: sil_global hidden [let] @empty_global : $GenericStruct<()>{{$}}
49+
sil_global hidden [let] @empty_global : $GenericStruct<()>
50+
sil_global private @empty_global_token : $Builtin.Word
51+
52+
// CHECK: sil_global @go : $Outer = {
53+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int32, 2
54+
// CHECK-NEXT: %1 = struct $Int32 (%0 : $Builtin.Int32)
55+
// CHECK-NEXT: %2 = struct $TStruct (%1 : $Int32)
56+
// CHECK-NEXT: %initval = struct $Outer (%1 : $Int32, %2 : $TStruct, %1 : $Int32)
57+
// CHECK-NEXT: }
58+
sil_global @go : $Outer
59+
sil_global private @globalinit_token0 : $Builtin.Word
60+
61+
// CHECK-LABEL: sil_global [let] @g1 : $Int32{{$}}
62+
sil_global [let] @g1 : $Int32
63+
sil_global private @g1_token : $Builtin.Word
64+
65+
// CHECK-LABEL: sil_global [let] @g2 : $Int32{{$}}
66+
sil_global [let] @g2 : $Int32
67+
sil_global private @g2_token : $Builtin.Word
68+
69+
70+
// CHECK-LABEL: sil [global_init_once_fn] [ossa] @globalinit_trivialglobal_func :
71+
// CHECK-NOT: alloc_global
72+
// CHECK-NOT: store
73+
// CHECK: } // end sil function 'globalinit_trivialglobal_func'
74+
sil [global_init_once_fn] [ossa] @globalinit_trivialglobal_func : $@convention(c) () -> () {
75+
bb0:
76+
alloc_global @$trivialglobal
77+
%1 = global_addr @$trivialglobal : $*TStruct
78+
%2 = integer_literal $Builtin.Int32, 10
79+
%3 = struct $Int32 (%2 : $Builtin.Int32)
80+
%4 = struct $TStruct (%3 : $Int32)
81+
store %4 to [trivial] %1 : $*TStruct
82+
%6 = tuple ()
83+
return %6 : $()
84+
}
85+
86+
// CHECK-LABEL: sil [global_init_once_fn] [ossa] @globalinit_nontrivialglobal_func :
87+
// CHECK: alloc_global
88+
// CHECK: store
89+
// CHECK: } // end sil function 'globalinit_nontrivialglobal_func'
90+
sil [global_init_once_fn] [ossa] @globalinit_nontrivialglobal_func : $@convention(c) () -> () {
91+
bb0:
92+
alloc_global @$nontrivialglobal
93+
%1 = global_addr @$nontrivialglobal : $*TClass
94+
%2 = integer_literal $Builtin.Int32, 10
95+
%3 = struct $Int32 (%2 : $Builtin.Int32)
96+
%4 = alloc_ref $TClass
97+
%5 = begin_borrow %4 : $TClass
98+
%6 = ref_element_addr %5 : $TClass, #TClass.x
99+
store %3 to [trivial] %6 : $*Int32
100+
end_borrow %5 : $TClass
101+
store %4 to [init] %1 : $*TClass
102+
%10 = tuple ()
103+
return %10 : $()
104+
}
105+
106+
// Check that we don't crash on an initializer struct with an "undef" operand.
107+
108+
// CHECK-LABEL: sil [global_init_once_fn] [ossa] @globalinit_with_undef :
109+
// CHECK: alloc_global
110+
// CHECK: store
111+
// CHECK: } // end sil function 'globalinit_with_undef'
112+
sil [global_init_once_fn] [ossa] @globalinit_with_undef : $@convention(c) () -> () {
113+
bb0:
114+
alloc_global @empty_global
115+
%1 = global_addr @empty_global : $*GenericStruct<()>
116+
%2 = struct $GenericStruct<()> (undef : $())
117+
store %2 to [trivial] %1 : $*GenericStruct<()>
118+
%4 = tuple ()
119+
return %4 : $()
120+
}
121+
122+
// CHECK-LABEL: sil [global_init_once_fn] @globalinit_nested_struct :
123+
// CHECK-NOT: alloc_global
124+
// CHECK-NOT: store
125+
// CHECK: } // end sil function 'globalinit_nested_struct'
126+
sil [global_init_once_fn] @globalinit_nested_struct : $@convention(c) () -> () {
127+
bb0:
128+
alloc_global @go
129+
%0 = global_addr @go : $*Outer
130+
%1 = integer_literal $Builtin.Int32, 2
131+
%2 = struct $Int32 (%1 : $Builtin.Int32)
132+
%3 = struct $TStruct (%2 : $Int32)
133+
%4 = struct $Outer (%2 : $Int32, %3 : $TStruct, %2 : $Int32)
134+
store %4 to %0 : $*Outer
135+
%r = tuple ()
136+
return %r : $()
137+
}
138+
139+
// CHECK-LABEL: sil [global_init_once_fn] @globalinit_mismatching_global :
140+
// CHECK: alloc_global
141+
// CHECK: store
142+
// CHECK: } // end sil function 'globalinit_mismatching_global'
143+
sil [global_init_once_fn] @globalinit_mismatching_global : $@convention(c) () -> () {
144+
bb0:
145+
alloc_global @g1
146+
%1 = global_addr @g2 : $*Int32
147+
%2 = integer_literal $Builtin.Int32, 10
148+
%3 = struct $Int32 (%2 : $Builtin.Int32)
149+
store %3 to %1 : $*Int32
150+
%6 = tuple ()
151+
return %6 : $()
152+
}
153+

0 commit comments

Comments
 (0)