mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Global initializers are executed only once. Therefore it's possible to hoist such an initializer call to a loop pre-header - in case there are no conflicting side-effects in the loop before the call. Also, the call must post-dominate the loop pre-header. Otherwise it would be executed speculatively.
253 lines
7.5 KiB
Plaintext
253 lines
7.5 KiB
Plaintext
// RUN: %target-sil-opt -enforce-exclusivity=checked -enable-sil-verify-all %s -licm | %FileCheck %s
|
|
|
|
sil_stage canonical
|
|
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
struct X {
|
|
@_hasStorage var i: Int64 { get set }
|
|
init(i: Int64)
|
|
init()
|
|
}
|
|
|
|
var globalX: X
|
|
|
|
sil_global hidden @globalX : $X
|
|
|
|
var globalY: X
|
|
|
|
sil_global hidden @globalY : $X
|
|
|
|
|
|
sil hidden_external @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
|
|
|
// public func hoist_access_with_conflict() {
|
|
// Tests Hoisting of begin/end access when there's a "sandwiched" unidentified access
|
|
//
|
|
// CHECK-LABEL: sil @hoist_access_with_conflict : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: br bb1
|
|
// CHECK: apply
|
|
// CHECK: load
|
|
// CHECK: cond_br
|
|
// CHECK: bb2
|
|
// CHECK: end_access [[BEGIN]]
|
|
// CHECK-LABEL: } // end sil function 'hoist_access_with_conflict'
|
|
sil @hoist_access_with_conflict : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
|
br bb1
|
|
|
|
bb1:
|
|
%u3 = begin_access [read] [dynamic] %0 : $*X
|
|
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
|
|
%u4 = load %u3 : $*X
|
|
end_access %u3 : $*X
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb2:
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// public func dont_hoist_access_with_conflict() {
|
|
// Tests *not* hoisting begin/end access when there's an unidentified access not protected by them
|
|
//
|
|
// CHECK-LABEL: sil @dont_hoist_access_with_conflict : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK: br bb1
|
|
// CHECK: apply
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load
|
|
// CHECK-NEXT: end_access [[BEGIN]]
|
|
// CHECK-LABEL: } // end sil function 'dont_hoist_access_with_conflict'
|
|
sil @dont_hoist_access_with_conflict : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
|
br bb1
|
|
|
|
bb1:
|
|
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
|
|
%u3 = begin_access [read] [dynamic] %0 : $*X
|
|
%u4 = load %u3 : $*X
|
|
end_access %u3 : $*X
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb2:
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// public func hoist_access_with_may_release() {
|
|
// Tests Hoisting of begin/end access when there's a "sandwiched" MayRelease instruction
|
|
//
|
|
// CHECK-LABEL: sil @hoist_access_with_may_release : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: br bb1
|
|
// CHECK: apply
|
|
// CHECK: load
|
|
// CHECK: cond_br
|
|
// CHECK: bb2
|
|
// CHECK: end_access [[BEGIN]]
|
|
// CHECK-LABEL: } // end sil function 'hoist_access_with_may_release'
|
|
sil @hoist_access_with_may_release : $@convention(thin) () -> () {
|
|
bb0:
|
|
%alloc = alloc_box ${ var Int32 }, var, name "y"
|
|
%0 = global_addr @globalX: $*X
|
|
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
|
br bb1
|
|
|
|
bb1:
|
|
%u3 = begin_access [read] [dynamic] %0 : $*X
|
|
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
|
|
%u4 = load %u3 : $*X
|
|
strong_release %alloc : ${ var Int32 }
|
|
end_access %u3 : $*X
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb2:
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// public func dont_hoist_access_with_may_release() {
|
|
// Tests *not* hoisting begin/end access when there's a MayRelease not protected by them
|
|
//
|
|
// CHECK-LABEL: sil @dont_hoist_access_with_may_release : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK: br bb1
|
|
// CHECK: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: load
|
|
// CHECK-NEXT: end_access [[BEGIN]]
|
|
// CHECK-LABEL: } // end sil function 'dont_hoist_access_with_may_release'
|
|
sil @dont_hoist_access_with_may_release : $@convention(thin) () -> () {
|
|
bb0:
|
|
%alloc = alloc_box ${ var Int32 }, var, name "y"
|
|
%0 = global_addr @globalX: $*X
|
|
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
|
br bb1
|
|
|
|
bb1:
|
|
%u3 = begin_access [read] [dynamic] %0 : $*X
|
|
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
|
|
%u4 = load %u3 : $*X
|
|
end_access %u3 : $*X
|
|
strong_release %alloc : ${ var Int32 }
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb2:
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// public func hoist_access_static() {
|
|
// Tests Hoisting of begin/end access when there's a static access
|
|
//
|
|
// CHECK-LABEL: sil @hoist_access_static : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK: [[BEGIN:%.*]] = begin_access [read] [static] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: br bb1
|
|
// CHECK: apply
|
|
// CHECK: load
|
|
// CHECK: cond_br
|
|
// CHECK: bb2
|
|
// CHECK: end_access [[BEGIN]]
|
|
// CHECK-LABEL: } // end sil function 'hoist_access_static'
|
|
sil @hoist_access_static : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
|
br bb1
|
|
|
|
bb1:
|
|
%u3 = begin_access [read] [static] %0 : $*X
|
|
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
|
|
%u4 = load %u3 : $*X
|
|
end_access %u3 : $*X
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb2:
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// public func hoist_access_static_and_dynamic() {
|
|
// Tests Hoisting of begin/end access when there's a static and distinct dynamic access
|
|
//
|
|
// CHECK-LABEL: sil @hoist_access_static_and_dynamic : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK: [[GLOBALY:%.*]] = global_addr @globalY : $*X
|
|
// CHECK: [[BEGIN:%.*]] = begin_access [read] [static] [[GLOBAL]] : $*X
|
|
// CHECK: [[BEGINY:%.*]] = begin_access [read] [dynamic] [[GLOBALY]] : $*X
|
|
// CHECK-NEXT: load [[BEGIN]]
|
|
// CHECK-NEXT: load [[BEGINY]]
|
|
// CHECK-NEXT: br bb1
|
|
// CHECK: cond_br
|
|
// CHECK: bb2
|
|
// CHECK: end_access [[BEGINY]]
|
|
// CHECK: end_access [[BEGIN]]
|
|
// CHECK-LABEL: } // end sil function 'hoist_access_static_and_dynamic'
|
|
sil @hoist_access_static_and_dynamic : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = global_addr @globalY: $*X
|
|
br bb1
|
|
|
|
bb1:
|
|
%x3 = begin_access [read] [static] %0 : $*X
|
|
%x4 = load %x3 : $*X
|
|
end_access %x3 : $*X
|
|
%y3 = begin_access [read] [dynamic] %1 : $*X
|
|
%y4 = load %y3 : $*X
|
|
end_access %y3 : $*X
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb2:
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// public func dont_hoist_access_static_and_dynamic() {
|
|
// Tests bailing out when there's a static and non-distinct dynamic access
|
|
//
|
|
// CHECK-LABEL: sil @dont_hoist_access_static_and_dynamic : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK: [[GLOBALX:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: br bb1
|
|
// CHECK: [[BEGIN:%.*]] = begin_access [read] [static] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load
|
|
// CHECK: end_access [[BEGIN]]
|
|
// CHECK: [[BEGINX:%.*]] = begin_access [read] [dynamic] [[GLOBALX]] : $*X
|
|
// CHECK-NEXT: load
|
|
// CHECK: end_access [[BEGINX]]
|
|
// CHECK: cond_br
|
|
// CHECK: bb2
|
|
// CHECK: return %{{.*}} : $()
|
|
// CHECK-LABEL: } // end sil function 'dont_hoist_access_static_and_dynamic'
|
|
sil @dont_hoist_access_static_and_dynamic : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = global_addr @globalX: $*X
|
|
br bb1
|
|
|
|
bb1:
|
|
%x3 = begin_access [read] [static] %0 : $*X
|
|
%x4 = load %x3 : $*X
|
|
end_access %x3 : $*X
|
|
%y3 = begin_access [read] [dynamic] %1 : $*X
|
|
%y4 = load %y3 : $*X
|
|
end_access %y3 : $*X
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb2:
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|