Files
swift-mirror/test/SILOptimizer/conditionforwarding_ossa.sil
Erik Eckstein 2ce9ddeb46 ConditionForwarding: don't violate ownership
Instructions in a block, which is moved, must not use any (non-trivial) value because we don't do liveness analysis.
When moving a block, there is no guarantee that the operand value is still alive at the new location.

Fixes an ownership violation error
rdar://146630743
2025-03-10 12:48:14 +01:00

457 lines
10 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -sil-print-debuginfo -enable-sil-verify-all %s -condition-forwarding | %FileCheck %s
import Builtin
import Swift
sil_stage canonical
enum E { case A, B }
enum E3 { case A, B, C }
class C {
@_hasStorage var x : Builtin.Int64
init()
}
final class D: C {}
sil [ossa] @callee : $@convention(thin) () -> ()
sil [ossa] @use_enum : $@convention(thin) (E) -> ()
sil [ossa] @use_int : $@convention(thin) (Builtin.Int64) -> ()
// CHECK-LABEL: sil [ossa] @simple_forwarding
// CHECK: bb0({{.*}}):
// CHECK-NEXT: br bb3
// CHECK: bb1:
// CHECK: br bb4,
// CHECK: bb2:
// CHECK: br bb5,
// CHECK: bb3:
// CHECK: apply
// CHECK: cond_br %0, bb1, bb2
// CHECK: bb4:
// CHECK: br bb6
// CHECK: bb5:
// CHECK: br bb6
// CHECK: bb6:
// CHECK: return
sil [ossa] @simple_forwarding : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
cond_br %0, bb1, bb2
bb1:
%1 = enum $E, #E.A!enumelt
br bb3(%1 : $E)
bb2:
%2 = enum $E, #E.B!enumelt
br bb3(%2 : $E)
bb3(%14 : $E):
%15 = function_ref @callee : $@convention(thin) () -> ()
%16 = apply %15() : $@convention(thin) () -> ()
switch_enum %14 : $E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5
bb4:
br bb6
bb5:
br bb6
bb6:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @closed_range_iterator_example
// CHECK: bb0({{.*}}):
// CHECK-NEXT: br bb3
// CHECK: bb1:
// CHECK: [[PL:%[0-9]+]] = struct $Int64
// CHECK: br bb4([[PL]] : $Int64)
// CHECK: bb2:
// CHECK: [[O:%[0-9]+]] = enum $Optional<Int64>
// CHECK: br bb5([[O]] : $Optional<Int64>)
// CHECK: bb3:
// CHECK: apply
// CHECK: cond_br %0, bb1, bb2
// CHECK: bb4({{%[0-9]+}} : $Int64):
// CHECK: br bb6
// CHECK: bb5([[O2:%[0-9]+]] : $Optional<Int64>):
// CHECK: br bb6([[O2]] : $Optional<Int64>)
// CHECK: bb6
// CHECK: return
sil [ossa] @closed_range_iterator_example : $@convention(thin) (Builtin.Int1, Builtin.Int64) -> Optional<Int64> {
bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int64):
cond_br %0, bb1, bb2
bb1:
%3 = integer_literal $Builtin.Int64, 1
%4 = integer_literal $Builtin.Int1, -1
%5 = builtin "sadd_with_overflow_Int64"(%1 : $Builtin.Int64, %3 : $Builtin.Int64, %4 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0
%7 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 1
cond_fail %7 : $Builtin.Int1
%9 = struct $Int64 (%6 : $Builtin.Int64)
%10 = enum $Optional<Int64>, #Optional.some!enumelt, %9 : $Int64
br bb3(%10 : $Optional<Int64>)
bb2:
%12 = enum $Optional<Int64>, #Optional.none!enumelt
br bb3(%12 : $Optional<Int64>)
bb3(%14 : $Optional<Int64>):
debug_value %14 : $Optional<Int64>
%15 = function_ref @callee : $@convention(thin) () -> ()
%16 = apply %15() : $@convention(thin) () -> ()
switch_enum %14 : $Optional<Int64>, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5
bb4(%18 : $Int64):
%19 = enum $Optional<Int64>, #Optional.some!enumelt, %18 : $Int64
br bb6(%19 : $Optional<Int64>)
bb5:
br bb6(%14 : $Optional<Int64>)
bb6(%22 : $Optional<Int64>):
return %22 : $Optional<Int64>
}
// CHECK-LABEL: sil [ossa] @simple_switch_enum_forwarding
// CHECK: bb0({{.*}}):
// CHECK-NEXT: br bb3
// CHECK: bb1:
// CHECK: br bb4,
// CHECK: bb2({{.*}}):
// CHECK: br bb5,
// CHECK: bb3:
// CHECK: apply
// CHECK: switch_enum %0 : $E3, case #E3.A!enumelt: bb1, default bb2
// CHECK: bb4:
// CHECK: br bb6
// CHECK: bb5:
// CHECK: br bb6
// CHECK: bb6:
// CHECK: return
sil [ossa] @simple_switch_enum_forwarding : $@convention(thin) (E3) -> () {
bb0(%0 : $E3):
switch_enum %0 : $E3, case #E3.A!enumelt: bb1, default bb2
bb1:
%1 = enum $E, #E.A!enumelt
br bb3(%1 : $E)
bb2(%defaultcase : $E3):
%2 = enum $E, #E.B!enumelt
br bb3(%2 : $E)
bb3(%14 : $E):
%15 = function_ref @callee : $@convention(thin) () -> ()
%16 = apply %15() : $@convention(thin) () -> ()
switch_enum %14 : $E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5
bb4:
br bb6
bb5:
br bb6
bb6:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @no_forwarding_side_effect_inst
// CHECK: bb0({{.*}}):
// CHECK: cond_br %0
// CHECK: bb3({{.*}}):
// CHECK: switch_enum
// CHECK: bb6
// CHECK: return
sil [ossa] @no_forwarding_side_effect_inst : $@convention(thin) (Builtin.Int1, @guaranteed C) -> () {
bb0(%0 : $Builtin.Int1, %1 : @guaranteed $C):
%e = ref_element_addr %1 : $C, #C.x
cond_br %0, bb1, bb2
bb1:
%2 = enum $E, #E.A!enumelt
br bb3(%2 : $E)
bb2:
%3 = enum $E, #E.B!enumelt
%4 = load [trivial] %e : $*Builtin.Int64
br bb3(%3 : $E)
bb3(%14 : $E):
%15 = function_ref @callee : $@convention(thin) () -> ()
%16 = apply %15() : $@convention(thin) () -> ()
switch_enum %14 : $E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5
bb4:
br bb6
bb5:
br bb6
bb6:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @no_forwarding_second_enum_use
// CHECK: bb0({{.*}}):
// CHECK: cond_br %0
// CHECK: bb3({{.*}}):
// CHECK: switch_enum
// CHECK: bb6
// CHECK: return
sil [ossa] @no_forwarding_second_enum_use : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
cond_br %0, bb1, bb2
bb1:
%1 = enum $E, #E.A!enumelt
br bb3(%1 : $E)
bb2:
%2 = enum $E, #E.B!enumelt
br bb3(%2 : $E)
bb3(%14 : $E):
%15 = function_ref @use_enum : $@convention(thin) (E) -> ()
%16 = apply %15(%14) : $@convention(thin) (E) -> ()
switch_enum %14 : $E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5
bb4:
br bb6
bb5:
br bb6
bb6:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @no_forwarding_bad_control_flow
// CHECK: bb0({{.*}}):
// CHECK: cond_br %0
// CHECK: bb3({{.*}}):
// CHECK: switch_enum
// CHECK: bb6
// CHECK: return
sil [ossa] @no_forwarding_bad_control_flow : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
cond_br %0, bb9, bb2
bb1:
%1 = enum $E, #E.A!enumelt
br bb3(%1 : $E)
bb2:
%2 = enum $E, #E.B!enumelt
br bb3(%2 : $E)
bb3(%14 : $E):
%15 = function_ref @callee : $@convention(thin) () -> ()
%16 = apply %15() : $@convention(thin) () -> ()
switch_enum %14 : $E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5
bb4:
br bb7
bb5:
cond_br undef, bb6, bb10
bb6:
br bb1
bb7:
br bb8
bb8:
%r = tuple ()
return %r : $()
bb9:
br bb1
bb10:
br bb7
}
// CHECK-LABEL: sil [ossa] @no_forwarding_multiple_block_args
// CHECK: bb0({{.*}}):
// CHECK: cond_br %0
// CHECK: bb3({{.*}}):
// CHECK: switch_enum
// CHECK: bb6
// CHECK: return
sil [ossa] @no_forwarding_multiple_block_args : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
cond_br %0, bb1, bb2
bb1:
%i1 = integer_literal $Builtin.Int64, 1
%1 = enum $E, #E.A!enumelt
br bb3(%1 : $E, %i1 : $Builtin.Int64)
bb2:
%i2 = integer_literal $Builtin.Int64, 2
%2 = enum $E, #E.B!enumelt
br bb3(%2 : $E, %i2 : $Builtin.Int64)
bb3(%14 : $E, %i3 : $Builtin.Int64):
%15 = function_ref @use_int : $@convention(thin) (Builtin.Int64) -> ()
%16 = apply %15(%i3) : $@convention(thin) (Builtin.Int64) -> ()
switch_enum %14 : $E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5
bb4:
br bb6
bb5:
br bb6
bb6:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @no_forwarding_additional_successor
// CHECK: bb0({{.*}}):
// CHECK: switch_enum %0
// CHECK: bb3({{.*}}):
// CHECK: switch_enum
// CHECK: bb6
// CHECK: return
sil [ossa] @no_forwarding_additional_successor : $@convention(thin) (E3) -> () {
bb0(%0 : $E3):
switch_enum %0 : $E3, case #E3.A!enumelt: bb1, case #E3.B!enumelt: bb2, case #E3.C!enumelt: bb6
bb1:
%1 = enum $E, #E.A!enumelt
br bb3(%1 : $E)
bb2:
%2 = enum $E, #E.B!enumelt
br bb3(%2 : $E)
bb3(%14 : $E):
%15 = function_ref @callee : $@convention(thin) () -> ()
%16 = apply %15() : $@convention(thin) () -> ()
switch_enum %14 : $E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5
bb4:
br bb7
bb5:
br bb7
bb6:
br bb7
bb7:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @forwarding_second_enum_use :
// CHECK-NOT: switch_enum
// CHECK-LABEL:} // end sil function 'forwarding_second_enum_use'
sil [ossa] @forwarding_second_enum_use : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
cond_br %0, bb1, bb2
bb1:
%1 = enum $E, #E.A!enumelt
br bb3(%1 : $E)
bb2:
%2 = enum $E, #E.B!enumelt
br bb3(%2 : $E)
bb3(%14 : $E):
switch_enum %14 : $E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5
bb4:
%15 = function_ref @use_enum : $@convention(thin) (E) -> ()
%16 = apply %15(%14) : $@convention(thin) (E) -> ()
br bb6
bb5:
br bb6
bb6:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @cast_with_type_dependent_operand :
// CHECK-NOT: switch_enum
// CHECK-LABEL: } // end sil function 'cast_with_type_dependent_operand'
sil [ossa] @cast_with_type_dependent_operand : $@convention(method) (@guaranteed C, @guaranteed D) -> () {
bb0(%0 : @guaranteed $C, %1 : @guaranteed $D):
checked_cast_br C in %0 to @dynamic_self D, bb1, bb2
bb1(%3 : @guaranteed $D):
%4 = enum $Optional<D>, #Optional.some!enumelt, %3
br bb3(%4)
bb2(%6 : @guaranteed $C):
%7 = enum $Optional<D>, #Optional.none!enumelt
br bb3(%7)
bb3(%9 : @guaranteed $Optional<D>):
%10 = borrowed %9 from (%0)
switch_enum %10, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5
bb4(%12 : @guaranteed $D):
br bb6
bb5:
br bb6
bb6:
%15 = tuple ()
return %15
}
// CHECK-LABEL: sil [ossa] @copy_from_limited_liferange :
// CHECK: switch_enum
// CHECK-LABEL: } // end sil function 'copy_from_limited_liferange'
sil [ossa] @copy_from_limited_liferange : $@convention(thin) (Builtin.Int1, @guaranteed C) -> () {
bb0(%0 : $Builtin.Int1, %1 : @guaranteed $C):
%2 = begin_borrow [lexical] %1
cond_br %0, bb1, bb2
bb1:
%4 = copy_value %2
%5 = enum $Optional<C>, #Optional.some!enumelt, %4
br bb3(%5)
bb2:
%7 = enum $Optional<C>, #Optional.none!enumelt
br bb3(%7)
bb3(%9 : @owned $Optional<C>):
end_borrow %2
switch_enum %9, case #Optional.some!enumelt: bb5, case #Optional.none!enumelt: bb4
bb4:
br bb6
bb5(%13 : @owned $C):
destroy_value %13
br bb6
bb6:
%16 = tuple ()
return %16
}