SimplifyLoad: improve simplification of load of global variable

Inline the initialization code of a global initializer if the load is a global_addr with a builtin "once" dependency.
This commit is contained in:
Erik Eckstein
2024-01-02 16:28:02 +01:00
parent 3229749257
commit cee4505840
2 changed files with 115 additions and 6 deletions

View File

@@ -98,7 +98,7 @@ extension LoadInst : OnoneSimplifyable, SILCombineSimplifyable {
/// The load of a global let variable is replaced by its static initializer value. /// The load of a global let variable is replaced by its static initializer value.
private func replaceLoadOfGlobalLet(_ context: SimplifyContext) -> Bool { private func replaceLoadOfGlobalLet(_ context: SimplifyContext) -> Bool {
guard let globalInitVal = getGlobalInitValue(address: address) else { guard let globalInitVal = getGlobalInitValue(address: address, context) else {
return false return false
} }
if !globalInitVal.canBeCopied(into: parentFunction, context) { 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`. /// 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 { switch address {
case let gai as GlobalAddrInst: case let gai as GlobalAddrInst:
if gai.global.isLet { 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: case let pta as PointerToAddressInst:
return globalLoadedViaAddressor(pointer: pta.pointer)?.staticInitValue return globalLoadedViaAddressor(pointer: pta.pointer)?.staticInitValue
case let sea as StructElementAddrInst: 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 return structVal.operands[sea.fieldIndex].value
} }
case let tea as TupleElementAddrInst: 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 return tupleVal.operands[tea.fieldIndex].value
} }
case let bai as BeginAccessInst: case let bai as BeginAccessInst:
return getGlobalInitValue(address: bai.address) return getGlobalInitValue(address: bai.address, context)
default: default:
break break
} }
return nil 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? { private func globalLoadedViaAddressor(pointer: Value) -> GlobalVariable? {
if let ai = pointer as? ApplyInst, if let ai = pointer as? ApplyInst,
let callee = ai.referencedFunction, let callee = ai.referencedFunction,

View File

@@ -23,6 +23,36 @@ sil_global [let] @gstr : $Str = {
class B { } class B { }
class E : 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 { sil [global_init] @gstr_addressor : $@convention(thin) () -> Builtin.RawPointer {
bb0: bb0:
%0 = global_addr @gstr : $*Str %0 = global_addr @gstr : $*Str
@@ -96,6 +126,64 @@ bb0:
return %3 : $Int64 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-LABEL: sil @load_first_char_from_string_literal
// CHECK: bb0: // CHECK: bb0:
// CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 97 // CHECK-NEXT: %0 = integer_literal $Builtin.Int8, 97