blob: eeac330ded1dc32f80457995901e1a90efec7ea1 [file] [log] [blame]
// RUN: %target-sil-opt -access-enforcement-release %s -enable-sil-verify-all | %FileCheck %s
//
// Test the AccessEnforcementReleaseSinking pass in isolation.
// This ensures that no upstream passes have removed SIL-level access markers
// that are required to ensure the pass is not overly optimistic.
sil_stage canonical
import Builtin
import Swift
import SwiftShims
struct X {
@_hasStorage var i: Int64 { get set }
init(i: Int64)
init()
}
var globalX: X
sil_global hidden @globalX : $X
sil hidden_external [global_init] @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
// public func testSimpleRelease() {
// Checks the simple case of release sinking
//
// CHECK-LABEL: sil @testSimpleRelease : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: release_value [[LOADED]]
// CHECK-LABEL: } // end sil function 'testSimpleRelease'
sil @testSimpleRelease : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
release_value %2 : $X
end_access %1 : $*X
%ret = tuple ()
return %ret : $()
}
// public func testMultiBlocklSimpleRelease() {
// Checks the simple case of release sinking with the begin_access in a different block
//
// CHECK-LABEL: sil @testMultiBlocklSimpleRelease : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: bb1
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: release_value [[LOADED]]
// CHECK-LABEL: } // end sil function 'testMultiBlocklSimpleRelease'
sil @testMultiBlocklSimpleRelease : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
br bb1
bb1:
release_value %2 : $X
end_access %1 : $*X
%ret = tuple ()
return %ret : $()
}
// public func testMultiBlocklBailOnRelease() {
// Checks bailing (for now) on the simple case due to the release being in a different block
//
// CHECK-LABEL: sil @testMultiBlocklBailOnRelease : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
// CHECK-NEXT: release_value [[LOADED]]
// CHECK-NEXT: br bb1
// CHECK: bb1
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-LABEL: } // end sil function 'testMultiBlocklBailOnRelease'
sil @testMultiBlocklBailOnRelease : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
release_value %2 : $X
br bb1
bb1:
end_access %1 : $*X
%ret = tuple ()
return %ret : $()
}
// public func testApplyBarrier() {
// Checks we don't sink across apply-site barrier
//
// CHECK-LABEL: sil @testApplyBarrier : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
// CHECK-NEXT: release_value [[LOADED]]
// CHECK: apply
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-LABEL: } // end sil function 'testApplyBarrier'
sil @testApplyBarrier : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
release_value %2 : $X
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
end_access %1 : $*X
%ret = tuple ()
return %ret : $()
}
// public func testUniquenessBarrier() {
// Checks we don't sink across a uniqueness check
//
// CHECK-LABEL: sil @testUniquenessBarrier : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
// CHECK-NEXT: release_value [[LOADED]]
// CHECK-NEXT: is_unique
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-LABEL: } // end sil function 'testUniquenessBarrier'
sil @testUniquenessBarrier : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
release_value %2 : $X
is_unique %1 : $*X
end_access %1 : $*X
%ret = tuple ()
return %ret : $()
}
// public func testBeginBarrier() {
// Checks we don't sink across begin_access barrier
//
// CHECK-LABEL: sil @testBeginBarrier : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
// CHECK-NEXT: release_value [[LOADED]]
// CHECK-NEXT: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-LABEL: } // end sil function 'testBeginBarrier'
sil @testBeginBarrier : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
release_value %2 : $X
%b0 = begin_access [modify] [dynamic] %0 : $*X
end_access %b0 : $*X
end_access %1 : $*X
%ret = tuple ()
return %ret : $()
}
// public func testSinkCrossMultiEnds() {
// Checks that we choose the *bottom* end_access when sinking
//
// CHECK-LABEL: sil @testSinkCrossMultiEnds : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: release_value [[LOADED]]
// CHECK-LABEL: } // end sil function 'testSinkCrossMultiEnds'
sil @testSinkCrossMultiEnds : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%b0 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
release_value %2 : $X
end_access %b0 : $*X
end_access %1 : $*X
%ret = tuple ()
return %ret : $()
}
// public func testSinkAfterBarrierEncounter() {
// Checks that we sink after barrier resetting
//
// CHECK-LABEL: sil @testSinkAfterBarrierEncounter : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: [[LOADED:%.*]] = load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: release_value [[LOADED]]
// CHECK-NEXT: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-LABEL: } // end sil function 'testSinkAfterBarrierEncounter'
sil @testSinkAfterBarrierEncounter : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
release_value %2 : $X
end_access %1 : $*X
%b0 = begin_access [modify] [dynamic] %0 : $*X
end_access %b0 : $*X
%ret = tuple ()
return %ret : $()
}
// testSinkAfterEscapingClosureCheck:
// <rdar://problem/45846920> TestFoundation, TestProcess, closure
// argument passed as @noescape to Objective-C has escaped
class IntWrapper {
var value: Builtin.Int64
init(v: Builtin.Int64) {
value = v
}
}
sil @takesNoEscapeBlockClosure : $@convention(thin) (@guaranteed @convention(block) @noescape () -> ()) -> ()
sil @closureThatModifiesCapture: $@convention(thin) ({ var Builtin.Int64 }) -> ()
sil [reabstraction_thunk] @thunkForCalleeGuaranteed : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> ()
sil [reabstraction_thunk] @withoutActuallyEscapingThunk : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> ()
// The Copy release cannot be moved below the is_escaping_closure check.
//
// CHECK-LABEL: sil @testSinkAfterEscapingClosureCheck : $@convention(thin) (@guaranteed IntWrapper) -> () {
// CHECK: bb0(%0 : $IntWrapper):
// CHECK: [[BA:%.*]] = begin_access [read] [dynamic] %{{.*}} : $*Builtin.Int64
// CHECK: [[COPY:%.*]] = copy_block %{{.*}} : $@convention(block) @noescape () -> ()
// CHECK: [[F:%.*]] = function_ref @takesNoEscapeBlockClosure : $@convention(thin) (@guaranteed @convention(block) @noescape () -> ()) -> ()
// CHECK: apply [[F]]([[COPY]]) : $@convention(thin) (@guaranteed @convention(block) @noescape () -> ()) -> ()
// CHECK: strong_release [[COPY]] : $@convention(block) @noescape () -> ()
// CHECK: is_escaping_closure
// CHECK: cond_fail
// CHECK: end_access [[BA]] : $*Builtin.Int64
// CHECK-LABEL: } // end sil function 'testSinkAfterEscapingClosureCheck'
sil @testSinkAfterEscapingClosureCheck : $@convention(thin) (@guaranteed IntWrapper) -> () {
bb0(%0 : $IntWrapper):
%va = ref_element_addr %0 : $IntWrapper, #IntWrapper.value
%ba = begin_access [read] [dynamic] %va : $*Builtin.Int64
%value = load %ba : $*Builtin.Int64
%box = alloc_box ${ var Builtin.Int64 }
%boxaddr = project_box %box : ${ var Builtin.Int64 }, 0
store %value to %boxaddr : $*Builtin.Int64
%closureF = function_ref @closureThatModifiesCapture : $@convention(thin) ({ var Builtin.Int64 }) -> ()
%closure = partial_apply [callee_guaranteed] %closureF(%box) : $@convention(thin) ({ var Builtin.Int64 }) -> ()
%conv = convert_escape_to_noescape %closure : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%thunk = function_ref @withoutActuallyEscapingThunk : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> ()
%pathunk = partial_apply [callee_guaranteed] %thunk(%conv) : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> ()
%md = mark_dependence %pathunk : $@callee_guaranteed () -> () on %conv : $@noescape @callee_guaranteed () -> ()
strong_retain %md : $@callee_guaranteed () -> ()
%allocblock = alloc_stack $@block_storage @callee_guaranteed () -> ()
%blockaddr = project_block_storage %allocblock : $*@block_storage @callee_guaranteed () -> ()
store %md to %blockaddr : $*@callee_guaranteed () -> ()
%blockF = function_ref @thunkForCalleeGuaranteed : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> ()
%initblock = init_block_storage_header %allocblock : $*@block_storage @callee_guaranteed () -> (), invoke %blockF : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> (), type $@convention(block) @noescape () -> ()
%copyblock = copy_block %initblock : $@convention(block) @noescape () -> ()
%f = function_ref @takesNoEscapeBlockClosure : $@convention(thin) (@guaranteed @convention(block) @noescape () -> ()) -> ()
%call = apply %f(%copyblock) : $@convention(thin) (@guaranteed @convention(block) @noescape () -> ()) -> ()
strong_release %copyblock : $@convention(block) @noescape () -> ()
%isescape = is_escaping_closure %md : $@callee_guaranteed () -> ()
cond_fail %isescape : $Builtin.Int1
end_access %ba : $*Builtin.Int64
destroy_addr %blockaddr : $*@callee_guaranteed() -> ()
dealloc_stack %allocblock : $*@block_storage @callee_guaranteed () -> ()
strong_release %box : ${ var Builtin.Int64 }
%ret = tuple ()
return %ret : $()
}