blob: 8c6dc402de1ed41fb429ed854ad8b98276f9e481 [file] [log] [blame]
// 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 [init] %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 [init] %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 : $()
}