Files
swift-mirror/test/SILOptimizer/copy-to-borrow-optimization.sil
Erik Eckstein 0f0aa0c17b Optimizer: require that there are no unreachable blocks and infinite loops in OSSA
These two new invariants eliminate corner cases which caused bugs if optimization didn't handle them.
Also, it will significantly simplify lifetime completion.

The implementation basically consists of these changes:
* add a flag in SILFunction which tells optimization if they need to take care of infinite loops
* add a utility to break infinite loops
* let all optimizations remove unreachable blocks and break infinite loops if necessary
* add verification to check the new SIL invariants

The new `breakIfniniteLoops` utility breaks infinite loops in the control flow by inserting an "artificial" loop exit to a new dead-end block with an `unreachable`.
It inserts a `cond_br` with a `builtin "infinite_loop_true_condition"`:
```
bb0:
  br bb1
bb1:
  br bb1              // back-end branch
```
->
```
bb0:
  br bb1
bb1:
  %1 = builtin "infinite_loop_true_condition"() // always true, but the compiler doesn't know
  cond_br %1, bb2, bb3
bb2:                  // new back-end block
  br bb1
bb3:                  // new dead-end block
  unreachable
```
2026-01-22 17:41:23 +01:00

2315 lines
85 KiB
Plaintext

