Files
swift-mirror/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift
Erik Eckstein 3d555a412a refactor two swift passes
NFC
2023-05-10 16:04:57 +02:00

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
}
}