diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift index cf97434cbe5..bf14b180da5 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift @@ -98,7 +98,7 @@ extension LoadInst : OnoneSimplifyable, SILCombineSimplifyable { /// The load of a global let variable is replaced by its static initializer value. private func replaceLoadOfGlobalLet(_ context: SimplifyContext) -> Bool { - guard let globalInitVal = getGlobalInitValue(address: address) else { + guard let globalInitVal = getGlobalInitValue(address: address, context) else { return false } if !globalInitVal.canBeCopied(into: parentFunction, context) { @@ -211,30 +211,51 @@ extension LoadInst : OnoneSimplifyable, SILCombineSimplifyable { } /// Returns the init value of a global which is loaded from `address`. -private func getGlobalInitValue(address: Value) -> Value? { +private func getGlobalInitValue(address: Value, _ context: SimplifyContext) -> Value? { switch address { case let gai as GlobalAddrInst: if gai.global.isLet { - return gai.global.staticInitValue + if let staticInitValue = gai.global.staticInitValue { + return staticInitValue + } + if let staticInitValue = getInitializerFromInitFunction(of: gai, context) { + return staticInitValue + } } case let pta as PointerToAddressInst: return globalLoadedViaAddressor(pointer: pta.pointer)?.staticInitValue case let sea as StructElementAddrInst: - if let structVal = getGlobalInitValue(address: sea.struct) as? StructInst { + if let structVal = getGlobalInitValue(address: sea.struct, context) as? StructInst { return structVal.operands[sea.fieldIndex].value } case let tea as TupleElementAddrInst: - if let tupleVal = getGlobalInitValue(address: tea.tuple) as? TupleInst { + if let tupleVal = getGlobalInitValue(address: tea.tuple, context) as? TupleInst { return tupleVal.operands[tea.fieldIndex].value } case let bai as BeginAccessInst: - return getGlobalInitValue(address: bai.address) + return getGlobalInitValue(address: bai.address, context) default: break } return nil } +private func getInitializerFromInitFunction(of globalAddr: GlobalAddrInst, _ context: SimplifyContext) -> Value? { + guard let dependentOn = globalAddr.dependencyToken, + let builtinOnce = dependentOn as? BuiltinInst, + builtinOnce.id == .Once, + let initFnRef = builtinOnce.operands[1].value as? FunctionRefInst else + { + return nil + } + let initFn = initFnRef.referencedFunction + context.notifyDependency(onBodyOf: initFn) + guard let (_, storeToGlobal) = getGlobalInitialization(of: initFn, allowGlobalValue: true) else { + return nil + } + return storeToGlobal.source +} + private func globalLoadedViaAddressor(pointer: Value) -> GlobalVariable? { if let ai = pointer as? ApplyInst, let callee = ai.referencedFunction, diff --git a/test/SILOptimizer/simplify_load.sil b/test/SILOptimizer/simplify_load.sil index db5788a3af3..2a7e8545d42 100644 --- a/test/SILOptimizer/simplify_load.sil +++ b/test/SILOptimizer/simplify_load.sil @@ -23,6 +23,36 @@ sil_global [let] @gstr : $Str = { class B { } class E : B { } +sil_global hidden [let] @gb : $B +sil_global hidden [let] @gb2 : $(B, Int64) + +sil_global private @gb_obj : $B = { + %initval = object $B () +} + +sil [global_init_once_fn] @init_gb : $@convention(c) () -> () { +bb0: + alloc_global @gb + %1 = global_addr @gb : $*B + %2 = global_value @gb_obj : $B + store %2 to %1 : $*B + %6 = tuple () + return %6 : $() +} + +sil [global_init_once_fn] @init_gb2 : $@convention(c) () -> () { +bb0: + alloc_global @gb2 + %1 = global_addr @gb2 : $*(B, Int64) + %2 = global_value @gb_obj : $B + %3 = integer_literal $Builtin.Int64, 10 + %4 = struct $Int64 (%3 : $Builtin.Int64) + %5 = tuple (%2 : $B, %4 : $Int64) + store %5 to %1 : $*(B, Int64) + %6 = tuple () + return %6 : $() +} + sil [global_init] @gstr_addressor : $@convention(thin) () -> Builtin.RawPointer { bb0: %0 = global_addr @gstr : $*Str @@ -96,6 +126,64 @@ bb0: return %3 : $Int64 } +// CHECK-LABEL: sil @load_global_object : +// CHECK: %1 = global_value @gb_obj +// CHECK-NEXT: return %1 +// CHECK: } // end sil function 'load_global_object' +sil @load_global_object : $@convention(thin) (Builtin.RawPointer) -> @owned B { +bb0(%0 : $Builtin.RawPointer): + %1 = function_ref @init_gb : $@convention(c) () -> () + %2 = builtin "once"(%0 : $Builtin.RawPointer, %1 : $@convention(c) () -> ()) : $Builtin.SILToken + %3 = global_addr @gb : $*B depends_on %2 + %4 = load %3 : $*B + return %4 : $B +} + +// CHECK-LABEL: sil @load_global_object_keep_once : +// CHECK: %2 = builtin "once" +// CHECK: %3 = global_value @gb_obj +// CHECK: fix_lifetime +// CHECK: return %3 +// CHECK: } // end sil function 'load_global_object_keep_once' +sil @load_global_object_keep_once : $@convention(thin) (Builtin.RawPointer) -> @owned B { +bb0(%0 : $Builtin.RawPointer): + %1 = function_ref @init_gb : $@convention(c) () -> () + %2 = builtin "once"(%0 : $Builtin.RawPointer, %1 : $@convention(c) () -> ()) : $Builtin.SILToken + %3 = global_addr @gb : $*B depends_on %2 + %4 = load %3 : $*B + %5 = global_addr @gb : $*B depends_on %2 + fix_lifetime %5 : $*B + return %4 : $B +} + +// CHECK-LABEL: sil @load_global_object_from_tuple : +// CHECK: %1 = global_value @gb_obj +// CHECK-NEXT: return %1 +// CHECK: } // end sil function 'load_global_object_from_tuple' +sil @load_global_object_from_tuple : $@convention(thin) (Builtin.RawPointer) -> @owned B { +bb0(%0 : $Builtin.RawPointer): + %1 = function_ref @init_gb2 : $@convention(c) () -> () + %2 = builtin "once"(%0 : $Builtin.RawPointer, %1 : $@convention(c) () -> ()) : $Builtin.SILToken + %3 = global_addr @gb2 : $*(B, Int64) depends_on %2 + %4 = tuple_element_addr %3 : $*(B, Int64), 0 + %5 = load %4 : $*B + return %5 : $B +} + +// CHECK-LABEL: sil @load_global_tuple : +// CHECK: %1 = global_value @gb_obj +// CHECK: [[T:%.*]] = tuple (%1 : $B, {{%.*}} : $Int64) +// CHECK-NEXT: return [[T]] +// CHECK: } // end sil function 'load_global_tuple' +sil @load_global_tuple : $@convention(thin) (Builtin.RawPointer) -> @owned (B, Int64) { +bb0(%0 : $Builtin.RawPointer): + %1 = function_ref @init_gb2 : $@convention(c) () -> () + %2 = builtin "once"(%0 : $Builtin.RawPointer, %1 : $@convention(c) () -> ()) : $Builtin.SILToken + %3 = global_addr @gb2 : $*(B, Int64) depends_on %2 + %4 = load %3 : $*(B, Int64) + return %4 : $(B, Int64) +} + // CHECK-LABEL: sil @load_first_char_from_string_literal // CHECK: bb0: // CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 97