// RUN: %target-sil-opt -copy-to-borrow-optimization %s | %FileCheck %s
sil_stage canonical
import Builtin
import Swift
//////////////////
// Declarations //
//////////////////
enum MyNever {}
enum FakeOptional<T> {
case none
case some(T)
}
sil @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
sil @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
sil @get_owned_obj : $@convention(thin) () -> @owned Builtin.NativeObject
sil @unreachable_guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> MyNever
sil @inout_user : $@convention(thin) (@inout FakeOptional<NativeObjectPair>) -> ()
sil @get_native_object : $@convention(thin) () -> @owned Builtin.NativeObject
sil [ossa] @get_enum : $@convention(thin) () -> @owned EnumA
struct NativeObjectPair {
var obj1 : Builtin.NativeObject
var obj2 : Builtin.NativeObject
}
sil @get_object_pair : $@convention(thin) () -> @owned NativeObjectPair
struct FakeOptionalNativeObjectPairPair {
var pair1 : FakeOptional<NativeObjectPair>
var pair2 : FakeOptional<NativeObjectPair>
}
sil @inout_user2 : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> ()
sil @get_nativeobject_pair : $@convention(thin) () -> @owned NativeObjectPair
sil @consume_nativeobject_pair : $@convention(thin) (@owned NativeObjectPair) -> ()
protocol MyFakeAnyObject : Klass {
func myFakeMethod()
}
final class Klass {
var base: Klass
let baseLet: Klass
}
class C {}
class Base {
@_hasStorage var c: C
}
class Derived : Base {
}
class Container {
@_hasStorage var b: Base
}
struct OptionalAndClass {
var o: FakeOptional<C>
var c: C
}
enum MultiPayload {
case a(Klass)
case b(MyInt)
}
sil @getC : $@convention(thin) () -> (@owned C)
extension Klass : MyFakeAnyObject {
func myFakeMethod()
}
struct NonTrivialStruct {
var val: Klass
}
class MyArrayStorageBase {
@_hasStorage let countAndCapacity: Int
}
struct MyBridgeStorage {
let rawValue: Builtin.NativeObject
}
struct MyArrayBuffer {
let storage: MyBridgeStorage
}
struct MyArray {
let buffer: MyArrayBuffer
}
sil @getKlass : $@convention(thin) () -> @owned Klass
sil @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
sil @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
sil @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> ()
sil @guaranteed_fakeoptional_classlet_user : $@convention(thin) (@guaranteed FakeOptional<ClassLet>) -> ()
sil @closure : $@convention(thin) (@inout_aliasable Klass) -> ()
sil @lendC : $@yield_once_2 @convention(thin) () -> @yields @guaranteed C
sil @useC : $@convention(thin) (@guaranteed C) -> () {
[global:]
}
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
}
struct TrivialStruct {
}
enum EnumA {
case none
case sometrivial(TrivialStruct)
case somenontrivial(NonTrivialStruct)
}
struct StructWithEnum {
var val: EnumA
}
sil @get_fakeoptional_nativeobject : $@convention(thin) () -> @owned FakeOptional<Builtin.NativeObject>
sil @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
///////////
// Tests //
///////////
// Simple in_guaranteed argument load_copy.
// CHECK-LABEL: sil [ossa] @load_copy_from_in_guaranteed : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> () {
// CHECK: bb0([[ARG:%.*]] :
// CHECK: load_borrow
// CHECK: load_borrow
// CHECK: load [copy]
// CHECK: } // end sil function 'load_copy_from_in_guaranteed'
sil [ossa] @load_copy_from_in_guaranteed : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $*Builtin.NativeObject):
%g = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
// Simple same bb.
%1 = load [copy] %0 : $*Builtin.NativeObject
apply %g(%1) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %1 : $Builtin.NativeObject
// Diamond.
%2 = load [copy] %0 : $*Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
apply %g(%2) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %2 : $Builtin.NativeObject
br bb3
bb2:
destroy_value %2 : $Builtin.NativeObject
br bb3
bb3:
// Consuming use blocks.
%3 = load [copy] %0 : $*Builtin.NativeObject
%4 = function_ref @owned_user : $@convention(thin) (@owned Builtin.NativeObject) -> ()
apply %4(%3) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_guaranteed_base :
// CHECK: ref_element_addr
// CHECK-NEXT: load_borrow
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow
// CHECK-NEXT: end_borrow
// CHECK-NEXT: return
// CHECK: } // end sil function 'dont_copy_let_properties_with_guaranteed_base'
sil [ossa] @dont_copy_let_properties_with_guaranteed_base : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%x : @guaranteed $ClassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%p = ref_element_addr [immutable] %x : $ClassLet, #ClassLet.aLet
%v = load [copy] %p : $*Klass
%b = begin_borrow %v : $Klass
apply %f(%b) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %b : $Klass
destroy_value %v : $Klass
return undef : $()
}
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_guaranteed_base_and_forwarding_uses :
// CHECK: ref_element_addr
// CHECK-NEXT: load_borrow
// CHECK-NEXT: unchecked_ref_cast
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow
// CHECK-NEXT: end_borrow
// CHECK-NEXT: return
// CHECK: } // end sil function 'dont_copy_let_properties_with_guaranteed_base_and_forwarding_uses'
sil [ossa] @dont_copy_let_properties_with_guaranteed_base_and_forwarding_uses : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%x : @guaranteed $ClassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%p = ref_element_addr [immutable] %x : $ClassLet, #ClassLet.aLet
%v = load [copy] %p : $*Klass
%c = unchecked_ref_cast %v : $Klass to $Klass
%b = begin_borrow %c : $Klass
apply %f(%b) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %b : $Klass
destroy_value %c : $Klass
return undef : $()
}
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_guaranteed_upcast_base :
// CHECK: ref_element_addr
// CHECK-NEXT: load_borrow
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow
// CHECK-NEXT: end_borrow
// CHECK-NEXT: return
// CHECK: } // end sil function 'dont_copy_let_properties_with_guaranteed_upcast_base'
sil [ossa] @dont_copy_let_properties_with_guaranteed_upcast_base : $@convention(thin) (@guaranteed SubclassLet) -> () {
bb0(%x : @guaranteed $SubclassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%u = upcast %x : $SubclassLet to $ClassLet
%p = ref_element_addr [immutable] %u : $ClassLet, #ClassLet.aLet
%v = load [copy] %p : $*Klass
%b = begin_borrow %v : $Klass
apply %f(%b) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %b : $Klass
destroy_value %v : $Klass
return undef : $()
}
// CHECK-LABEL: sil [ossa] @dont_copy_let_global :
// CHECK: global_addr
// CHECK-NEXT: load_borrow
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow
// CHECK-NEXT: end_borrow
// CHECK-NEXT: return
// CHECK-NEXT: } // end sil function 'dont_copy_let_global'
sil [ossa] @dont_copy_let_global : $@convention(thin) () -> () {
bb0:
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%p = global_addr @a_let_global : $*Klass
%v = load [copy] %p : $*Klass
%b = begin_borrow %v : $Klass
apply %f(%b) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %b : $Klass
destroy_value %v : $Klass
return undef : $()
}
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_guaranteed_base_structural
// CHECK: ref_element_addr
// CHECK-NEXT: tuple_element_addr
// CHECK-NEXT: load_borrow
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow
// CHECK-NEXT: end_borrow
// CHECK-NEXT: return
sil [ossa] @dont_copy_let_properties_with_guaranteed_base_structural : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%x : @guaranteed $ClassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%p = ref_element_addr [immutable] %x : $ClassLet, #ClassLet.aLetTuple
%q = tuple_element_addr %p : $*(Klass, Klass), 1
%v = load [copy] %q : $*Klass
%b = begin_borrow %v : $Klass
apply %f(%b) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %b : $Klass
destroy_value %v : $Klass
return undef : $()
}
// CHECK-LABEL: sil [ossa] @do_copy_var_properties_with_guaranteed_base
// CHECK: ref_element_addr
// CHECK-NEXT: load [copy]
// CHECK-NEXT: apply
// CHECK-NEXT: destroy
// CHECK-NEXT: return
sil [ossa] @do_copy_var_properties_with_guaranteed_base : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%x : @guaranteed $ClassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%p = ref_element_addr %x : $ClassLet, #ClassLet.aVar
%v = load [copy] %p : $*Klass
apply %f(%v) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %v : $Klass
return undef : $()
}
// CHECK-LABEL: sil [ossa] @do_copy_var_properties_with_guaranteed_base_unreachable :
// CHECK: ref_element_addr
// CHECK-NEXT: load [copy]
// CHECK-NEXT: apply
// CHECK-NEXT: destroy_value [dead_end]
// CHECK-NEXT: unreachable
sil [ossa] @do_copy_var_properties_with_guaranteed_base_unreachable : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%x : @guaranteed $ClassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%p = ref_element_addr %x : $ClassLet, #ClassLet.aVar
%v = load [copy] %p : $*Klass
apply %f(%v) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value [dead_end] %v
unreachable
}
// CHECK-LABEL: sil [ossa] @do_copy_var_global
// CHECK: global_addr
// CHECK-NEXT: load [copy]
// CHECK-NEXT: apply
// CHECK-NEXT: destroy
// CHECK-NEXT: return
sil [ossa] @do_copy_var_global : $@convention(thin) () -> () {
bb0:
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%p = global_addr @a_var_global : $*Klass
%v = load [copy] %p : $*Klass
apply %f(%v) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %v : $Klass
return undef : $()
}
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates
// CHECK: [[OUTER:%.*]] = begin_borrow
// CHECK-NEXT: ref_element_addr
// CHECK-NEXT: [[INNER:%.*]] = load_borrow
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow [[INNER]]
// CHECK-NEXT: end_borrow [[OUTER]]
// CHECK-NEXT: destroy_value
sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates : $@convention(thin) (@owned ClassLet) -> () {
bb0(%x : @owned $ClassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%a = begin_borrow %x : $ClassLet
%p = ref_element_addr [immutable] %a : $ClassLet, #ClassLet.aLet
%v = load [copy] %p : $*Klass
apply %f(%v) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %v : $Klass
end_borrow %a : $ClassLet
destroy_value %x : $ClassLet
return undef : $()
}
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase :
// CHECK: load_borrow
// CHECK: } // end sil function 'dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase'
sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase : $@convention(thin) (@owned ClassLet) -> () {
bb0(%x : @owned $ClassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%a = begin_borrow %x : $ClassLet
%p = ref_element_addr [immutable] %a : $ClassLet, #ClassLet.aLetTuple
%v = load [copy] %p : $*(Klass, Klass)
(%v1, %v2) = destructure_tuple %v : $(Klass, Klass)
apply %f(%v1) : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%v2) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %v1 : $Klass
destroy_value %v2 : $Klass
end_borrow %a : $ClassLet
destroy_value %x : $ClassLet
return undef : $()
}
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2 :
// CHECK: load_borrow
// CHECK: } // end sil function 'dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2'
sil [ossa] @dont_copy_let_properties_with_borrowed_base_that_dominates_projtestcase_2 : $@convention(thin) (@owned ClassLet) -> () {
bb0(%x : @owned $ClassLet):
%f = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
%a = begin_borrow %x : $ClassLet
%p = ref_element_addr [immutable] %a : $ClassLet, #ClassLet.aLet
%v = load [copy] %p : $*Klass
%v_cast = unchecked_ref_cast %v : $Klass to $Builtin.NativeObject
apply %f(%v_cast) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %v_cast : $Builtin.NativeObject
end_borrow %a : $ClassLet
destroy_value %x : $ClassLet
return undef : $()
}
// CHECK-LABEL: sil [ossa] @dont_copy_let_properties_with_multi_borrowed_base_that_dominates
// CHECK: [[OUTER:%.*]] = begin_borrow
// CHECK-NEXT: ref_element_addr
// CHECK-NEXT: [[INNER:%.*]] = load_borrow
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow [[INNER]]
// CHECK-NEXT: end_borrow [[OUTER]]
// CHECK-NEXT: [[OUTER:%.*]] = begin_borrow
// CHECK-NEXT: ref_element_addr
// CHECK-NEXT: [[INNER:%.*]] = load_borrow
// CHECK-NEXT: [[INNER2:%.*]] = begin_borrow [[INNER]]
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow [[INNER2]]
// CHECK-NEXT: end_borrow [[INNER]]
// CHECK-NEXT: end_borrow [[OUTER]]
// CHECK-NEXT: destroy_value
sil [ossa] @dont_copy_let_properties_with_multi_borrowed_base_that_dominates : $@convention(thin) (@owned ClassLet) -> () {
bb0(%x : @owned $ClassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%a = begin_borrow %x : $ClassLet
%p = ref_element_addr [immutable] %a : $ClassLet, #ClassLet.aLet
%v = load [copy] %p : $*Klass
apply %f(%v) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %v : $Klass
end_borrow %a : $ClassLet
%b = begin_borrow %x : $ClassLet
%q = ref_element_addr [immutable] %b : $ClassLet, #ClassLet.aLet
%w = load [copy] %q : $*Klass
%b2 = begin_borrow %w : $Klass
apply %f(%b2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %b2 : $Klass
destroy_value %w : $Klass
end_borrow %b : $ClassLet
destroy_value %x : $ClassLet
return undef : $()
}
// CHECK-LABEL: sil [ossa] @do_copy_let_properties_with_borrowed_base_that_does_not_dominate
// CHECK: begin_borrow
// CHECK-NEXT: ref_element_addr
// CHECK-NEXT: load [copy]
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow
// CHECK-NEXT: destroy_value
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow
// CHECK-NEXT: destroy_value
sil [ossa] @do_copy_let_properties_with_borrowed_base_that_does_not_dominate : $@convention(thin) (@owned ClassLet) -> () {
bb0(%x : @owned $ClassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%a = begin_borrow %x : $ClassLet
%p = ref_element_addr [immutable] %a : $ClassLet, #ClassLet.aLet
%v = load [copy] %p : $*Klass
%b = begin_borrow %v : $Klass
apply %f(%b) : $@convention(thin) (@guaranteed Klass) -> ()
// End the lifetime of the base object first...
end_borrow %a : $ClassLet
destroy_value %x : $ClassLet
// ...then end the lifetime of the copy.
apply %f(%b) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %b : $Klass
destroy_value %v : $Klass
return undef : $()
}
// CHECK-LABEL: sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2 :
// CHECK: [[OUTER:%.*]] = begin_borrow
// CHECK-NEXT: ref_element_addr
// CHECK-NEXT: [[INNER:%.*]] = load_borrow
// CHECK-NEXT: [[INNER2:%.*]] = begin_borrow [[INNER]]
// CHECK-NEXT: apply
// CHECK-NEXT: end_borrow [[INNER2]]
// CHECK-NEXT: end_borrow [[INNER]]
// CHECK-NEXT: end_borrow [[OUTER]]
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: ref_element_addr
// CHECK-NEXT: load [copy]
// CHECK-NEXT: end_borrow
// CHECK-NEXT: destroy_value
// CHECK-NEXT: // function_ref
// CHECK-NEXT: function_ref
// CHECK-NEXT: enum
// CHECK-NEXT: apply
// CHECK-NEXT: destroy_value
// CHECK: } // end sil function 'do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2'
sil [ossa] @do_or_dont_copy_let_properties_with_multi_borrowed_base_when_it_dominates_2 : $@convention(thin) (@owned ClassLet) -> () {
bb0(%x : @owned $ClassLet):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%a = begin_borrow %x : $ClassLet
%p = ref_element_addr [immutable] %a : $ClassLet, #ClassLet.aLet
%v = load [copy] %p : $*Klass
%c = begin_borrow %v : $Klass
apply %f(%c) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %c : $Klass
destroy_value %v : $Klass
end_borrow %a : $ClassLet
%b = begin_borrow %x : $ClassLet
%q = ref_element_addr %b : $ClassLet, #ClassLet.aLet
%w = load [copy] %q : $*Klass
// End the lifetime of the base object first...
end_borrow %b : $ClassLet
destroy_value %x : $ClassLet
// ...then end the lifetime of the copy.
%f2 = function_ref @guaranteed_fakeoptional_klass_user : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> ()
%w2 = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, %w : $Klass
apply %f2(%w2) : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> ()
destroy_value %w2 : $FakeOptional<Klass>
return undef : $()
}
// Make sure that we put the end_borrow on the load_borrow, not LHS or RHS.
//
// CHECK-LABEL: sil [ossa] @destructure_load_copy_to_load_borrow : $@convention(thin) (@guaranteed ClassLet) -> () {
// CHECK: bb0([[ARG:%.*]] :
// CHECK: [[INTERIOR_POINTER:%.*]] = ref_element_addr [immutable] [[ARG]]
// CHECK: [[BORROWED_VAL:%.*]] = load_borrow [[INTERIOR_POINTER]]
// CHECK: ([[LHS:%.*]], [[RHS:%.*]]) = destructure_tuple [[BORROWED_VAL]]
// CHECK: apply {{%.*}}([[LHS]])
// CHECK: apply {{%.*}}([[RHS]])
// CHECK: end_borrow [[BORROWED_VAL]]
// CHECK: } // end sil function 'destructure_load_copy_to_load_borrow'
sil [ossa] @destructure_load_copy_to_load_borrow : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%0 : @guaranteed $ClassLet):
%1 = ref_element_addr [immutable] %0 : $ClassLet, #ClassLet.aLetTuple
%2 = load [copy] %1 : $*(Klass, Klass)
(%3, %4) = destructure_tuple %2 : $(Klass, Klass)
%5 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
%6 = apply %5(%3) : $@convention(thin) (@guaranteed Klass) -> ()
%7 = apply %5(%4) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
destroy_value %4 : $Klass
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @single_init_allocstack : $@convention(thin) (@owned Klass) -> () {
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'single_init_allocstack'
sil [ossa] @single_init_allocstack : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = alloc_stack $Klass
store %0 to [init] %1 : $*Klass
%2 = load [copy] %1 : $*Klass
%3 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %2 : $Klass
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @multiple_init_allocstack : $@convention(thin) (@owned Klass) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'multiple_init_allocstack'
sil [ossa] @multiple_init_allocstack : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%0a = copy_value %0 : $Klass
%1 = alloc_stack $Klass
store %0 to [init] %1 : $*Klass
%2 = load [copy] %1 : $*Klass
%3 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %2 : $Klass
destroy_addr %1 : $*Klass
store %0a to [init] %1 : $*Klass
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @single_init_different_block : $@convention(thin) (@owned Klass) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'single_init_different_block'
sil [ossa] @single_init_different_block : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = alloc_stack $Klass
br bb1
bb1:
store %0 to [init] %1 : $*Klass
%2 = load [copy] %1 : $*Klass
%3 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %2 : $Klass
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @single_init_loadtake : $@convention(thin) (@owned Klass) -> () {
// CHECK: load_borrow
// CHECK: load [take]
// CHECK: } // end sil function 'single_init_loadtake'
sil [ossa] @single_init_loadtake : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = alloc_stack $Klass
store %0 to [init] %1 : $*Klass
%2 = load [copy] %1 : $*Klass
%3 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %2 : $Klass
%4 = load [take] %1 : $*Klass
destroy_value %4 : $Klass
dealloc_stack %1 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_1 : $@convention(thin) (@inout NativeObjectPair) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'inout_argument_never_written_to_1'
sil [ossa] @inout_argument_never_written_to_1 : $@convention(thin) (@inout NativeObjectPair) -> () {
bb0(%0 : $*NativeObjectPair):
%2 = load [copy] %0 : $*NativeObjectPair
(%3, %4) = destructure_struct %2 : $NativeObjectPair
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%4) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %3 : $Builtin.NativeObject
destroy_value %4 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_2 : $@convention(thin) (@inout NativeObjectPair) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'inout_argument_never_written_to_2'
sil [ossa] @inout_argument_never_written_to_2 : $@convention(thin) (@inout NativeObjectPair) -> () {
bb0(%0 : $*NativeObjectPair):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%3 = load [copy] %2 : $*Builtin.NativeObject
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %3 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// We can handle this since the store is outside of our region.
//
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_3 : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'inout_argument_never_written_to_3'
sil [ossa] @inout_argument_never_written_to_3 : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @owned $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%3 = load [copy] %2 : $*Builtin.NativeObject
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %3 : $Builtin.NativeObject
store %1 to [assign] %2 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// We cannot handle this since the store is inside of our region.
//
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_4 : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
// CHECK: load [copy]
// CHECK: } // end sil function 'inout_argument_never_written_to_4'
sil [ossa] @inout_argument_never_written_to_4 : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @owned $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%3 = load [copy] %2 : $*Builtin.NativeObject
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
store %1 to [assign] %2 : $*Builtin.NativeObject
destroy_value %3 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// We cannot handle this since the store is inside of our region.
//
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_4a : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
// CHECK: load [copy]
// CHECK: } // end sil function 'inout_argument_never_written_to_4a'
sil [ossa] @inout_argument_never_written_to_4a : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @owned $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%3 = load [copy] %2 : $*Builtin.NativeObject
store %1 to [assign] %2 : $*Builtin.NativeObject
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %3 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// We can handle this since the store is outside of our region.
//
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_5 : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'inout_argument_never_written_to_5'
sil [ossa] @inout_argument_never_written_to_5 : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @owned $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
store %1 to [assign] %2 : $*Builtin.NativeObject
%3 = load [copy] %2 : $*Builtin.NativeObject
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %3 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// We can handle this since the store is outside of our region.
//
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_6 : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'inout_argument_never_written_to_6'
sil [ossa] @inout_argument_never_written_to_6 : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @owned $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
store %1 to [assign] %2 : $*Builtin.NativeObject
%3 = load [copy] %2 : $*Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
br bb3
bb2:
br bb3
bb3:
destroy_value %3 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// We can handle this since the store is outside of our region.
//
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_6a : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'inout_argument_never_written_to_6a'
sil [ossa] @inout_argument_never_written_to_6a : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @owned $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
br bb0a
bb0a:
store %1 to [assign] %2 : $*Builtin.NativeObject
%3 = load [copy] %2 : $*Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
br bb3
bb2:
br bb3
bb3:
destroy_value %3 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// We can handle this since the store is outside of our region.
//
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_6b : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'inout_argument_never_written_to_6b'
sil [ossa] @inout_argument_never_written_to_6b : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @owned $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
store %1 to [assign] %2 : $*Builtin.NativeObject
br bb0a
bb0a:
%3 = load [copy] %2 : $*Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
br bb3
bb2:
br bb3
bb3:
destroy_value %3 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// We can handle this since the store is outside of our region.
//
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_6c : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'inout_argument_never_written_to_6c'
sil [ossa] @inout_argument_never_written_to_6c : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @owned $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%1a = copy_value %1 : $Builtin.NativeObject
store %1 to [assign] %2 : $*Builtin.NativeObject
br bb0a
bb0a:
%3 = load [copy] %2 : $*Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
br bb3
bb2:
br bb3
bb3:
destroy_value %3 : $Builtin.NativeObject
store %1a to [assign] %2 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_begin_access_1 : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'inout_argument_never_written_to_begin_access_1'
sil [ossa] @inout_argument_never_written_to_begin_access_1 : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @guaranteed $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%2a = begin_access [modify] [static] %2 : $*Builtin.NativeObject
%3 = load [copy] %2a : $*Builtin.NativeObject
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %3 : $Builtin.NativeObject
end_access %2a : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_begin_access_1a : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
// CHECK: load [copy]
// CHECK: } // end sil function 'inout_argument_never_written_to_begin_access_1a'
sil [ossa] @inout_argument_never_written_to_begin_access_1a : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @guaranteed $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%2a = begin_access [modify] [static] %2 : $*Builtin.NativeObject
%3 = load [copy] %2 : $*Builtin.NativeObject
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
end_access %2a : $*Builtin.NativeObject
destroy_value %3 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_begin_access_1b : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
// CHECK: load [copy]
// CHECK: } // end sil function 'inout_argument_never_written_to_begin_access_1b'
sil [ossa] @inout_argument_never_written_to_begin_access_1b : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @guaranteed $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%3 = load [copy] %2 : $*Builtin.NativeObject
%2a = begin_access [modify] [static] %2 : $*Builtin.NativeObject
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
end_access %2a : $*Builtin.NativeObject
destroy_value %3 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_begin_access_1c : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
// CHECK: load [copy]
// CHECK: } // end sil function 'inout_argument_never_written_to_begin_access_1c'
sil [ossa] @inout_argument_never_written_to_begin_access_1c : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @guaranteed $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%3 = load [copy] %2 : $*Builtin.NativeObject
%2a = begin_access [modify] [static] %2 : $*Builtin.NativeObject
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %3 : $Builtin.NativeObject
end_access %2a : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_begin_access_2a : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
// CHECK: load [copy]
// CHECK: } // end sil function 'inout_argument_never_written_to_begin_access_2a'
sil [ossa] @inout_argument_never_written_to_begin_access_2a : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @guaranteed $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%3 = load [copy] %2 : $*Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
%2a = begin_access [modify] [static] %2 : $*Builtin.NativeObject
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %3 : $Builtin.NativeObject
end_access %2a : $*Builtin.NativeObject
br bb3
bb2:
destroy_value %3 : $Builtin.NativeObject
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_begin_access_2b : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
// CHECK: load [copy]
// CHECK: } // end sil function 'inout_argument_never_written_to_begin_access_2b'
sil [ossa] @inout_argument_never_written_to_begin_access_2b : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @guaranteed $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%3 = load [copy] %2 : $*Builtin.NativeObject
br bb0a
bb0a:
%2a = begin_access [modify] [static] %2 : $*Builtin.NativeObject
cond_br undef, bb1, bb2
bb1:
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %3 : $Builtin.NativeObject
br bb3
bb2:
destroy_value %3 : $Builtin.NativeObject
br bb3
bb3:
end_access %2a : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_begin_access_2c : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
// CHECK: load_borrow
// CHECK: bb2:
// CHECK: apply
// CHECK-NEXT: end_borrow
// CHECK: bb3:
// CHECK-NEXT: end_borrow
// CHECK: } // end sil function 'inout_argument_never_written_to_begin_access_2c'
sil [ossa] @inout_argument_never_written_to_begin_access_2c : $@convention(thin) (@inout NativeObjectPair, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $*NativeObjectPair, %1 : @guaranteed $Builtin.NativeObject):
%2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1
%2a = begin_access [modify] [static] %2 : $*Builtin.NativeObject
br bb1
bb1:
%3 = load [copy] %2a : $*Builtin.NativeObject
cond_br undef, bb2, bb3
bb2:
%5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %3 : $Builtin.NativeObject
br bb4
bb3:
destroy_value %3 : $Builtin.NativeObject
br bb4
bb4:
end_access %2a : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @switch_enum_test_loadcopy_no_default : $@convention(thin) (@owned FakeOptional<Builtin.NativeObject>) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'switch_enum_test_loadcopy_no_default'
sil [ossa] @switch_enum_test_loadcopy_no_default : $@convention(thin) (@owned FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : @owned $FakeOptional<Builtin.NativeObject>):
%0a = alloc_stack $FakeOptional<Builtin.NativeObject>
store %0 to [init] %0a : $*FakeOptional<Builtin.NativeObject>
%1 = load [copy] %0a : $*FakeOptional<Builtin.NativeObject>
switch_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%2 : @owned $Builtin.NativeObject):
destroy_value %2 : $Builtin.NativeObject
br bb3
bb2:
br bb3
bb3:
destroy_addr %0a : $*FakeOptional<Builtin.NativeObject>
dealloc_stack %0a : $*FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @switch_enum_multi_payload :
// CHECK: %1 = load_borrow %0
// CHECK: bb1(%3 : @guaranteed $Klass):
// CHECK-NEXT: end_borrow %1
// CHECK: bb2(%6 : $MyInt):
// CHECK-NEXT: end_borrow %1
// CHECK: } // end sil function 'switch_enum_multi_payload'
sil [ossa] @switch_enum_multi_payload : $@convention(thin) (@inout MultiPayload) -> () {
bb0(%0 : $*MultiPayload):
%1 = load [copy] %0 : $*MultiPayload
switch_enum %1 : $MultiPayload, case #MultiPayload.a!enumelt: bb1, case #MultiPayload.b!enumelt: bb2
bb1(%3 : @owned $Klass):
destroy_value %3 : $Klass
br bb3
bb2(%6 : $MyInt):
br bb3
bb3:
%8 = tuple()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @switch_enum_test_loadcopy_with_default : $@convention(thin) (@owned FakeOptional<Builtin.NativeObject>) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'switch_enum_test_loadcopy_with_default'
sil [ossa] @switch_enum_test_loadcopy_with_default : $@convention(thin) (@owned FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : @owned $FakeOptional<Builtin.NativeObject>):
%0a = alloc_stack $FakeOptional<Builtin.NativeObject>
store %0 to [init] %0a : $*FakeOptional<Builtin.NativeObject>
%1 = load [copy] %0a : $*FakeOptional<Builtin.NativeObject>
switch_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb1, default bb2
bb1(%2 : @owned $Builtin.NativeObject):
destroy_value %2 : $Builtin.NativeObject
br bb3
bb2:
br bb3
bb3:
destroy_addr %0a : $*FakeOptional<Builtin.NativeObject>
dealloc_stack %0a : $*FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @switch_enum_test_loadcopy_with_leaked_enum_case : $@convention(thin) (@owned FakeOptional<Builtin.NativeObject>) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'switch_enum_test_loadcopy_with_leaked_enum_case'
sil [ossa] @switch_enum_test_loadcopy_with_leaked_enum_case : $@convention(thin) (@owned FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : @owned $FakeOptional<Builtin.NativeObject>):
%0a = alloc_stack $FakeOptional<Builtin.NativeObject>
store %0 to [init] %0a : $*FakeOptional<Builtin.NativeObject>
%1 = load [copy] %0a : $*FakeOptional<Builtin.NativeObject>
switch_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%2 : @owned $Builtin.NativeObject):
destroy_value [dead_end] %2 : $Builtin.NativeObject
unreachable
bb2:
br bb3
bb3:
destroy_addr %0a : $*FakeOptional<Builtin.NativeObject>
dealloc_stack %0a : $*FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @convert_load_copy_to_load_borrow_despite_switch_enum_functionarg : $@convention(thin) (@guaranteed FakeOptional<ClassLet>) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'convert_load_copy_to_load_borrow_despite_switch_enum_functionarg'
sil [ossa] @convert_load_copy_to_load_borrow_despite_switch_enum_functionarg : $@convention(thin) (@guaranteed FakeOptional<ClassLet>) -> () {
bb0(%0 : @guaranteed $FakeOptional<ClassLet>):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
switch_enum %0 : $FakeOptional<ClassLet>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%1 : @guaranteed $ClassLet):
%2 = ref_element_addr [immutable] %1 : $ClassLet, #ClassLet.aLet
%3 = load [copy] %2 : $*Klass
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
br bb3
bb2:
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @convert_load_copy_to_load_borrow_despite_switch_enum_beginborrow : $@convention(thin) (@owned FakeOptional<ClassLet>) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'convert_load_copy_to_load_borrow_despite_switch_enum_beginborrow'
sil [ossa] @convert_load_copy_to_load_borrow_despite_switch_enum_beginborrow : $@convention(thin) (@owned FakeOptional<ClassLet>) -> () {
bb0(%0 : @owned $FakeOptional<ClassLet>):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%0a = begin_borrow %0 : $FakeOptional<ClassLet>
switch_enum %0a : $FakeOptional<ClassLet>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%1 : @guaranteed $ClassLet):
%2 = ref_element_addr [immutable] %1 : $ClassLet, #ClassLet.aLet
%3 = load [copy] %2 : $*Klass
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
br bb3
bb2:
br bb3
bb3:
end_borrow %0a : $FakeOptional<ClassLet>
destroy_value %0 : $FakeOptional<ClassLet>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @convert_load_copy_to_load_borrow_despite_switch_enum_loadborrow : $@convention(thin) (@in_guaranteed FakeOptional<ClassLet>) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'convert_load_copy_to_load_borrow_despite_switch_enum_loadborrow'
sil [ossa] @convert_load_copy_to_load_borrow_despite_switch_enum_loadborrow : $@convention(thin) (@in_guaranteed FakeOptional<ClassLet>) -> () {
bb0(%0 : $*FakeOptional<ClassLet>):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
%0a = load_borrow %0 : $*FakeOptional<ClassLet>
switch_enum %0a : $FakeOptional<ClassLet>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%1 : @guaranteed $ClassLet):
%2 = ref_element_addr [immutable] %1 : $ClassLet, #ClassLet.aLet
%3 = load [copy] %2 : $*Klass
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
br bb3
bb2:
br bb3
bb3:
end_borrow %0a : $FakeOptional<ClassLet>
%9999 = tuple()
return %9999 : $()
}
// TODO: We can support this in a little bit once the rest of SemanticARCOpts is
// guaranteed to be safe with guaranteed phis.
//
// CHECK-LABEL: sil [ossa] @convert_load_copy_to_load_borrow_despite_switch_enum_guaranteedphi_1 : $@convention(thin) (@owned FakeOptional<ClassLet>) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'convert_load_copy_to_load_borrow_despite_switch_enum_guaranteedphi_1'
sil [ossa] @convert_load_copy_to_load_borrow_despite_switch_enum_guaranteedphi_1 : $@convention(thin) (@owned FakeOptional<ClassLet>) -> () {
bb0(%0 : @owned $FakeOptional<ClassLet>):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
cond_br undef, bb0a, bb0b
bb0a:
%0a = begin_borrow %0 : $FakeOptional<ClassLet>
br bb0c(%0a : $FakeOptional<ClassLet>)
bb0b:
%0b = begin_borrow %0 : $FakeOptional<ClassLet>
br bb0c(%0b : $FakeOptional<ClassLet>)
bb0c(%0c : @guaranteed $FakeOptional<ClassLet>):
%0d = borrowed %0c : $FakeOptional<ClassLet> from (%0: $FakeOptional<ClassLet>)
switch_enum %0d : $FakeOptional<ClassLet>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%1 : @guaranteed $ClassLet):
%2 = ref_element_addr [immutable] %1 : $ClassLet, #ClassLet.aLet
%3 = load [copy] %2 : $*Klass
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
br bb3
bb2:
br bb3
bb3:
end_borrow %0d : $FakeOptional<ClassLet>
destroy_value %0 : $FakeOptional<ClassLet>
%9999 = tuple()
return %9999 : $()
}
// Make sure that if begin_borrow has a consuming end scope use, we can still
// eliminate load [copy].
//
// CHECK-LABEL: sil [ossa] @convert_load_copy_to_load_borrow_despite_switch_enum_guaranteedphi_2 : $@convention(thin) (@owned FakeOptional<ClassLet>) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'convert_load_copy_to_load_borrow_despite_switch_enum_guaranteedphi_2'
sil [ossa] @convert_load_copy_to_load_borrow_despite_switch_enum_guaranteedphi_2 : $@convention(thin) (@owned FakeOptional<ClassLet>) -> () {
bb0(%0 : @owned $FakeOptional<ClassLet>):
%f = function_ref @black_hole : $@convention(thin) (@guaranteed Klass) -> ()
cond_br undef, bb1, bb2
bb1:
%0a = begin_borrow %0 : $FakeOptional<ClassLet>
br bb3(%0a : $FakeOptional<ClassLet>)
bb2:
%0b = begin_borrow %0 : $FakeOptional<ClassLet>
%0b2 = unchecked_enum_data %0b : $FakeOptional<ClassLet>, #FakeOptional.some!enumelt
%2 = ref_element_addr [immutable] %0b2 : $ClassLet, #ClassLet.aLet
%3 = load [copy] %2 : $*Klass
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
br bb3(%0b : $FakeOptional<ClassLet>)
bb3(%0c : @guaranteed $FakeOptional<ClassLet>):
%0d = borrowed %0c : $FakeOptional<ClassLet> from (%0: $FakeOptional<ClassLet>)
%f2 = function_ref @guaranteed_fakeoptional_classlet_user : $@convention(thin) (@guaranteed FakeOptional<ClassLet>) -> ()
apply %f2(%0d) : $@convention(thin) (@guaranteed FakeOptional<ClassLet>) -> ()
end_borrow %0d : $FakeOptional<ClassLet>
destroy_value %0 : $FakeOptional<ClassLet>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_read_access : $@convention(thin) (@guaranteed ClassLet) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_read_access'
sil [ossa] @loadcopy_to_loadborrow_from_read_access : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%0 : @guaranteed $ClassLet):
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
%2 = begin_access [read] [dynamic] %1 : $*Klass
%3 = load [copy] %2 : $*Klass
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
end_access %2 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_without_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_without_writes'
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_without_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%0 : @guaranteed $ClassLet):
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
%2 = begin_access [modify] [dynamic] %1 : $*Klass
%3 = load [copy] %2 : $*Klass
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
end_access %2 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
// CHECK-NOT: load [copy]
// CHECK: load_borrow
// CHECK-NOT: load [copy]
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes'
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%0 : @guaranteed $ClassLet):
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
%2 = begin_access [modify] [dynamic] %1 : $*Klass
%3 = load [copy] %2 : $*Klass
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
destroy_addr %2 : $*Klass
end_access %2 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// We will never be able to handle this unless we can hoist the copy before the
// destroy_addr. Once we have begin_borrows around all interior_pointers, we can
// handle this version.
//
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_2 : $@convention(thin) (@guaranteed ClassLet) -> () {
// CHECK-NOT: load_borrow
// CHECK: load [copy]
// CHECK-NOT: load_borrow
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes_2'
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_2 : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%0 : @guaranteed $ClassLet):
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
%2 = begin_access [modify] [dynamic] %1 : $*Klass
%3 = load [copy] %2 : $*Klass
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_addr %2 : $*Klass
destroy_value %3 : $Klass
end_access %2 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// We will never be able to handle this since we can't hoist the destroy_value
// before the guaranteed_klass_user.
//
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_3 : $@convention(thin) (@guaranteed ClassLet) -> () {
// CHECK-NOT: load_borrow
// CHECK: load [copy]
// CHECK-NOT: load_borrow
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes_3'
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_3 : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%0 : @guaranteed $ClassLet):
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
%2 = begin_access [modify] [dynamic] %1 : $*Klass
%3 = load [copy] %2 : $*Klass
destroy_addr %2 : $*Klass
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
end_access %2 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// We will never be able to handle this since the end_access is before the use
// of %3, so we can not form a long enough load_borrow.
//
// CHECK-LABEL: sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_4 : $@convention(thin) (@guaranteed ClassLet) -> () {
// CHECK-NOT: load_borrow
// CHECK: load [copy]
// CHECK-NOT: load_borrow
// CHECK: } // end sil function 'loadcopy_to_loadborrow_from_mut_access_with_writes_4'
sil [ossa] @loadcopy_to_loadborrow_from_mut_access_with_writes_4 : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%0 : @guaranteed $ClassLet):
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
%2 = begin_access [modify] [dynamic] %1 : $*Klass
%3 = load [copy] %2 : $*Klass
end_access %2 : $*Klass
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%3) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %3 : $Klass
%9999 = tuple()
return %9999 : $()
}
// Make sure that we do not promote the load [copy] to a load_borrow since it
// has a use outside of the access scope.
//
// CHECK-LABEL: sil [ossa] @deadEndBlockDoNotPromote : $@convention(method) (@guaranteed ClassLet) -> () {
// CHECK: load_borrow
// CHECK: load [copy]
// CHECK: } // end sil function 'deadEndBlockDoNotPromote'
sil [ossa] @deadEndBlockDoNotPromote : $@convention(method) (@guaranteed ClassLet) -> () {
bb0(%0 : @guaranteed $ClassLet):
%4 = ref_element_addr %0 : $ClassLet, #ClassLet.anotherLet
%5 = load [copy] %4 : $*ClassLet
%6 = begin_borrow %5 : $ClassLet
%7 = ref_element_addr %6 : $ClassLet, #ClassLet.anOptionalLet
%8 = begin_access [read] [dynamic] %7 : $*FakeOptional<Klass>
%9 = load [copy] %8 : $*FakeOptional<Klass>
end_access %8 : $*FakeOptional<Klass>
end_borrow %6 : $ClassLet
destroy_value %5 : $ClassLet
switch_enum %9 : $FakeOptional<Klass>, case #FakeOptional.none!enumelt: bb1, case #FakeOptional.some!enumelt: bb2
bb1:
%107 = tuple ()
return %107 : $()
bb2(%39 : @owned $Klass):
destroy_value [dead_end] %39 : $Klass
unreachable
}
// CHECK-LABEL: sil [ossa] @destructure_with_differing_lifetimes_inout_1 : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> () {
// CHECK-NOT: load_borrow
// CHECK: } // end sil function 'destructure_with_differing_lifetimes_inout_1'
sil [ossa] @destructure_with_differing_lifetimes_inout_1 : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> () {
bb0(%0 : $*FakeOptionalNativeObjectPairPair):
%0a = struct_element_addr %0 : $*FakeOptionalNativeObjectPairPair, #FakeOptionalNativeObjectPairPair.pair1
%1 = load [copy] %0a : $*FakeOptional<NativeObjectPair>
switch_enum %1 : $FakeOptional<NativeObjectPair>, case #FakeOptional.some!enumelt: bb1, default bb2
bb2:
br bbEnd
bb1(%3 : @owned $NativeObjectPair):
(%3a, %3b) = destructure_struct %3 : $NativeObjectPair
cond_br undef, bb1a, bb1b
bb1a:
destroy_value %3a : $Builtin.NativeObject
destroy_value %3b : $Builtin.NativeObject
br bbEnd
bb1b:
destroy_value %3a : $Builtin.NativeObject
%f = function_ref @inout_user2 : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> ()
apply %f(%0) : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> ()
destroy_value %3b : $Builtin.NativeObject
br bbEnd
bbEnd:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @destructure_with_differing_lifetimes_inout_2 : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> () {
// CHECK-NOT: load_borrow
// CHECK: } // end sil function 'destructure_with_differing_lifetimes_inout_2'
sil [ossa] @destructure_with_differing_lifetimes_inout_2 : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> () {
bb0(%0 : $*FakeOptionalNativeObjectPairPair):
%0a = struct_element_addr %0 : $*FakeOptionalNativeObjectPairPair, #FakeOptionalNativeObjectPairPair.pair1
%1 = load [copy] %0a : $*FakeOptional<NativeObjectPair>
switch_enum %1 : $FakeOptional<NativeObjectPair>, case #FakeOptional.some!enumelt: bb1, default bb2
bb2:
br bbEnd
bb1(%3 : @owned $NativeObjectPair):
(%3a, %3b) = destructure_struct %3 : $NativeObjectPair
cond_br undef, bb1a, bb1b
bb1a:
destroy_value %3a : $Builtin.NativeObject
br bb1ab
bb1ab:
destroy_value %3b : $Builtin.NativeObject
br bbEnd
bb1b:
destroy_value %3a : $Builtin.NativeObject
%f = function_ref @inout_user2 : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> ()
apply %f(%0) : $@convention(thin) (@inout FakeOptionalNativeObjectPairPair) -> ()
cond_br undef, bb1ba, bb1bb
bb1ba:
br bb1baEnd
bb1bb:
br bb1baEnd
bb1baEnd:
destroy_value %3b : $Builtin.NativeObject
br bbEnd
bbEnd:
%9999 = tuple()
return %9999 : $()
}
// Just make sure that we do not crash on this code
//
// CHECK-LABEL: sil [ossa] @improper_dead_end_block_crasher_test : $@convention(thin) (Builtin.RawPointer) -> () {
// CHECK: } // end sil function 'improper_dead_end_block_crasher_test'
sil [ossa] @improper_dead_end_block_crasher_test : $@convention(thin) (Builtin.RawPointer) -> () {
bb0(%0 : $Builtin.RawPointer):
%1 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*Klass
%2 = load_borrow %1 : $*Klass
%3 = ref_element_addr %2 : $Klass, #Klass.baseLet
%4 = load [copy] %3 : $*Klass
%f = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%4) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %4 : $Klass
cond_br undef, bb1, bb2
bb1:
end_borrow %2
unreachable
bb2:
end_borrow %2
unreachable
}
// Make sure that we ignore type dependent operands when walking through the use
// list to determine if we are writing to a piece of memory.
protocol HasClassProperty {
var c: Klass { get set }
}
struct HasClassPropertyWrapper {
var h: HasClassProperty
var otherProperty: Builtin.NativeObject
}
sil @use_hasclassproperty : $@convention(method) <τ_0_0 where τ_0_0 : HasClassProperty> (@inout τ_0_0) -> (@owned Builtin.NativeObject, @error Error)
// Make sure we don't crash on this code. We used to crash by not ignoring the
// type dependent operand usage of %2.
//
// CHECK-LABEL: sil [ossa] @use_class_property_wrapper : $@convention(thin) (@inout HasClassPropertyWrapper) -> () {
// CHECK: load_borrow
// CHECK: } // end sil function 'use_class_property_wrapper'
sil [ossa] @use_class_property_wrapper : $@convention(thin) (@inout HasClassPropertyWrapper) -> () {
bb0(%0 : $*HasClassPropertyWrapper):
%1 = struct_element_addr %0 : $*HasClassPropertyWrapper, #HasClassPropertyWrapper.h
%2 = open_existential_addr mutable_access %1 : $*HasClassProperty to $*@opened("85AB1D00-DF62-11EB-A413-ACDE48001122", HasClassProperty) Self
%3 = function_ref @use_hasclassproperty : $@convention(method) <τ_0_0 where τ_0_0 : HasClassProperty> (@inout τ_0_0) -> (@owned Builtin.NativeObject, @error Error)
%4 = struct_element_addr %0 : $*HasClassPropertyWrapper, #HasClassPropertyWrapper.otherProperty
%5 = load [copy] %4 : $*Builtin.NativeObject
%f2 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %f2(%5) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
try_apply %3<@opened("85AB1D00-DF62-11EB-A413-ACDE48001122", HasClassProperty) Self>(%2) : $@convention(method) <τ_0_0 where τ_0_0 : HasClassProperty> (@inout τ_0_0) -> (@owned Builtin.NativeObject, @error Error), normal bb1, error bb2
bb1(%str : @owned $Builtin.NativeObject):
destroy_value %str : $Builtin.NativeObject
destroy_value %5 : $Builtin.NativeObject
br bb3
bb2(%error : @owned $Error):
destroy_value %error : $Error
destroy_value %5 : $Builtin.NativeObject
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// Make sure we don't crash on this code. We used to crash for @out args on the access path to the load
sil [ossa] @test_opt_out_arg : $@convention(thin)(@in NonTrivialStruct) -> (@out NonTrivialStruct) {
bb0(%0 : $*NonTrivialStruct, %1 : $*NonTrivialStruct):
copy_addr %1 to [init] %0 : $*NonTrivialStruct
%ele = struct_element_addr %0 : $*NonTrivialStruct, #NonTrivialStruct.val
%ld = load [copy] %ele : $*Klass
destroy_value %ld : $Klass
destroy_addr %1 : $*NonTrivialStruct
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @switch_enum_test :
// CHECK: load [copy]
// CHECK: } // end sil function 'switch_enum_test'
sil [ossa] @switch_enum_test : $@convention(thin) (@inout StructWithEnum) -> () {
bb0(%0 : $*StructWithEnum):
%1 = struct_element_addr %0 : $*StructWithEnum, #StructWithEnum.val
%2 = struct_element_addr %0 : $*StructWithEnum, #StructWithEnum.val
%3 = load [copy] %2 : $*EnumA
destroy_addr %1 : $*EnumA
switch_enum %3 : $EnumA, case #EnumA.sometrivial!enumelt: bb1, case #EnumA.somenontrivial!enumelt:bb2, case #EnumA.none!enumelt: bb3
bb1(%6 : $TrivialStruct):
%f = function_ref @get_enum : $@convention(thin) () -> @owned EnumA
%r = apply %f() : $@convention(thin) () -> @owned EnumA
%ele = struct_element_addr %0 : $*StructWithEnum, #StructWithEnum.val
store %r to [init] %ele :$*EnumA
%9999 = tuple()
return %9999 : $()
bb2(%7 : @owned $NonTrivialStruct):
destroy_value [dead_end] %7 : $NonTrivialStruct
unreachable
bb3:
unreachable
}
// CHECK-LABEL: sil [ossa] @promote_moved_from_load_copy : {{.*}} {
// CHECK: [[ADDR:%[^,]+]] = alloc_stack $C
// CHECK: [[GET_C:%[^,]+]] = function_ref @getC
// CHECK: [[STORED:%[^,]+]] = apply [[GET_C]]()
// CHECK: store [[STORED]] to [init] [[ADDR]]
// CHECK: [[LOADED:%[^,]+]] = load_borrow [[ADDR]]
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [lexical] [[LOADED]]
// CHECK: end_borrow [[LIFETIME]]
// CHECK: end_borrow [[LOADED]]
// CHECK: destroy_addr [[ADDR]]
// CHECK: dealloc_stack [[ADDR]]
// CHECK-LABEL: } // end sil function 'promote_moved_from_load_copy'
sil [ossa] @promote_moved_from_load_copy : $@convention(thin) () -> () {
bb0:
%addr = alloc_stack $C
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%stored = apply %getC() : $@convention(thin) () -> (@owned C)
store %stored to [init] %addr : $*C
%loaded = load [copy] %addr : $*C
debug_value %loaded : $C, let, name "x"
%lifetime = move_value [lexical] %loaded : $C
destroy_value %lifetime : $C
destroy_addr %addr : $*C
dealloc_stack %addr : $*C
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @dont_promote_moved_from_load_copy_with_destroy_addr_above_range : {{.*}} {
// CHECK: load [copy]
// CHECK-LABEL: } // end sil function 'dont_promote_moved_from_load_copy_with_destroy_addr_above_range'
sil [ossa] @dont_promote_moved_from_load_copy_with_destroy_addr_above_range : $@convention(thin) () -> () {
bb0:
%addr = alloc_stack $C
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%stored = apply %getC() : $@convention(thin) () -> (@owned C)
store %stored to [init] %addr : $*C
%loaded = load [copy] %addr : $*C
destroy_addr %addr : $*C
%lifetime = move_value [lexical] %loaded : $C
destroy_value %lifetime : $C
dealloc_stack %addr : $*C
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @load_take_between_load_copy_and_move_inout : {{.*}} {
// CHECK: load [copy]
// CHECK-LABEL: } // end sil function 'load_take_between_load_copy_and_move_inout'
sil [ossa] @load_take_between_load_copy_and_move_inout : $@convention(thin) (@inout C) -> () {
entry(%addr : $*C):
%v1 = load [copy] %addr : $*C
%v2 = load [take] %addr : $*C
%m1 = move_value [lexical] %v1 : $C
destroy_value %m1 : $C
store %v2 to [init] %addr : $*C
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @load_take_between_load_copy_and_move_stack : {{.*}} {
// CHECK: load [copy]
// CHECK-LABEL: } // end sil function 'load_take_between_load_copy_and_move_stack'
sil [ossa] @load_take_between_load_copy_and_move_stack : $@convention(thin) () -> () {
entry:
%addr = alloc_stack $C
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%stored = apply %getC() : $@convention(thin) () -> (@owned C)
store %stored to [init] %addr : $*C
%v1 = load [copy] %addr : $*C
%v2 = load [take] %addr : $*C
%m1 = move_value [lexical] %v1 : $C
destroy_value %m1 : $C
store %v2 to [init] %addr : $*C
destroy_addr %addr : $*C
dealloc_stack %addr : $*C
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @store_borrow_unaliased :
// CHECK: load_borrow
// CHECK-LABEL: } // end sil function 'store_borrow_unaliased'
sil [ossa] @store_borrow_unaliased : $@convention(thin) (@guaranteed C, @in_guaranteed C) -> () {
bb0(%0 : @guaranteed $C, %1 : $*C):
%2 = alloc_stack $C
%3 = store_borrow %0 to %2 : $*C
%4 = load [copy] %1 : $*C
end_borrow %3 : $*C
destroy_value %4 : $C
dealloc_stack %2 : $*C
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @store_borrow_aliased :
// CHECK: load [copy]
// CHECK-LABEL: } // end sil function 'store_borrow_aliased'
sil [ossa] @store_borrow_aliased : $@convention(thin) (@guaranteed C) -> () {
bb0(%0 : @guaranteed $C):
%1 = alloc_stack $C
%2 = store_borrow %0 to %1 : $*C
%3 = load [copy] %2 : $*C
end_borrow %2 : $*C
destroy_value %3 : $C
dealloc_stack %1 : $*C
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @load_extends_borrow_scope :
// CHECK: load [copy]
// CHECK-LABEL: } // end sil function 'load_extends_borrow_scope'
sil [ossa] @load_extends_borrow_scope : $@convention(thin) (@owned Klass) -> @owned Klass {
bb0(%0 : @owned $Klass):
%1 = begin_borrow %0 : $Klass
%2 = ref_element_addr %1 : $Klass, #Klass.base
%3 = load [copy] %2 : $*Klass
end_borrow %1 : $Klass
destroy_value %3 : $Klass
return %0 : $Klass
}
// CHECK-LABEL: sil [ossa] @write_in_unreachable_block :
// CHECK: load [copy]
// CHECK-LABEL: } // end sil function 'write_in_unreachable_block'
sil [ossa] @write_in_unreachable_block : $@convention(thin) (@inout Klass) -> () {
bb0(%0 : $*Klass):
%1 = load [copy] %0 : $*Klass
cond_br undef, bb1, bb2
bb1:
%3 = begin_borrow %1 : $Klass
%4 = ref_element_addr %3 : $Klass, #Klass.baseLet
destroy_addr %0 : $*Klass
%5 = load [copy] %4 : $*Klass
end_borrow %3 : $Klass
destroy_value [dead_end] %1 : $Klass
destroy_value [dead_end] %5 : $Klass
unreachable
bb2:
destroy_value %1 : $Klass
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @no_write_in_unreachable_block :
// CHECK: load_borrow
// CHECK-LABEL: } // end sil function 'no_write_in_unreachable_block'
sil [ossa] @no_write_in_unreachable_block : $@convention(thin) (@inout Klass) -> () {
bb0(%0 : $*Klass):
%1 = load [copy] %0 : $*Klass
cond_br undef, bb1, bb2
bb1:
%3 = begin_borrow %1 : $Klass
%4 = ref_element_addr %3 : $Klass, #Klass.baseLet
%5 = load [copy] %4 : $*Klass
destroy_value [dead_end] %5
end_borrow %3
destroy_value [dead_end] %1
unreachable
bb2:
destroy_value %1 : $Klass
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend : $@convention(thin) (@in_guaranteed EnumWithIndirectCase) -> () {
// CHECK: bb0
// CHECK-NEXT: load_borrow
// CHECK: } // end sil function 'enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend'
sil [ossa] @enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend : $@convention(thin) (@in_guaranteed EnumWithIndirectCase) -> () {
bb0(%0 : $*EnumWithIndirectCase):
%1 = load [copy] %0 : $*EnumWithIndirectCase
switch_enum %1 : $EnumWithIndirectCase, case #EnumWithIndirectCase.first!enumelt: bb1, case #EnumWithIndirectCase.second!enumelt: bb2
bb1:
%9999 = tuple()
return %9999 : $()
// NOTE: Eventually this will need to be changed when project_box has to be
// guarded by begin_borrow.
bb2(%2 : @owned ${ var Builtin.NativeObject }):
%3 = project_box %2 : ${ var Builtin.NativeObject }, 0
%4 = load [copy] %3 : $*Builtin.NativeObject
%user = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %user(%4) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value [dead_end] %4
destroy_value [dead_end] %2
unreachable
}
// CHECK-LABEL: sil [ossa] @enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend_2 : $@convention(thin) (@in_guaranteed EnumWithIndirectCase) -> () {
// CHECK: bb0
// CHECK-NEXT: load_borrow
// CHECK: } // end sil function 'enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend_2'
sil [ossa] @enum_with_indirect_case_projectbox_loadcopy_to_loadborrow_deadend_2 : $@convention(thin) (@in_guaranteed EnumWithIndirectCase) -> () {
bb0(%0 : $*EnumWithIndirectCase):
%1 = load [copy] %0 : $*EnumWithIndirectCase
switch_enum %1 : $EnumWithIndirectCase, case #EnumWithIndirectCase.first!enumelt: bb1, case #EnumWithIndirectCase.second!enumelt: bb2
bb1:
%9999 = tuple()
return %9999 : $()
// NOTE: Eventually this will need to be changed when project_box has to be
// guarded by begin_borrow.
bb2(%2 : @owned ${ var Builtin.NativeObject }):
%3 = project_box %2 : ${ var Builtin.NativeObject }, 0
%4 = load [copy] %3 : $*Builtin.NativeObject
%user = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
apply %user(%4) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %2 : ${ var Builtin.NativeObject }
destroy_value [dead_end] %4
unreachable
}
// CHECK-LABEL: sil [ossa] @use_interior_pointer_in_deadend_block :
// CHECK: load [copy]
// CHECK: } // end sil function 'use_interior_pointer_in_deadend_block'
sil [ossa] @use_interior_pointer_in_deadend_block : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%0 : @guaranteed $ClassLet):
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
%2 = load [copy] %1 : $*Klass
%3 = begin_borrow %2 : $Klass
%4 = ref_element_addr %3 : $Klass, #Klass.base
cond_br undef, bb1, bb2
bb1:
%f = function_ref @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
apply %f(%4) : $@convention(thin) (@in_guaranteed Klass) -> ()
end_borrow %3
destroy_value [dead_end] %2
unreachable
bb2:
end_borrow %3 : $Klass
destroy_value %2 : $Klass
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @use_interior_pointer_not_in_deadend_block :
// TODO: not optimized right now
// xCHECK: load_borrow
// CHECK: } // end sil function 'use_interior_pointer_not_in_deadend_block'
sil [ossa] @use_interior_pointer_not_in_deadend_block : $@convention(thin) (@guaranteed ClassLet) -> () {
bb0(%0 : @guaranteed $ClassLet):
%1 = ref_element_addr %0 : $ClassLet, #ClassLet.aVar
%2 = load [copy] %1 : $*Klass
%3 = begin_borrow %2 : $Klass
%4 = ref_element_addr %3 : $Klass, #Klass.base
cond_br undef, bb1, bb2
bb1:
%f = function_ref @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
apply %f(%1) : $@convention(thin) (@in_guaranteed Klass) -> ()
end_borrow %3
destroy_value [dead_end] %2
unreachable
bb2:
end_borrow %3 : $Klass
destroy_value %2 : $Klass
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @multiple_destroys_and_switch_enum :
// CHECK: bb0(%0 : $*OptionalAndClass):
// CHECK-NEXT: %1 = load_borrow
// CHECK-NOT: end_borrow
// CHECK: bb3:
// CHECK-NEXT: end_borrow %1
// CHECK: } // end sil function 'multiple_destroys_and_switch_enum'
sil [ossa] @multiple_destroys_and_switch_enum : $@convention(thin) (@in_guaranteed OptionalAndClass) -> () {
bb0(%0 : $*OptionalAndClass):
%1 = load [copy] %0 : $*OptionalAndClass
(%2, %3) = destructure_struct %1 : $OptionalAndClass
switch_enum %2 : $FakeOptional<C>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%5 : @owned $C):
destroy_value %5 : $C
br bb3
bb2:
br bb3
bb3:
destroy_value %3 : $C
%10 = tuple ()
return %10 : $()
}
// CHECK-LABEL: sil [ossa] @load_copy_with_borrow_users :
// CHECK: %1 = load_borrow
// CHECK: end_borrow %1
// CHECK-NEXT: tuple ()
// CHECK: } // end sil function 'load_copy_with_borrow_users'
sil [ossa] @load_copy_with_borrow_users : $@convention(thin) (@inout Klass) -> () {
bb0(%0 : $*Klass):
%ld1 = load [copy] %0 : $*Klass
%b = begin_borrow %ld1 : $Klass
%c = copy_value %b : $Klass
%stk = alloc_stack $Klass
store %c to [init] %stk : $*Klass
%f = function_ref @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
apply %f(%stk) : $@convention(thin) (@in_guaranteed Klass) -> ()
%ld2 = load [take] %stk : $*Klass
destroy_value %ld2 : $Klass
dealloc_stack %stk : $*Klass
end_borrow %b : $Klass
destroy_value %ld1 : $Klass
%t = tuple ()
return %t : $()
}
// CHECK-LABEL: sil [ossa] @pa_user :
// CHECK: %1 = load_borrow
// CHECK: %2 = begin_borrow %1
// CHECK: end_borrow %2
// CHECK-NEXT: end_borrow %1
// CHECK: } // end sil function 'pa_user'
sil [ossa] @pa_user : $@convention(thin) (@inout Klass) -> () {
bb0(%0 : $*Klass):
%ld1 = load [copy] %0 : $*Klass
%b = begin_borrow %ld1 : $Klass
%c = copy_value %b : $Klass
%stk = alloc_stack $Klass
store %c to [init] %stk : $*Klass
%f = function_ref @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
apply %f(%stk) : $@convention(thin) (@in_guaranteed Klass) -> ()
%ld2 = load [take] %stk : $*Klass
destroy_value %ld2 : $Klass
dealloc_stack %stk : $*Klass
end_borrow %b : $Klass
destroy_value %ld1 : $Klass
%closure = function_ref @closure : $@convention(thin) (@inout_aliasable Klass) -> ()
%pa = partial_apply [callee_guaranteed] [on_stack] %closure(%0) : $@convention(thin) (@inout_aliasable Klass) -> ()
destroy_value %pa : $@noescape @callee_guaranteed () -> ()
%t = tuple ()
return %t : $()
}
// CHECK-LABEL: sil [ossa] @not_refcount_preserving_cast :
// CHECK: %1 = load [copy] %0
// CHECK: } // end sil function 'not_refcount_preserving_cast'
sil [ossa] @not_refcount_preserving_cast : $@convention(thin) (@inout AnyObject) -> () {
bb0(%0 : $*AnyObject):
%1 = load [copy] %0 : $*AnyObject
checked_cast_br AnyObject in %1 : $AnyObject to Klass, bb1, bb2
bb1(%3 : @owned $Klass):
destroy_value %3 : $Klass
br bb3
bb2(%6 : @owned $AnyObject):
destroy_value %6 : $AnyObject
br bb3
bb3:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @dead_end_loop :
// CHECK: %1 = load [copy] %0
// CHECK: } // end sil function 'dead_end_loop'
sil [ossa] @dead_end_loop : $@convention(thin) (@inout Klass) -> () {
bb0(%0 : $*Klass):
%1 = load [copy] %0 : $*Klass
cond_br undef, bb5, bb1
bb1:
br bb2
bb2:
%4 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %4(%1) : $@convention(thin) (@guaranteed Klass) -> ()
%6 = function_ref @getKlass : $@convention(thin) () -> @owned Klass
%7 = apply %6() : $@convention(thin) () -> @owned Klass
store %7 to [assign] %0 : $*Klass
cond_br undef, bb4, bb3
bb3:
br bb2
bb4:
destroy_value [dead_end] %1
unreachable
bb5:
destroy_value %1 : $Klass
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @blocked_by_end_borrow :
// CHECK: %7 = load [copy] %6
// CHECK: } // end sil function 'blocked_by_end_borrow'
sil [ossa] @blocked_by_end_borrow : $@convention(thin) (@guaranteed Container) -> () {
bb0(%0 : @guaranteed $Container):
%1 = function_ref @useC : $@convention(thin) (@guaranteed C) -> ()
%2 = ref_element_addr [immutable] %0 : $Container, #Container.b
%3 = load_borrow %2 : $*Base
%4 = unconditional_checked_cast %3 : $Base to Derived
%5 = upcast %4 : $Derived to $Base
%6 = ref_element_addr [immutable] %5 : $Base, #Base.c
%7 = load [copy] %6 : $*C
end_borrow %3 : $Base
%9 = apply %1(%7) : $@convention(thin) (@guaranteed C) -> ()
destroy_value %7 : $C
return %9 : $()
}
// CHECK-LABEL: sil [ossa] @remove_copy_of_guaranteed_arg :
// CHECK: %1 = destructure_struct %0
// CHECK-NOT: destroy_value
// CHECK: } // end sil function 'remove_copy_of_guaranteed_arg'
sil [ossa] @remove_copy_of_guaranteed_arg : $@convention(thin) (@guaranteed NonTrivialStruct) -> @owned Klass {
bb0(%0 : @guaranteed $NonTrivialStruct):
%1 = copy_value %0
%2 = destructure_struct %1
%3 = begin_borrow %2
%4 = ref_element_addr %3, #Klass.base
%5 = load [copy] %4 : $*Klass
end_borrow %3
destroy_value %2
return %5
}
// CHECK-LABEL: sil [ossa] @remove_copy_of_borrow :
// CHECK: %3 = apply %2(%1)
// CHECK-NOT: destroy_value
// CHECK: } // end sil function 'remove_copy_of_borrow'
sil [ossa] @remove_copy_of_borrow : $@convention(thin) (@owned Klass) -> @owned Klass {
bb0(%0 : @owned $Klass):
%1 = begin_borrow %0
%2 = copy_value %1
%3 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %2
end_borrow %1
return %0
}
// CHECK-LABEL: sil [ossa] @dont_remove_copy_of_borrow :
// CHECK: %2 = copy_value %1
// CHECK: } // end sil function 'dont_remove_copy_of_borrow'
sil [ossa] @dont_remove_copy_of_borrow : $@convention(thin) (@owned Klass) -> @owned Klass {
bb0(%0 : @owned $Klass):
%1 = begin_borrow %0
%2 = copy_value %1
%3 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %1
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %2
return %0
}
// CHECK-LABEL: sil [ossa] @remove_copy_of_forwarded_borrow :
// CHECK: [[DS:%.*]] = destructure_struct
// CHECK-NOT: copy_value
// CHECK: apply {{%[0-9]+}}([[DS]])
// CHECK: } // end sil function 'remove_copy_of_forwarded_borrow'
sil [ossa] @remove_copy_of_forwarded_borrow : $@convention(thin) (@owned Optional<NonTrivialStruct>) -> @owned Optional<NonTrivialStruct> {
bb0(%0 : @owned $Optional<NonTrivialStruct>):
%1 = begin_borrow %0
switch_enum %1, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
bb1(%3 : @guaranteed $NonTrivialStruct):
%4 = destructure_struct %3
%5 = copy_value %4
%6 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
%7 = apply %6(%5) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %5
br bb3
bb2:
br bb3
bb3:
end_borrow %1
return %0
}
// CHECK-LABEL: sil [ossa] @user_in_dead_end_block :
// CHECK: %2 = copy_value %1
// CHECK: } // end sil function 'user_in_dead_end_block'
sil [ossa] @user_in_dead_end_block : $@convention(thin) (@owned Klass) -> @owned Klass {
bb0(%0 : @owned $Klass):
%1 = begin_borrow %0
%2 = copy_value %1
end_borrow %1
%4 = function_ref @guaranteed_klass_user : $@convention(thin) (@guaranteed Klass) -> ()
%5 = apply %4(%2) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value [dead_end] %2
destroy_value [dead_end] %0
unreachable
}
// CHECK-LABEL: sil [ossa] @end_borrows_in_liferange_exit_block :
// CHECK: %1 = load_borrow %0
// CHECK: %2 = begin_borrow [lexical] %1
// CHECK: bb1({{.*}} : @guaranteed $C):
// CHECK-NEXT: end_borrow %2
// CHECK-NEXT: end_borrow %1
// CHECK: bb2:
// CHECK-NEXT: end_borrow %2
// CHECK-NEXT: end_borrow %1
// CHECK: } // end sil function 'end_borrows_in_liferange_exit_block'
sil [ossa] @end_borrows_in_liferange_exit_block : $@convention(thin) (@in_guaranteed Optional<C>) -> () {
bb0(%0 : $*Optional<C>):
%1 = load [copy] %0
%2 = move_value [lexical] %1
switch_enum %2, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
bb1(%4 : @owned $C):
destroy_value %4
br bb3
bb2:
br bb3
bb3:
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @lifetime_ending_enum_data :
// CHECK: %1 = load_borrow %0
// CHECK-NEXT: %2 = unchecked_enum_data %1
// CHECK-NEXT: end_borrow %1
// CHECK: } // end sil function 'lifetime_ending_enum_data'
sil [ossa] @lifetime_ending_enum_data : $@convention(method) (@in_guaranteed MultiPayload) -> MyInt {
bb0(%0 : $*MultiPayload):
%1 = load [copy] %0
%2 = unchecked_enum_data %1, #MultiPayload.b!enumelt
return %2
}
// CHECK-LABEL: sil [ossa] @duplicate_lifetime_end1 :
// CHECK: load_borrow %0
// CHECK: } // end sil function 'duplicate_lifetime_end1'
sil [ossa] @duplicate_lifetime_end1 : $@convention(thin) (@in_guaranteed (C, Optional<C>)) -> () {
bb0(%0 : $*(C, Optional<C>)):
%1 = load [copy] %0
(%2, %3) = destructure_tuple %1
switch_enum %3, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
bb1(%4 : @owned $C):
destroy_value %2
destroy_value %4
br bb3
bb2:
debug_value %2
destroy_value %2
br bb3
bb3:
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @duplicate_lifetime_end2 :
// CHECK: load_borrow %0
// CHECK: } // end sil function 'duplicate_lifetime_end2'
sil [ossa] @duplicate_lifetime_end2 : $@convention(thin) (@in_guaranteed (C, Optional<C>)) -> () {
bb0(%0 : $*(C, Optional<C>)):
%1 = load [copy] %0
(%2, %3) = destructure_tuple %1
switch_enum %3, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
bb1(%4 : @owned $C):
destroy_value %2
destroy_value %4
br bb3
bb2:
destroy_value %2
br bb3
bb3:
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @keep_yield2ed_copy : {{.*}} {
// CHECK: copy_value
// CHECK: } // end sil function 'keep_yield2ed_copy'
sil [ossa] @keep_yield2ed_copy : $@convention(thin) () -> () {
%lendC = function_ref @lendC : $@yield_once_2 @convention(thin) () -> @yields @guaranteed C
(%c, %token, %alloc) = begin_apply %lendC() : $@yield_once_2 @convention(thin) () -> @yields @guaranteed C
%copy = copy_value %c : $C
end_apply %token as $()
dealloc_stack %alloc : $*Builtin.SILToken
%useC = function_ref @useC : $@convention(thin) (@guaranteed C) -> ()
apply %useC(%copy) : $@convention(thin) (@guaranteed C) -> ()
destroy_value %copy : $C
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @borrowed_from_forward1 : {{.*}} {
// CHECK-NOT: copy_value
// CHECK-LABEL: } // end sil function 'borrowed_from_forward1'
sil [ossa] @borrowed_from_forward1 : $@convention(thin) (@guaranteed C) -> () {
bb0(%0 : @guaranteed $C):
br bb1(%0)
bb1(%1 : @guaranteed $C):
%2 = borrowed %1 from (%0)
%copy = copy_value %2
%useC = function_ref @useC : $@convention(thin) (@guaranteed C) -> ()
apply %useC(%copy) : $@convention(thin) (@guaranteed C) -> ()
destroy_value %copy
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @borrowed_from_forward2 : {{.*}} {
// CHECK-NOT: copy_value
// CHECK-LABEL: } // end sil function 'borrowed_from_forward2'
sil [ossa] @borrowed_from_forward2 : $@convention(thin) (@guaranteed MyArray) -> () {
bb0(%0 : @guaranteed $MyArray):
%1 = struct_extract %0, #MyArray.buffer
%2 = struct_extract %1, #MyArrayBuffer.storage
%3 = struct_extract %2, #MyBridgeStorage.rawValue
%4 = unchecked_ref_cast %3 to $MyArrayStorageBase
br bb1(%4)
bb1(%6 : @guaranteed $MyArrayStorageBase):
%7 = borrowed %6 from (%0)
%8 = copy_value %7
%9 = begin_borrow %8
%10 = ref_element_addr [immutable] %9, #MyArrayStorageBase.countAndCapacity
end_borrow %9
destroy_value %8
%13 = tuple ()
return %13
}
// CHECK-LABEL: sil [ossa] @test_updating_borrowed_from :
// CHECK: borrowed {{.*}} from (%0)
// CHECK-LABEL: } // end sil function 'test_updating_borrowed_from'
sil [ossa] @test_updating_borrowed_from : $@convention(thin) (@guaranteed NonTrivialStruct) -> () {
bb0(%0 : @guaranteed $NonTrivialStruct):
%1 = destructure_struct %0 : $NonTrivialStruct
%2 = copy_value %1 : $Klass
%3 = begin_borrow %2 : $Klass
br bb1(%3 : $Klass)
bb1(%5 : @reborrow $Klass):
%6 = borrowed %5 : $Klass from (%2 : $Klass)
end_borrow %6 : $Klass
destroy_value %2 : $Klass
%9 = tuple ()
return %9 : $()
}