mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
136 lines
3.8 KiB
Swift
136 lines
3.8 KiB
Swift
//===--- InitializeStaticGlobals.swift -------------------------------------==//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import SIL
|
|
|
|
/// 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.
|
|
///
|
|
let initializeStaticGlobalsPass = FunctionPass(name: "initialize-static-globals") {
|
|
(function: Function, context: FunctionPassContext) in
|
|
|
|
if !function.isGlobalInitOnceFunction {
|
|
return
|
|
}
|
|
|
|
guard let (allocInst, storeToGlobal) = getGlobalInitialization(of: function) else {
|
|
return
|
|
}
|
|
|
|
if !allocInst.global.canBeInitializedStatically {
|
|
return
|
|
}
|
|
|
|
context.createStaticInitializer(for: allocInst.global,
|
|
initValue: storeToGlobal.source as! SingleValueInstruction)
|
|
context.erase(instruction: allocInst)
|
|
context.erase(instruction: storeToGlobal)
|
|
}
|
|
|
|
/// Analyses the global initializer function and returns the `alloc_global` and `store`
|
|
/// instructions which initialize the global.
|
|
///
|
|
/// The function's single basic block must contain following code pattern:
|
|
/// ```
|
|
/// alloc_global @the_global
|
|
/// %a = global_addr @the_global
|
|
/// %i = some_const_initializer_insts
|
|
/// store %i to %a
|
|
/// ```
|
|
private func getGlobalInitialization(of function: Function) -> (allocInst: AllocGlobalInst, storeToGlobal: StoreInst)? {
|
|
|
|
guard let block = function.singleBlock else {
|
|
return nil
|
|
}
|
|
|
|
var allocInst: AllocGlobalInst? = nil
|
|
var globalAddr: GlobalAddrInst? = nil
|
|
var store: StoreInst? = nil
|
|
|
|
for inst in block.instructions {
|
|
switch inst {
|
|
case is ReturnInst,
|
|
is DebugValueInst,
|
|
is DebugStepInst:
|
|
break
|
|
case let agi as AllocGlobalInst:
|
|
if allocInst != nil {
|
|
return nil
|
|
}
|
|
allocInst = agi
|
|
case let ga as GlobalAddrInst:
|
|
if globalAddr != nil {
|
|
return nil
|
|
}
|
|
guard let agi = allocInst, agi.global == ga.global else {
|
|
return nil
|
|
}
|
|
globalAddr = ga
|
|
case let si as StoreInst:
|
|
if store != nil {
|
|
return nil
|
|
}
|
|
guard let ga = globalAddr else {
|
|
return nil
|
|
}
|
|
if si.destination != ga {
|
|
return nil
|
|
}
|
|
store = si
|
|
default:
|
|
if !inst.isValidInStaticInitializerOfGlobal {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
if let store = store {
|
|
return (allocInst: allocInst!, storeToGlobal: store)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
private extension Function {
|
|
var singleBlock: BasicBlock? {
|
|
let block = entryBlock
|
|
if block.next != nil {
|
|
return nil
|
|
}
|
|
return block
|
|
}
|
|
}
|