Files
swift-mirror/test/SILOptimizer/copy_propagation.sil
Nate Chandler 63e3ca7e17 [CanOSSALifetime] Xfer lexical value to callees.
When canonicalizing a lexical lifetime, don't treat applies which
consume a copy of the value as deinit barriers.  Doing so forces another
copy of the def being canonicalized to remain after the apply.

Instead, allow the lifetime to be transferred into the callee.  This is
the same behavior that already exists for lexical lifetimes represented
with the `begin_borrow [lexical]` + `copy_value` instruction sequence.
2023-04-03 16:29:21 -07:00

1050 lines
40 KiB
Plaintext

// RUN: %target-sil-opt -copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK,CHECK-OPT
// RUN: %target-sil-opt -mandatory-copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK,CHECK-ONONE
// REQUIRES: asserts
// Most CopyPropagation tests are still in copy_propagation_opaque.sil.
sil_stage canonical
import Builtin
import Swift
class B { }
class C {
var a: Builtin.Int64
}
sil [ossa] @dummy : $@convention(thin) () -> ()
sil [ossa] @barrier : $@convention(thin) () -> ()
sil [ossa] @getOwnedC : $@convention(thin) () -> (@owned C)
sil [ossa] @getOwnedB : $@convention(thin) () -> (@owned B)
sil [ossa] @takeOwnedC : $@convention(thin) (@owned C) -> ()
sil [ossa] @takeOwnedCTwice : $@convention(thin) (@owned C, @owned C) -> ()
sil [ossa] @takeOwnedCAndGuaranteedC : $@convention(thin) (@owned C, @guaranteed C) -> ()
sil [ossa] @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> ()
sil [ossa] @borrowB : $@convention(thin) (@guaranteed B) -> ()
sil [ossa] @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
// -O hoists the destroy
//
// CHECK-LABEL: sil [ossa] @testDestroyAfterCall : {{.*}} {
// CHECK: bb0:
// CHECK: [[ARG:%.*]] = apply
// CHECK: destroy_value [[ARG]] : $B
// CHECK: apply
// CHECK-LABEL: } // end sil function 'testDestroyAfterCall'
sil [ossa] @testDestroyAfterCall : $@convention(thin) () -> () {
bb0:
%getOwnedB = function_ref @getOwnedB : $@convention(thin) () -> (@owned B)
%arg = apply %getOwnedB() : $@convention(thin) () -> (@owned B)
%f = function_ref @dummy : $@convention(thin) () -> ()
%call = apply %f() : $@convention(thin) () -> ()
destroy_value %arg : $B
%10 = tuple ()
return %10 : $()
}
// -O removes the copy/destroy
//
// CHECK-LABEL: sil [ossa] @testDestroyAfterConsumingStore : {{.*}} {
// CHECK: bb0:
// CHECK: [[ARG:%.*]] = apply
// CHECK: [[ADR:%.*]] = alloc_stack $C
// CHECK-NOT: copy_value
// CHECK: store [[ARG]] to [init] [[ADR]] : $*C
// CHECK-NOT: destroy_value
// CHECK: destroy_addr
// CHECK-LABEL: } // end sil function 'testDestroyAfterConsumingStore'
sil [ossa] @testDestroyAfterConsumingStore : $@convention(thin) () -> () {
bb0:
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
%arg = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
%adr = alloc_stack $C
%copy = copy_value %arg : $C
store %copy to [init] %adr : $*C
destroy_value %arg : $C
destroy_addr %adr : $*C
dealloc_stack %adr : $*C
%10 = tuple ()
return %10 : $()
}
// -O removes the copy/destroy
//
// CHECK-LABEL: sil [ossa] @testDestroyAfterConsumingStoreAndCall : {{.*}} {
// CHECK: bb0:
// CHECK: [[ARG:%.*]] = apply
// CHECK: [[ADR:%.*]] = alloc_stack $C
// CHECK-NOT: copy_value
// CHECK: store %{{.*}} to [init] [[ADR]] : $*C
// CHECK-NOT: destroy_value
// CHECK: apply
// CHECK: destroy_addr
// CHECK-LABEL: } // end sil function 'testDestroyAfterConsumingStoreAndCall'
sil [ossa] @testDestroyAfterConsumingStoreAndCall : $@convention(thin) () -> () {
bb0:
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
%arg = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
%adr = alloc_stack $C
%copy = copy_value %arg : $C
store %copy to [init] %adr : $*C
%f = function_ref @dummy : $@convention(thin) () -> ()
%call = apply %f() : $@convention(thin) () -> ()
destroy_value %arg : $C
destroy_addr %adr : $*C
dealloc_stack %adr : $*C
%10 = tuple ()
return %10 : $()
}
// -O removes the copy/destroy
//
// CHECK-LABEL: sil [ossa] @testDestroyAfterConsumingCall : {{.*}} {
// CHECK: bb0:
// CHECK: [[ARG:%.*]] = apply
// CHECK-NOT: copy_value
// CHECK: apply {{.*}} : $@convention(thin) (@owned C) -> ()
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function 'testDestroyAfterConsumingCall'
sil [ossa] @testDestroyAfterConsumingCall : $@convention(thin) () -> () {
bb0:
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
%arg = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
%copy = copy_value %arg : $C
%f = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> ()
%call = apply %f(%copy) : $@convention(thin) (@owned C) -> ()
destroy_value %arg : $C
%10 = tuple ()
return %10 : $()
}
// -O removes the copy/destroy
//
// CHECK-LABEL: sil [ossa] @testDestroyAfterConsumingCallAndCall : {{.*}} {
// CHECK: bb0:
// CHECK: [[ARG:%.*]] = apply
// CHECK-NOT: copy_value
// CHECK: apply %{{.*}}(%{{.*}}) : $@convention(thin) (@owned C) -> ()
// CHECK-NOT: destroy_value
// CHECK: apply
// CHECK-LABEL: } // end sil function 'testDestroyAfterConsumingCallAndCall'
sil [ossa] @testDestroyAfterConsumingCallAndCall : $@convention(thin) () -> () {
bb0:
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
%arg = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
%copy = copy_value %arg : $C
%f1 = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> ()
%call1 = apply %f1(%copy) : $@convention(thin) (@owned C) -> ()
%f2 = function_ref @dummy : $@convention(thin) () -> ()
%call2 = apply %f2() : $@convention(thin) () -> ()
destroy_value %arg : $C
%10 = tuple ()
return %10 : $()
}
// CHECK-LABEL: sil [ossa] @testPhi : $@convention(thin) () -> @owned C {
// CHECK: bb0:
// CHECK: [[C:%.*]] = apply %0() : $@convention(thin) () -> @owned C
// CHECK: debug_value [[C]] : $C, let, name "Hello Tom"
// CHECK: apply %{{.*}}([[C]]) : $@convention(thin) (@guaranteed C) -> ()
// CHECK: cond_br undef, bb1, bb2
// CHECK: bb1:
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK: br bb3([[C]] : $C)
// CHECK: bb2:
// CHECK: destroy_value %1 : $C
// CHECK: apply %0() : $@convention(thin) () -> @owned C
// CHECK: br bb3(
// CHECK: bb3(%{{.*}} : @owned $C)
// CHECK-NEXT: br bb4(%{{.*}} : $C)
// CHECK: bb4(%{{.*}} : @owned $C):
// CHECK-NEXT: return %{{.*}} : $C
// CHECK-LABEL: } // end sil function 'testPhi'
sil [ossa] @testPhi : $@convention(thin) () -> @owned C {
bb0:
%f1 = function_ref @getOwnedC : $@convention(thin) () -> @owned C
%s = apply %f1() : $@convention(thin) () -> @owned C
debug_value %s : $C, let, name "Hello Tom"
%f2 = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> ()
%use = apply %f2(%s) : $@convention(thin) (@guaranteed C) -> ()
cond_br undef, bb1, bb2
bb1:
%cp = copy_value %s : $C
br bb3(%cp : $C)
bb2:
%s2 = apply %f1() : $@convention(thin) () -> @owned C
br bb3(%s2 : $C)
bb3(%arg3 : @owned $C):
destroy_value %s : $C
br bb4(%arg3 : $C)
bb4(%arg4 : @owned $C):
return %arg4 : $C
}
// CHECK-LABEL: sil [ossa] @debugValueAfterDestroy : {{.*}} {
// CHECK: [[INSTANCE:%[^,]+]] = apply
// CHECK: debug_value [[INSTANCE]]
// CHECK: destroy_value [[INSTANCE]]
// CHECK-LABEL: } // end sil function 'debugValueAfterDestroy'
sil [ossa] @debugValueAfterDestroy : $@convention(thin) () -> () {
%getOwnedB = function_ref @getOwnedB : $@convention(thin) () -> (@owned B)
%dummy = function_ref @dummy : $@convention(thin) () -> ()
%borrowB = function_ref @borrowB : $@convention(thin) (@guaranteed B) -> ()
%b = apply %getOwnedB() : $@convention(thin) () -> (@owned B)
apply %dummy() : $@convention(thin) () -> ()
apply %dummy() : $@convention(thin) () -> ()
apply %dummy() : $@convention(thin) () -> ()
apply %borrowB(%b) : $@convention(thin) (@guaranteed B) -> ()
debug_value %b : $B
destroy_value %b : $B
apply %dummy() : $@convention(thin) () -> ()
apply %dummy() : $@convention(thin) () -> ()
apply %dummy() : $@convention(thin) () -> ()
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @deadArgDef : {{.*}} {
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[EXIT]]([[INSTANCE:%[^,]+]] :
// CHECK-NEXT: destroy_value [[INSTANCE]]
// CHECK: apply {{%[^,]+}}
// CHECK: apply {{%[^,]+}}
// CHECK: apply {{%[^,]+}}
// CHECK-LABEL: } // end sil function 'deadArgDef'
sil [ossa] @deadArgDef : $@convention(thin) () -> () {
%getOwnedB = function_ref @getOwnedB : $@convention(thin) () -> (@owned B)
%dummy = function_ref @dummy : $@convention(thin) () -> ()
%borrowB = function_ref @borrowB : $@convention(thin) (@guaranteed B) -> ()
%b = apply %getOwnedB() : $@convention(thin) () -> (@owned B)
apply %borrowB(%b) : $@convention(thin) (@guaranteed B) -> ()
br exit(%b : $B)
exit(%b2 : @owned $B):
apply %dummy() : $@convention(thin) () -> ()
apply %dummy() : $@convention(thin) () -> ()
apply %dummy() : $@convention(thin) () -> ()
destroy_value %b2 : $B
%retval = tuple ()
return %retval : $()
}
// -----------------------------------------------------------------------------
// Test begin/end_apply
// -----------------------------------------------------------------------------
struct Struct {
var st: Int
}
sil @swift_modifyAtWritableKeyPath : $@yield_once @convention(thin) <τ_0_0, τ_0_1> (@inout τ_0_0, @guaranteed WritableKeyPath<τ_0_0, τ_0_1>) -> @yields @inout τ_0_1
sil @modifyInt : $@convention(thin) (@inout Int) -> ()
// CHECK-LABEL: sil hidden [ossa] @testBeginApply : $@convention(thin) (@inout Struct) -> () {
// CHECK: begin_apply
// CHECK-NOT: destroy
// CHECK: apply
// CHECK-NOT: destroy
// CHECK: end_apply
// CHECK-NOT: destroy
// CHECK: destroy_value %{{.*}} : $WritableKeyPath<Struct, Int>
// CHECK-NOT: destroy
// CHECK-LABEL: } // end sil function 'testBeginApply'
sil hidden [ossa] @testBeginApply : $@convention(thin) (@inout Struct) -> () {
bb0(%0 : $*Struct):
%2 = keypath $WritableKeyPath<Struct, Int>, (root $Struct; stored_property #Struct.st : $Int)
debug_value %2 : $WritableKeyPath<Struct, Int>, let, name "kp"
%4 = copy_value %2 : $WritableKeyPath<Struct, Int>
%5 = function_ref @swift_modifyAtWritableKeyPath : $@yield_once @convention(thin) <τ_0_0, τ_0_1> (@inout τ_0_0, @guaranteed WritableKeyPath<τ_0_0, τ_0_1>) -> @yields @inout τ_0_1
(%6, %7) = begin_apply %5<Struct, Int>(%0, %4) : $@yield_once @convention(thin) <τ_0_0, τ_0_1> (@inout τ_0_0, @guaranteed WritableKeyPath<τ_0_0, τ_0_1>) -> @yields @inout τ_0_1
%8 = function_ref @modifyInt : $@convention(thin) (@inout Int) -> ()
%9 = apply %8(%6) : $@convention(thin) (@inout Int) -> ()
end_apply %7
destroy_value %4 : $WritableKeyPath<Struct, Int>
destroy_value %2 : $WritableKeyPath<Struct, Int>
%13 = tuple ()
return %13 : $()
}
// -----------------------------------------------------------------------------
// Test project_box
// -----------------------------------------------------------------------------
// FIXME: project_box is currently a PointerEscape, so box live ranges are not canonicalized.
//
// CHECK-LABEL: sil [ossa] @testProjectBox : $@convention(thin) (@owned B) -> @owned B {
// CHECK: copy_value
// CHECK: destroy_value
// CHECK: destroy_value
// CHECK-LABEL: } // end sil function 'testProjectBox'
sil [ossa] @testProjectBox : $@convention(thin) (@owned B) -> @owned B {
bb0(%0 : @owned $B):
%box = alloc_box $<τ_0_0> { var τ_0_0 } <B>
%boxadr = project_box %box : $<τ_0_0> { var τ_0_0 } <B>, 0
store %0 to [init] %boxadr : $*B
%load = load [copy] %boxadr : $*B
%copy = copy_value %box : $<τ_0_0> { var τ_0_0 } <B>
destroy_value %box : $<τ_0_0> { var τ_0_0 } <B>
destroy_value %copy : $<τ_0_0> { var τ_0_0 } <B>
return %load : $B
}
// -----------------------------------------------------------------------------
// Test mark_dependence
// -----------------------------------------------------------------------------
// FIXME: mark_dependence is currently a PointerEscape, so dependent live ranges are not canonicalized.
//
// CHECK-LABEL: sil [ossa] @testMarkDependence : {{.*}} {
// CHECK: copy_value
// CHECK: destroy_value
// CHECK: destroy_value
// CHECK-LABEL: } // end sil function 'testMarkDependence'
sil [ossa] @testMarkDependence : $@convention(thin) (@inout Builtin.Int64) -> Builtin.Int64 {
bb0(%0 : $*Builtin.Int64):
%getOwnedB = function_ref @getOwnedB : $@convention(thin) () -> (@owned B)
%1 = apply %getOwnedB() : $@convention(thin) () -> (@owned B)
%ptr = mark_dependence %0 : $*Builtin.Int64 on %1 : $B
%val = load [trivial] %ptr : $*Builtin.Int64
%copy = copy_value %1 : $B
destroy_value %1 : $B
destroy_value %copy : $B
return %val : $Builtin.Int64
}
// -----------------------------------------------------------------------------
// Test OperandOwnership::BitwiseEscape
// -----------------------------------------------------------------------------
// CHECK-LABEL: sil [ossa] @testBitwiseEscape : $@convention(thin) (@guaranteed C) -> Builtin.RawPointer {
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function 'testBitwiseEscape'
sil [ossa] @testBitwiseEscape : $@convention(thin) (@guaranteed C) -> Builtin.RawPointer {
bb0(%0 : @guaranteed $C):
%raw = ref_to_raw_pointer %0 : $C to $Builtin.RawPointer
%copy = copy_value %0 : $C
destroy_value %copy : $C
return %raw : $Builtin.RawPointer
}
// =============================================================================
// Test extending liveness though overlapping access scopes.
// =============================================================================
class X {}
class Y {}
sil [ossa] @getObject : $@convention(thin) () -> @owned AnyObject
// No overlap (access ignored):
// def
// use
// begin_access
// end_access
// destroy
//
// CHECK-LABEL: sil [ossa] @testNoOverlapInLiveBlock : $@convention(thin) () -> () {
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
// CHECK-NOT: copy_value
// CHECK: store [[DEF]] to [init]
// CHECK: begin_access
// CHECK: end_access
// CHECK: bb1:
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testNoOverlapInLiveBlock'
sil [ossa] @testNoOverlapInLiveBlock : $@convention(thin) () -> () {
bb0:
%box = alloc_box ${ var AnyObject }, var, name "x"
%adr = project_box %box : ${ var AnyObject }, 0
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
// def
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
%copy = copy_value %def : $AnyObject
// use
store %def to [init] %adr : $*AnyObject
// end canonical lifetime
%access = begin_access [read] [dynamic] %adr : $*AnyObject
%obj = load [copy] %access : $*AnyObject
end_access %access : $*AnyObject
br bb1
bb1:
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
destroy_value %copy : $AnyObject
destroy_value %obj : $AnyObject
destroy_value %box : ${ var AnyObject }
%v = tuple ()
return %v : $()
}
// No overlap (access ignored):
// def
// use
// br...
// bb...
// begin_access
// end_access
// destroy
//
// CHECK-LABEL: sil [ossa] @testNoOverlapInDeadBlock : $@convention(thin) () -> () {
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
// CHECK-NOT: copy_value
// CHECK: store [[DEF]] to [init] %{{.*}} : $*AnyObject
// CHECK: br bb1
// CHECK: bb1:
// CHECK: begin_access
// CHECK: end_access
// CHECK: br bb2
// CHECK: bb2:
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testNoOverlapInDeadBlock'
sil [ossa] @testNoOverlapInDeadBlock : $@convention(thin) () -> () {
bb0:
%box = alloc_box ${ var AnyObject }, var, name "x"
%adr = project_box %box : ${ var AnyObject }, 0
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
// def
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
%copy = copy_value %def : $AnyObject
// use
store %def to [init] %adr : $*AnyObject
// end canonical lifetime
br bb1
bb1:
%access = begin_access [read] [dynamic] %adr : $*AnyObject
%obj = load [copy] %access : $*AnyObject
end_access %access : $*AnyObject
br bb2
bb2:
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
destroy_value %copy : $AnyObject
destroy_value %obj : $AnyObject
destroy_value %box : ${ var AnyObject }
%v = tuple ()
return %v : $()
}
// Overlapping (must extend pruned liveness):
//
// %def
// begin_access // access scope unrelated to def
// use %def // pruned liveness ends here
// end_access
//
// CHECK-LABEL: sil [ossa] @testOverlapInLiveBlock : $@convention(thin) () -> () {
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
// CHECK: begin_access
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
// CHECK: store [[COPY]] to [init] %{{.*}} : $*AnyObject
// CHECK: end_access
// CHECK: destroy_value [[DEF]] : $AnyObject
// CHECK: br bb1
// CHECK: bb1:
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testOverlapInLiveBlock'
sil [ossa] @testOverlapInLiveBlock : $@convention(thin) () -> () {
bb0:
%box = alloc_box ${ var AnyObject }, var, name "x"
%adr = project_box %box : ${ var AnyObject }, 0
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
// def
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
%copy = copy_value %def : $AnyObject
%access = begin_access [read] [dynamic] %adr : $*AnyObject
// use
store %def to [init] %adr : $*AnyObject
%obj = load [copy] %access : $*AnyObject
end_access %access : $*AnyObject
// Branch to avoid reusing the destroy_value
br bb1
bb1:
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
destroy_value %copy : $AnyObject
destroy_value %obj : $AnyObject
destroy_value %box : ${ var AnyObject }
%v = tuple ()
return %v : $()
}
// Overlapping (must extend pruned liveness):
//
// %def
// begin_access // access scope unrelated to def
// use %def // pruned liveness ends here
// br...
// bb...
// end_access
//
// CHECK-LABEL: sil [ossa] @testOverlapInDeadBlock : $@convention(thin) () -> () {
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
// CHECK: begin_access
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
// CHECK: store [[COPY]] to [init] %{{.*}} : $*AnyObject
// CHECK: br bb1
// CHECK: bb1:
// CHECK: end_access
// CHECK: destroy_value [[DEF]] : $AnyObject
// CHECK: br bb2
// CHECK: bb2:
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testOverlapInDeadBlock'
sil [ossa] @testOverlapInDeadBlock : $@convention(thin) () -> () {
bb0:
%box = alloc_box ${ var AnyObject }, var, name "x"
%adr = project_box %box : ${ var AnyObject }, 0
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
// def
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
%copy = copy_value %def : $AnyObject
%access = begin_access [read] [dynamic] %adr : $*AnyObject
// use
store %def to [init] %adr : $*AnyObject
br bb1
bb1:
%obj = load [copy] %access : $*AnyObject
end_access %access : $*AnyObject
br bb2
bb2:
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
destroy_value %copy : $AnyObject
destroy_value %obj : $AnyObject
destroy_value %box : ${ var AnyObject }
%v = tuple ()
return %v : $()
}
// Fully Overlapping (must extend pruned liveness):
//
// begin_access // access scope unrelated to def
// %def
// use %def // pruned liveness ends here
// end_access
//
// CHECK-LABEL: sil [ossa] @testFullOverlapInDefBlock : $@convention(thin) () -> () {
// CHECK: begin_access
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
// CHECK: store [[COPY]] to [init] %{{.*}} : $*AnyObject
// CHECK: end_access
// CHECK: destroy_value [[DEF]] : $AnyObject
// CHECK: br bb1
// CHECK: bb1:
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testFullOverlapInDefBlock'
sil [ossa] @testFullOverlapInDefBlock : $@convention(thin) () -> () {
bb0:
%box = alloc_box ${ var AnyObject }, var, name "x"
%adr = project_box %box : ${ var AnyObject }, 0
%access = begin_access [read] [dynamic] %adr : $*AnyObject
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
// def
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
%copy = copy_value %def : $AnyObject
// use
store %def to [init] %adr : $*AnyObject
%obj = load [copy] %access : $*AnyObject
end_access %access : $*AnyObject
// Branch to avoid reusing the destroy_value
br bb1
bb1:
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
destroy_value %copy : $AnyObject
destroy_value %obj : $AnyObject
destroy_value %box : ${ var AnyObject }
%v = tuple ()
return %v : $()
}
// Fully Overlapping (must extend pruned liveness):
//
// begin_access // access scope unrelated to def
// br...
// bb...
// %def
// use %def // pruned liveness ends here
// end_access
//
// CHECK-LABEL: sil [ossa] @testFullOverlapBeforeDefBlock : $@convention(thin) () -> () {
// CHECK: begin_access
// CHECK: br bb1
// CHECK: bb1:
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
// CHECK: store [[COPY]] to [init] %{{.*}} : $*AnyObject
// CHECK: end_access
// CHECK: destroy_value [[DEF]] : $AnyObject
// CHECK: br bb2
// CHECK: bb2:
// CHECK-NOT: destroy_value [[DEF]] : $AnyObject
// CHECK-LABEL: } // end sil function 'testFullOverlapBeforeDefBlock'
sil [ossa] @testFullOverlapBeforeDefBlock : $@convention(thin) () -> () {
bb0:
%box = alloc_box ${ var AnyObject }, var, name "x"
%adr = project_box %box : ${ var AnyObject }, 0
%access = begin_access [read] [dynamic] %adr : $*AnyObject
br bb1
bb1:
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
// def
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
%copy = copy_value %def : $AnyObject
// use
store %def to [init] %adr : $*AnyObject
%obj = load [copy] %access : $*AnyObject
end_access %access : $*AnyObject
// Branch to avoid reusing the destroy_value
br bb2
bb2:
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
destroy_value %copy : $AnyObject
destroy_value %obj : $AnyObject
destroy_value %box : ${ var AnyObject }
%v = tuple ()
return %v : $()
}
// Original Overlapping (unnecessarily extends pruned liveness):
//
// %def
// begin_access // access scope unrelated to def
// use %def // pruned liveness ends here
// destroy %def
// end_access
//
// CHECK-LABEL: sil [ossa] @testOriginalOverlapInLiveBlock : $@convention(thin) () -> () {
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
// CHECK: begin_access
// CHECK: store [[DEF]] to [init] %{{.*}} : $*AnyObject
// CHECK: end_access
// CHECK: br bb1
// CHECK-LABEL: } // end sil function 'testOriginalOverlapInLiveBlock'
sil [ossa] @testOriginalOverlapInLiveBlock : $@convention(thin) () -> () {
bb0:
%box = alloc_box ${ var AnyObject }, var, name "x"
%adr = project_box %box : ${ var AnyObject }, 0
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
// def
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
%copy = copy_value %def : $AnyObject
%access = begin_access [read] [dynamic] %adr : $*AnyObject
// use
store %def to [init] %adr : $*AnyObject
destroy_value %copy : $AnyObject
%obj = load [copy] %access : $*AnyObject
end_access %access : $*AnyObject
br bb1
bb1:
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
%call = apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
destroy_value %obj : $AnyObject
destroy_value %box : ${ var AnyObject }
%v = tuple ()
return %v : $()
}
// Original Overlapping (unnecessarily extends pruned liveness):
//
// CHECK-LABEL: sil [ossa] @testOriginalOverlapInDeadBlock : $@convention(thin) () -> () {
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
// CHECK: begin_access
// CHECK: store [[DEF]] to [init] %{{.*}} : $*AnyObject
// CHECK: br bb1
// CHECK: bb1:
// CHECK: end_access
// CHECK-LABEL: } // end sil function 'testOriginalOverlapInDeadBlock'
sil [ossa] @testOriginalOverlapInDeadBlock : $@convention(thin) () -> () {
bb0:
%box = alloc_box ${ var AnyObject }, var, name "x"
%adr = project_box %box : ${ var AnyObject }, 0
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
// def
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
%copy = copy_value %def : $AnyObject
%access = begin_access [read] [dynamic] %adr : $*AnyObject
// use
store %def to [init] %adr : $*AnyObject
br bb1
bb1:
destroy_value %copy : $AnyObject
%obj = load [copy] %access : $*AnyObject
end_access %access : $*AnyObject
// Use %obj so it doesn't get deleted because it's unused a trigger a cascade
// of deletions.
%f2 = function_ref @takeGuaranteedAnyObject : $@convention(thin) (@guaranteed AnyObject) -> ()
apply %f2(%obj) : $@convention(thin) (@guaranteed AnyObject) -> ()
destroy_value %obj : $AnyObject
destroy_value %box : ${ var AnyObject }
%v = tuple ()
return %v : $()
}
// Interleaved access (requires iterative lifetime extension):
//
// %def
// begin_access X
// use %def // Initial pruned lifetime boundary
// begin_access Y
// end_access X // Lifetime boundary after first extension
// end_access Y // Lifetime boundary after second extension
// destroy %def
//
// CHECK-LABEL: sil [ossa] @testInterleavedAccessScope : $@convention(thin) (@inout AnyObject) -> () {
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
// CHECK: begin_access {{.*}} : $*X
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
// CHECK: store [[COPY]] to [assign] %{{.*}} : $*AnyObject
// CHECK: begin_access {{.*}} : $*Y
// CHECK: end_access {{.*}} : $*X
// CHECK: end_access {{.*}} : $*Y
// CHECK: destroy_value [[DEF]] : $AnyObject
// CHECK: br bb1
// CHECK: bb1:
// CHECK-NOT: destroy_value {{.*}} : $AnyObject
// CHECK-LABEL: } // end sil function 'testInterleavedAccessScope'
sil [ossa] @testInterleavedAccessScope : $@convention(thin) (@inout AnyObject) -> () {
bb0(%0 : $*AnyObject):
%x = alloc_box ${ var X }, var, name "x"
%xadr = project_box %x : ${ var X }, 0
%y = alloc_box ${ var Y }, var, name "y"
%yadr = project_box %y : ${ var Y }, 0
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
// def
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
%copy = copy_value %def : $AnyObject
%accessX = begin_access [read] [dynamic] %xadr : $*X
// use
store %def to [assign] %0 : $*AnyObject
%accessY = begin_access [read] [dynamic] %yadr : $*Y
// accessX overlaps pruned liveness on the first iteration
end_access %accessX : $*X
// accessY only overlaps pruned liveness on the second iteration
end_access %accessY : $*Y
br bb1
bb1:
destroy_value %copy : $AnyObject
destroy_value %y : ${ var Y }
destroy_value %x : ${ var X }
%v = tuple ()
return %v : $()
}
// Interleaved non-local access (requires iterative lifetime extension):
//
// %def
// begin_access X
// use %def // Initial pruned lifetime boundary
// br bb1
// bb1:
// begin_access Y
// br bb2
// bb2:
// end_access X // Lifetime boundary after first extension
// br bb3
// bb3:
// end_access Y // Lifetime boundary after second extension
// br bb4
// bb4:
// destroy %def
//
// CHECK-LABEL: sil [ossa] @testInterleavedNonLocalAccessScope : $@convention(thin) (@inout AnyObject) -> () {
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
// CHECK: begin_access {{.*}} : $*X
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
// CHECK: store [[COPY]] to [assign] %{{.*}} : $*AnyObject
// CHECK: br bb1
// CHECK: bb1:
// CHECK: begin_access {{.*}} : $*Y
// CHECK: br bb2
// CHECK: bb2:
// CHECK: end_access {{.*}} : $*X
// CHECK: br bb3
// CHECK: bb3:
// CHECK: end_access {{.*}} : $*Y
// CHECK: destroy_value [[DEF]] : $AnyObject
// CHECK: br bb4
// CHECK: bb4:
// CHECK-NOT: destroy_value {{.*}} : $AnyObject
// CHECK-LABEL: } // end sil function 'testInterleavedNonLocalAccessScope'
sil [ossa] @testInterleavedNonLocalAccessScope : $@convention(thin) (@inout AnyObject) -> () {
bb0(%0 : $*AnyObject):
%x = alloc_box ${ var X }, var, name "x"
%xadr = project_box %x : ${ var X }, 0
%y = alloc_box ${ var Y }, var, name "y"
%yadr = project_box %y : ${ var Y }, 0
%f = function_ref @getObject : $@convention(thin) () -> @owned AnyObject
// def
%def = apply %f() : $@convention(thin) () -> @owned AnyObject
%copy = copy_value %def : $AnyObject
%accessX = begin_access [read] [dynamic] %xadr : $*X
// use
store %def to [assign] %0 : $*AnyObject
br bb1
bb1:
%accessY = begin_access [read] [dynamic] %yadr : $*Y
br bb2
bb2:
// accessX overlaps pruned liveness on the first iteration
end_access %accessX : $*X
br bb3
bb3:
// accessY only overlaps pruned liveness on the second iteration
end_access %accessY : $*Y
br bb4
bb4:
destroy_value %copy : $AnyObject
destroy_value %y : ${ var Y }
destroy_value %x : ${ var X }
%v = tuple ()
return %v : $()
}
// Test a dead begin_borrow (with no scope ending uses). Make sure
// copy-propagation doesn't end the lifetime before the dead borrow.
//
// CHECK-LABEL: sil hidden [ossa] @testDeadBorrow : {{.*}} {
// CHECK: bb0:
// CHECK: copy_value %1 : $C
// CHECK: destroy_value
// CHECK: copy_value %1 : $C
// CHECK: begin_borrow
// CHECK: unreachable
// CHECK-LABEL: } // end sil function 'testDeadBorrow'
sil hidden [ossa] @testDeadBorrow : $@convention(thin) () -> () {
bb0:
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
%0 = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
%1 = copy_value %0 : $C
destroy_value %1 : $C
%6 = copy_value %0 : $C
%7 = begin_borrow %6 : $C
unreachable
}
// Test that copy propagation doesn't hoist a destroy_value corresponding to
// a move value [lexical] over a barrier.
// CHECK-LABEL: sil [ossa] @dont_hoist_move_value_lexical_destroy_over_barrier_apply : {{.*}} {
// CHECK: [[GET_OWNED_C:%.*]] = function_ref @getOwnedC
// CHECK: [[INSTANCE:%.*]] = apply [[GET_OWNED_C]]
// CHECK: [[LIFETIME:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier
// CHECK: [[TAKE_GUARANTEED_C:%[^,]+]] = function_ref @takeGuaranteedC
// CHECK: apply [[TAKE_GUARANTEED_C]]([[LIFETIME]])
// CHECK: apply [[BARRIER]]()
// CHECK: destroy_value [[LIFETIME]]
// CHECK-LABEL: } // end sil function 'dont_hoist_move_value_lexical_destroy_over_barrier_apply'
sil [ossa] @dont_hoist_move_value_lexical_destroy_over_barrier_apply : $@convention(thin) () -> () {
entry:
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
%instance = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
%lifetime = move_value [lexical] %instance : $C
%barrier = function_ref @barrier : $@convention(thin) () -> ()
%takeGuaranteedC = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> ()
apply %takeGuaranteedC(%lifetime) : $@convention(thin) (@guaranteed C) -> ()
apply %barrier() : $@convention(thin) () -> ()
destroy_value %lifetime : $C
%retval = tuple ()
return %retval : $()
}
// Canonicalize the moved-from value when a move_value is deemed redundant and
// eliminated.
// CHECK-LABEL: sil [ossa] @canonicalize_source_of_redundant_move_value : {{.*}} {
// CHECK: [[GET_OWNED_C:%[^,]+]] = function_ref @getOwnedC
// CHECK: [[TAKE_OWNED_C:%[^,]+]] = function_ref @takeOwnedC
// CHECK: [[C:%[^,]+]] = apply [[GET_OWNED_C]]() : $@convention(thin) () -> @owned C
// CHECK: apply [[TAKE_OWNED_C]]([[C]]) : $@convention(thin) (@owned C) -> ()
// CHECK-LABEL: } // end sil function 'canonicalize_source_of_redundant_move_value'
sil [ossa] @canonicalize_source_of_redundant_move_value : $@convention(thin) () -> () {
%getOwnedC = function_ref @getOwnedC : $@convention(thin) () -> (@owned C)
%takeOwnedC = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> ()
%c = apply %getOwnedC() : $@convention(thin) () -> (@owned C)
%c2 = move_value %c : $C
%c3 = copy_value %c2 : $C
apply %takeOwnedC(%c3) : $@convention(thin) (@owned C) -> ()
destroy_value %c2 : $C
%retval = tuple ()
return %retval : $()
}
// Verify that a dead copy_value is deleted.
// CHECK-LABEL: sil [ossa] @delete_dead_reborrow_copy : {{.*}} {
// CHECK-NOT: copy_value
// CHECK-LABEL: } // end sil function 'delete_dead_reborrow_copy'
sil [ossa] @delete_dead_reborrow_copy : $@convention(thin) (@owned X) -> () {
bb0(%instance : @owned $X):
%lifetime = begin_borrow %instance : $X
br bb1(%lifetime : $X)
bb1(%reborrow : @guaranteed $X):
%dead_copy = copy_value %reborrow : $X
end_borrow %reborrow : $X
destroy_value %dead_copy : $X
br exit
exit:
destroy_value %instance : $X
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block : {{.*}} {
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $C
// CHECK-NOT: copy_value
// CHECK: begin_access
// CHECK-NOT: copy_value
// CHECK: store [[INSTANCE]] to [init] {{%[^,]+}}
// CHECK-LABEL: } // end sil function 'dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block'
sil [ossa] @dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block : $@convention(thin) (@owned C) -> () {
bb0(%instance : @owned $C):
%addr = alloc_stack [lexical] $C, var
%borrow = begin_borrow %instance : $C
%copy = copy_value %borrow : $C
end_borrow %borrow : $C
destroy_value %instance : $C
%access = begin_access [static] [modify] %addr : $*C
store %copy to [init] %addr : $*C
end_access %access : $*C
destroy_addr %addr : $*C
dealloc_stack %addr : $*C
%retval = tuple()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @dont_extend_beyond_nonoverlapping_endaccess_after_destroyvalue_in_consuming_block : $@convention(thin) () -> () {
// CHECK: begin_access
// CHECK: destroy_value
// CHECK: end_access
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function 'dont_extend_beyond_nonoverlapping_endaccess_after_destroyvalue_in_consuming_block'
sil [ossa] @dont_extend_beyond_nonoverlapping_endaccess_after_destroyvalue_in_consuming_block : $@convention(thin) () -> () {
bb0:
%instance = apply undef() : $@convention(thin) () -> @owned C
%addr = alloc_stack [lexical] $C, var
%access = begin_access [static] [modify] %addr : $*C
apply undef(%instance) : $@convention(thin) (@guaranteed C) -> ()
destroy_value %instance : $C
end_access %access : $*C
dealloc_stack %addr : $*C
%retval = tuple()
return %retval : $()
}
sil @closureGetter : $@convention(thin) () -> @owned Optional<@Sendable @callee_guaranteed () -> ()>
// This pattern is produced by SILGen doUncheckedConversion.
// rdar://100527903 (Overrelease of optional closure when using shorthand syntax)
//
// Copy propagation cannot handle a PointerEscape.
//
// CHECK-LABEL: sil hidden [ossa] @testBitwiseClosureEscape : $@convention(thin) () -> () {
// CHECK: [[V:%.*]] = apply %0() : $@convention(thin) () -> @owned Optional<@Sendable @callee_guaranteed () -> ()>
// CHECK: [[CP1:%.*]] = copy_value [[V]] : $Optional<@Sendable @callee_guaranteed () -> ()>
// CHECK: destroy_value [[V]] : $Optional<@Sendable @callee_guaranteed () -> ()>
// CHECK: [[CAST:%.*]] = unchecked_bitwise_cast [[CP1]] : $Optional<@Sendable @callee_guaranteed () -> ()> to $Optional<@callee_guaranteed () -> ()>
// CHECK-NEXT: [[CASTCP:%.*]] = copy_value [[CAST]] : $Optional<@callee_guaranteed () -> ()>
// CHECK: store [[CASTCP]] to [init]
// CHECK: destroy_value [[CP1]] : $Optional<@Sendable @callee_guaranteed () -> ()>
// CHECK-LABEL: } // end sil function 'testBitwiseClosureEscape'
sil hidden [ossa] @testBitwiseClosureEscape : $@convention(thin) () -> () {
bb0:
%2 = function_ref @closureGetter : $@convention(thin) () -> @owned Optional<@Sendable @callee_guaranteed () -> ()>
%3 = apply %2() : $@convention(thin) () -> @owned Optional<@Sendable @callee_guaranteed () -> ()>
%13 = copy_value %3 : $Optional<@Sendable @callee_guaranteed () -> ()>
%14 = alloc_stack $Optional<@callee_guaranteed () -> ()>
destroy_value %3 : $Optional<@Sendable @callee_guaranteed () -> ()>
%17 = unchecked_bitwise_cast %13 : $Optional<@Sendable @callee_guaranteed () -> ()> to $Optional<@callee_guaranteed () -> ()>
%18 = copy_value %17 : $Optional<@callee_guaranteed () -> ()>
store %18 to [init] %14 : $*Optional<@callee_guaranteed () -> ()>
destroy_addr %14 : $*Optional<@callee_guaranteed () -> ()>
destroy_value %13 : $Optional<@Sendable @callee_guaranteed () -> ()>
dealloc_stack %14 : $*Optional<@callee_guaranteed () -> ()>
%99 = tuple ()
return %99 : $()
}
// CHECK-LABEL: sil [ossa] @hoist_destroy_of_copy_of_lexical_over_deinit_barrier : $@convention(thin) (@owned C) -> () {
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned
// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier
// CHECK: [[BORROW:%[^,]+]] = function_ref @takeGuaranteedC
// CHECK: apply [[BORROW]]([[INSTANCE]])
// The destroy of the copy should be hoisted over the deinit barrier, and then
// canonicalization of the lexical value should remove the copy.
// CHECK: destroy_value [[INSTANCE]]
// CHECK: apply [[BARRIER]]()
// CHECK-LABEL: } // end sil function 'hoist_destroy_of_copy_of_lexical_over_deinit_barrier'
sil [ossa] @hoist_destroy_of_copy_of_lexical_over_deinit_barrier : $(@owned C) -> () {
entry(%instance : @owned $C):
%barrier = function_ref @barrier : $@convention(thin) () -> ()
%borrow = function_ref @takeGuaranteedC : $@convention(thin) (@guaranteed C) -> ()
%copy = copy_value %instance : $C
apply %borrow(%instance) : $@convention(thin) (@guaranteed C) -> ()
destroy_value %instance : $C
apply %barrier() : $@convention(thin) () -> ()
destroy_value %copy : $C
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @forward_owned_lexical_value_to_callee : {{.*}} {
// CHECK-NOT: copy_value
// CHECK-LABEL: } // end sil function 'forward_owned_lexical_value_to_callee'
sil [ossa] @forward_owned_lexical_value_to_callee : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%copy = copy_value %instance : $C
%consume = function_ref @takeOwnedC : $@convention(thin) (@owned C) -> ()
apply %consume(%copy) : $@convention(thin) (@owned C) -> ()
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @cantForwardBecauseBorrowing : {{.*}} {
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] :
// CHECK: [[COPY:%[^,]+]] = copy_value [[INSTANCE]]
// CHECK: [[CONSUME_AND_BORROW:%[^,]+]] = function_ref @takeOwnedCAndGuaranteedC
// CHECK: apply [[CONSUME_AND_BORROW]]([[COPY]], [[INSTANCE]])
// CHECK: destroy_value [[INSTANCE]]
// CHECK-LABEL: } // end sil function 'cantForwardBecauseBorrowing'
sil [ossa] @cantForwardBecauseBorrowing :$@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%copy = copy_value %instance : $C
%consumeAndBorrow = function_ref @takeOwnedCAndGuaranteedC : $@convention(thin) (@owned C, @guaranteed C) -> ()
apply %consumeAndBorrow(%copy, %instance) : $@convention(thin) (@owned C, @guaranteed C) -> ()
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}