Files
swift-mirror/test/SILOptimizer/semantic-arc-opts-lifetime-joining.sil
Meghana Gupta 8f00bc4fe8 Bailout from an illegal transformation in semantic arc opts
tryJoinIfDestroyConsumingUseInSameBlock replaces a copy with its operand
when there is no use of the copy's operand between the copy's forwarded consuming use
and the copy operand's destroy in the same block. It is illegal to do this transformation
when there is a non-consuming use of the copy operand after the forwarded consuming use of the copy.

The code checking this illegal case was not considerin the case where the consuming use of the copy
was in the same instruction as the non-consuming use of the copy operand.

rdar://154712867
2025-07-15 03:41:05 -07:00

1020 lines
36 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -module-name Swift -enable-sil-verify-all -semantic-arc-opts -sil-semantic-arc-peepholes-lifetime-joining %s | %FileCheck %s
//
// Enabling specific ARC opts requires an asserts build.
// REQUIRES: 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
struct Bool {
var value : Builtin.Int1
}
sil @closureCapturesBool : $@convention(thin) (@guaranteed { var Bool }) -> ()
sil @closureArgumentEscapes : $@convention(thin) (@owned @callee_guaranteed () -> ()) -> ()
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) -> ()
class X {}
struct S {}
sil @getX : $@convention(thin) () -> @owned X
sil @getS : $@convention(thin) (@owned X) -> @out S
sil @loadWeakX_from : $@convention(thin) (@in_guaranteed S) -> @owned FakeOptional<X>
///////////
// 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] @join_liveranges_in_same_block_5 : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK-NOT: copy_value
// CHECK-LABEL: } // end sil function 'join_liveranges_in_same_block_5'
sil [ossa] @join_liveranges_in_same_block_5 : $@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_succeed_8 : $@convention(thin) () -> () {
// CHECK-NOT: copy_value
// CHECK: } // end sil function 'join_live_range_with_borrowscopes_multipledestroys_succeed_8'
sil [ossa] @join_live_range_with_borrowscopes_multipledestroys_succeed_8 : $@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>
}
// Don't do this optimization:
// Eliminate borrowed copy with useless lifetime:
// %5 = copy_value %0 : ${ var Bool }
//
// CHECK: sil hidden [ossa] @testCapturedSingleDestroyCopy : $@convention(thin) () -> Bool {
// CHECK: load [trivial] %{{.*}} : $*Bool
// CHECK: destroy_value %0 : ${ var Bool }
sil hidden [ossa] @testCapturedSingleDestroyCopy : $@convention(thin) () -> Bool {
bb0:
// var test = false
%0 = alloc_box ${ var Bool }, var, name "test"
%1 = project_box %0 : ${ var Bool }, 0
%2 = integer_literal $Builtin.Int1, 0
%3 = struct $Bool (%2 : $Builtin.Int1)
store %3 to [trivial] %1 : $*Bool
// capture test in an escaping closure
%5 = copy_value %0 : ${ var Bool }
%6 = function_ref @closureCapturesBool : $@convention(thin) (@guaranteed { var Bool }) -> ()
%7 = partial_apply [callee_guaranteed] %6(%5) : $@convention(thin) (@guaranteed { var Bool }) -> ()
%8 = function_ref @closureArgumentEscapes : $@convention(thin) (@owned @callee_guaranteed () -> ()) -> ()
%9 = apply %8(%7) : $@convention(thin) (@owned @callee_guaranteed () -> ()) -> ()
// return test
%10 = begin_access [read] [dynamic] %1 : $*Bool
%11 = load [trivial] %10 : $*Bool
end_access %10 : $*Bool
destroy_value %0 : ${ var Bool }
return %11 : $Bool
}
// Don't do this optimization:
// Eliminate copy of lexical value which ends after lifetime of copy IF there
// may be deinit barriers between the final consume and the copy.
//
// CHECK-LABEL: sil [ossa] @testDestroyedLexicalValue : {{.*}} {
// CHECK: [[GET:%[^,]+]] = function_ref @getX
// CHECK: [[X:%[^,]+]] = apply [[GET]]()
// CHECK: [[MX:%[^,]+]] = move_value [lexical] [[X]]
// CHECK: destroy_value [[MX]] : $X
// CHECK-LABEL: } // end sil function 'testDestroyedLexicalValue'
sil [ossa] @testDestroyedLexicalValue : $@convention(thin) () -> @owned FakeOptional<X> {
bb0:
%getX = function_ref @getX : $@convention(thin) () -> @owned X
%x = apply %getX() : $@convention(thin) () -> @owned X
%mx = move_value [lexical] %x : $X
%a = alloc_stack [lexical] $S, let, name "s"
%c = copy_value %mx : $X
%getS = function_ref @getS : $@convention(thin) (@owned X) -> @out S
%s = apply %getS(%a, %c) : $@convention(thin) (@owned X) -> @out S
%loadWeakX_from = function_ref @loadWeakX_from : $@convention(thin) (@in_guaranteed S) -> @owned FakeOptional<X>
%o = apply %loadWeakX_from(%a) : $@convention(thin) (@in_guaranteed S) -> @owned FakeOptional<X>
// ^^^^^ Deinit barrier
destroy_addr %a : $*S
dealloc_stack %a : $*S
destroy_value %mx : $X
return %o : $FakeOptional<X>
}
// CHECK-LABEL: sil [ossa] @testDestroyedLexicalValueNoBarriers : {{.*}} {
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function 'testDestroyedLexicalValueNoBarriers'
sil [ossa] @testDestroyedLexicalValueNoBarriers : $@convention(thin) () -> () {
bb0:
%getX = function_ref @getX : $@convention(thin) () -> @owned X
%x = apply %getX() : $@convention(thin) () -> @owned X
%mx = move_value [lexical] %x : $X
%a = alloc_stack [lexical] $S, let, name "s"
%c = copy_value %mx : $X
%getS = function_ref @getS : $@convention(thin) (@owned X) -> @out S
%s = apply %getS(%a, %c) : $@convention(thin) (@owned X) -> @out S
destroy_addr %a : $*S
dealloc_stack %a : $*S
destroy_value %mx : $X
%r = tuple ()
return %r : $()
}
sil @consume_and_borrow : $@convention(thin) (@owned Klass, @guaranteed Klass) -> ()
sil @borrow : $@convention(thin) (@guaranteed Klass) -> ()
sil @consume : $@convention(thin) (@owned Klass) -> ()
sil @get_klass : $@convention(thin) () -> @owned Klass
// CHECK-LABEL: sil hidden [ossa] @borrow_and_consume_copyable_test1 :
// CHECK-NOT: copy_value
// CHECK-LABEL: } // end sil function 'borrow_and_consume_copyable_test1'
sil hidden [ossa] @borrow_and_consume_copyable_test1 : $@convention(thin) () -> () {
bb0:
%0 = function_ref @get_klass : $@convention(thin) () -> @owned Klass
%1 = apply %0() : $@convention(thin) () -> @owned Klass
%2 = move_value [var_decl] %1
%3 = copy_value %2
%4 = function_ref @borrow : $@convention(thin) (@guaranteed Klass) -> ()
%5 = apply %4(%2) : $@convention(thin) (@guaranteed Klass) -> ()
%6 = function_ref @consume : $@convention(thin) (@owned Klass) -> ()
%7 = apply %6(%3) : $@convention(thin) (@owned Klass) -> ()
destroy_value %2
%9 = tuple ()
return %9
}
// CHECK-LABEL: sil hidden [ossa] @borrow_and_consume_copyable_test2 :
// CHECK: copy_value
// CHECK-LABEL: } // end sil function 'borrow_and_consume_copyable_test2'
sil hidden [ossa] @borrow_and_consume_copyable_test2 : $@convention(thin) () -> () {
bb0:
%0 = function_ref @get_klass : $@convention(thin) () -> @owned Klass
%1 = apply %0() : $@convention(thin) () -> @owned Klass
%2 = move_value [var_decl] %1
%3 = copy_value %2
%4 = function_ref @consume_and_borrow : $@convention(thin) (@owned Klass, @guaranteed Klass) -> ()
%5 = apply %4(%3, %2) : $@convention(thin) (@owned Klass, @guaranteed Klass) -> ()
destroy_value %2
%7 = tuple ()
return %7
}