Files
swift-mirror/test/SILOptimizer/copy_propagation.sil
Erik Eckstein e14c1d1f62 SIL, Optimizer: update and handle borrowed-from instructions
Compute, update and handle borrowed-from instruction in various utilities and passes.
Also, used borrowed-from to simplify `gatherBorrowIntroducers` and `gatherEnclosingValues`.
Replace those utilities by `Value.getBorrowIntroducers` and `Value.getEnclosingValues`, which return a lazily computed Sequence of borrowed/enclosing values.
2024-04-10 13:38:10 +02:00

1074 lines
41 KiB
Plaintext

// RUN: %target-sil-opt -update-borrowed-from -copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK,CHECK-OPT
// RUN: %target-sil-opt -update-borrowed-from -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
}
struct MOS : ~Copyable {
deinit {}
}
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) -> ()
sil [ossa] @getMOS : $() -> (@owned MOS)
// -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 as $()
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-NOT: copy_value
// CHECK: begin_borrow
// CHECK: destroy_value
// 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 : $()
}
// CHECK-LABEL: sil [ossa] @dontShortenDeadMoveOnlyLifetime : {{.*}} {
// CHECK: [[GET:%[^,]+]] = function_ref @getMOS
// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier
// CHECK: [[MOS:%[^,]+]] = apply [[GET]]()
// CHECK: [[MOV:%[^,]+]] = move_value [lexical] [[MOS]]
// CHECK: apply [[BARRIER]]()
// CHECK: destroy_value [[MOV]]
// CHECK-LABEL: } // end sil function 'dontShortenDeadMoveOnlyLifetime'
sil [ossa] @dontShortenDeadMoveOnlyLifetime : $@convention(thin) () -> () {
%get = function_ref @getMOS : $@convention(thin) () -> (@owned MOS)
%barrier = function_ref @barrier : $@convention(thin) () -> ()
%mos = apply %get() : $@convention(thin) () -> (@owned MOS)
// Note: This must be lexical so that it doesn't get eliminated as redundant.
%mov = move_value [lexical] %mos : $MOS
apply %barrier() : $@convention(thin) () -> ()
destroy_value %mov : $MOS
%retval = tuple ()
return %retval : $()
}