| // RUN: %target-sil-opt -enable-sil-verify-all %s -late-codemotion | FileCheck %s |
| |
| import Builtin |
| import Swift |
| |
| class B { } |
| class E : B { } |
| |
| enum FakeOptional<T> { |
| case None |
| case Some(T) |
| } |
| |
| |
| class C {} |
| |
| enum Either { |
| case First(Builtin.NativeObject) |
| case Second(C) |
| } |
| |
| enum ThreeCaseEnum { |
| case A |
| case B |
| case C |
| } |
| |
| struct S { |
| var ptr : Builtin.NativeObject |
| } |
| |
| enum ThreeCaseAbstractionDifference { |
| case A(Builtin.Int32) |
| case B(Builtin.NativeObject) |
| case C(S) |
| } |
| |
| sil @user : $@convention(thin) (Builtin.NativeObject) -> () |
| sil @optional_user : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () |
| sil @blocker : $@convention(thin) () -> () |
| sil @get_object : $@convention(thin) () -> Builtin.NativeObject |
| |
| // CHECK-LABEL: sil @sink_from_preds |
| // CHECK: bb1: |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb2: |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb3: |
| // CHECK: strong_retain |
| // CHECK: return |
| sil @sink_from_preds : $@convention(thin) (Builtin.Int1, B) -> () { |
| bb0(%0 : $Builtin.Int1, %1 : $B): |
| cond_br %0, bb1, bb2 |
| |
| bb1: |
| %2 = strong_retain %1 : $B |
| br bb3 |
| |
| bb2: |
| %3 = strong_retain %1 : $B |
| br bb3 |
| |
| bb3: |
| %4 = tuple() |
| return %4 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_from_preds_through_args |
| // CHECK: bb1: |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb2: |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb3([[A:%[0-9]+]] : $B): |
| // CHECK: strong_retain [[A]] |
| // CHECK: return |
| sil @sink_from_preds_through_args : $@convention(thin) (Builtin.Int1, B, B) -> () { |
| bb0(%0 : $Builtin.Int1, %1 : $B, %2 : $B): |
| cond_br %0, bb1, bb2 |
| |
| bb1: |
| %r1 = strong_retain %1 : $B |
| br bb3(%1 : $B) |
| |
| bb2: |
| %r2 = strong_retain %2 : $B |
| br bb3(%2 : $B) |
| |
| bb3(%a1 : $B): |
| %t1 = tuple() |
| return %t1 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_from_preds_not_through_args |
| // CHECK: bb1: |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb2: |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb3({{%[0-9]+}} : $B): |
| // CHECK: strong_retain %1 |
| // CHECK: return |
| sil @sink_from_preds_not_through_args : $@convention(thin) (Builtin.Int1, B, B) -> () { |
| bb0(%0 : $Builtin.Int1, %1 : $B, %2 : $B): |
| cond_br %0, bb1, bb2 |
| |
| bb1: |
| %r1 = strong_retain %1 : $B |
| br bb3(%1 : $B) |
| |
| bb2: |
| %r2 = strong_retain %1 : $B |
| br bb3(%2 : $B) |
| |
| bb3(%a1 : $B): |
| %t1 = tuple() |
| return %t1 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_from_preds_through_args2 |
| // CHECK: bb1: |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb2: |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb3({{%[0-9]+}} : $B, [[A:%[0-9]+]] : $B): |
| // CHECK: strong_retain [[A]] |
| // CHECK: return |
| sil @sink_from_preds_through_args2 : $@convention(thin) (Builtin.Int1, B, B, B) -> () { |
| bb0(%0 : $Builtin.Int1, %1 : $B, %2 : $B, %3 : $B): |
| cond_br %0, bb1, bb2 |
| |
| bb1: |
| %r1 = strong_retain %1 : $B |
| br bb3(%3 : $B, %1 : $B) |
| |
| bb2: |
| %r2 = strong_retain %2 : $B |
| br bb3(%3 : $B, %2 : $B) |
| |
| bb3(%a1 : $B, %a2 : $B): |
| %t1 = tuple() |
| return %t1 : $() |
| } |
| |
| // CHECK-LABEL: sil @do_not_sink_from_preds_through_args |
| // CHECK: bb1: |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb2: |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: br bb3 |
| // CHECK-NOT: strong_retain |
| // CHECK: return |
| sil @do_not_sink_from_preds_through_args : $@convention(thin) (Builtin.Int1, B, B, B) -> () { |
| bb0(%0 : $Builtin.Int1, %1 : $B, %2 : $B, %3 : $B): |
| cond_br %0, bb1, bb2 |
| |
| bb1: |
| %r1 = strong_retain %1 : $B |
| br bb3(%1 : $B) |
| |
| bb2: |
| %r2 = strong_retain %2 : $B |
| br bb3(%3 : $B) |
| |
| bb3(%a1 : $B): |
| %t1 = tuple() |
| return %t1 : $() |
| } |
| |
| // CHECK-LABEL: sil @do_not_sink_from_preds_through_args2 |
| // CHECK: bb1: |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb2: |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: br bb3 |
| // CHECK-NOT: strong_retain |
| // CHECK: return |
| sil @do_not_sink_from_preds_through_args2 : $@convention(thin) (Builtin.Int1, B, B, B) -> () { |
| bb0(%0 : $Builtin.Int1, %1 : $B, %2 : $B, %3 : $B): |
| cond_br %0, bb1, bb2 |
| |
| bb1: |
| %r1 = strong_retain %1 : $B |
| br bb3(%1 : $B, %3 : $B) |
| |
| bb2: |
| %r2 = strong_retain %2 : $B |
| br bb3(%3 : $B, %2 : $B) |
| |
| bb3(%a1 : $B, %a2 : $B): |
| %t1 = tuple() |
| return %t1 : $() |
| } |
| |
| // CHECK-LABEL: sil @do_not_sink_from_preds_through_args3 |
| // CHECK: bb2: |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: br bb5 |
| // CHECK: bb3: |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: br bb5 |
| // CHECK: bb4: |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: br bb5 |
| // CHECK-NOT: strong_retain |
| // CHECK: return |
| sil @do_not_sink_from_preds_through_args3 : $@convention(thin) (Builtin.Int1, B, B) -> () { |
| bb0(%0 : $Builtin.Int1, %1 : $B, %2 : $B): |
| cond_br %0, bb1, bb2 |
| |
| bb1: |
| cond_br %0, bb3, bb4 |
| |
| bb2: |
| %r1 = strong_retain %1 : $B |
| br bb5(%1 : $B) |
| |
| bb3: |
| %r2 = strong_retain %2 : $B |
| br bb5(%2 : $B) |
| |
| bb4: |
| %r3 = strong_retain %1 : $B |
| br bb5(%2 : $B) |
| |
| bb5(%a1 : $B): |
| %t1 = tuple() |
| return %t1 : $() |
| } |
| |
| // CHECK-LABEL: sil @do_not_sink_from_preds_through_args4 |
| // CHECK: bb2: |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: br bb5 |
| // CHECK: bb3: |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: br bb5 |
| // CHECK: bb4: |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: br bb5 |
| // CHECK-NOT: strong_retain |
| // CHECK: return |
| sil @do_not_sink_from_preds_through_args4 : $@convention(thin) (Builtin.Int1, B, B) -> () { |
| bb0(%0 : $Builtin.Int1, %1 : $B, %2 : $B): |
| cond_br %0, bb1, bb2 |
| |
| bb1: |
| cond_br %0, bb3, bb4 |
| |
| bb2: |
| %r1 = strong_retain %1 : $B |
| br bb5(%2 : $B) |
| |
| bb3: |
| %r2 = strong_retain %2 : $B |
| br bb5(%2 : $B) |
| |
| bb4: |
| %r3 = strong_retain %1 : $B |
| br bb5(%1 : $B) |
| |
| bb5(%a1 : $B): |
| %t1 = tuple() |
| return %t1 : $() |
| } |
| |
| // CHECK-LABEL: sil @do_not_sink_local_uses |
| // CHECK: integer_literal |
| // CHECK: integer_literal |
| // CHECK: return |
| sil @do_not_sink_local_uses : $@convention(thin) (Builtin.Int1) -> Builtin.Int32 { |
| bb0(%0 : $Builtin.Int1): |
| cond_br %0, bb1, bb3 |
| |
| bb1: |
| %1 = integer_literal $Builtin.Int32, 7 |
| br bb2(%1 : $Builtin.Int32) |
| |
| bb3: |
| %2 = integer_literal $Builtin.Int32, 8 |
| br bb2(%2 : $Builtin.Int32) |
| |
| bb2(%3 : $Builtin.Int32): |
| return %3 : $Builtin.Int32 |
| } |
| |
| // CHECK-LABEL: sil @deep_sink1 |
| // CHECK: integer_literal |
| // CHECK: br bb3 |
| // CHECK: integer_literal |
| // CHECK: br bb3 |
| // CHECK: bb3 |
| // CHECK: strong_retain |
| // CHECK-NEXT: return |
| sil @deep_sink1 : $@convention(thin) (Builtin.Int1, B) -> Builtin.Int32 { |
| bb0(%0 : $Builtin.Int1, %1 : $B): |
| cond_br %0, bb1, bb2 |
| |
| bb1: |
| %2 = strong_retain %1 : $B |
| %3 = integer_literal $Builtin.Int32, 9 |
| br bb3(%3 : $Builtin.Int32) |
| |
| bb2: |
| %4 = strong_retain %1 : $B |
| %5 = integer_literal $Builtin.Int32, 7 |
| br bb3(%5 : $Builtin.Int32) |
| |
| bb3(%6 : $Builtin.Int32): |
| return %6 : $Builtin.Int32 |
| } |
| |
| // CHECK-LABEL: sil @deep_sink2 |
| // CHECK-NOT: integer_literal |
| // CHECK: br bb3 |
| // CHECK-NOT: integer_literal |
| // CHECK: br bb3 |
| // CHECK: bb3 |
| // CHECK: integer_literal |
| // CHECK: strong_retain |
| // CHECK-NEXT: return |
| sil @deep_sink2 : $@convention(thin) (Builtin.Int1, B) -> Builtin.Int32 { |
| bb0(%0 : $Builtin.Int1, %1 : $B): |
| cond_br %0, bb1, bb2 |
| |
| bb1: |
| %2 = strong_retain %1 : $B |
| %3 = integer_literal $Builtin.Int32, 7 |
| br bb3(%3 : $Builtin.Int32) |
| |
| bb2: |
| %4 = strong_retain %1 : $B |
| %5 = integer_literal $Builtin.Int32, 7 |
| br bb3(%5 : $Builtin.Int32) |
| |
| bb3(%6 : $Builtin.Int32): |
| return %6 : $Builtin.Int32 |
| } |
| |
| // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_1 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0({{.*}}): |
| // CHECK-NEXT: function_ref blocker |
| // CHECK-NEXT: function_ref @blocker |
| // CHECK-NEXT: retain_value |
| // CHECK-NEXT: apply |
| // CHECK-NEXT: alloc_stack |
| // CHECK-NEXT: dealloc_stack |
| // CHECK-NEXT: switch_enum |
| // CHECK: bb1({{.*}}): |
| // CHECK-NEXT: unchecked_enum_data |
| // CHECK-NEXT: strong_retain |
| // CHECK-NOT: retain_value |
| // CHECK: bb2: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| // CHECK-NOT: strong_retain |
| // CHECK: bb3: |
| // CHECK-NOT: retain_value |
| // CHECK-NOT: strong_retain |
| sil @sink_ref_count_ops_enum_over_switch_enum_1 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| %1 = function_ref @blocker : $@convention(thin) () -> () |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| apply %1() : $@convention(thin) () -> () |
| %3 = alloc_stack $Builtin.Int32 |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| dealloc_stack %3#0 : $*@local_storage Builtin.Int32 |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1(%2 : $Builtin.NativeObject): |
| apply %1() : $@convention(thin) () -> () |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| %4 = tuple() |
| return %4 : $() |
| } |
| |
| // This test makes sure that if any of the switches targets have a predecessor |
| // besides the switch enum, we don't do anything. |
| // |
| // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_2 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0({{.*}}): |
| // CHECK-NEXT: function_ref blocker |
| // CHECK-NEXT: function_ref @blocker |
| // CHECK-NEXT: cond_br |
| // CHECK: bb1: |
| // CHECK-NEXT: retain_value |
| // CHECK-NEXT: switch_enum |
| // CHECK: bb2: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| // CHECK: bb3: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| // CHECK: bb4: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| sil @sink_ref_count_ops_enum_over_switch_enum_2 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| %1 = function_ref @blocker : $@convention(thin) () -> () |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb2, case #FakeOptional.None!enumelt: bb3 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| br bb4 |
| |
| bb4: |
| %2 = tuple() |
| return %2 : $() |
| } |
| |
| // This test makes sure that if any of the switches targets have a predecessor |
| // besides the switch enum, we don't do anything. |
| // |
| // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_3 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| // CHECK: bb0({{.*}}): |
| // CHECK-NEXT: function_ref blocker |
| // CHECK-NEXT: function_ref @blocker |
| // CHECK-NEXT: cond_br |
| // CHECK: bb1: |
| // CHECK-NEXT: retain_value |
| // CHECK-NEXT: switch_enum |
| // CHECK: bb2: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| // CHECK: bb3: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| // CHECK: bb4: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| sil @sink_ref_count_ops_enum_over_switch_enum_3 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.Int32>): |
| %1 = function_ref @blocker : $@convention(thin) () -> () |
| cond_br undef, bb1, bb3 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.Int32> |
| switch_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: bb2, case #FakeOptional.None!enumelt: bb3 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| br bb4 |
| |
| bb4: |
| %2 = tuple() |
| return %2 : $() |
| } |
| |
| // This test makes sure that we do move ref counts over instructions that can not reference it. |
| // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_4 : |
| // CHECK: bb0({{%[0-9]+}} : $FakeOptional<Builtin.Int32>, [[ARG1:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>): |
| // CHECK-NOT: retain_value [[ARG1]] |
| // CHECK: switch_enum |
| // CHECK: bb1: |
| // CHECK: unchecked_enum_data [[ARG1]] |
| // CHECK: strong_retain |
| // CHECK: bb2: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value [[ARG1]] |
| // CHECK-NOT: strong_retain |
| // CHECK: bb3: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value [[ARG1]] |
| // CHECK-NOT: strong_retain |
| sil @sink_ref_count_ops_enum_over_switch_enum_4 : $@convention(thin) (FakeOptional<Builtin.Int32>, FakeOptional<Builtin.NativeObject>) -> FakeOptional<Builtin.NativeObject> { |
| bb0(%0 : $FakeOptional<Builtin.Int32>, %1 : $FakeOptional<Builtin.NativeObject>): |
| %2 = function_ref @blocker : $@convention(thin) () -> () |
| retain_value %1 : $FakeOptional<Builtin.NativeObject> |
| %3 = alloc_stack $FakeOptional<Builtin.Int32> |
| retain_value %0 : $FakeOptional<Builtin.Int32> |
| switch_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| apply %2() : $@convention(thin) () -> () |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| dealloc_stack %3#0 : $*@local_storage FakeOptional<Builtin.Int32> |
| return %1 : $FakeOptional<Builtin.NativeObject> |
| } |
| |
| /// This version does not work since we have a release before the terminator |
| /// even though we have the select_enum before it. |
| // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_5 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| // CHECK: bb0({{.*}}): |
| // CHECK: bb1: |
| // CHECK-NEXT: function_ref blocker |
| // CHECK-NEXT: function_ref @blocker |
| // CHECK-NEXT: alloc_stack |
| // CHECK-NEXT: dealloc_stack |
| // CHECK-NEXT: retain_value |
| // CHECK-NEXT: release_value |
| // CHECK-NEXT: switch_enum |
| // CHECK: bb2: |
| // CHECK-NOT: retain_value |
| // CHECK: bb3: |
| // CHECK-NOT: retain_value |
| // CHECK: bb4: |
| // CHECK-NOT: retain_value |
| sil @sink_ref_count_ops_enum_over_switch_enum_5 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.Int32>): |
| br bb10 |
| |
| bb10: |
| %1 = function_ref @blocker : $@convention(thin) () -> () |
| retain_value %0 : $FakeOptional<Builtin.Int32> |
| %3 = alloc_stack $Builtin.Int32 |
| dealloc_stack %3#0 : $*@local_storage Builtin.Int32 |
| release_value %0 : $FakeOptional<Builtin.Int32> |
| switch_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| %4 = tuple() |
| return %4 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_1 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0({{.*}}): |
| // CHECK-NEXT: integer_literal |
| // CHECK-NEXT: integer_literal |
| // CHECK-NEXT: function_ref blocker |
| // CHECK-NEXT: function_ref @blocker |
| // CHECK-NEXT: retain_value |
| // CHECK-NEXT: apply |
| // CHECK-NEXT: alloc_stack |
| // CHECK-NEXT: dealloc_stack |
| // CHECK-NEXT: select_enum |
| // CHECK-NEXT: cond_br |
| // CHECK: bb1: |
| // CHECK-NEXT: unchecked_enum_data |
| // CHECK-NEXT: strong_retain |
| // CHECK-NOT: retain_value |
| // CHECK: bb2: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| // CHECK-NOT: strong_retain |
| // CHECK: bb3: |
| // CHECK-NOT: retain_value |
| // CHECK-NOT: strong_retain |
| sil @sink_ref_count_ops_enum_over_select_enum_1 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| %t = integer_literal $Builtin.Int1, 1 |
| %f = integer_literal $Builtin.Int1, 0 |
| %1 = function_ref @blocker : $@convention(thin) () -> () |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| apply %1() : $@convention(thin) () -> () |
| %3 = alloc_stack $Builtin.Int32 |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| dealloc_stack %3#0 : $*@local_storage Builtin.Int32 |
| %100 = select_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1 |
| cond_br %100, bb1, bb2 |
| |
| bb1: |
| apply %1() : $@convention(thin) () -> () |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| %4 = tuple() |
| return %4 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_2 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| // CHECK: bb0({{.*}}): |
| // CHECK-NEXT: integer_literal |
| // CHECK-NEXT: integer_literal |
| // CHECK-NEXT: function_ref blocker |
| // CHECK-NEXT: function_ref @blocker |
| // CHECK-NEXT: cond_br |
| // CHECK: bb1: |
| // CHECK-NEXT: select_enum |
| // CHECK-NEXT: retain_value |
| // CHECK-NEXT: cond_br |
| // CHECK: bb2: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| // CHECK: bb3: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| // CHECK: bb4: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| sil @sink_ref_count_ops_enum_over_select_enum_2 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.Int32>): |
| %t = integer_literal $Builtin.Int1, 1 |
| %f = integer_literal $Builtin.Int1, 0 |
| %1 = function_ref @blocker : $@convention(thin) () -> () |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.Int32> |
| %100 = select_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1 |
| cond_br %100, bb2, bb3 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| br bb4 |
| |
| bb4: |
| %2 = tuple() |
| return %2 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_3 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| // CHECK: bb0({{.*}}): |
| // CHECK-NEXT: integer_literal |
| // CHECK-NEXT: integer_literal |
| // CHECK-NEXT: function_ref blocker |
| // CHECK-NEXT: function_ref @blocker |
| // CHECK-NEXT: cond_br |
| // CHECK: bb1: |
| // CHECK-NEXT: select_enum |
| // CHECK-NEXT: retain_value |
| // CHECK-NEXT: cond_br |
| // CHECK: bb2: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| // CHECK: bb3: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| // CHECK: bb4: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value |
| sil @sink_ref_count_ops_enum_over_select_enum_3 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.Int32>): |
| %t = integer_literal $Builtin.Int1, 1 |
| %f = integer_literal $Builtin.Int1, 0 |
| %1 = function_ref @blocker : $@convention(thin) () -> () |
| cond_br undef, bb1, bb3 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.Int32> |
| %100 = select_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1 |
| cond_br %100, bb2, bb3 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| br bb4 |
| |
| bb4: |
| %2 = tuple() |
| return %2 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_4 : $@convention(thin) (FakeOptional<Builtin.Int32>, FakeOptional<Builtin.NativeObject>) -> FakeOptional<Builtin.NativeObject> { |
| // CHECK: bb0({{%[0-9]+}} : $FakeOptional<Builtin.Int32>, [[ARG1:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>): |
| // CHECK-NOT: retain_value [[ARG1]] |
| // CHECK: select_enum |
| // CHECK: cond_br |
| // CHECK: bb1: |
| // CHECK: unchecked_enum_data [[ARG1]] |
| // CHECK: retain_value |
| // CHECK: bb2: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value [[ARG1]] |
| // CHECK: bb3: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: retain_value [[ARG1]] |
| sil @sink_ref_count_ops_enum_over_select_enum_4 : $@convention(thin) (FakeOptional<Builtin.Int32>, FakeOptional<Builtin.NativeObject>) -> FakeOptional<Builtin.NativeObject> { |
| bb0(%0 : $FakeOptional<Builtin.Int32>, %1 : $FakeOptional<Builtin.NativeObject>): |
| %t = integer_literal $Builtin.Int1, 1 |
| %f = integer_literal $Builtin.Int1, 0 |
| %2 = function_ref @blocker : $@convention(thin) () -> () |
| retain_value %1 : $FakeOptional<Builtin.NativeObject> |
| retain_value %0 : $FakeOptional<Builtin.Int32> |
| %100 = select_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1 |
| cond_br %100, bb1, bb2 |
| |
| bb1: |
| apply %2() : $@convention(thin) () -> () |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| return %1 : $FakeOptional<Builtin.NativeObject> |
| } |
| |
| /// This version does not work since we have a release before the terminator |
| /// even though we have the select_enum before it. |
| // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_5 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| // CHECK: bb0({{.*}}): |
| // CHECK: bb1: |
| // CHECK-NEXT: function_ref blocker |
| // CHECK-NEXT: function_ref @blocker |
| // CHECK-NEXT: alloc_stack |
| // CHECK-NEXT: dealloc_stack |
| // CHECK-NEXT: select_enum |
| // CHECK-NEXT: retain_value |
| // CHECK-NEXT: release_value |
| // CHECK-NEXT: cond_br |
| // CHECK: bb2: |
| // CHECK-NOT: retain_value |
| // CHECK: bb3: |
| // CHECK-NOT: retain_value |
| // CHECK: bb4: |
| // CHECK-NOT: retain_value |
| sil @sink_ref_count_ops_enum_over_select_enum_5 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.Int32>): |
| %t = integer_literal $Builtin.Int1, 1 |
| %f = integer_literal $Builtin.Int1, 0 |
| br bb10 |
| |
| bb10: |
| %1 = function_ref @blocker : $@convention(thin) () -> () |
| retain_value %0 : $FakeOptional<Builtin.Int32> |
| %3 = alloc_stack $Builtin.Int32 |
| dealloc_stack %3#0 : $*@local_storage Builtin.Int32 |
| %100 = select_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1 |
| release_value %0 : $FakeOptional<Builtin.Int32> |
| cond_br %100, bb1, bb2 |
| |
| bb1: |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| %4 = tuple() |
| return %4 : $() |
| } |
| |
| // Sink the struct instruction |
| // CHECK-LABEL: sil @sink_struct : $@convention(thin) (FakeOptional<Builtin.Int32>) -> Bool { |
| // CHECK: bb0({{.*}}): |
| // CHECK: switch_enum |
| // CHECK: bb1: |
| // CHECK: integer_literal $Builtin.Int1, -1 |
| // CHECK: br bb3({{.*}} : $Builtin.Int1) |
| // CHECK: bb2: |
| // CHECK: integer_literal $Builtin.Int1, 0 |
| // CHECK: br bb3({{.*}} : $Builtin.Int1) |
| // CHECK: bb3({{.*}} : $Builtin.Int1): |
| // CHECK: struct $Bool |
| // CHECK: return |
| sil @sink_struct : $@convention(thin) (FakeOptional<Builtin.Int32>) -> Bool { |
| bb0(%0 : $FakeOptional<Builtin.Int32>): |
| switch_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| %6 = integer_literal $Builtin.Int1, -1 |
| %7 = struct $Bool (%6 : $Builtin.Int1) |
| br bb3(%7 : $Bool) |
| |
| bb2: |
| %9 = integer_literal $Builtin.Int1, 0 |
| %10 = struct $Bool (%9 : $Builtin.Int1) |
| br bb3(%10 : $Bool) |
| |
| bb3(%12 : $Bool): |
| return %12 : $Bool |
| } |
| |
| // Sink retain down the successors so we can pair up retain with release on the |
| // fast path. |
| class Test { |
| func testing() -> UInt64 |
| } |
| sil_global @test : $Test |
| sil_global @x : $UInt64 |
| |
| // CHECK-LABEL: @sink_refcount_to_succs |
| sil @sink_refcount_to_succs : $@convention(thin) () -> () { |
| bb0: |
| %0 = global_addr @test : $*Test |
| %1 = alloc_ref $Test |
| store %1 to %0 : $*Test |
| %7 = global_addr @x : $*UInt64 |
| %8 = integer_literal $Builtin.Int64, 0 |
| %9 = struct $UInt64 (%8 : $Builtin.Int64) |
| store %9 to %7 : $*UInt64 |
| br bb1 |
| bb1: |
| // CHECK: bb1: |
| // CHECK-NOT: strong_retain |
| %17 = load %0 : $*Test |
| strong_retain %17 : $Test |
| %19 = class_method %17 : $Test, #Test.testing!1 : Test -> () -> UInt64 , $@convention(method) (@guaranteed Test) -> UInt64 |
| %20 = load %7 : $*UInt64 |
| checked_cast_br [exact] %17 : $Test to $Test, bb3, bb4 |
| bb2: |
| // CHECK: bb2: |
| // CHECK-NOT: strong_retain |
| %36 = tuple () |
| return %36 : $() |
| bb3(%38 : $Test): |
| // CHECK: bb3( |
| // CHECK: strong_retain |
| // CHECK: strong_release |
| strong_release %17 : $Test |
| br bb2 |
| bb4: |
| // CHECK: bb4: |
| // CHECK: strong_retain |
| // CHECK: apply |
| %65 = apply %19(%17) : $@convention(method) (@guaranteed Test) -> UInt64 |
| br bb2 |
| } |
| |
| sil @virtual_callee : $@convention(method) (@guaranteed Test) -> UInt64 |
| sil_vtable Test { |
| #Test.testing!1: virtual_callee |
| } |
| |
| // Don't sink a retain over switch_enum with a default case. |
| // |
| // CHECK-LABEL: sil @dont_sink_retain_over_default_case_switch_enum : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| // CHECK: bb0({{.*}}): |
| // CHECK-NEXT: retain_value |
| // CHECK-NEXT: switch_enum |
| // CHECK: return |
| sil @dont_sink_retain_over_default_case_switch_enum : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.Int32>): |
| retain_value %0 : $FakeOptional<Builtin.Int32> |
| switch_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: bb1, default bb2 |
| |
| bb1: |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| sil @arc_user : $@convention(thin) () -> () |
| |
| // The retains here aren't able to be moved to a successor BB. But we still want to move them as far |
| // as possible down the current BB. The first retain should be moved to above the release_value. |
| // The second retain shouldn't move at all |
| // The third retain should move below the sadd builtin |
| // The fourth retain should move below the arc_user builtin, but above the apply |
| // The fifth retain should *NOT* move below the is_unique, unless we can prove that the pointers do not |
| // alias. |
| // And the last retain should remain where it is at the end of the BB. |
| // |
| // CHECK-LABEL: sil @move_retain_down_bb : $@convention(thin) (FakeOptional<Builtin.Int32>, FakeOptional<Builtin.Int32>, FakeOptional<Builtin.Int32>, FakeOptional<Builtin.Int32>, FakeOptional<Builtin.Int32>) -> (Builtin.Int64, Builtin.Int1) { |
| // CHECK: bb0({{.*}}): |
| // CHECK-NEXT: retain_value %1 |
| // CHECK-NEXT: retain_value %0 |
| // CHECK-NEXT: release_value %4 |
| // CHECK-NEXT: function_ref arc_user |
| // CHECK-NEXT: function_ref @arc_user |
| // CHECK-NEXT: retain_value %3 |
| // CHECK-NEXT: retain_value %2 |
| // CHECK-NEXT: apply |
| // CHECK-NEXT: integer_literal $Builtin.Word, 1 |
| // CHECK-NEXT: retain_value %3 |
| // CHECK-NEXT: is_unique |
| // CHECK-NEXT: retain_value %3 |
| // CHECK-NEXT: switch_enum |
| |
| // CHECK: return |
| sil @move_retain_down_bb : $@convention(thin) (FakeOptional<Builtin.Int32>, FakeOptional<Builtin.Int32>, FakeOptional<Builtin.Int32>, FakeOptional<Builtin.Int32>, FakeOptional<Builtin.Int32>) -> (Builtin.Int64, Builtin.Int1) { |
| bb0(%0 : $FakeOptional<Builtin.Int32>, %1 : $FakeOptional<Builtin.Int32>, %2 : $FakeOptional<Builtin.Int32>, %3 : $FakeOptional<Builtin.Int32>, %4 : $FakeOptional<Builtin.Int32>): |
| retain_value %0 : $FakeOptional<Builtin.Int32> |
| retain_value %1 : $FakeOptional<Builtin.Int32> |
| release_value %4 : $FakeOptional<Builtin.Int32> |
| retain_value %2 : $FakeOptional<Builtin.Int32> |
| retain_value %3 : $FakeOptional<Builtin.Int32> |
| %1000 = function_ref @arc_user : $@convention(thin) () -> () |
| apply %1000 () : $@convention(thin) () -> () |
| retain_value %3 : $FakeOptional<Builtin.Int32> |
| %500 = integer_literal $Builtin.Word, 1 |
| %501 = is_unique %500 : $Builtin.Word |
| retain_value %3 : $FakeOptional<Builtin.Int32> |
| switch_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: bb1, default bb2 |
| |
| bb1: |
| %100 = integer_literal $Builtin.Int64, 1 |
| %101 = integer_literal $Builtin.Int64, 2 |
| %102 = integer_literal $Builtin.Int1, 1 |
| %10 = builtin "ssub_with_overflow_Int64"(%100 : $Builtin.Int64, %101 : $Builtin.Int64, %102 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) |
| br bb3(%10 : $(Builtin.Int64, Builtin.Int1)) |
| |
| bb2: |
| %200 = integer_literal $Builtin.Int64, 3 |
| %201 = integer_literal $Builtin.Int64, 4 |
| %202 = integer_literal $Builtin.Int1, 1 |
| %11 = builtin "sadd_with_overflow_Int64"(%200 : $Builtin.Int64, %201 : $Builtin.Int64, %202 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) |
| br bb3(%11 : $(Builtin.Int64, Builtin.Int1)) |
| |
| bb3(%12 : $(Builtin.Int64, Builtin.Int1)): |
| return %12 : $(Builtin.Int64, Builtin.Int1) |
| } |
| |
| // CHECK-LABEL: sil @sink_retains_from_preds : $@convention(thin) (Builtin.NativeObject) -> () { |
| // CHECK: bb1: |
| // CHECK-NOT: retain_value |
| // CHECK: bb2: |
| // CHECK-NOT: retain_value |
| // CHECK: bb3: |
| // CHECK: retain_value |
| sil @sink_retains_from_preds : $@convention(thin) (Builtin.NativeObject) -> () { |
| bb0(%0 : $Builtin.NativeObject): |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| retain_value %0 : $Builtin.NativeObject |
| br bb3 |
| |
| bb2: |
| retain_value %0 : $Builtin.NativeObject |
| br bb3 |
| |
| bb3: |
| %1 = tuple() |
| return %1 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test1 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0 |
| // CHECK-NEXT: retain_value |
| // CHECK-NEXT: release_value |
| sil @enum_simplification_test1 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| release_value %0 : $FakeOptional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test2 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0 |
| // CHECK: strong_retain |
| // CHECK: strong_release |
| sil @enum_simplification_test2 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| %1 = unchecked_enum_data %0 : $FakeOptional<Builtin.NativeObject>, #FakeOptional.Some!enumelt.1 |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| release_value %0 : $FakeOptional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test3 : $@convention(thin) () -> () { |
| // CHECK: bb0 |
| // CHECK-NEXT: enum |
| // CHECK-NOT: strong_retain |
| // CHECK-NOT: strong_release |
| // CHECK-NEXT: tuple |
| // CHECK-NEXT: return |
| sil @enum_simplification_test3 : $@convention(thin) () -> () { |
| bb0: |
| %0 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.None!enumelt |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| release_value %0 : $FakeOptional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test4 : $@convention(thin) () -> () { |
| // CHECK: bb0 |
| // CHECK-NEXT: get_object |
| // CHECK-NEXT: function_ref @get_object |
| // CHECK-NEXT: apply |
| // CHECK-NEXT: enum |
| // CHECK: strong_retain |
| // CHECK: strong_release |
| sil @enum_simplification_test4 : $@convention(thin) () -> () { |
| bb0: |
| %0 = function_ref @get_object : $@convention(thin) () -> Builtin.NativeObject |
| %1 = apply %0() : $@convention(thin) () -> Builtin.NativeObject |
| %2 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.Some!enumelt.1, %1 : $Builtin.NativeObject |
| retain_value %2 : $FakeOptional<Builtin.NativeObject> |
| release_value %2 : $FakeOptional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test5 : $@convention(thin) () -> () { |
| // CHECK: bb1: |
| // CHECK: strong_retain |
| // CHECK: bb2: |
| // CHECK: strong_retain |
| // CHECK: bb3: |
| // CHECK-NOT: strong_retain |
| sil @enum_simplification_test5 : $@convention(thin) () -> () { |
| bb0: |
| %0 = function_ref @get_object : $@convention(thin) () -> Builtin.NativeObject |
| %1 = apply %0() : $@convention(thin) () -> Builtin.NativeObject |
| %2 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.Some!enumelt.1, %1 : $Builtin.NativeObject |
| %3 = function_ref @blocker : $@convention(thin) () -> () |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| retain_value %2 : $FakeOptional<Builtin.NativeObject> |
| apply %3() : $@convention(thin) () -> () |
| br bb3 |
| |
| bb2: |
| retain_value %2 : $FakeOptional<Builtin.NativeObject> |
| apply %3() : $@convention(thin) () -> () |
| br bb3 |
| |
| bb3: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // This test is testing that we can properly simplify unchecked_enum_data if we |
| // know the tag. We also sink it. |
| // |
| // CHECK-LABEL: sil @enum_simplification_test6a : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb1: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: strong_retain |
| // CHECK-NOT: retain_value |
| // CHECK: bb2: |
| // CHECK-NOT: unchecked_enum_data |
| // CHECK-NOT: strong_retain |
| // CHECK-NOT: retain_value |
| // CHECK: bb3: |
| // CHECK: strong_retain |
| sil @enum_simplification_test6a : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| %1 = unchecked_enum_data %0 : $FakeOptional<Builtin.NativeObject>, #FakeOptional.Some!enumelt.1 |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb3 |
| |
| bb2: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb3 |
| |
| bb3: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // Because our enum is not local to this function, we can not move retain_value |
| // %0 past blocker. |
| // |
| // CHECK-LABEL: sil @enum_simplification_test6b : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb1: |
| // CHECK: strong_retain |
| // CHECK: bb2: |
| // CHECK: strong_retain |
| // CHECK: bb3: |
| // CHECK-NOT: strong_retain |
| sil @enum_simplification_test6b : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| %1 = unchecked_enum_data %0 : $FakeOptional<Builtin.NativeObject>, #FakeOptional.Some!enumelt.1 |
| %2 = function_ref @blocker : $@convention(thin) () -> () |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| apply %2() : $@convention(thin) () -> () |
| br bb3 |
| |
| bb2: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb3 |
| |
| bb3: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| |
| // CHECK-LABEL: sil @enum_simplification_test7 : $@convention(thin) () -> () { |
| // CHECK-NOT: retain |
| // CHECK-NOT: retain |
| // CHECK: return |
| sil @enum_simplification_test7 : $@convention(thin) () -> () { |
| bb0: |
| %0 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.None!enumelt |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb3 |
| |
| bb2: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb3 |
| |
| bb3: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test8 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0([[INPUT:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>): |
| // CHECK: bb2: |
| // CHECK-NEXT: retain_value [[INPUT]] |
| // CHECK: bb3: |
| // CHECK-NEXT: [[PAYLOAD:%[0-9]+]] = unchecked_enum_data [[INPUT]] |
| // CHECK-NEXT: strong_retain [[PAYLOAD]] |
| // CHECK: bb4: |
| // CHECK: retain_value [[INPUT]] |
| sil @enum_simplification_test8 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| %1 = unchecked_enum_data %0 : $FakeOptional<Builtin.NativeObject>, #FakeOptional.Some!enumelt.1 |
| cond_br undef, bb2, bb3 |
| |
| bb2: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb4 |
| |
| bb3: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb4 |
| |
| bb4: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test9 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0([[INPUT:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>): |
| // CHECK: bb1: |
| // CHECK-NEXT: [[PAYLOAD:%[0-9]+]] = unchecked_enum_data [[INPUT]] |
| // CHECK-NEXT: strong_retain [[PAYLOAD]] |
| // CHECK: bb2: |
| // CHECK-NOT: retain |
| // CHECK: bb3: |
| // CHECK: retain_value [[INPUT]] |
| sil @enum_simplification_test9 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| %1 = function_ref @blocker : $@convention(thin) () -> () |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| apply %1() : $@convention(thin) () -> () |
| br bb3 |
| |
| bb2: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb3 |
| |
| bb3: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // Note, this is the same as test9, but for enums |
| // CHECK-LABEL: sil @enum_simplification_test9_enums : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0([[INPUT:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>): |
| // CHECK: bb1: |
| // CHECK-NEXT: [[PAYLOAD:%[0-9]+]] = unchecked_enum_data [[INPUT]] |
| // CHECK-NEXT: strong_retain [[PAYLOAD]] |
| // CHECK: bb2: |
| // CHECK-NOT: retain |
| // CHECK: bb3: |
| // CHECK: retain_value [[INPUT]] |
| sil @enum_simplification_test9_enums : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| %t = integer_literal $Builtin.Int1, 1 |
| %f = integer_literal $Builtin.Int1, 0 |
| %2 = function_ref @blocker : $@convention(thin) () -> () |
| %1 = select_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1 |
| cond_br %1, bb1, bb2 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| apply %2() : $@convention(thin) () -> () |
| br bb3 |
| |
| bb2: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb3 |
| |
| bb3: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| //////////////// |
| // Loop Tests // |
| //////////////// |
| |
| // CHECK-LABEL: sil @enum_simplification_test10 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0([[INPUT:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>): |
| // CHECK: retain_value [[INPUT]] |
| // CHECK: retain_value [[INPUT]] |
| // CHECK: retain_value [[INPUT]] |
| // CHECK: retain_value [[INPUT]] |
| sil @enum_simplification_test10 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| // Single BB Loop |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| cond_br undef, bb1, bb9999 |
| |
| // Two BB Loop. We can use loop or domination to handle this case. But we don't |
| // handle it now. |
| bb2: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb3 |
| |
| bb3: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| cond_br undef, bb2, bb9999 |
| |
| bb9999: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // Make sure we don't propagate state out of loops. |
| // CHECK-LABEL: sil @enum_simplification_test11 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0([[INPUT:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>): |
| // CHECK: retain_value [[INPUT]] |
| // CHECK: retain_value [[INPUT]] |
| sil @enum_simplification_test11 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| cond_br undef, bb1, bb2 |
| |
| bb2: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test12 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb1: |
| // CHECK: release |
| // CHECK: bb2: |
| // CHECK-NOT: release |
| // CHECK: bb3: |
| // CHECK-NOT: release |
| sil @enum_simplification_test12 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| release_value %0 : $FakeOptional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test13 : $@convention(thin) (Either) -> () { |
| // CHECK: bb1: |
| // CHECK: strong_release |
| // CHECK: bb2: |
| // CHECK: strong_release |
| // CHECK: bb3: |
| // CHECK-NOT: release_value |
| sil @enum_simplification_test13 : $@convention(thin) (Either) -> () { |
| bb0(%0 : $Either): |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| %1 = unchecked_enum_data %0 : $Either, #Either.First!enumelt.1 |
| br bb3 |
| |
| bb2: |
| %2 = unchecked_enum_data %0 : $Either, #Either.Second!enumelt.1 |
| br bb3 |
| |
| bb3: |
| release_value %0 : $Either |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test14 : $@convention(thin) (Either) -> () { |
| // CHECK: bb0( |
| // CHECK-NOT: release |
| // CHECK: bb1: |
| // CHECK: release |
| sil @enum_simplification_test14 : $@convention(thin) (Either) -> () { |
| bb0(%0 : $Either): |
| %1 = unchecked_enum_data %0 : $Either, #Either.First!enumelt.1 |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| release_value %0 : $Either |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_test15 : $@convention(thin) (Either, ThreeCaseEnum) -> () { |
| // CHECK: bb1: |
| // CHECK: release |
| // CHECK: bb2: |
| // CHECK-NOT: release |
| // CHECK: bb3: |
| // CHECK-NOT: release |
| sil @enum_simplification_test15 : $@convention(thin) (Either, ThreeCaseEnum) -> () { |
| bb0(%0 : $Either, %1 : $ThreeCaseEnum): |
| %2 = unchecked_enum_data %0 : $Either, #Either.First!enumelt.1 |
| switch_enum %1 : $ThreeCaseEnum, case #ThreeCaseEnum.A!enumelt: bb1, |
| case #ThreeCaseEnum.B!enumelt: bb2, |
| case #ThreeCaseEnum.C!enumelt: bb3 |
| |
| bb1: |
| release_value %0 : $Either |
| br bb4 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| br bb4 |
| |
| bb4: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| sil @unknown : $@convention(thin) () -> () |
| |
| // CHECK-LABEL: sil @enum_simplification_test16 : $@convention(thin) (FakeOptional<C>) -> () { |
| // CHECK: bb1: |
| // CHECK-NOT: release |
| // CHECK: bb2: |
| // CHECK: unchecked_enum_data |
| // CHECK: release |
| // CHECK: bb3 |
| // CHECK: release |
| sil @enum_simplification_test16 : $@convention(thin) (FakeOptional<C>) -> () { |
| bb0(%0 : $FakeOptional<C>): |
| switch_enum %0 : $FakeOptional<C>, |
| case #FakeOptional.None!enumelt: bb1, |
| case #FakeOptional.Some!enumelt.1: bb2 |
| |
| bb1: |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| %2 = integer_literal $Builtin.Word, 2 |
| %3 = integer_literal $Builtin.Word, 3 |
| %4 = integer_literal $Builtin.Int1, 0 |
| %5 = builtin "sadd_with_overflow_Word"(%2 : $Builtin.Word, %3 : $Builtin.Word, %4 : $Builtin.Int1) : $(Builtin.Word, Builtin.Int1) |
| release_value %0 : $FakeOptional<C> |
| %9998 = function_ref @unknown : $@convention(thin) () -> () |
| apply %9998() : $@convention(thin) () -> () |
| release_value %0 : $FakeOptional<C> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @enum_simplification_abstraction_differences : $@convention(thin) (ThreeCaseAbstractionDifference) -> () { |
| // CHECK: bb0( |
| // CHECK-NOT: release |
| // CHECK: bb1: |
| // CHECK-NOT: release |
| // CHECK: bb2: |
| // CHECK: strong_release |
| // CHECK: bb3: |
| // CHECK: release_value |
| // CHECK: bb4: |
| // CHECK-NOT: release_value |
| sil @enum_simplification_abstraction_differences : $@convention(thin) (ThreeCaseAbstractionDifference) -> () { |
| bb0(%0 : $ThreeCaseAbstractionDifference): |
| switch_enum %0 : $ThreeCaseAbstractionDifference, |
| case #ThreeCaseAbstractionDifference.A!enumelt.1: bb1, |
| case #ThreeCaseAbstractionDifference.B!enumelt.1: bb2, |
| case #ThreeCaseAbstractionDifference.C!enumelt.1: bb3 |
| |
| bb1: |
| br bb4 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| br bb4 |
| |
| bb4: |
| release_value %0 : $ThreeCaseAbstractionDifference |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_retains_out_of_switch_regions : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb1: |
| // CHECK-NOT: retain_value |
| // CHECK: bb2: |
| // CHECK-NOT: retain_value |
| // CHECK: bb3: |
| // CHECK: retain_value |
| sil @sink_retains_out_of_switch_regions : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| %1 = tuple() |
| return %1 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_retains_through_multiple_switches : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK-NOT: retain_value |
| // CHECK: bb9: |
| // CHECK: retain_value |
| sil @sink_retains_through_multiple_switches : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb4, case #FakeOptional.None!enumelt: bb5 |
| |
| bb4: |
| br bb6 |
| |
| bb5: |
| br bb6 |
| |
| bb6: |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb7, case #FakeOptional.None!enumelt: bb8 |
| |
| bb7: |
| br bb9 |
| |
| bb8: |
| br bb9 |
| |
| bb9: |
| %1 = tuple() |
| return %1 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_retains_through_switch_with_body : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK-NOT: retain_value |
| // CHECK: bb5: |
| // CHECK: retain_value |
| sil @sink_retains_through_switch_with_body : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| br bb3 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| br bb5 |
| |
| bb4: |
| br bb5 |
| |
| bb5: |
| %1 = tuple() |
| return %1 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_retains_through_separated_switches : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK-NOT: retain_value |
| // CHECK: bb5: |
| // CHECK: retain_value |
| sil @sink_retains_through_separated_switches : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| cond_br undef, bb3, bb4 |
| |
| bb2: |
| br bb5 |
| |
| bb3: |
| br bb5 |
| |
| bb4: |
| br bb5 |
| |
| bb5: |
| %1 = tuple() |
| return %1 : $() |
| } |
| |
| // Make sure that we blot pointers from the enumbbtoenumbbcaselist map after |
| // merging predecessors. This ensures that the failure to merge unrelating |
| // values does not stop sinking out of retain, release regions. |
| // CHECK-LABEL: sil @enumbbtoenumbbcaselist_invalidation_test : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb3: |
| // CHECK: retain_value |
| sil @enumbbtoenumbbcaselist_invalidation_test : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| br bb3 |
| |
| bb2: |
| %1 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.None!enumelt |
| br bb3 |
| |
| bb3: |
| %2 = tuple() |
| return %2 : $() |
| } |
| |
| // CHECK-LABEL: sil @delete_instead_of_sinkretainsintounreachable_bb : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| // CHECK: bb0( |
| // CHECK-NOT: retain_value |
| // CHECK: bb1: |
| // CHECK-NOT: retain_value |
| // CHECK: bb2: |
| // CHECK: retain_value |
| sil @delete_instead_of_sinkretainsintounreachable_bb : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $FakeOptional<Builtin.NativeObject>): |
| retain_value %0 : $FakeOptional<Builtin.NativeObject> |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| unreachable |
| |
| bb2: |
| %1 = tuple() |
| return %1 : $() |
| } |
| |
| // CHECK-LABEL: sil @sink_retain_over_switch_enum_cast |
| // CHECK: bb0( |
| // CHECK-NOT: retain_value |
| // CHECK: switch_enum |
| // CHECK: bb1: |
| // CHECK-NOT: retain_value |
| // CHECK: br bb3 |
| // CHECK: bb2: |
| // CHECK-NOT: retain_value |
| // CHECK: br bb3 |
| // CHECK: bb3: |
| // CHECK: retain_value |
| sil @sink_retain_over_switch_enum_cast : $@convention(thin) (Builtin.NativeObject) -> () { |
| bb0(%0 : $Builtin.NativeObject): |
| retain_value %0 : $Builtin.NativeObject |
| %1 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.Some!enumelt.1, %0 : $Builtin.NativeObject |
| switch_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: bb1, case #FakeOptional.None!enumelt: bb2 |
| |
| bb1: |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| %4 = tuple() |
| return %4 : $() |
| } |
| |
| class F {} |
| class G {} |
| |
| // CHECK-LABEL: sil @cast_consumption |
| // CHECK: store |
| // CHECK-NEXT: strong_retain |
| // CHECK-NEXT: unconditional_checked_cast_addr |
| // CHECK-NEXT: strong_release |
| sil @cast_consumption : $@convention(thin) (@owned F) -> () { |
| bb0(%0 : $F): |
| %1 = alloc_stack $F |
| store %0 to %1#1 : $*F |
| strong_retain %0 : $F |
| unconditional_checked_cast_addr take_always F in %1#1 : $*F to G in %1#1 : $*F |
| strong_release %0 : $F |
| dealloc_stack %1#0 : $*@local_storage F |
| %2 = tuple () |
| return %2 : $() |
| } |
| |
| // Make sure that is_unique stops code motion. |
| // CHECK-LABEL: sil @is_unique_stops_codemotion : $@convention(thin) (@inout Builtin.NativeObject) -> () { |
| // CHECK: bb0([[IN:%[0-9]+]] : $*Builtin.NativeObject): |
| // CHECK: [[LD:%[0-9]+]] = load [[IN]] : $*Builtin.NativeObject |
| // CHECK: strong_retain [[LD]] : $Builtin.NativeObject |
| // CHECK: is_unique [[IN]] : $*Builtin.NativeObject |
| sil @is_unique_stops_codemotion : $@convention(thin) (@inout Builtin.NativeObject) -> () { |
| bb0(%0 : $*Builtin.NativeObject): |
| %1 = load %0 : $*Builtin.NativeObject |
| strong_retain %1 : $Builtin.NativeObject |
| is_unique %0 : $*Builtin.NativeObject |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| sil @no_return_func : $@convention(thin) @noreturn () -> () |
| |
| // Make sure that we do not move retains over noreturn functions. |
| // CHECK-LABEL: sil @no_return_stops_codemotion : $@convention(thin) (Builtin.NativeObject) -> () { |
| // CHECK: apply |
| // CHECK-NEXT: unreachable |
| sil @no_return_stops_codemotion : $@convention(thin) (Builtin.NativeObject) -> () { |
| bb0(%0 : $Builtin.NativeObject): |
| %1 = alloc_ref $C |
| strong_retain %1 : $C |
| %9999 = function_ref @no_return_func : $@convention(thin) @noreturn () -> () |
| apply %9999() : $@convention(thin) @noreturn () -> () |
| unreachable |
| } |
| |
| // CHECK-LABEL: sil @hoist_release_partially_available_retain |
| // CHECK: bb0 |
| // CHECK: cond_br undef, bb1, bb2 |
| // CHECK: bb1: |
| // CHECK: strong_retain |
| // CHECK: strong_retain |
| // CHECK: strong_release |
| // CHECK: strong_release |
| // CHECK: br bb3 |
| // CHECK: bb2: |
| // CHECK: strong_retain |
| // CHECK: strong_retain |
| // CHECK: apply |
| // CHECK: strong_release |
| // CHECK: strong_release |
| // CHECK: br bb3 |
| // CHECK: bb3: |
| // CHECK-NOT: strong_release |
| // CHECK: return |
| |
| sil @hoist_release_partially_available_retain : $@convention(thin) (Builtin.NativeObject, Builtin.NativeObject) -> () { |
| bb0(%0 : $Builtin.NativeObject, %1: $Builtin.NativeObject): |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| strong_retain %0: $Builtin.NativeObject |
| strong_retain %1: $Builtin.NativeObject |
| br bb3 |
| |
| bb2: |
| strong_retain %0: $Builtin.NativeObject |
| strong_retain %1: $Builtin.NativeObject |
| %2 = function_ref @blocker : $@convention(thin) () -> () |
| apply %2() : $@convention(thin) () -> () |
| br bb3 |
| |
| bb3: |
| strong_release %0: $Builtin.NativeObject |
| strong_release %1: $Builtin.NativeObject |
| %5 = tuple() |
| return %5 : $() |
| } |