Files
swift-mirror/test/SILOptimizer/licm_exclusivity.sil
Erik Eckstein 3701de4731 AliasAnalysis: check for immutable scopes.
If the "regular" alias analysis thinks that an instruction may write to an address, check if the instruction is in an immutable scope of V.
That means that even if we don't know anything about the instruction (e.g. a call to an unknown function), we can be sure that it cannot write to the address.

An immutable scope is for example a read-only begin_access/end_access scope.
Another example is a borrow scope of an immutable copy-on-write buffer, for example:
   %b = begin_borrow %array_buffer
   %addr = ref_element_addr [immutable] %b : $BufferType, #BufferType.someField
2021-05-18 10:14:44 +02:00

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: load
// CHECK-NEXT: br bb1
// CHECK: apply
// CHECK-NEXT: 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: load
// CHECK-NEXT: br bb1
// CHECK: apply
// 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: load
// CHECK-NEXT: br bb1
// CHECK: apply
// CHECK-NEXT: 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 : $()
}