mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
205 lines
7.1 KiB
Plaintext
205 lines
7.1 KiB
Plaintext
// RUN: %target-sil-opt -copy-propagation -enable-sil-opaque-values -enable-sil-verify-all %s | %FileCheck %s
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
// 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-NOT: destroy
|
|
// 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: 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-NOT: destroy
|
|
// CHECK: [[CPY2:%.*]] = copy_value %0 : $T
|
|
// CHECK-NOT: destroy
|
|
// CHECK: [[R:%.*]] = tuple ([[CPY1]] : $T, [[CPY2]] : $T)
|
|
// CHECK-NOT: destroy
|
|
// 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-NEXT: // function_ref multiReturnValue
|
|
// CHECK-NEXT: [[F:%.*]] = function_ref @multiReturnValue : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @out τ_0_0)
|
|
// CHECK-NEXT: [[CALL:%.*]] = apply [[F]]<T>(%0) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @out τ_0_0)
|
|
// CHECK-NEXT: ([[D1:%.*]], [[D2:%.*]]) = destructure_tuple [[CALL]] : $(T, T)
|
|
// CHECK-NEXT: destroy_value [[D2]] : $T
|
|
// 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-NEXT: %3 = struct_extract %2 : $Bool, #Bool._value
|
|
// CHECK-NEXT: cond_br %3, bb1, bb2
|
|
//
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: %5 = copy_value %0 : $T
|
|
// CHECK-NEXT: br bb3(%5 : $T)
|
|
//
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: %7 = copy_value %1 : $T
|
|
// CHECK-NEXT: br bb3(%7 : $T)
|
|
//
|
|
// CHECK: bb3(%9 : @owned $T):
|
|
// CHECK-NEXT: return %9 : $T
|
|
// 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 : $@convention(thin) <T> (@in T, @inout T) -> () {
|
|
// CHECK: bb0(%0 : @owned $T, %1 : $*T):
|
|
//
|
|
// The original copy_value is deleted.
|
|
// CHECK-NEXT: debug_value %0 : $T
|
|
//
|
|
// A new copy_value is inserted before the consuming store.
|
|
// CHECK-NEXT: %3 = copy_value %0 : $T
|
|
// CHECK-NEXT: store %3 to [assign] %1 : $*T
|
|
//
|
|
// The non-consuming use now uses the original value.
|
|
// CHECK-NEXT: debug_value %0 : $T
|
|
//
|
|
// A new destroy is inserted after the last use.
|
|
// CHECK-NEXT: destroy_value %0 : $T
|
|
// CHECK-NEXT: debug_value_addr %1 : $*T
|
|
//
|
|
// The original destroy is deleted.
|
|
// CHECK-NEXT: %8 = tuple ()
|
|
// CHECK-NEXT: return %8 : $()
|
|
// CHECK-LABEL: // end sil function 'testConsume'
|
|
sil [ossa] @testConsume : $@convention(thin) <T> (@in T, @inout T) -> () {
|
|
bb0(%arg : @owned $T, %addr : $*T):
|
|
%copy = copy_value %arg : $T
|
|
debug_value %copy : $T
|
|
store %copy to [assign] %addr : $*T
|
|
debug_value %arg : $T
|
|
debug_value_addr %addr : $*T
|
|
destroy_value %arg : $T
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @testDestroyEdge : $@convention(thin) <T> (@in T, @inout T, Builtin.Int1) -> () {
|
|
// CHECK: bb0(%0 : @owned $T, %1 : $*T, %2 : $Builtin.Int1):
|
|
// CHECK-NEXT: cond_br %2, bb2, bb1
|
|
//
|
|
// CHECK: bb1:
|
|
//
|
|
// The critical edge is split.
|
|
// CHECK-NEXT: destroy_value %0 : $T
|
|
// CHECK-NEXT: br bb3
|
|
//
|
|
// CHECK: bb2:
|
|
// The original copy is deleted.
|
|
// CHECK-NEXT: debug_value %0 : $T
|
|
// CHECK-NEXT: destroy_value %0 : $T
|
|
// CHECK-NEXT: br bb3
|
|
//
|
|
// CHECK: bb3:
|
|
// The original destroy is deleted.
|
|
// CHECK-NEXT: %9 = tuple ()
|
|
// CHECK-NEXT: return %9 : $()
|
|
// CHECK-LABEL: } // end sil function 'testDestroyEdge'
|
|
sil [ossa] @testDestroyEdge : $@convention(thin) <T> (@in T, @inout T, Builtin.Int1) -> () {
|
|
bb0(%arg : @owned $T, %addr : $*T, %z : $Builtin.Int1):
|
|
cond_br %z, bb1, bb2
|
|
|
|
bb1:
|
|
debug_value %arg : $T
|
|
%copy = copy_value %arg : $T
|
|
destroy_value %copy : $T
|
|
br bb2
|
|
|
|
bb2:
|
|
destroy_value %arg : $T
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
sil [ossa] @takeGuaranteedAndOwnedArg : $@convention(thin) <T> (@in_guaranteed T, @in T) -> ()
|
|
|
|
// 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: // function_ref takeGuaranteedAndOwnedArg
|
|
// CHECK-NEXT: function_ref @takeGuaranteedAndOwnedArg : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0, @in τ_0_0) -> ()
|
|
// CHECK-NEXT: [[CPY:%.*]] = copy_value %0 : $T
|
|
// 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 : $()
|
|
}
|