mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
634 lines
23 KiB
Swift
634 lines
23 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 AST
|
|
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
|
|
|
|
guard // In case of a preceding error, there is no guarantee that the SIL is valid.
|
|
!context.hadError,
|
|
// Is `function` a global init function?
|
|
let global = function.initializedGlobal,
|
|
// Even if the init value is a constant, the initializer can have side effects, e.g.
|
|
// let g = { print("hello"); return 27 }()
|
|
!function.hasSideEffects(besideStoringTo: global),
|
|
// Try to get a constant global init value.
|
|
let initValue = GlobalInitValue(of: function, context)
|
|
else {
|
|
return
|
|
}
|
|
|
|
initValue.materialize(into: global, from: function, context)
|
|
|
|
// The initial value can contain a `begin_access` if it references another global variable by address, e.g.
|
|
// var p = Point(x: 10, y: 20)
|
|
// let o = UnsafePointer(&p)
|
|
//
|
|
global.stripAccessInstructionFromInitializer(context)
|
|
|
|
function.removeAllInitializationCode(for: global, context)
|
|
}
|
|
|
|
/// A tree, which represents a constant init value of a global variable.
|
|
private indirect enum GlobalInitValue {
|
|
case undefined
|
|
|
|
// A value which is a SIL "constant", e.g. a literal instruction, a struct/tuple of literals, etc.
|
|
case constant(Value)
|
|
|
|
// A struct, tuple or vector, which is _not_ a SIL "constant".
|
|
// For example, a struct or vector which is initialized by storing its elements.
|
|
case aggregate([GlobalInitValue])
|
|
|
|
// An enum case without a payload of an address-only enum.
|
|
case enumCase(caseIndex: Int)
|
|
|
|
// An enum case which is not a SIL "constant", e.g. because it's address-only
|
|
case enumCaseWithPayload(caseIndex: Int, payload: GlobalInitValue)
|
|
|
|
init?(of globalInitFunction: Function, _ context: FunctionPassContext) {
|
|
self = .undefined
|
|
var global: GlobalVariable? = nil
|
|
|
|
// The init values for temporary stack locations. This is needed if e.g. a vector is initialized in a
|
|
// stack location and the loaded from it and stored in the global.
|
|
var stackValues = Dictionary<AllocStackInst, GlobalInitValue>()
|
|
|
|
for inst in globalInitFunction.instructions {
|
|
switch inst {
|
|
case let allocGlobal as AllocGlobalInst:
|
|
guard global == nil, allocGlobal.global.canBeInitializedStatically else {
|
|
return nil
|
|
}
|
|
global = allocGlobal.global
|
|
case let ga as GlobalAddrInst where ga.global == global:
|
|
// SILGen only creates a single global_addr in the init function. So no need to support multiple ones.
|
|
guard case .undefined = self,
|
|
let selfInitVal = GlobalInitValue(of: ga, context)
|
|
else {
|
|
return nil
|
|
}
|
|
self = selfInitVal
|
|
case let allocStack as AllocStackInst:
|
|
guard let stackInitVal = GlobalInitValue(of: allocStack, context) else {
|
|
return nil
|
|
}
|
|
stackValues[allocStack] = stackInitVal
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
// At this point some `.constant` elements can be (or contain) loads from stack locations.
|
|
// Replace those elements with the init value of the corresponding stack location.
|
|
resolveLoads(from: &stackValues, context)
|
|
|
|
if !isValid(context) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private init?(of address: Value, _ context: FunctionPassContext) {
|
|
var builder = InitValueBuilder(originalAddress: address)
|
|
if builder.walkDownUses(ofAddress: address, path: UnusedWalkingPath()) == .abortWalk {
|
|
return nil
|
|
}
|
|
self = builder.initValue
|
|
}
|
|
|
|
enum InitValue {
|
|
// The common case
|
|
case value(Value)
|
|
|
|
// For payload-less cases of address-only enums. Such cases are initialized purely with an `inject_enum_addr`,
|
|
// and we don't have a `Value` which represents the resulting enum(-case).
|
|
case enumCaseWithoutPayload(InjectEnumAddrInst)
|
|
|
|
var parentFunction: Function {
|
|
switch self {
|
|
case .value(let value): return value.parentFunction
|
|
case .enumCaseWithoutPayload(let iea): return iea.parentFunction
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sets an element in the constant tree.
|
|
// Returns true if this was successful. One reason for being not successful is if a certain
|
|
// element is set twice, i.e. does not have a single defined value.
|
|
mutating func setElement(to value: InitValue, at path: SmallProjectionPath, type: Type) -> Bool {
|
|
let (kind, index, subPath) = path.pop()
|
|
switch kind {
|
|
case .root:
|
|
guard case .undefined = self else {
|
|
// The element was set twice.
|
|
return false
|
|
}
|
|
switch value {
|
|
case .value(let value):
|
|
self = .constant(value)
|
|
case .enumCaseWithoutPayload:
|
|
fatalError("should have been handled in the .enumCase of the SmallProjectionPath below")
|
|
}
|
|
return true
|
|
|
|
case .enumCase:
|
|
return setEnumCase(to: value, at: subPath, index: index, type: type)
|
|
|
|
case .structField:
|
|
guard let structFields = type.getNominalFields(in: value.parentFunction) else {
|
|
return false
|
|
}
|
|
return setField(to: value, at: subPath, index: index, type: structFields[index], numFields: structFields.count)
|
|
|
|
case .tupleField:
|
|
let tupleElements = type.tupleElements
|
|
return setField(to: value, at: subPath, index: index, type: tupleElements[index], numFields: tupleElements.count)
|
|
|
|
case .vectorBase:
|
|
let elementType = type.builtinFixedArrayElementType(in: value.parentFunction)
|
|
guard let vectorSize = type.builtinFixedArraySizeType.valueOfInteger else {
|
|
return false
|
|
}
|
|
let (indexKind, vectorIndex, vectorSubPath) = subPath.pop()
|
|
switch indexKind {
|
|
case .indexedElement:
|
|
return setField(to: value, at: vectorSubPath, index: vectorIndex, type: elementType, numFields: vectorSize)
|
|
case .anyIndexedElement:
|
|
// A non-constant index means: all elements are initialized with the same value.
|
|
// (that's what we checked in `storesToAllVectorElements`)
|
|
for i in 0..<vectorSize {
|
|
if !setField(to: value, at: vectorSubPath, index: i, type: elementType, numFields: vectorSize) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
default:
|
|
// A missing index in the path means: the first element
|
|
return setField(to: value, at: subPath, index: 0, type: elementType, numFields: vectorSize)
|
|
}
|
|
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
private mutating func setField(
|
|
to value: InitValue, at path: SmallProjectionPath,
|
|
index: Int, type: Type, numFields: Int
|
|
) -> Bool {
|
|
if case .undefined = self {
|
|
// Initialize the aggregate array if not done, yet.
|
|
self = .aggregate(Array(repeating: GlobalInitValue.undefined, count: numFields))
|
|
}
|
|
if case .aggregate(let fields) = self {
|
|
var newFields = fields
|
|
self = .undefined // avoid copy-on-write
|
|
if !newFields[index].setElement(to: value, at: path, type: type) {
|
|
return false
|
|
}
|
|
self = .aggregate(newFields)
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
private mutating func setEnumCase(to value: InitValue, at path: SmallProjectionPath, index: Int, type: Type) -> Bool {
|
|
switch value {
|
|
|
|
case .enumCaseWithoutPayload(let iea):
|
|
guard case .undefined = self else {
|
|
// The enum was set twice.
|
|
return false
|
|
}
|
|
assert(index == iea.caseIndex)
|
|
self = .enumCase(caseIndex: index)
|
|
|
|
case .value:
|
|
guard let payloadType = type.getEnumCases(in: value.parentFunction)!.getPayloadType(ofCaseIndex: index) else {
|
|
return false
|
|
}
|
|
switch self {
|
|
case .undefined:
|
|
// It's the first time we set the payload or a sub-field of it.
|
|
var payload = GlobalInitValue.undefined
|
|
if !payload.setElement(to: value, at: path, type: payloadType) {
|
|
return false
|
|
}
|
|
self = .enumCaseWithPayload(caseIndex: index, payload: payload)
|
|
case .enumCaseWithPayload(let existingIndex, var payload) where index == existingIndex:
|
|
// Some sub-field of the enum-payload was already set.
|
|
self = .undefined // avoid copy-on-write
|
|
if !payload.setElement(to: value, at: path, type: payloadType) {
|
|
return false
|
|
}
|
|
self = .enumCaseWithPayload(caseIndex: index, payload: payload)
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
/// Creates SIL for this global init value in the initializer of the `global`.
|
|
func materialize(into global: GlobalVariable, from function: Function, _ context: FunctionPassContext) {
|
|
var cloner = Cloner(cloneToGlobal: global, context)
|
|
defer { cloner.deinitialize() }
|
|
let builder = Builder(staticInitializerOf: global, context)
|
|
|
|
_ = materializeRecursively(type: global.type, &cloner, builder, function)
|
|
}
|
|
|
|
private func materializeRecursively(
|
|
type: Type,
|
|
_ cloner: inout Cloner<FunctionPassContext>,
|
|
_ builder: Builder,
|
|
_ function: Function
|
|
) -> Value {
|
|
switch self {
|
|
case .undefined:
|
|
fatalError("cannot materialize undefined init value")
|
|
|
|
case .constant(let value):
|
|
return cloner.cloneRecursivelyToGlobal(value: value)
|
|
|
|
case .aggregate(let fields):
|
|
if type.isStruct {
|
|
let fieldTypes = type.getNominalFields(in: function)!
|
|
let fieldValues = zip(fields, fieldTypes).map {
|
|
$0.0.materializeRecursively(type: $0.1, &cloner, builder, function)
|
|
}
|
|
return builder.createStruct(type: type, elements: fieldValues)
|
|
}
|
|
if type.isTuple {
|
|
let elementValues = zip(fields, type.tupleElements).map {
|
|
$0.0.materializeRecursively(type: $0.1, &cloner, builder, function)
|
|
}
|
|
return builder.createTuple(type: type, elements: elementValues)
|
|
}
|
|
assert(type.isBuiltinFixedArray)
|
|
let elementType = type.builtinFixedArrayElementType(in: function)
|
|
let elementValues = fields.map {
|
|
$0.materializeRecursively(type: elementType, &cloner, builder, function)
|
|
}
|
|
return builder.createVector(type: type, arguments: elementValues)
|
|
|
|
case .enumCase(let caseIndex):
|
|
return builder.createEnum(caseIndex: caseIndex, payload: nil, enumType: type)
|
|
|
|
case .enumCaseWithPayload(let caseIndex, let payload):
|
|
let payloadType = type.getEnumCases(in: function)!.getPayloadType(ofCaseIndex: caseIndex)!
|
|
let payloadValue = payload.materializeRecursively(type: payloadType, &cloner, builder, function)
|
|
return builder.createEnum(caseIndex: caseIndex, payload: payloadValue, enumType: type)
|
|
}
|
|
}
|
|
|
|
// Replace `.constant` elements, which are (or contain) loads, init value of the corresponding stack location.
|
|
// Some from stack locations.
|
|
mutating func resolveLoads(
|
|
from stackValues: inout Dictionary<AllocStackInst, GlobalInitValue>,
|
|
_ context: FunctionPassContext
|
|
) {
|
|
var resolvedAllocStacks = InstructionSet(context)
|
|
defer { resolvedAllocStacks.deinitialize() }
|
|
resolveLoadsRecursively(from: &stackValues, &resolvedAllocStacks, context)
|
|
}
|
|
|
|
private mutating func resolveLoadsRecursively(
|
|
from stackValues: inout Dictionary<AllocStackInst, GlobalInitValue>,
|
|
_ resolvedAllocStacks: inout InstructionSet,
|
|
_ context: FunctionPassContext
|
|
) {
|
|
switch self {
|
|
case .undefined, .enumCase:
|
|
break
|
|
case .constant(let value):
|
|
if value.containsLoad(context) {
|
|
switch value {
|
|
case is StructInst, is TupleInst:
|
|
self = .aggregate((value as! Instruction).operands.lazy.map { .constant($0.value) })
|
|
resolveLoadsRecursively(from: &stackValues, &resolvedAllocStacks, context)
|
|
case let ei as EnumInst:
|
|
self = .enumCaseWithPayload(caseIndex: ei.caseIndex, payload: .constant(ei.payload!))
|
|
resolveLoadsRecursively(from: &stackValues, &resolvedAllocStacks, context)
|
|
case let li as LoadInst:
|
|
guard let allocStack = li.address as? AllocStackInst,
|
|
var stackInit = stackValues[allocStack]
|
|
else {
|
|
self = .undefined
|
|
return
|
|
}
|
|
if resolvedAllocStacks.insert(allocStack) {
|
|
stackInit.resolveLoadsRecursively(from: &stackValues, &resolvedAllocStacks, context)
|
|
stackValues[allocStack] = stackInit
|
|
}
|
|
self = stackInit
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
case .aggregate(let fields):
|
|
var newFields = fields
|
|
self = .undefined // avoid copy-on-write
|
|
for i in 0..<fields.count {
|
|
newFields[i].resolveLoadsRecursively(from: &stackValues, &resolvedAllocStacks, context)
|
|
}
|
|
self = .aggregate(newFields)
|
|
case .enumCaseWithPayload(let caseIndex, var payload):
|
|
payload.resolveLoadsRecursively(from: &stackValues, &resolvedAllocStacks, context)
|
|
self = .enumCaseWithPayload(caseIndex: caseIndex, payload: payload)
|
|
}
|
|
}
|
|
|
|
func isValid(_ context: FunctionPassContext) -> Bool {
|
|
switch self {
|
|
case .undefined:
|
|
return false
|
|
case .constant(let value):
|
|
return value.isValidGlobalInitValue(context)
|
|
case .aggregate(let fields):
|
|
return fields.allSatisfy { $0.isValid(context) }
|
|
case .enumCase:
|
|
return true
|
|
case .enumCaseWithPayload(_, let payload):
|
|
return payload.isValid(context)
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension Value {
|
|
/// Returns true if this value is a `load` or a struct/tuple/enum with a `load` operand.
|
|
func containsLoad(_ context: FunctionPassContext) -> Bool {
|
|
var worklist = ValueWorklist(context)
|
|
defer { worklist.deinitialize() }
|
|
worklist.pushIfNotVisited(self)
|
|
while let v = worklist.pop() {
|
|
switch v {
|
|
case is LoadInst:
|
|
return true
|
|
case is StructInst, is TupleInst:
|
|
worklist.pushIfNotVisited(contentsOf: (v as! Instruction).operands.lazy.map { $0.value })
|
|
case let ei as EnumInst:
|
|
if let payload = ei.payload {
|
|
worklist.pushIfNotVisited(payload)
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
private struct InitValueBuilder: AddressDefUseWalker {
|
|
let originalAddress: Value
|
|
var initValue = GlobalInitValue.undefined
|
|
|
|
mutating func leafUse(address: Operand, path: UnusedWalkingPath) -> WalkResult {
|
|
switch address.instruction {
|
|
case let store as StoreInst:
|
|
let accessPath = store.destination.lookThroughRawLayoutAddress.constantAccessPath
|
|
switch accessPath.base {
|
|
case .global, .stack:
|
|
if !initValue.setElement(to: .value(store.source), at: accessPath.projectionPath, type: originalAddress.type) {
|
|
return .abortWalk
|
|
}
|
|
return .continueWalk
|
|
case .index(let indexAddr):
|
|
// If we have a non-constant index in the access path, check if this is from a loop which
|
|
// initializes all elements.
|
|
guard store.storesToAllVectorElements(using: indexAddr) else {
|
|
return .abortWalk
|
|
}
|
|
let nonConstAccessPath = store.destination.accessPath
|
|
guard indexAddr.base.constantAccessPath.base == nonConstAccessPath.base else {
|
|
return .abortWalk
|
|
}
|
|
// The `nonConstAccessPath` now contains a single `.anyIndexedElement`.
|
|
if !initValue.setElement(to: .value(store.source), at: nonConstAccessPath.projectionPath, type: originalAddress.type) {
|
|
return .abortWalk
|
|
}
|
|
return .continueWalk
|
|
default:
|
|
fatalError("could not compute access path")
|
|
}
|
|
case let injectEnum as InjectEnumAddrInst:
|
|
if injectEnum.element.hasAssociatedValues {
|
|
if !injectEnum.operand.value.type.isLoadable(in: injectEnum.parentFunction) {
|
|
// TODO: we don't support non-loadable enum cases with payload yet, because IRGen support is missing.
|
|
// e.g. `var global: Atomic<Int>? = Atomic<Int>(0)`
|
|
// FixedTypeInfo (= used for non-loadable types) is missing the ability to pack a payload into an enum.
|
|
return .abortWalk
|
|
}
|
|
return .continueWalk
|
|
}
|
|
let accessPath = injectEnum.enum.getAccessPath(fromInitialPath: SmallProjectionPath(.enumCase,
|
|
index: injectEnum.caseIndex))
|
|
switch accessPath.base {
|
|
case .global, .stack:
|
|
if !initValue.setElement(to: .enumCaseWithoutPayload(injectEnum), at: accessPath.projectionPath, type: originalAddress.type) {
|
|
return .abortWalk
|
|
}
|
|
return .continueWalk
|
|
default:
|
|
return .abortWalk
|
|
}
|
|
|
|
case is LoadInst, is DeallocStackInst:
|
|
return .continueWalk
|
|
case let bi as BuiltinInst:
|
|
switch bi.id {
|
|
case .PrepareInitialization:
|
|
return .continueWalk
|
|
case .AddressOfRawLayout:
|
|
if let addr2Ptr = bi.uses.getSingleUser(ofType: PointerToAddressInst.self) {
|
|
return walkDownUses(ofAddress: addr2Ptr, path: path)
|
|
}
|
|
return .abortWalk
|
|
default:
|
|
return .abortWalk
|
|
}
|
|
default:
|
|
return .abortWalk
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension StoreInst {
|
|
func storesToAllVectorElements(using indexAddr: IndexAddrInst) -> Bool {
|
|
if let vectorBase = indexAddr.base as? VectorBaseAddrInst,
|
|
let headerBr = vectorBase.parentBlock.terminator as? BranchInst,
|
|
headerBr.targetBlock == parentBlock,
|
|
let vectorSize = vectorBase.vector.type.builtinFixedArraySizeType.valueOfInteger,
|
|
let (start, loopCount, increment) = getLoopInductionInfo(of: indexAddr.index.lookThroughIndexScalarCast),
|
|
start == 0, loopCount == vectorSize, increment == 1
|
|
{
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Matches the pattern:
|
|
/// ```
|
|
/// %startValue = integer_literal <start>
|
|
/// %incrementValue = integer_literal <increment>
|
|
/// %loopEndValue = integer_literal <loopCount>
|
|
/// br loopBlock(%startValue)
|
|
/// loopBlock(%inductionVar):
|
|
/// %add = builtin "sadd_with_overflow"(%inductionVar, %incrementValue, ...)
|
|
/// %incrementedInductionVar = tuple_extract %add, 0
|
|
/// %loopBreakCondition = builtin "cmp_eq"(%incrementedInductionVar, %loopEndValue)
|
|
/// cond_br %loopBreakCondition, exitBlock, backEdgeBlock
|
|
/// backEdgeBlock:
|
|
/// br loopBlock(%incrementedInductionVar)
|
|
/// exitBlock:
|
|
/// ```
|
|
private func getLoopInductionInfo(of inductionVar: Value) -> (start: Int, loopCount: Int, increment: Int)? {
|
|
let loopBlock = inductionVar.parentBlock
|
|
if let inductionVarArg = inductionVar as? Argument, inductionVarArg.parentBlock == loopBlock,
|
|
let inductionVarPhi = Phi(inductionVarArg),
|
|
let condBr = loopBlock.terminator as? CondBranchInst,
|
|
let loopBreakCondition = condBr.condition as? BuiltinInst, loopBreakCondition.id == .ICMP_EQ,
|
|
let incrementedInductionVar = loopBreakCondition.arguments[0] as? TupleExtractInst,
|
|
incrementedInductionVar.fieldIndex == 0,
|
|
let add = incrementedInductionVar.tuple as? BuiltinInst, add.id == .SAddOver,
|
|
add.arguments[0] == inductionVarArg,
|
|
let incrementValue = add.arguments[1] as? IntegerLiteralInst,
|
|
let increment = incrementValue.value,
|
|
let loopEndValue = loopBreakCondition.arguments[1] as? IntegerLiteralInst,
|
|
let loopCount = loopEndValue.value,
|
|
let backEdgeBranch = condBr.falseBlock.terminator as? BranchInst,
|
|
backEdgeBranch.targetBlock == loopBlock,
|
|
inductionVarPhi.incomingOperand(inPredecessor: backEdgeBranch.parentBlock).value == incrementedInductionVar,
|
|
let startValue = inductionVarPhi.incomingValue(notInBlock: backEdgeBranch.parentBlock) as? IntegerLiteralInst,
|
|
let start = startValue.value
|
|
{
|
|
return (start, loopCount, increment)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
private extension Phi {
|
|
func incomingValue(notInBlock: BasicBlock) -> Value? {
|
|
if let block = predecessors.lazy.filter({ $0 != notInBlock }).singleElement {
|
|
return incomingOperand(inPredecessor: block).value
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private extension Function {
|
|
func hasSideEffects(besideStoringTo global: GlobalVariable) -> Bool {
|
|
return instructions.contains { inst in
|
|
switch inst {
|
|
case is DeallocStackInst, is DebugStepInst, is DebugValueInst, is BeginAccessInst, is EndAccessInst:
|
|
return false
|
|
case let alloc as AllocGlobalInst where alloc.global == global:
|
|
return false
|
|
case let store as StoreInst:
|
|
return !store.destination.lookThroughRawLayoutAddress.isAddressOfStack(orGlobal: global)
|
|
case let injectEnum as InjectEnumAddrInst:
|
|
return !injectEnum.enum.isAddressOfStack(orGlobal: global)
|
|
case let bi as BuiltinInst where bi.id == .PrepareInitialization:
|
|
return false
|
|
default:
|
|
return inst.hasUnspecifiedSideEffects
|
|
}
|
|
}
|
|
}
|
|
|
|
func removeAllInitializationCode(for global: GlobalVariable, _ context: FunctionPassContext) {
|
|
for inst in instructions {
|
|
switch inst {
|
|
case let allocGlobal as AllocGlobalInst where allocGlobal.global == global:
|
|
context.erase(instruction: allocGlobal)
|
|
case let globalAddr as GlobalAddrInst where globalAddr.global == global:
|
|
context.erase(instructionIncludingAllUsers: globalAddr)
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
context.removeTriviallyDeadInstructionsIgnoringDebugUses(in: self)
|
|
}
|
|
}
|
|
|
|
private extension Value {
|
|
func isAddressOfStack(orGlobal global: GlobalVariable) -> Bool {
|
|
switch accessBase {
|
|
case .global(let g) where g == global:
|
|
return true
|
|
case .stack:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Looks through a `@_rawLayout` projection, which "type casts" a raw-layout struct to it's content, which
|
|
/// must match the like-type of the raw-layout, e.g.
|
|
/// ```
|
|
/// @_rawLayout(like: T)
|
|
/// struct S {}
|
|
///
|
|
/// %2 = builtin "addressOfRawLayout"<S>(%1 : $*S) : $Builtin.RawPointer
|
|
/// %3 = pointer_to_address %2 to $*T
|
|
/// ```
|
|
var lookThroughRawLayoutAddress: Value {
|
|
if let ptr2Addr = self as? PointerToAddressInst,
|
|
let builtin = ptr2Addr.pointer as? BuiltinInst, builtin.id == .AddressOfRawLayout,
|
|
let likeType = builtin.arguments[0].type.rawLayoutSubstitutedLikeType,
|
|
builtin.arguments[0].type.rawLayoutSubstitutedCountType == nil,
|
|
likeType.canonical == type.canonicalType
|
|
{
|
|
return builtin.arguments[0]
|
|
}
|
|
return self
|
|
}
|
|
}
|
|
|
|
private extension EnumCases {
|
|
func getPayloadType(ofCaseIndex caseIndex: Int) -> Type? {
|
|
return first(where: { $0.index == caseIndex })!.payload
|
|
}
|
|
}
|