blob: dc09e017af2fb592e33c807b4eb7ae875938de1b [file] [log] [blame]
// RUN: %target-sil-opt -module-name Swift -enable-sil-verify-all -semantic-arc-opts -sil-semantic-arc-peepholes-lifetime-joining %s | %FileCheck %s
// REQUIRES: swift_stdlib_asserts
// NOTE: Some of our tests here depend on borrow elimination /not/ running!
// Please do not add it to clean up the IR like we did in
// semanticarcopts-loadcopy-to-loadborrow!
sil_stage canonical
import Builtin
//////////////////
// Declarations //
//////////////////
typealias AnyObject = Builtin.AnyObject
enum MyNever {}
enum FakeOptional<T> {
case none
case some(T)
}
sil [ossa] @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
sil [ossa] @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
sil [ossa] @get_owned_obj : $@convention(thin) () -> @owned Builtin.NativeObject
sil [ossa] @unreachable_guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> MyNever
sil [ossa] @inout_user : $@convention(thin) (@inout FakeOptional<NativeObjectPair>) -> ()
sil [ossa] @get_native_object : $@convention(thin) () -> @owned Builtin.NativeObject
struct NativeObjectPair {
var obj1 : Builtin.NativeObject
var obj2 : Builtin.NativeObject
}
sil [ossa] @get_object_pair : $@convention(thin) () -> @owned NativeObjectPair
struct FakeOptionalNativeObjectPairPair {
var pair1 : FakeOptional<NativeObjectPair>
var pair2 : FakeOptional<NativeObjectPair>
}
sil [ossa] @inout_user2 : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> ()
sil [ossa] @get_nativeobject_pair : $@convention(thin) () -> @owned NativeObjectPair
sil [ossa] @consume_nativeobject_pair : $@convention(thin) (@owned NativeObjectPair) -> ()
protocol MyFakeAnyObject : Klass {
func myFakeMethod()
}
final class Klass {
var base: Klass
let baseLet: Klass
}
extension Klass : MyFakeAnyObject {
func myFakeMethod()
}
sil [ossa] @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
sil [ossa] @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> ()
sil [ossa] @guaranteed_fakeoptional_classlet_user : $@convention(thin) (@guaranteed FakeOptional<ClassLet>) -> ()
struct MyInt {
var value: Builtin.Int32
}
struct StructWithDataAndOwner {
var data : Builtin.Int32
var owner : Klass
}
struct StructMemberTest {
var c : Klass
var s : StructWithDataAndOwner
var t : (Builtin.Int32, StructWithDataAndOwner)
}
class ClassLet {
@_hasStorage let aLet: Klass
@_hasStorage var aVar: Klass
@_hasStorage let aLetTuple: (Klass, Klass)
@_hasStorage let anOptionalLet: FakeOptional<Klass>
@_hasStorage let anotherLet: ClassLet
}
class SubclassLet: ClassLet {}
sil_global [let] @a_let_global : $Klass
sil_global @a_var_global : $Klass
enum EnumWithIndirectCase {
case first
indirect case second(Builtin.NativeObject)
}
struct StructWithEnumWithIndirectCaseField {
var i: Builtin.Int23
var field : EnumWithIndirectCase
}
sil [ossa] @get_fakeoptional_nativeobject : $@convention(thin) () -> @owned FakeOptional<Builtin.NativeObject>
struct NativeObjectWrapper {
var innerWrapper : Builtin.NativeObject
}
sil @owned_user_object_pair : $@convention(thin) (@owned NativeObjectPair) -> ()
///////////
// Tests //
///////////
// CHECK-LABEL: sil [ossa] @join_liveranges_in_same_block_1 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_liveranges_in_same_block_1'
sil [ossa] @join_liveranges_in_same_block_1 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
destroy_value %0 : $Builtin.NativeObject
destroy_value %1 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_liveranges_in_same_block_2 : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_liveranges_in_same_block_2'
sil [ossa] @join_liveranges_in_same_block_2 : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
destroy_value %0 : $Builtin.NativeObject
return %1 : $Builtin.NativeObject
}
// CHECK-LABEL: sil [ossa] @join_liveranges_in_same_block_3 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_liveranges_in_same_block_3'
sil [ossa] @join_liveranges_in_same_block_3 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
br bb1
bb1:
destroy_value %0 : $Builtin.NativeObject
%f = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %f(%1) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb2
bb2:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_liveranges_in_same_block_4 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_liveranges_in_same_block_4'
sil [ossa] @join_liveranges_in_same_block_4 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
br bb1
bb1:
destroy_value %0 : $Builtin.NativeObject
%f = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %f(%1) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
%f2 = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %f2(%1) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb2
bb2:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @donot_join_liveranges_in_same_block_1 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: copy_value
// CHECK: } // end sil function 'donot_join_liveranges_in_same_block_1'
sil [ossa] @donot_join_liveranges_in_same_block_1 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
br bb1
bb1:
%f = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %f(%1) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
destroy_value %0 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// Forwarding case.
//
// CHECK-LABEL: sil [ossa] @donot_join_liveranges_in_same_block_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'donot_join_liveranges_in_same_block_2'
sil [ossa] @donot_join_liveranges_in_same_block_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
br bb1
bb1:
destroy_value %0 : $Builtin.NativeObject
%2 = unchecked_ref_cast %1 : $Builtin.NativeObject to $Builtin.NativeObject
%f = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %f(%2) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%9999 = tuple()
return %9999 : $()
}
// Forwarding case. We need LiveRanges for this.
//
// CHECK-LABEL: sil [ossa] @donot_join_liveranges_in_same_block_3 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: copy_value
// CHECK: } // end sil function 'donot_join_liveranges_in_same_block_3'
sil [ossa] @donot_join_liveranges_in_same_block_3 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
br bb1
bb1:
%2 = unchecked_ref_cast %0 : $Builtin.NativeObject to $Builtin.NativeObject
destroy_value %2 : $Builtin.NativeObject
%f = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %f(%1) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%9999 = tuple()
return %9999 : $()
}
// Now test cases where we find our consumer is in the return block or is a
// return itself.
//
// CHECK-LABEL: sil [ossa] @join_liveranges_not_same_block_with_consuming_return : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_liveranges_not_same_block_with_consuming_return'
sil [ossa] @join_liveranges_not_same_block_with_consuming_return : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
br bb1
bb1:
destroy_value %0 : $Builtin.NativeObject
br bb2
bb2:
return %1 : $Builtin.NativeObject
}
// CHECK-LABEL: sil [ossa] @join_liveranges_not_same_block_consumed_in_return_block : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_liveranges_not_same_block_consumed_in_return_block'
sil [ossa] @join_liveranges_not_same_block_consumed_in_return_block : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
br bb1
bb1:
destroy_value %0 : $Builtin.NativeObject
br bb2
bb2:
%f = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %f(%1) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @donot_join_liveranges_not_same_block_1 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: copy_value
// CHECK: } // end sil function 'donot_join_liveranges_not_same_block_1'
sil [ossa] @donot_join_liveranges_not_same_block_1 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
br bb1
bb1:
destroy_value %0 : $Builtin.NativeObject
br bb2
bb2:
%f = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %f(%1) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @donot_join_liveranges_not_same_block_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: copy_value
// CHECK: } // end sil function 'donot_join_liveranges_not_same_block_2'
sil [ossa] @donot_join_liveranges_not_same_block_2 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = copy_value %0 : $Builtin.NativeObject
br bb1
bb1:
%f = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %f(%1) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb2
bb2:
destroy_value %0 : $Builtin.NativeObject
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_liverange_multiple_destroy_value : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK: } // end sil function 'join_liverange_multiple_destroy_value'
sil [ossa] @join_liverange_multiple_destroy_value : $@convention(thin) () -> () {
bb0:
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%constructingUse = function_ref @get_owned_obj : $@convention(thin) () -> @owned Builtin.NativeObject
%obj = apply %constructingUse() : $@convention(thin) () -> @owned Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
%obj2 = copy_value %obj : $Builtin.NativeObject
destroy_value %obj : $Builtin.NativeObject
cond_br undef, bb1a, bb1b
bb1a:
apply %consumingUse(%obj2) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb1b:
apply %consumingUse(%obj2) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb2:
apply %consumingUse(%obj) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb4
bb3:
br bb4
bb4:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_liverange_forwarding_chain_1 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK: } // end sil function 'join_liverange_forwarding_chain_1'
sil [ossa] @join_liverange_forwarding_chain_1 : $@convention(thin) () -> () {
bb0:
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%constructingUse = function_ref @get_owned_obj : $@convention(thin) () -> @owned Builtin.NativeObject
%obj = apply %constructingUse() : $@convention(thin) () -> @owned Builtin.NativeObject
%obj2 = copy_value %obj : $Builtin.NativeObject
destroy_value %obj : $Builtin.NativeObject
%obj3 = unchecked_ref_cast %obj2 : $Builtin.NativeObject to $Builtin.NativeObject
apply %consumingUse(%obj3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_liverange_multiple_destroy_value_forwarding_chain_1 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK: } // end sil function 'join_liverange_multiple_destroy_value_forwarding_chain_1'
sil [ossa] @join_liverange_multiple_destroy_value_forwarding_chain_1 : $@convention(thin) () -> () {
bb0:
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%constructingUse = function_ref @get_owned_obj : $@convention(thin) () -> @owned Builtin.NativeObject
%obj = apply %constructingUse() : $@convention(thin) () -> @owned Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
%obj2 = copy_value %obj : $Builtin.NativeObject
destroy_value %obj : $Builtin.NativeObject
%obj3 = unchecked_ref_cast %obj2 : $Builtin.NativeObject to $Builtin.NativeObject
cond_br undef, bb1a, bb1b
bb1a:
apply %consumingUse(%obj3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb1b:
apply %consumingUse(%obj3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb2:
apply %consumingUse(%obj) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb4
bb3:
br bb4
bb4:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_liverange_multiple_destroy_value_forwarding_chain_2 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK: } // end sil function 'join_liverange_multiple_destroy_value_forwarding_chain_2'
sil [ossa] @join_liverange_multiple_destroy_value_forwarding_chain_2 : $@convention(thin) () -> () {
bb0:
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%constructingUse = function_ref @get_owned_obj : $@convention(thin) () -> @owned Builtin.NativeObject
%obj = apply %constructingUse() : $@convention(thin) () -> @owned Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
%obj2 = copy_value %obj : $Builtin.NativeObject
%obj3 = unchecked_ref_cast %obj2 : $Builtin.NativeObject to $Builtin.NativeObject
destroy_value %obj : $Builtin.NativeObject
cond_br undef, bb1a, bb1b
bb1a:
apply %consumingUse(%obj3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb1b:
apply %consumingUse(%obj3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb2:
apply %consumingUse(%obj) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb4
bb3:
br bb4
bb4:
%9999 = tuple()
return %9999 : $()
}
// We do not support destructures and other multiple value instructions yet...
//
// CHECK-LABEL: sil [ossa] @join_liverange_multiple_destroy_value_forwarding_chain_3 : $@convention(thin) () -> () {
// CHECK: copy_value
// CHECK: destroy_value
// CHECK: } // end sil function 'join_liverange_multiple_destroy_value_forwarding_chain_3'
sil [ossa] @join_liverange_multiple_destroy_value_forwarding_chain_3 : $@convention(thin) () -> () {
bb0:
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%constructingUse = function_ref @get_owned_obj : $@convention(thin) () -> @owned Builtin.NativeObject
%obj = apply %constructingUse() : $@convention(thin) () -> @owned Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
%obj2 = copy_value %obj : $Builtin.NativeObject
%obj2a = struct $NativeObjectWrapper(%obj2 : $Builtin.NativeObject)
(%obj3) = destructure_struct %obj2a : $NativeObjectWrapper
destroy_value %obj : $Builtin.NativeObject
cond_br undef, bb1a, bb1b
bb1a:
apply %consumingUse(%obj3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb1b:
apply %consumingUse(%obj3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb2:
apply %consumingUse(%obj) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb4
bb3:
br bb4
bb4:
%9999 = tuple()
return %9999 : $()
}
// This case succeeds since our borrow scope does not overlap with our
// forwarding region.
//
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_succeed_1 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_succeed_1'
sil [ossa] @join_live_range_with_borrowscopes_succeed_1 : $@convention(thin) () -> () {
bb0:
%consumingUse = function_ref @owned_user_object_pair : $@convention(thin) (@owned NativeObjectPair) -> ()
%constructingUse = function_ref @get_object_pair : $@convention(thin) () -> @owned NativeObjectPair
%obj = apply %constructingUse() : $@convention(thin) () -> @owned NativeObjectPair
%borrowedObj = begin_borrow %obj : $NativeObjectPair
%obj1 = struct_extract %borrowedObj : $NativeObjectPair, #NativeObjectPair.obj1
%guaranteedUse = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %guaranteedUse(%obj1) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
end_borrow %borrowedObj : $NativeObjectPair
%2 = copy_value %obj : $NativeObjectPair
br bb1
bb1:
destroy_value %obj : $NativeObjectPair
apply %consumingUse(%2) : $@convention(thin) (@owned NativeObjectPair) -> ()
br bb2
bb2:
%9999 = tuple()
return %9999 : $()
}
// In this case, we validate that we can perform the optimization with
// forwarding insts.
//
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_succeed_2 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_succeed_2'
sil [ossa] @join_live_range_with_borrowscopes_succeed_2 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
%borrowedObj = begin_borrow %obj : $Klass
end_borrow %borrowedObj : $Klass
%2 = copy_value %obj : $Klass
br bb1
bb1:
destroy_value %obj : $Klass
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb2
bb2:
%9999 = tuple()
return %9999 : $()
}
// Here we succeed even though %2's lifetime ends at the unchecked_ref_cast, we
// are able to know that
//
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_succeed_3 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_succeed_3'
sil [ossa] @join_live_range_with_borrowscopes_succeed_3 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
%borrowedObj = begin_borrow %obj : $Klass
end_borrow %borrowedObj : $Klass
%2 = copy_value %obj : $Klass
br bb1
bb1:
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
destroy_value %obj : $Klass
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb2
bb2:
%9999 = tuple()
return %9999 : $()
}
// In this case we fail since we don't want to have to deal with splitting the
// scope of %borrowedObj at %3.
//
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_fail_1 : $@convention(thin) () -> () {
// CHECK: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_fail_1'
sil [ossa] @join_live_range_with_borrowscopes_fail_1 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
%borrowedObj = begin_borrow %obj : $Klass
%2 = copy_value %obj : $Klass
br bb1
bb1:
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
end_borrow %borrowedObj : $Klass
destroy_value %obj : $Klass
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb2
bb2:
%9999 = tuple()
return %9999 : $()
}
// This case succeeds since our borrow scope does not overlap with our
// forwarding region.
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_1 : $@convention(thin) () -> () {
bb0:
%consumingUse = function_ref @owned_user_object_pair : $@convention(thin) (@owned NativeObjectPair) -> ()
%constructingUse = function_ref @get_object_pair : $@convention(thin) () -> @owned NativeObjectPair
%obj = apply %constructingUse() : $@convention(thin) () -> @owned NativeObjectPair
%borrowedObj = begin_borrow %obj : $NativeObjectPair
%obj1 = struct_extract %borrowedObj : $NativeObjectPair, #NativeObjectPair.obj1
%guaranteedUse = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %guaranteedUse(%obj1) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
end_borrow %borrowedObj : $NativeObjectPair
cond_br undef, bb1, bb2
bb1:
%2 = copy_value %obj : $NativeObjectPair
destroy_value %obj : $NativeObjectPair
apply %consumingUse(%2) : $@convention(thin) (@owned NativeObjectPair) -> ()
br bb3
bb2:
destroy_value %obj : $NativeObjectPair
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// In this case, we validate that we can perform the optimization with
// forwarding insts.
//
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_2 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_multipledestroys_succeed_2'
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_2 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
%borrowedObj = begin_borrow %obj : $Klass
// We cheat and use end_borrow so we don't optimize this borrow.
end_borrow %borrowedObj : $Klass
cond_br undef, bb1, bb2
bb1:
%2 = copy_value %obj : $Klass
destroy_value %obj : $Klass
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb2:
destroy_value %obj : $Klass
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_3 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_multipledestroys_succeed_3'
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_3 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
%borrowedObj = begin_borrow %obj : $Klass
cond_br undef, bb1, bb2
bb1:
end_borrow %borrowedObj : $Klass
%2 = copy_value %obj : $Klass
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
destroy_value %obj : $Klass
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb2:
end_borrow %borrowedObj : $Klass
destroy_value %obj : $Klass
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_4 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_multipledestroys_succeed_4'
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_4 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
%borrowedObj = begin_borrow %obj : $Klass
cond_br undef, bb1, bb2
bb1:
%2 = copy_value %obj : $Klass
end_borrow %borrowedObj : $Klass
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
destroy_value %obj : $Klass
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb2:
end_borrow %borrowedObj : $Klass
destroy_value %obj : $Klass
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_5 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_multipledestroys_succeed_5'
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_5 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
cond_br undef, bb1, bb2
bb1:
%2 = copy_value %obj : $Klass
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
destroy_value %obj : $Klass
%4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $Klass
%5 = unchecked_ref_cast %4 : $Klass to $Builtin.NativeObject
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%5) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb2:
destroy_value %obj : $Klass
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_6 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_multipledestroys_succeed_6'
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_6 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
cond_br undef, bb1, bb2
bb1:
%2 = copy_value %obj : $Klass
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
destroy_value %obj : $Klass
br bb1a
bb1a:
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb2:
destroy_value %obj : $Klass
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_7 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_multipledestroys_succeed_7'
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_7 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
cond_br undef, bb1, bb2
bb1:
%2 = copy_value %obj : $Klass
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
destroy_value %obj : $Klass
cond_br undef, bb1a, bb1b
bb1a:
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb1c
bb1b:
destroy_value %3 : $Builtin.NativeObject
br bb1c
bb1c:
br bb3
bb2:
destroy_value %obj : $Klass
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_fail_2 : $@convention(thin) () -> () {
// CHECK: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_multipledestroys_fail_2'
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_fail_2 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
cond_br undef, bb1, bb2
bb1:
%2 = copy_value %obj : $Klass
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
destroy_value %obj : $Klass
br bb3
bb2:
destroy_value %obj : $Klass
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// In this case we fail since we don't want to have to deal with splitting the
// scope of %borrowedObj at %3.
//
// CHECK-LABEL: sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_fail_1 : $@convention(thin) () -> () {
// CHECK: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_multipledestroys_fail_1'
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_fail_1 : $@convention(thin) () -> () {
bb0:
%obj = alloc_ref $Klass
%borrowedObj = begin_borrow %obj : $Klass
cond_br undef, bb1, bb2
bb1:
%2 = copy_value %obj : $Klass
%3 = unchecked_ref_cast %2 : $Klass to $Builtin.NativeObject
end_borrow %borrowedObj : $Klass
destroy_value %obj : $Klass
%consumingUse = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %consumingUse(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
br bb3
bb2:
end_borrow %borrowedObj : $Klass
destroy_value %obj : $Klass
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// Make sure we leave only one copy in bb2 and no destroys
//
// CHECK-LABEL: sil [ossa] @join_test_with_forwarding_inst : $@convention(thin) () -> @owned FakeOptional<Builtin.NativeObject> {
// CHECK: bb2:
// CHECK: copy_value
// CHECK-NOT: destroy_value
// CHECK-NOT: copy_value
// CHECK: br bb3(
// CHECK: } // end sil function 'join_test_with_forwarding_inst'
sil [ossa] @join_test_with_forwarding_inst : $@convention(thin) () -> @owned FakeOptional<Builtin.NativeObject> {
bb0:
%allocStack = alloc_stack $Builtin.NativeObject
%0 = function_ref @get_fakeoptional_nativeobject : $@convention(thin) () -> @owned FakeOptional<Builtin.NativeObject>
%1 = apply %0() : $@convention(thin) () -> @owned FakeOptional<Builtin.NativeObject>
cond_br undef, bb1, bb2
bb1:
destroy_value %1 : $FakeOptional<Builtin.NativeObject>
%2 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.none!enumelt
br bb3(%2 : $FakeOptional<Builtin.NativeObject>)
bb2:
%3 = unchecked_enum_data %1 : $FakeOptional<Builtin.NativeObject>, #FakeOptional.some!enumelt
%4 = copy_value %3 : $Builtin.NativeObject
store %3 to [init] %allocStack : $*Builtin.NativeObject
%4c = copy_value %4 : $Builtin.NativeObject
destroy_value %4 : $Builtin.NativeObject
%5 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.some!enumelt, %4c : $Builtin.NativeObject
destroy_addr %allocStack : $*Builtin.NativeObject
br bb3(%5 : $FakeOptional<Builtin.NativeObject>)
bb3(%result : @owned $FakeOptional<Builtin.NativeObject>):
dealloc_stack %allocStack : $*Builtin.NativeObject
return %result : $FakeOptional<Builtin.NativeObject>
}