mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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
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: 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 : $()
|
|
}
|