mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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.
548 lines
24 KiB
Plaintext
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
|
|
}
|