// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -late-codemotion -release-hoisting -retain-sinking -disable-with-critical-edge=1 | %FileCheck %s import Builtin import Swift class B { } class E : B { } struct A { var i : Builtin.Int32 } enum FakeOptional { case none case some(T) } class C {} class C2 { var current: A init() } 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) } class X {} sil @user : $@convention(thin) (Builtin.NativeObject) -> () sil @use_C2 : $@convention(thin) (C2) -> () sil @optional_user : $@convention(thin) (FakeOptional) -> () 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) -> () { // 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: strong_retain // 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) -> () { bb0(%0 : $FakeOptional): %1 = function_ref @blocker : $@convention(thin) () -> () retain_value %0 : $FakeOptional apply %1() : $@convention(thin) () -> () %3 = alloc_stack $Builtin.Int32 retain_value %0 : $FakeOptional dealloc_stack %3 : $*Builtin.Int32 switch_enum %0 : $FakeOptional, 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) -> () { // 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) -> () { bb0(%0 : $FakeOptional): %1 = function_ref @blocker : $@convention(thin) () -> () cond_br undef, bb1, bb2 bb1: retain_value %0 : $FakeOptional switch_enum %0 : $FakeOptional, 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) -> () { // 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) -> () { bb0(%0 : $FakeOptional): %1 = function_ref @blocker : $@convention(thin) () -> () cond_br undef, bb1, bb3 bb1: retain_value %0 : $FakeOptional switch_enum %0 : $FakeOptional, 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 cannot reference it. // CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_4 : // CHECK: bb0({{%[0-9]+}} : $FakeOptional, [[ARG1:%[0-9]+]] : $FakeOptional): // CHECK-NOT: retain_value [[ARG1]] // CHECK: switch_enum // CHECK: bb1: // CHECK: retain_value // 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, FakeOptional) -> FakeOptional { bb0(%0 : $FakeOptional, %1 : $FakeOptional): %2 = function_ref @blocker : $@convention(thin) () -> () retain_value %1 : $FakeOptional %3 = alloc_stack $FakeOptional retain_value %0 : $FakeOptional switch_enum %1 : $FakeOptional, 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 : $*FakeOptional return %1 : $FakeOptional } /// 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) -> () { // 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) -> () { bb0(%0 : $FakeOptional): br bb10 bb10: %1 = function_ref @blocker : $@convention(thin) () -> () retain_value %0 : $FakeOptional %3 = alloc_stack $Builtin.Int32 dealloc_stack %3 : $*Builtin.Int32 release_value %0 : $FakeOptional switch_enum %0 : $FakeOptional, 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) -> () { // 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: 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) -> () { bb0(%0 : $FakeOptional): %t = integer_literal $Builtin.Int1, 1 %f = integer_literal $Builtin.Int1, 0 %1 = function_ref @blocker : $@convention(thin) () -> () retain_value %0 : $FakeOptional apply %1() : $@convention(thin) () -> () %3 = alloc_stack $Builtin.Int32 retain_value %0 : $FakeOptional dealloc_stack %3 : $*Builtin.Int32 %100 = select_enum %0 : $FakeOptional, 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) -> () { // 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: retain_value // CHECK-NEXT: select_enum // 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) -> () { bb0(%0 : $FakeOptional): %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 %100 = select_enum %0 : $FakeOptional, 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) -> () { // 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: retain_value // CHECK-NEXT: select_enum // 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) -> () { bb0(%0 : $FakeOptional): %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 %100 = select_enum %0 : $FakeOptional, 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, FakeOptional) -> FakeOptional { // CHECK: bb0({{%[0-9]+}} : $FakeOptional, [[ARG1:%[0-9]+]] : $FakeOptional): // CHECK-NOT: retain_value [[ARG1]] // CHECK: select_enum // CHECK: cond_br // CHECK: bb1: // 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, FakeOptional) -> FakeOptional { bb0(%0 : $FakeOptional, %1 : $FakeOptional): %t = integer_literal $Builtin.Int1, 1 %f = integer_literal $Builtin.Int1, 0 %2 = function_ref @blocker : $@convention(thin) () -> () retain_value %1 : $FakeOptional retain_value %0 : $FakeOptional %100 = select_enum %1 : $FakeOptional, 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 } /// 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) -> () { // 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) -> () { bb0(%0 : $FakeOptional): %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 %3 = alloc_stack $Builtin.Int32 dealloc_stack %3 : $*Builtin.Int32 %100 = select_enum %0 : $FakeOptional, case #FakeOptional.some!enumelt.1: %t, case #FakeOptional.none!enumelt: %f : $Builtin.Int1 release_value %0 : $FakeOptional 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) -> 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) -> Bool { bb0(%0 : $FakeOptional): switch_enum %0 : $FakeOptional, 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 } sil @arc_user : $@convention(thin) () -> () // CHECK-LABEL: sil @enum_simplification_test1 : $@convention(thin) (FakeOptional) -> () { // CHECK: bb0 // CHECK-NEXT: retain_value // CHECK-NEXT: release_value sil @enum_simplification_test1 : $@convention(thin) (FakeOptional) -> () { bb0(%0 : $FakeOptional): retain_value %0 : $FakeOptional release_value %0 : $FakeOptional %9999 = tuple() return %9999 : $() } // CHECK-LABEL: sil @enum_simplification_test2 : $@convention(thin) (FakeOptional) -> () { // CHECK: bb0 // CHECK: strong_retain // CHECK: release_value sil @enum_simplification_test2 : $@convention(thin) (FakeOptional) -> () { bb0(%0 : $FakeOptional): %1 = unchecked_enum_data %0 : $FakeOptional, #FakeOptional.some!enumelt.1 retain_value %0 : $FakeOptional release_value %0 : $FakeOptional %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, #FakeOptional.none!enumelt retain_value %0 : $FakeOptional release_value %0 : $FakeOptional %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: 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, #FakeOptional.some!enumelt.1, %1 : $Builtin.NativeObject retain_value %2 : $FakeOptional release_value %2 : $FakeOptional %9999 = tuple() return %9999 : $() } //////////////// // Loop Tests // //////////////// // CHECK-LABEL: sil @enum_simplification_test10 : $@convention(thin) (FakeOptional) -> () { // CHECK: bb0([[INPUT:%[0-9]+]] : $FakeOptional): // CHECK: retain_value [[INPUT]] // CHECK: retain_value [[INPUT]] // CHECK: retain_value [[INPUT]] // CHECK: retain_value [[INPUT]] sil @enum_simplification_test10 : $@convention(thin) (FakeOptional) -> () { bb0(%0 : $FakeOptional): switch_enum %0 : $FakeOptional, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2 // Single BB Loop bb1: retain_value %0 : $FakeOptional 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 br bb3 bb3: retain_value %0 : $FakeOptional cond_br undef, bb2, bb9999 bb9999: retain_value %0 : $FakeOptional %9999 = tuple() return %9999 : $() } // Make sure we don't propagate state out of loops. // CHECK-LABEL: sil @enum_simplification_test11 : $@convention(thin) (FakeOptional) -> () { // CHECK: bb0([[INPUT:%[0-9]+]] : $FakeOptional): // CHECK: retain_value [[INPUT]] // CHECK: retain_value [[INPUT]] sil @enum_simplification_test11 : $@convention(thin) (FakeOptional) -> () { bb0(%0 : $FakeOptional): switch_enum %0 : $FakeOptional, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2 bb1: retain_value %0 : $FakeOptional cond_br undef, bb1, bb2 bb2: retain_value %0 : $FakeOptional %9999 = tuple() return %9999 : $() } // CHECK-LABEL: sil @enum_simplification_test12 : $@convention(thin) (FakeOptional) -> () { // CHECK: bb1: // CHECK: release // CHECK: bb2: // CHECK-NOT: release // CHECK: bb3: // CHECK-NOT: release sil @enum_simplification_test12 : $@convention(thin) (FakeOptional) -> () { bb0(%0 : $FakeOptional): switch_enum %0 : $FakeOptional, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2 bb1: br bb3 bb2: br bb3 bb3: release_value %0 : $FakeOptional %9999 = tuple() return %9999 : $() } // CHECK-LABEL: sil @enum_simplification_test13 : $@convention(thin) (Either) -> () { // CHECK: bb0 // CHECK: release_value // CHECK: bb1: // CHECK-NOT: strong_release // CHECK-NOT: release_value // CHECK: bb2: // CHECK-NOT: strong_release // CHECK-NOT: release_value // 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) -> () { // CHECK: bb1: // CHECK-NOT: release // CHECK: bb2: // CHECK: release_value // CHECK: bb3 // CHECK: release sil @enum_simplification_test16 : $@convention(thin) (FakeOptional) -> () { bb0(%0 : $FakeOptional): switch_enum %0 : $FakeOptional, 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 %9998 = function_ref @unknown : $@convention(thin) () -> () apply %9998() : $@convention(thin) () -> () release_value %0 : $FakeOptional %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: release_value // 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) -> () { // 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) -> () { bb0(%0 : $FakeOptional): switch_enum %0 : $FakeOptional, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2 bb1: retain_value %0 : $FakeOptional br bb3 bb2: br bb3 bb3: %1 = tuple() return %1 : $() } // CHECK-LABEL: sil @sink_retains_through_multiple_switches : $@convention(thin) (FakeOptional) -> () { // CHECK-NOT: retain_value // CHECK: bb9: // CHECK: retain_value sil @sink_retains_through_multiple_switches : $@convention(thin) (FakeOptional) -> () { bb0(%0 : $FakeOptional): retain_value %0 : $FakeOptional switch_enum %0 : $FakeOptional, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2 bb1: br bb3 bb2: br bb3 bb3: switch_enum %0 : $FakeOptional, case #FakeOptional.some!enumelt.1: bb4, case #FakeOptional.none!enumelt: bb5 bb4: br bb6 bb5: br bb6 bb6: switch_enum %0 : $FakeOptional, 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) -> () { // CHECK-NOT: retain_value // CHECK: bb5: // CHECK: retain_value sil @sink_retains_through_switch_with_body : $@convention(thin) (FakeOptional) -> () { bb0(%0 : $FakeOptional): retain_value %0 : $FakeOptional switch_enum %0 : $FakeOptional, 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 : $() } // 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) -> () { // CHECK: bb3: // CHECK: retain_value sil @enumbbtoenumbbcaselist_invalidation_test : $@convention(thin) (FakeOptional) -> () { bb0(%0 : $FakeOptional): switch_enum %0 : $FakeOptional, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2 bb1: retain_value %0 : $FakeOptional br bb3 bb2: %1 = enum $FakeOptional, #FakeOptional.none!enumelt br bb3 bb3: %2 = tuple() return %2 : $() } 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 : $*F strong_retain %0 : $F unconditional_checked_cast_addr take_always F in %1 : $*F to G in %1 : $*F strong_release %0 : $F dealloc_stack %1 : $*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) () -> Never // 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) () -> Never apply %9999() : $@convention(thin) () -> Never unreachable } // CHECK-LABEL: sil @hoist_release_partially_available_retain // CHECK: bb0 // CHECK: cond_br undef, bb1, bb2 // CHECK: bb1: // CHECK: strong_retain // CHECK: strong_release // CHECK: br bb3 // CHECK: bb2: // CHECK: strong_retain // CHECK: apply // 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 br bb3 bb2: strong_retain %0: $Builtin.NativeObject %2 = function_ref @blocker : $@convention(thin) () -> () apply %2() : $@convention(thin) () -> () br bb3 bb3: strong_release %0: $Builtin.NativeObject %5 = tuple() return %5 : $() } // Make sure release can be hoisted across memory that do not escape. // CHECK-LABEL: sil @hoist_release_across_local_memory_use // CHECK: bb1: // CHECK: strong_release // CHECK: br bb3 // CHECK: bb2: // CHECK: strong_release // CHECK: br bb3 // CHECK: bb3: // CHECK-NOT: strong_release // CHECK: return sil @hoist_release_across_local_memory_use : $@convention(thin) (Builtin.NativeObject) ->() { bb0(%0 : $Builtin.NativeObject): %1 = alloc_stack $A cond_br undef, bb1, bb2 bb1: strong_retain %0 : $Builtin.NativeObject br bb3 bb2: br bb3 bb3: %2 = integer_literal $Builtin.Int32, 0 %3 = struct $A (%2 : $Builtin.Int32) store %3 to %1 : $*A strong_release %0 : $Builtin.NativeObject dealloc_stack %1 : $*A %5 = tuple() return %5 : $() } // Make sure release can not be hoisted across memory that do escape. i.e. the release // deinit can read or write to the memory. // CHECK-LABEL: sil @hoist_release_across_escaping_param_memory_use // CHECK: bb1: // CHECK-NOT: strong_release // CHECK: br bb3 // CHECK: bb2: // CHECK-NOT: strong_release // CHECK: br bb3 // CHECK: bb3: // CHECK: store // CHECK: strong_release // CHECK: return sil @hoist_release_across_escaping_param_memory_use : $@convention(thin) (Builtin.NativeObject, C2) ->() { bb0(%0 : $Builtin.NativeObject, %1 : $C2): %2 = ref_element_addr %1 : $C2, #C2.current cond_br undef, bb1, bb2 bb1: strong_retain %0 : $Builtin.NativeObject br bb3 bb2: br bb3 bb3: %3 = integer_literal $Builtin.Int32, 0 %4 = struct $A (%3 : $Builtin.Int32) store %4 to %2 : $*A strong_release %0 : $Builtin.NativeObject %5 = tuple() return %5 : $() } // Make sure release can not be hoisted across memory that do escape, even though its allocated locally. // i.e. the release // deinit can read or write to the memory. // CHECK-LABEL: sil @hoist_release_across_escaping_local_alloc_memory_use // CHECK: bb1: // CHECK-NOT: strong_release // CHECK: br bb3 // CHECK: bb2: // CHECK-NOT: strong_release // CHECK: br bb3 // CHECK: bb3: // CHECK: store // CHECK: strong_release // CHECK: return sil @hoist_release_across_escaping_local_alloc_memory_use : $@convention(thin) (Builtin.NativeObject) ->() { bb0(%0 : $Builtin.NativeObject): %1 = alloc_ref $C2 %2 = ref_element_addr %1 : $C2, #C2.current cond_br undef, bb1, bb2 bb1: strong_retain %0 : $Builtin.NativeObject br bb3 bb2: br bb3 bb3: %22 = function_ref @use_C2 : $@convention(thin) (C2) -> () %23 = apply %22(%1) : $@convention(thin) (C2) -> () %3 = integer_literal $Builtin.Int32, 0 %4 = struct $A (%3 : $Builtin.Int32) store %4 to %2 : $*A strong_release %0 : $Builtin.NativeObject %5 = tuple() return %5 : $() } // CHECK-LABEL: sil @hoist_silargument_release // CHECK: bb1 // CHECK: release // CHECK: br // CHECK: bb2 // CHECK: release // CHECK: br // CHECK: bb3 sil @hoist_silargument_release : $@convention(thin) (@owned X, @owned X) -> () { bb0(%0 : $X, %1 : $X): cond_br undef, bb1, bb2 bb1: strong_retain %0 : $X %5 = function_ref @blocker : $@convention(thin) () -> () apply %5() : $@convention(thin) () -> () br bb3(%0 : $X) bb2: strong_retain %1 : $X br bb3(%1 : $X) bb3(%3 : $X): strong_release %3 : $X %23 = tuple () return %23 : $() }