Files
swift-mirror/test/SILOptimizer/copy_propagation_opaque.sil
Erik Eckstein 98805c9141 Optimizer: let the InstructionDeleter respect deinit-barriers by default
The InstructionDeleter can remove instructions including their destroys and then insert compensating destroys at a new place.
This is effectively destroy-hoisting which doesn't respect deinit-barriers. Therefore it's not done for lexical lifetimes.
However, since https://github.com/swiftlang/swift/pull/85334, the optimizer should treat _all_ lifetimes as fixed and not only lexical lifetimes.

This change adds a `assumeFixedLifetimes` flag to InstructionDeleter which is on by default.
Only mandatory passes (like OSLogOptimization) should turn this off.
2025-12-03 15:53:56 +01:00

548 lines
24 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-opaque-values -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK,CHECK-OPT
// RUN: %target-sil-opt -sil-print-types -copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-opaque-values -debug-only=copy-propagation %s -o /dev/null 2>&1 | %FileCheck %s --check-prefix=CHECK-TRACE
//
// Mandatory copy propagation does not currently handle address-only
// types. It needs a visible layout to inject poison refs.
// %target-sil-opt -mandatory-copy-propagation -canonical-ossa-rewrite-borrows -enable-sil-opaque-values -enable-sil-verify-all %s | %FileCheck %s --check-prefixes=CHECK,CHECK-ONONE
//
// The CHECK-DEBUG prefix is for the controlling the
// debug_values preservation. CHECK-DEBUG are not currently tested
// because we don't have a flag to enable debug_value preservation,
// and it isn't clear that it's useful.
//
// REQUIRES: asserts
sil_stage raw
import Builtin
import Swift
sil [ossa] @getOwnedT : $@convention(thin) <T> () -> (@owned T)
sil [ossa] @takeOwned : $@convention(thin) <T> (@in T) -> ()
sil [ossa] @takeMultipleOwned : $@convention(thin) <T> (@in T, @in T) -> ()
sil [ossa] @takeGuaranteed : $@convention(thin) <T> (@in_guaranteed T) -> ()
sil [ossa] @takeGuaranteedAndOwnedArg : $@convention(thin) <T> (@in_guaranteed T, @in T) -> ()
// Once Mem2Reg supports ownership, it will leave behind extra copies as
// seen in the SIL test below for simple assignment:
// public func testVarAssign<T>(_ t: T) -> T {
// var u = t
// return u
// }
// CopyPropagation should leave behind a single copy and no destroys.
//
// CHECK-LABEL: sil [ossa] @testVarAssign : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
// CHECK: bb0(%0 : @guaranteed $T):
// CHECK-NOT: destroy
// CHECK: [[CPY:%.*]] = copy_value %0 : $T
// CHECK_CHECK-NOT: destroy
// CHECK_CHECK: return [[CPY]] : $T
// CHECK-LABEL: } // end sil function 'testVarAssign'
sil [ossa] @testVarAssign : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T):
%1 = copy_value %0 : $T
%2 = copy_value %1 : $T
destroy_value %1 : $T
return %2 : $T
}
// CHECK-LABEL: sil [ossa] @multiReturnValue : $@convention(thin) <T> (@in_guaranteed T) -> (@out T, @out T) {
// CHECK: bb0(%0 : @guaranteed $T):
// CHECK-NOT: destroy
// CHECK: [[CPY1:%.*]] = copy_value %0 : $T
// CHECK_CHECK-NOT: destroy
// CHECK_CHECK: [[CPY2:%.*]] = copy_value %0 : $T
// CHECK_CHECK-NOT: destroy
// CHECK_CHECK: [[R:%.*]] = tuple ([[CPY1]] : $T, [[CPY2]] : $T)
// CHECK_CHECK-NOT: destroy
// CHECK_CHECK: return [[R]] : $(T, T)
// CHECK-LABEL: } // end sil function 'multiReturnValue'
sil [ossa] @multiReturnValue : $@convention(thin) <T> (@in_guaranteed T) -> (@out T, @out T) {
bb0(%0 : @guaranteed $T):
%1 = copy_value %0 : $T
%2 = copy_value %1 : $T
%3 = copy_value %1 : $T
%4 = tuple (%2 : $T, %3 : $T)
destroy_value %1 : $T
return %4 : $(T, T)
}
// CHECK-LABEL: sil [ossa] @multiCallResult : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
// CHECK: bb0(%0 : @guaranteed $T):
// CHECK_CHECK-NEXT: // function_ref multiReturnValue
// CHECK_CHECK-NEXT: [[F:%.*]] = function_ref @multiReturnValue : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @out τ_0_0)
// CHECK_CHECK-NEXT: [[CALL:%.*]] = apply [[F]]<T>(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @out τ_0_0)
// CHECK_CHECK-NEXT: ([[D1:%.*]], [[D2:%.*]]) = destructure_tuple [[CALL]] : $(T, T)
// CHECK_CHECK-NEXT: destroy_value [[D2]] : $T
// CHECK_CHECK-NEXT: return [[D1]] : $T
// CHECK-LABEL: } // end sil function 'multiCallResult'
sil [ossa] @multiCallResult : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
bb0(%0 : @guaranteed $T):
%1 = copy_value %0 : $T
%2 = function_ref @multiReturnValue : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @out τ_0_0)
%3 = apply %2<T>(%1) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @out τ_0_0)
(%4, %5) = destructure_tuple %3 : $(T, T)
%6 = copy_value %4 : $T
%7 = copy_value %5 : $T
destroy_value %1 : $T
destroy_value %4 : $T
destroy_value %5 : $T
destroy_value %7 : $T
return %6 : $T
}
// CHECK-LABEL: sil [ossa] @testPhi : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T, Bool) -> @out T {
// CHECK: bb0(%0 : @guaranteed $T, %1 : @guaranteed $T, %2 : $Bool):
// CHECK_CHECK-NEXT: struct_extract %2 : $Bool, #Bool._value
// CHECK_CHECK-NEXT: cond_br %{{.*}}, bb1, bb2
//
// CHECK: bb1:
// CHECK_CHECK-NEXT: copy_value %0 : $T
// CHECK_CHECK-NEXT: br bb3(%
//
// CHECK: bb2:
// CHECK_CHECK-NEXT: copy_value %1 : $T
// CHECK_CHECK-NEXT: br bb3(%
//
// CHECK: bb3(%
// CHECK_CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'testPhi'
sil [ossa] @testPhi : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T, Bool) -> @out T {
bb0(%0 : @guaranteed $T, %1 : @guaranteed $T, %2 : $Bool):
%3 = copy_value %0 : $T
%4 = copy_value %1 : $T
%5 = struct_extract %2 : $Bool, #Bool._value
cond_br %5, bb1, bb2
bb1:
%7 = copy_value %3 : $T
br bb3(%7 : $T)
bb2:
%9 = copy_value %4 : $T
br bb3(%9 : $T)
bb3(%11 : @owned $T):
destroy_value %4 : $T
destroy_value %3 : $T
return %11 : $T
}
// CHECK-LABEL: sil [ossa] @testConsume : {{.*}} {
// CHECK: bb0([[ADDR:%[^,]+]] : $*T):
// CHECK: [[INSTANCE:%[^,]+]] = apply
//
// TODO: opt will reuse the original copy for the consuming store.
// CHECK-DEBUG-NEXT: [[STOREVAL:%.*]] = copy_value [[INSTANCE]] : $T
//
// CHECK-NEXT: debug_value [[INSTANCE]] : $T
// CHECK-DEBUG-NEXT: store [[STOREVAL]] to [assign] [[ADDR]] : $*T
// CHECK-NEXT: store [[INSTANCE]] to [assign] [[ADDR]] : $*T
//
// The non-consuming use now uses the original value.
// CHECK-DEBUG-NEXT: debug_value [[INSTANCE]] : $T
// CHECK-NEXT: debug_value [[ADDR]] : $*T, expr op_deref
//
// The original destroy is deleted with optimizations enabled.
// CHECK-DEBUG-NEXT: destroy_value [[INSTANCE]] : $T
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
// CHECK-LABEL: // end sil function 'testConsume'
sil [ossa] @testConsume : $@convention(thin) <T> (@inout T) -> () {
bb0(%addr : $*T):
%getOwnedT = function_ref @getOwnedT : $@convention(thin) <T> () -> (@owned T)
%arg = apply %getOwnedT<T>() : $@convention(thin) <T> () -> (@owned T)
%copy = copy_value %arg : $T
debug_value %copy : $T
store %copy to [assign] %addr : $*T
debug_value %arg : $T
debug_value %addr : $*T, expr op_deref
destroy_value %arg : $T
%v = tuple ()
return %v : $()
}
// CHECK-LABEL: sil [ossa] @testDestroyEdge : {{.*}} {
// CHECK: bb0([[BIT:%[^,]+]] : $Builtin.Int1):
// CHECK: [[INSTANCE:%[^,]+]] = apply
// CHECK-OPT-NEXT: destroy_value [[INSTANCE]] : $T
// CHECK-ONONE-NEXT: destroy_value [poison] [[INSTANCE]] : $T
// CHECK-DEBUG-NEXT: cond_br [[BIT]], bb2, bb1
//
// CHECK: bb1:
// Debug build inserts a new destroy
// CHECK-DEBUG-NEXT: destroy_value [[INSTANCE]] : $T
// CHECK-NEXT: br bb3
//
// CHECK: bb2:
// The original copy is deleted in both cases.
// CHECK-DEBUG-NEXT: debug_value [[INSTANCE]] : $T
// CHECK-DEBUG-NEXT: destroy_value [[INSTANCE]] : $T
// CHECK-NEXT: br bb3
//
// CHECK: bb3:
// The original destroy is deleted in both cases.
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'testDestroyEdge'
sil [ossa] @testDestroyEdge : $@convention(thin) <T> (Builtin.Int1) -> () {
bb0(%z : $Builtin.Int1):
%getOwnedT = function_ref @getOwnedT : $@convention(thin) <T> () -> (@owned T)
%arg = apply %getOwnedT<T>() : $@convention(thin) <T> () -> (@owned T)
cond_br %z, bb2, bb1
bb1:
br bb3
bb2:
debug_value %arg : $T
%copy = copy_value %arg : $T
destroy_value %copy : $T
br bb3
bb3:
destroy_value %arg : $T
%10 = tuple ()
return %10 : $()
}
// Test the same user instruction with both @guaranteed and @owned operands taking the same copied value.
// We need to keep the value alive to the end of the instruction.
//
// CHECK-LABEL: sil [ossa] @testGuaranteedAndOwnedArg : $@convention(thin) <T> (@in T) -> () {
// CHECK: bb0(%0 : @owned $T):
// CHECK-NEXT: [[CPY:%.*]] = copy_value %0 : $T
// CHECK-NEXT: // function_ref takeGuaranteedAndOwnedArg
// CHECK-NEXT: function_ref @takeGuaranteedAndOwnedArg : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0, @in τ_0_0) -> ()
// CHECK-NEXT: apply %{{.*}}<T>(%0, [[CPY]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0, @in τ_0_0) -> ()
// CHECK-NEXT: destroy_value %0 : $T
// CHECK-NEXT: return %{{.*}} : $()
// CHECK-LABEL: } // end sil function 'testGuaranteedAndOwnedArg'
sil [ossa] @testGuaranteedAndOwnedArg : $@convention(thin) <T> (@in T) -> () {
bb(%0 : @owned $T):
%copy = copy_value %0 : $T
%f = function_ref @takeGuaranteedAndOwnedArg : $@convention(thin) <T> (@in_guaranteed T, @in T) -> ()
%call = apply %f<T>(%0, %copy) : $@convention(thin) <T> (@in_guaranteed T, @in T) -> ()
destroy_value %0 : $T
return %call : $()
}
// Reuse one of the copies for the apply. Eliminate the other copy and destroy.
// Which copy is reused is unfortunately sensitive to the use list order.
//
// CHECK-TRACE-LABEL: CopyPropagation: testCopy2OperReuse
// CHECK-TRACE: Removing destroy_value %1 : $T
// CHECK-TRACE: Removing %{{.*}} = copy_value %1 : $T
// CHECK-TRACE-NOT: Removing
//
// CHECK-LABEL: sil [ossa] @testCopy2OperReuse : {{.*}} {
// CHECK: bb0:
// CHECK: [[INSTANCE:%[^,]+]] = apply
// CHECK-NEXT: [[CP:%.*]] = copy_value [[INSTANCE]] : $T
// CHECK-ONONE-NEXT: [[CP:%.*]] = copy_value [[INSTANCE]] : $T
// CHECK-NEXT: // function_ref takeMultipleOwned
// CHECK-NEXT: function_ref @takeMultipleOwned : $@convention(thin) <τ_0_0> (@in τ_0_0, @in τ_0_0) -> ()
// CHECK-NEXT: apply %{{.*}}<T>([[CP]], [[INSTANCE]]) : $@convention(thin) <τ_0_0> (@in τ_0_0, @in τ_0_0) -> ()
// CHECK-ONONE-NEXT: destroy_value
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'testCopy2OperReuse'
sil [ossa] @testCopy2OperReuse : $@convention(thin) <T> () -> () {
bb0:
%getOwnedT = function_ref @getOwnedT : $@convention(thin) <T> () -> (@owned T)
%arg = apply %getOwnedT<T>() : $@convention(thin) <T> () -> (@owned T)
%copy1 = copy_value %arg : $T
%copy2 = copy_value %arg : $T
%f = function_ref @takeMultipleOwned : $@convention(thin) <T> (@in T, @in T) -> ()
%call = apply %f<T>(%copy1, %copy2) : $@convention(thin) <T> (@in T, @in T) -> ()
destroy_value %arg : $T
%10 = tuple ()
return %10 : $()
}
// Reuse one copy and eliminate the other copy and destroy.
//
// CHECK-TRACE-LABEL: *** CopyPropagation: testCopy2CallReuse
// CHECK-TRACE: Removing destroy_value %1 : $T
// CHECK-TRACE: Removing %{{.*}} = copy_value %1 : $T
// CHECK-TRACE-NOT: Removing
//
// CHECK-LABEL: sil [ossa] @testCopy2CallReuse : {{.*}} {
// CHECK: bb0:
// CHECK: [[INSTANCE:%[^,]+]] = apply
// CHECK-NEXT: [[CP:%.*]] = copy_value [[INSTANCE]] : $T
// CHECK-ONONE-NEXT: [[CP:%.*]] = copy_value [[INSTANCE]] : $T
// CHECK-NEXT: // function_ref
// CHECK-NEXT: function_ref
// CHECK-NEXT: apply %{{.*}}<T>([[CP]])
// CHECK-NEXT: apply %{{.*}}<T>([[INSTANCE]])
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'testCopy2CallReuse'
sil [ossa] @testCopy2CallReuse : $@convention(thin) <T> () -> () {
bb0:
%getOwnedT = function_ref @getOwnedT : $@convention(thin) <T> () -> (@owned T)
%arg = apply %getOwnedT<T>() : $@convention(thin) <T> () -> (@owned T)
%copy1 = copy_value %arg : $T
%copy2 = copy_value %arg : $T
%f = function_ref @takeOwned : $@convention(thin) <T> (@in T) -> ()
%call1 = apply %f<T>(%copy1) : $@convention(thin) <T> (@in T) -> ()
%call2 = apply %f<T>(%copy2) : $@convention(thin) <T> (@in T) -> ()
destroy_value %arg : $T
%10 = tuple ()
return %10 : $()
}
// bb1 has a consuming instruction but is also live-out. Reuse the copy in bb1.
//
// CHECK-TRACE-LABEL: *** CopyPropagation: liveoutConsume
// CHECK-TRACE: Removing destroy_value %2 : $T
// CHECK-TRACE: Removing %{{.*}} = copy_value %2 : $T
// CHECK-TRACE-NOT: Removing
//
// CHECK-LABEL: sil [ossa] @liveoutConsume : {{.*}} {
// CHECK: bb0([[BIT:%[^,]+]] : $Builtin.Int1):
// CHECK: [[INSTANCE:%[^,]+]] = apply
// CHECK-NOT: copy_value
// CHECK: cond_br [[BIT]], bb2, bb1
// CHECK: bb1:
// CHECK: copy_value [[INSTANCE]] : $T
// CHECK: apply
// CHECK: br bb3
// CHECK: bb3:
// CHECK-NOT: copy_value
// CHECK: apply
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function 'liveoutConsume'
sil [ossa] @liveoutConsume : $@convention(thin) <T> (Builtin.Int1) -> () {
bb0(%z : $Builtin.Int1):
%getOwnedT = function_ref @getOwnedT : $@convention(thin) <T> () -> (@owned T)
%arg = apply %getOwnedT<T>() : $@convention(thin) <T> () -> (@owned T)
%copy1 = copy_value %arg : $T
cond_br %z, bb2, bb1
bb1:
%copy2 = copy_value %arg : $T
%f1 = function_ref @takeOwned : $@convention(thin) <T> (@in T) -> ()
%call1 = apply %f1<T>(%copy2) : $@convention(thin) <T> (@in T) -> ()
br bb3
bb2:
br bb3
bb3:
%f2 = function_ref @takeOwned : $@convention(thin) <T> (@in T) -> ()
%call2 = apply %f2<T>(%copy1) : $@convention(thin) <T> (@in T) -> ()
destroy_value %arg : $T
%10 = tuple ()
return %10 : $()
}
// The LiveWithin block has a destroy, but it's before the first use.
//
// CHECK-TRACE-LABEL: *** CopyPropagation: testDestroyBeforeUse
// CHECK-TRACE: Removing destroy_value %2 : $T
// CHECK-TRACE: Removing %{{.*}} = copy_value %1 : $T
//
// CHECK-LABEL: sil [ossa] @testDestroyBeforeUse : {{.*}} {
// CHECK: bb0:
// CHECK-NOT: copy_value
// CHECK-NOT: destroy_value
// CHECK: apply
// CHECK-NOT: destroy_value
// CHECK: return
// CHECK-LABEL: } // end sil function 'testDestroyBeforeUse'
sil [ossa] @testDestroyBeforeUse : $@convention(thin) <T> () -> () {
bb0:
%getOwnedT = function_ref @getOwnedT : $@convention(thin) <T> () -> (@owned T)
%arg = apply %getOwnedT<T>() : $@convention(thin) <T> () -> (@owned T)
%copy = copy_value %arg : $T
destroy_value %copy : $T
%f = function_ref @takeOwned : $@convention(thin) <T> (@in T) -> ()
%call2 = apply %f<T>(%arg) : $@convention(thin) <T> (@in T) -> ()
%10 = tuple ()
return %10 : $()
}
// The LiveWithin block has a destroy, but it's after an unrelated call.
//
// CHECK-TRACE-LABEL: *** CopyPropagation: testDestroyAfterCall
// CHECK-TRACE-NOT: Removing
//
// CHECK-LABEL: sil [ossa] @testDestroyAfterCall : $@convention(thin) <T> (@in T, @in T) -> () {
// CHECK: bb0(%0 : @owned $T, %1 : @owned $T):
// CHECK-ONONE: destroy_value %1 : $T
// CHECK: apply %{{.*}}<T>(%0) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
// CHECK-OPT: destroy_value %1 : $T
// CHECK-LABEL: } // end sil function 'testDestroyAfterCall'
sil [ossa] @testDestroyAfterCall : $@convention(thin) <T> (@in T, @in T) -> () {
bb0(%arg1 : @owned $T, %arg2 : @owned $T):
%f = function_ref @takeOwned : $@convention(thin) <T> (@in T) -> ()
%call = apply %f<T>(%arg1) : $@convention(thin) <T> (@in T) -> ()
destroy_value %arg2 : $T
%10 = tuple ()
return %10 : $()
}
// A copy may have multiple uses
//
// CHECK-TRACE-LABEL: *** CopyPropagation: testSharedCopy
// CHECK-TRACE: Removing destroy_value %1 : $T
// CHECK-TRACE: Removing %2 = copy_value %1 : $T
// CHECK-TRACE-NOT: Removing
//
// CHECK-LABEL: sil [ossa] @testSharedCopy : {{.*}} {
// CHECK-NOT: copy_value
// CHECK: apply
// CHECK: apply
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function 'testSharedCopy'
sil [ossa] @testSharedCopy : $@convention(thin) <T> () -> () {
bb0:
%getOwnedT = function_ref @getOwnedT : $@convention(thin) <T> () -> (@owned T)
%arg = apply %getOwnedT<T>() : $@convention(thin) <T> () -> (@owned T)
%copy = copy_value %arg : $T
%f1 = function_ref @takeGuaranteed : $@convention(thin) <T> (@in_guaranteed T) -> ()
%call1 = apply %f1<T>(%copy) : $@convention(thin) <T> (@in_guaranteed T) -> ()
%f2 = function_ref @takeOwned : $@convention(thin) <T> (@in T) -> ()
%call2 = apply %f2<T>(%copy) : $@convention(thin) <T> (@in T) -> ()
destroy_value %arg : $T
%10 = tuple ()
return %10 : $()
}
// CHECK-TRACE-LABEL: *** CopyPropagation: testBorrowCopy
// CHECK-TRACE: Canonicalizing: %1
// CHECK-TRACE: Removing destroy_value %3 : $T
// CHECK-TRACE: Removing %3 = copy_value %1 : $T
//
// CHECK-LABEL: sil [ossa] @testBorrowCopy : {{.*}} {
// CHECK-LABEL: bb0:
// CHECK: [[INSTANCE:%[^,]+]] = apply
// CHECK-NEXT: begin_borrow
// CHECK-NEXT: end_borrow
// CHECK-NEXT: destroy_value [[INSTANCE]] : $T
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
// CHECK-LABEL: }
sil [ossa] @testBorrowCopy : $@convention(thin) <T> () -> () {
bb0:
%getOwnedT = function_ref @getOwnedT : $@convention(thin) <T> () -> (@owned T)
%arg = apply %getOwnedT<T>() : $@convention(thin) <T> () -> (@owned T)
%3 = begin_borrow %arg : $T
%4 = copy_value %3 : $T
end_borrow %3 : $T
destroy_value %4 : $T
destroy_value %arg : $T
%17 = tuple ()
return %17 : $()
}
// CHECK-TRACE-LABEL: *** CopyPropagation: testCopyBorrow
//
// CHECK-LABEL: sil [ossa] @testCopyBorrow : $@convention(thin) <T> (@in T) -> () {
// CHECK: bb0(%0 : @owned $T):
// CHECK: destroy_value %0 : $T
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'testCopyBorrow'
sil [ossa] @testCopyBorrow : $@convention(thin) <T> (@in T) -> () {
bb0(%0 : @owned $T):
%1 = copy_value %0 : $T
%2 = begin_borrow %1 : $T
end_borrow %2 : $T
destroy_value %1 : $T
destroy_value %0 : $T
%17 = tuple ()
return %17 : $()
}
sil @testThrows : $@convention(thin) <τ_0_0> (τ_0_0) -> (@error Error)
// CHECK-TRACE-LABEL: *** CopyPropagation: testTryApply
//
// CHECK-LABEL: sil [ossa] @testTryApply : {{.*}} {
// CHECK: bb0:
// CHECK: [[INSTANCE:%[^,]+]] = apply
// CHECK: function_ref @testThrows : $@convention(thin) <τ_0_0> (τ_0_0) -> @error any Error
// CHECK: try_apply %{{.*}}<T>([[INSTANCE]]) : $@convention(thin) <τ_0_0> (τ_0_0) -> @error any Error, normal bb1, error bb2
// CHECK: bb1({{%[^,]+}} : $()):
// CHECK: destroy_value [[INSTANCE]] : $T
// CHECK: br bb3
// CHECK: bb2(%{{.*}} : @owned $any Error):
// CHECK: destroy_value [[INSTANCE]] : $T
// CHECK: destroy_value %{{.*}} : $any Error
// CHECK: br bb3
// CHECK: bb3:
// CHECK-NOT: destroy
// CHECK: return
// CHECK-LABEL: } // end sil function 'testTryApply'
sil [ossa] @testTryApply : $@convention(thin) <T> () -> (@error Error) {
bb0:
%getOwnedT = function_ref @getOwnedT : $@convention(thin) <T> () -> (@owned T)
%0 = apply %getOwnedT<T>() : $@convention(thin) <T> () -> (@owned T)
%1 = copy_value %0 : $T
destroy_value %0 : $T
%f = function_ref @testThrows : $@convention(thin) <τ_0_0> (τ_0_0) -> (@error Error)
try_apply %f<T>(%1) : $@convention(thin) <τ_0_0> (τ_0_0) -> (@error Error), normal bb1, error bb2
bb1(%returnval : $()):
br bb3
bb2(%error : @owned $Error):
destroy_value %error : $Error
br bb3
bb3:
destroy_value %1 : $T
%17 = tuple ()
return %17 : $()
}
// -----------------------------------------------------------------------------
// Test that convert_escape_to_noescape is a PointerEscape
sil @closure : $@convention(thin) <T> (@thick T.Type) -> @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> Bool for <T, T>
sil @takeClosure : $@convention(thin) <T> (@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <T, T>) -> (@owned AnyObject, @error Error)
// CHECK-TRACE-LABEL: *** CopyPropagation: testConvertFunction
//
// CHECK-LABEL: sil [ossa] @testConvertFunction : $@convention(thin) <T> (@in_guaranteed T) -> @owned AnyObject {
// CHECK: bb0(%0 : @guaranteed $T):
// CHECK: [[CLOSURE:%.*]] = apply %{{.*}}<T>(%{{.*}}) : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> Bool for <τ_0_0, τ_0_0>
// CHECK: [[CONVERT:%.*]] = convert_function [[CLOSURE]] : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> Bool for <T, T> to $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error any Error) for <T, T>
// CHECK: [[COPY:%.*]] = copy_value [[CONVERT]] : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error any Error) for <T, T>
// CHECK: [[NOESCAPE:%.*]] = convert_escape_to_noescape [[COPY]] : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error any Error) for <T, T> to $@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error any Error) for <T, T>
// CHECK: try_apply %{{.*}}<T>([[NOESCAPE]]) : $@convention(thin) <τ_0_0> (@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error any Error) for <τ_0_0, τ_0_0>) -> (@owned AnyObject, @error any Error), normal bb1, error bb2
// CHECK: bb1
// CHECK: destroy_value [[COPY]] : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error any Error) for <T, T>
// CHECK: destroy_value [[CONVERT]] : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error any Error) for <T, T>
// CHECK: return
// CHECK: bb2
// CHECK: destroy_value [[COPY]] : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error any Error) for <T, T>
// CHECK: unreachable
// CHECK-LABEL: } // end sil function 'testConvertFunction'
sil [ossa] @testConvertFunction : $@convention(thin) <T> (@in_guaranteed T) -> @owned AnyObject {
bb0(%0 : @guaranteed $T):
%2 = metatype $@thick T.Type
%3 = function_ref @closure : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> Bool for <τ_0_0, τ_0_0>
%4 = apply %3<T>(%2) : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> Bool for <τ_0_0, τ_0_0>
%5 = convert_function %4 : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> Bool for <T, T> to $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <T, T>
%6 = copy_value %5 : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <T, T>
%7 = convert_escape_to_noescape %6 : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <T, T> to $@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <T, T>
%8 = function_ref @takeClosure : $@convention(thin) <τ_0_0> (@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <τ_0_0, τ_0_0>) -> (@owned AnyObject, @error Error)
try_apply %8<T>(%7) : $@convention(thin) <τ_0_0> (@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <τ_0_0, τ_0_0>) -> (@owned AnyObject, @error Error), normal bb1, error bb2
bb1(%10 : @owned $AnyObject):
destroy_value %7 : $@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <T, T>
destroy_value %6 : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <T, T>
destroy_value %5 : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <T, T>
return %10 : $AnyObject
bb2(%14 : @owned $Error):
destroy_value %7 : $@noescape @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <T, T>
destroy_value %6 : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_1) -> (Bool, @error Error) for <T, T>
unreachable
}