// 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 { // var u = t // return u // } // CopyPropagation should leave behind a single copy and no destroys. // // CHECK-LABEL: sil [ossa] @testVarAssign : $@convention(thin) (@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) (@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) (@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) (@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) (@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]](%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) (@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(%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) (@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) (@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) (@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) (@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) (@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) (@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) (@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) (@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 %{{.*}}(%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) (@in T) -> () { bb(%0 : @owned $T): %copy = copy_value %0 : $T %f = function_ref @takeGuaranteedAndOwnedArg : $@convention(thin) (@in_guaranteed T, @in T) -> () %call = apply %f(%0, %copy) : $@convention(thin) (@in_guaranteed T, @in T) -> () destroy_value %0 : $T return %call : $() }