// RUN: %target-sil-opt -sil-print-types -module-name Swift -enable-sil-verify-all %s -early-codemotion -retain-sinking | %FileCheck %s import Builtin enum Never {} struct Int { var value : Builtin.Int64 } struct Int32 { var value : Builtin.Int32 } struct Int64 { var value : Builtin.Int64 } struct UInt64 { var value : Builtin.Int64 } struct Bool { var value : Builtin.Int1 } class fuzz { } enum Boo { case one case two } class B { } class E : B { } 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) } struct foo { var a: Int init(a: Int) init() } enum Optional { case none case some(T) } class Klass {} sil @user : $@convention(thin) (Builtin.NativeObject) -> () sil @user_int : $@convention(thin) (Int) -> () sil @optional_user : $@convention(thin) (Optional) -> () sil @blocker : $@convention(thin) () -> () sil @get_object : $@convention(thin) () -> Builtin.NativeObject sil @foo_init : $@convention(thin) (@thin foo.Type) -> foo // 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: strong_retain %1 : $B br bb3 bb2: strong_retain %1 : $B br bb3 bb3: %4 = tuple() return %4 : $() } // 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: strong_retain %1 : $B %3 = integer_literal $Builtin.Int32, 9 br bb3(%3 : $Builtin.Int32) bb2: 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: strong_retain %1 : $B %3 = integer_literal $Builtin.Int32, 7 br bb3(%3 : $Builtin.Int32) bb2: 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) (Optional) -> () { // 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) (Optional) -> () { bb0(%0 : $Optional): %1 = function_ref @blocker : $@convention(thin) () -> () retain_value %0 : $Optional apply %1() : $@convention(thin) () -> () %3 = alloc_stack $Builtin.Int32 retain_value %0 : $Optional dealloc_stack %3 : $*Builtin.Int32 switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.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 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]+}} : $Optional, [[ARG1:%[0-9]+]] : $Optional): // 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) (Optional, Optional) -> Optional { bb0(%0 : $Optional, %1 : $Optional): %2 = function_ref @blocker : $@convention(thin) () -> () retain_value %1 : $Optional %3 = alloc_stack $Optional retain_value %0 : $Optional switch_enum %1 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 bb1: apply %2() : $@convention(thin) () -> () br bb3 bb2: br bb3 bb3: dealloc_stack %3 : $*Optional return %1 : $Optional } /// 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) (Optional) -> () { // 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) (Optional) -> () { bb0(%0 : $Optional): br bb10 bb10: %1 = function_ref @blocker : $@convention(thin) () -> () retain_value %0 : $Optional %3 = alloc_stack $Builtin.Int32 dealloc_stack %3 : $*Builtin.Int32 release_value %0 : $Optional switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.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) (Optional) -> () { // 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: 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) (Optional) -> () { bb0(%0 : $Optional): %t = integer_literal $Builtin.Int1, 1 %f = integer_literal $Builtin.Int1, 0 %1 = function_ref @blocker : $@convention(thin) () -> () retain_value %0 : $Optional apply %1() : $@convention(thin) () -> () %3 = alloc_stack $Builtin.Int32 retain_value %0 : $Optional dealloc_stack %3 : $*Builtin.Int32 %100 = select_enum %0 : $Optional, case #Optional.some!enumelt: %t, case #Optional.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) (Optional) -> () { // 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: bb2: // CHECK-NEXT: select_enum // CHECK-NEXT: cond_br // CHECK: bb3: // CHECK-NOT: unchecked_enum_data // CHECK-NOT: retain_value // CHECK-LABEL: } // end sil function 'sink_ref_count_ops_enum_over_select_enum_2' sil @sink_ref_count_ops_enum_over_select_enum_2 : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): %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 : $Optional %100 = select_enum %0 : $Optional, case #Optional.some!enumelt: %t, case #Optional.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) (Optional) -> () { // 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: bb2: // CHECK-NOT: retain_value // CHECK-NEXT: select_enum // CHECK-NEXT: cond_br // CHECK-NOT: unchecked_enum_data // CHECK-NOT: retain_value // CHECK-LABEL: } // end sil function 'sink_ref_count_ops_enum_over_select_enum_3' sil @sink_ref_count_ops_enum_over_select_enum_3 : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): %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 : $Optional %100 = select_enum %0 : $Optional, case #Optional.some!enumelt: %t, case #Optional.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) (Optional, Optional) -> Optional { // CHECK: bb0({{%[0-9]+}} : $Optional, [[ARG1:%[0-9]+]] : $Optional): // 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) (Optional, Optional) -> Optional { bb0(%0 : $Optional, %1 : $Optional): %t = integer_literal $Builtin.Int1, 1 %f = integer_literal $Builtin.Int1, 0 %2 = function_ref @blocker : $@convention(thin) () -> () retain_value %1 : $Optional retain_value %0 : $Optional %100 = select_enum %1 : $Optional, case #Optional.some!enumelt: %t, case #Optional.none!enumelt: %f : $Builtin.Int1 cond_br %100, bb1, bb2 bb1: apply %2() : $@convention(thin) () -> () br bb3 bb2: br bb3 bb3: return %1 : $Optional } /// 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) (Optional) -> () { // 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) (Optional) -> () { bb0(%0 : $Optional): %t = integer_literal $Builtin.Int1, 1 %f = integer_literal $Builtin.Int1, 0 br bb10 bb10: %1 = function_ref @blocker : $@convention(thin) () -> () retain_value %0 : $Optional %3 = alloc_stack $Builtin.Int32 dealloc_stack %3 : $*Builtin.Int32 %100 = select_enum %0 : $Optional, case #Optional.some!enumelt: %t, case #Optional.none!enumelt: %f : $Builtin.Int1 release_value %0 : $Optional 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) (Optional) -> 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) (Optional) -> Bool { bb0(%0 : $Optional): switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.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 } // CHECK-LABEL: sil [ossa] @sink_struct_ossa : // 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: } // end sil function 'sink_struct_ossa' sil [ossa] @sink_struct_ossa : $@convention(thin) (Optional) -> Bool { bb0(%0 : $Optional): switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 bb1(%2 : $Builtin.Int32): %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 } // CHECK-LABEL: sil [ossa] @dont_sink_owned_trivial_enum : // CHECK: bb1: // CHECK-NEXT: enum // CHECK: bb2: // CHECK-NEXT: enum // CHECK: bb3([[A:%.*]] : @owned $Optional): // CHECK-NEXT: return [[A]] // CHECK: } // end sil function 'dont_sink_owned_trivial_enum' sil [ossa] @dont_sink_owned_trivial_enum : $@convention(thin) () -> @owned Optional { bb0: cond_br undef, bb1, bb2 bb1: %2 = enum $Optional, #Optional.none!enumelt br bb3(%2) bb2: %4 = enum $Optional, #Optional.none!enumelt br bb3(%4) bb3(%6 : @owned $Optional): return %6 } // 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 : (Test) -> () -> UInt64, $@convention(method) (@guaranteed Test) -> UInt64 %20 = load %7 : $*UInt64 checked_cast_br [exact] Test in %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: @virtual_callee } // 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: strong_retain 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) (Optional) -> () { // CHECK: bb0 // CHECK-NEXT: retain_value // CHECK-NEXT: release_value sil @enum_simplification_test1 : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): retain_value %0 : $Optional release_value %0 : $Optional %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 $Optional, #Optional.none!enumelt retain_value %0 : $Optional release_value %0 : $Optional %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 $Optional, #Optional.some!enumelt, %1 : $Builtin.NativeObject retain_value %2 : $Optional release_value %2 : $Optional %9999 = tuple() return %9999 : $() } // CHECK-LABEL: sil @enum_simplification_test5 : $@convention(thin) () -> () { // CHECK: bb1: // CHECK-NOT: strong_retain // CHECK: bb2: // CHECK-NOT: strong_retain // CHECK: bb3: // CHECK: 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 $Optional, #Optional.some!enumelt, %1 : $Builtin.NativeObject %3 = function_ref @blocker : $@convention(thin) () -> () cond_br undef, bb1, bb2 bb1: retain_value %2 : $Optional apply %3() : $@convention(thin) () -> () br bb3 bb2: retain_value %2 : $Optional 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) (Optional) -> () { // 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: retain_value sil @enum_simplification_test6a : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): %1 = unchecked_enum_data %0 : $Optional, #Optional.some!enumelt cond_br undef, bb1, bb2 bb1: retain_value %0 : $Optional br bb3 bb2: retain_value %0 : $Optional br bb3 bb3: %9999 = tuple() return %9999 : $() } // Because our enum is not local to this function, we cannot move retain_value // %0 past blocker. // // CHECK-LABEL: sil @enum_simplification_test6b : $@convention(thin) (Optional) -> () { // CHECK: bb1: // CHECK: strong_retain // CHECK: bb2: // CHECK: strong_retain // CHECK: bb3: // CHECK-NOT: retain_value sil @enum_simplification_test6b : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): %1 = unchecked_enum_data %0 : $Optional, #Optional.some!enumelt %2 = function_ref @blocker : $@convention(thin) () -> () cond_br undef, bb1, bb2 bb1: retain_value %0 : $Optional apply %2() : $@convention(thin) () -> () br bb3 bb2: retain_value %0 : $Optional 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 $Optional, #Optional.none!enumelt cond_br undef, bb1, bb2 bb1: retain_value %0 : $Optional br bb3 bb2: retain_value %0 : $Optional br bb3 bb3: %9999 = tuple() return %9999 : $() } // CHECK-LABEL: sil @enum_simplification_test8 : $@convention(thin) (Optional) -> () { // CHECK: bb0([[INPUT:%[0-9]+]] : $Optional): // CHECK-NOT: retain_value [[INPUT]] // CHECK: bb2: // CHECK-NEXT: [[PAYLOAD:%[0-9]+]] = unchecked_enum_data [[INPUT]] // CHECK-NOT: strong_retain // CHECK-NOT: retain_value // CHECK: bb4: // CHECK: retain_value [[INPUT]] sil @enum_simplification_test8 : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): cond_br undef, bb1, bb2 bb1: %1 = unchecked_enum_data %0 : $Optional, #Optional.some!enumelt cond_br undef, bb2, bb3 bb2: retain_value %0 : $Optional br bb4 bb3: retain_value %0 : $Optional br bb4 bb4: retain_value %0 : $Optional %9999 = tuple() return %9999 : $() } // CHECK-LABEL: sil @enum_simplification_test9 : $@convention(thin) (Optional) -> () { // CHECK: bb0([[INPUT:%[0-9]+]] : $Optional): // CHECK: bb1: // CHECK: strong_retain // CHECK: bb2: // CHECK-NOT: retain // CHECK: bb3: // CHECK: retain_value [[INPUT]] sil @enum_simplification_test9 : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): %1 = function_ref @blocker : $@convention(thin) () -> () switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 bb1: retain_value %0 : $Optional apply %1() : $@convention(thin) () -> () br bb3 bb2: retain_value %0 : $Optional br bb3 bb3: retain_value %0 : $Optional %9999 = tuple() return %9999 : $() } // Note, this is the same as test9, but for enums // CHECK-LABEL: sil @enum_simplification_test9_enums : $@convention(thin) (Optional) -> () { // CHECK: bb0([[INPUT:%[0-9]+]] : $Optional): // CHECK: bb1: // CHECK: strong_retain // CHECK: bb2: // CHECK-NOT: retain // CHECK: bb3: // CHECK: retain_value [[INPUT]] sil @enum_simplification_test9_enums : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): %t = integer_literal $Builtin.Int1, 1 %f = integer_literal $Builtin.Int1, 0 %2 = function_ref @blocker : $@convention(thin) () -> () %1 = select_enum %0 : $Optional, case #Optional.some!enumelt: %t, case #Optional.none!enumelt: %f : $Builtin.Int1 cond_br %1, bb1, bb2 bb1: retain_value %0 : $Optional apply %2() : $@convention(thin) () -> () br bb3 bb2: retain_value %0 : $Optional br bb3 bb3: retain_value %0 : $Optional %9999 = tuple() return %9999 : $() } //////////////// // Loop Tests // //////////////// // CHECK-LABEL: sil @enum_simplification_test10 : $@convention(thin) (Optional) -> () { // CHECK: bb0([[INPUT:%[0-9]+]] : $Optional): // CHECK: retain_value [[INPUT]] // CHECK: retain_value [[INPUT]] // CHECK: retain_value [[INPUT]] // CHECK: retain_value [[INPUT]] sil @enum_simplification_test10 : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb3 bb1: br bb2 // Single BB Loop bb2: retain_value %0 : $Optional cond_br undef, bb2, bb9999 bb3: br bb4 // Two BB Loop. We can use loop or domination to handle this case. But we don't // handle it now. bb4: retain_value %0 : $Optional br bb5 bb5: retain_value %0 : $Optional cond_br undef, bb4, bb9999 bb9999: retain_value %0 : $Optional %9999 = tuple() return %9999 : $() } // Make sure we don't propagate state out of loops. // CHECK-LABEL: sil @enum_simplification_test11 : $@convention(thin) (Optional) -> () { // CHECK: bb0([[INPUT:%[0-9]+]] : $Optional): // CHECK: retain_value [[INPUT]] // CHECK: retain_value [[INPUT]] sil @enum_simplification_test11 : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 bb1: br bb3 bb2: br bb5 bb3: retain_value %0 : $Optional cond_br undef, bb3, bb4 bb4: br bb5 bb5: retain_value %0 : $Optional %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 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 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 @sink_retains_out_of_switch_regions : $@convention(thin) (Optional) -> () { // 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) (Optional) -> () { bb0(%0 : $Optional): switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 bb1: retain_value %0 : $Optional br bb3 bb2: br bb3 bb3: %1 = tuple() return %1 : $() } // CHECK-LABEL: sil @sink_retains_through_multiple_switches : $@convention(thin) (Optional) -> () { // CHECK-NOT: retain_value // CHECK: bb9: // CHECK: retain_value sil @sink_retains_through_multiple_switches : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): retain_value %0 : $Optional switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 bb1: br bb3 bb2: br bb3 bb3: switch_enum %0 : $Optional, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5 bb4: br bb6 bb5: br bb6 bb6: switch_enum %0 : $Optional, case #Optional.some!enumelt: bb7, case #Optional.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) (Optional) -> () { // CHECK-NOT: retain_value // CHECK: bb5: // CHECK: retain_value sil @sink_retains_through_switch_with_body : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): retain_value %0 : $Optional switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.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) (Optional) -> () { // CHECK-NOT: retain_value // CHECK: bb3: // CHECK: retain_value // CHECK: bb4: // CHECK: retain_value // CHECK: bb5: // CHECK-NOT: retain_value sil @sink_retains_through_separated_switches : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): retain_value %0 : $Optional switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.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) (Optional) -> () { // CHECK: bb3: // CHECK: retain_value sil @enumbbtoenumbbcaselist_invalidation_test : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 bb1: retain_value %0 : $Optional br bb3 bb2: %1 = enum $Optional, #Optional.none!enumelt br bb3 bb3: %2 = tuple() return %2 : $() } // We can do the deletion in simplify-cfg. // // CHECK-LABEL: sil @delete_instead_of_sinkretainsintounreachable_bb : $@convention(thin) (Optional) -> () { // CHECK: bb0( // CHECK-NOT: retain_value // CHECK: bb1: // CHECK-NEXT: unreachable // CHECK: bb2: // CHECK: retain_value sil @delete_instead_of_sinkretainsintounreachable_bb : $@convention(thin) (Optional) -> () { bb0(%0 : $Optional): retain_value %0 : $Optional 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: strong_retain sil @sink_retain_over_switch_enum_cast : $@convention(thin) (Builtin.NativeObject) -> () { bb0(%0 : $Builtin.NativeObject): retain_value %0 : $Builtin.NativeObject %1 = unchecked_ref_cast %0 : $Builtin.NativeObject to $Optional switch_enum %1 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.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 : $*F strong_retain %0 : $F unconditional_checked_cast_addr 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 } class URefd { } class Cont { unowned var x : URefd unowned var y : URefd init() } // CHECK-LABEL: sil @sink_unowned_to_ref_through_args // CHECK: [[LD1:%[0-9]+]] = load // CHECK-NEXT: br bb3([[LD1]] : $@sil_unowned URefd) // CHECK: [[LD2:%[0-9]+]] = load // CHECK-NEXT: br bb3([[LD2]] : $@sil_unowned URefd) // CHECK: bb3([[ARG:%[0-9]+]] : $@sil_unowned URefd): // CHECK-NEXT: [[R:%[0-9]+]] = unowned_to_ref [[ARG]] // CHECK-NEXT: return [[R]] sil @sink_unowned_to_ref_through_args : $@convention(thin) (Builtin.Int1, Cont) -> URefd { bb0(%0 : $Builtin.Int1, %1 : $Cont): cond_br %0, bb1, bb2 bb1: %x1 = ref_element_addr %1 : $Cont, #Cont.x %x2 = load %x1 : $*@sil_unowned URefd %x3 = unowned_to_ref %x2 : $@sil_unowned URefd to $URefd br bb3(%x3 : $URefd) bb2: %y1 = ref_element_addr %1 : $Cont, #Cont.y %y2 = load %y1 : $*@sil_unowned URefd %y3 = unowned_to_ref %y2 : $@sil_unowned URefd to $URefd br bb3(%y3 : $URefd) bb3(%12 : $URefd): return %12 : $URefd } class X { func ping() {} } class Y : X { } // CHECK-LABEL: sil @canonicalize_releases sil @canonicalize_releases : $@convention(thin) (@owned Optional) -> () { bb0(%0 : $Optional): debug_value %0 : $Optional, let, name "x" // id: %1 switch_enum %0 : $Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb3 // id: %2 bb1: // Preds: bb0 %3 = unchecked_enum_data %0 : $Optional, #Optional.some!enumelt // users: %4, %13, %14, %15 checked_cast_br [exact] X in %3 : $X to X, bb4, bb5 // id: %4 // Make sure we were able to sink the release. // CHECK: strong_release // CHECK-NEXT: tuple () // CHECK-NEXT: return bb2: // Preds: bb4 bb5 %5 = tuple () // user: %6 return %5 : $() // id: %6 bb3: // Preds: bb0 %7 = integer_literal $Builtin.Int1, -1 // user: %8 cond_fail %7 : $Builtin.Int1 // id: %8 unreachable // id: %9 // Canonicalize the re bb4(%10 : $X): // Preds: bb1 strong_release %10 : $X // id: %11 br bb2 // id: %12 bb5: // Preds: bb1 %13 = class_method %3 : $X, #X.ping : (X) -> () -> (), $@convention(method) (@guaranteed X) -> () // user: %14 %14 = apply %13(%3) : $@convention(method) (@guaranteed X) -> () strong_release %3 : $X // id: %15 br bb2 // id: %16 } // The input swift code: // func foo(x : X) -> Int { // if let z = x as? Y { // z.ping() // z.ping() // } // return 0 // } //CHECK-LABEL: sil @canonicalize_casts // Make sure we are replacing all uses of %3 with %0. sil @canonicalize_casts: $@convention(thin) (@owned X) -> Int32 { bb0(%0 : $X): debug_value %0 : $X, let, name "x" // id: %1 checked_cast_br X in %0 : $X to Y, bb1, bb3 // id: %2 bb1(%3 : $Y): // Preds: bb0 debug_value %3 : $Y, let, name "z" // id: %4 %5 = upcast %3 : $Y to $X // users: %6, %7, %21, %23 %6 = class_method %5 : $X, #X.ping : (X) -> () -> (), $@convention(method) (@guaranteed X) -> () // users: %21, %23 checked_cast_br [exact] X in %5 : $X to Y, bb5, bb6 // id: %7 bb2: // Preds: bb5 bb6 //CHECK: strong_release %0 //CHECK-NEXT: strong_release %0 strong_release %0 : $X // id: %8 strong_release %3 : $Y // id: %9 br bb4 // id: %10 bb3: // Preds: bb0 br bb4 // id: %11 bb4: // Preds: bb2 bb3 %12 = integer_literal $Builtin.Int32, 0 // user: %13 %13 = struct $Int32 (%12 : $Builtin.Int32) // user: %15 strong_release %0 : $X // id: %14 return %13 : $Int32 // id: %15 bb5(%16 : $Y): // Preds: bb1 //CHECK: strong_retain %0 //CHECK-NEXT: strong_retain %0 strong_retain %0 : $X // id: %17 strong_retain %3 : $Y // id: %18 br bb2 // id: %19 bb6: // Preds: bb1 //CHECK: strong_retain %0 //CHECK-NEXT: apply //CHECK-NEXT: strong_retain %0 //CHECK-NEXT: apply strong_retain %3 : $Y // id: %20 %21 = apply %6(%5) : $@convention(method) (@guaranteed X) -> () strong_retain %0 : $X // id: %22 %23 = apply %6(%5) : $@convention(method) (@guaranteed X) -> () br bb2 // id: %24 } // Make sure we are not crashing on this one: sil @direct_branch0: $@convention(thin) (@owned X) -> Int { bb0(%0 : $X): br bb1(%0 : $X) bb1(%1 : $X): strong_release %1 : $X br bb2(%1 : $X) bb2(%4 : $X): strong_release %4 : $X br bb2(%4 : $X) } //CHECK-LABEL: @cond_branch0 sil @cond_branch0: $@convention(thin) (@owned X) -> Int { bb0(%0 : $X): cond_br undef, bb1(%0 : $X), bb2(%0 : $X) bb1(%1 : $X): //CHECK: strong_release %0 strong_release %1 : $X br bb2(%1 : $X) bb2(%4 : $X): br bb3(%4 : $X) bb3(%6 : $X): //CHECK: strong_release %6 strong_release %6 : $X br bb3(%6 : $X) } // CHECK-LABEL: sil @sink_literals_through_arguments sil @sink_literals_through_arguments : $@convention(thin) (Builtin.Int1) -> Builtin.RawPointer { bb0(%0 : $Builtin.Int1): cond_br %0, bb1, bb2 bb1: %x1 = string_literal utf8 "0" br bb3(%x1 : $Builtin.RawPointer) bb2: %y1 = string_literal utf8 "0" br bb3(%y1 : $Builtin.RawPointer) bb3(%z1 : $Builtin.RawPointer): //CHECK: string_literal utf8 "0" //CHECK-NEXT: return return %z1 : $Builtin.RawPointer } // CHECK-LABEL: sil [ossa] @sink_literals_through_arguments_ossa : // CHECK: string_literal utf8 "0" // CHECK-NEXT: return // CHECK: } // end sil function 'sink_literals_through_arguments_ossa' sil [ossa] @sink_literals_through_arguments_ossa : $@convention(thin) (Builtin.Int1) -> Builtin.RawPointer { bb0(%0 : $Builtin.Int1): cond_br %0, bb1, bb2 bb1: %x1 = string_literal utf8 "0" br bb3(%x1 : $Builtin.RawPointer) bb2: %y1 = string_literal utf8 "0" br bb3(%y1 : $Builtin.RawPointer) bb3(%z1 : $Builtin.RawPointer): return %z1 : $Builtin.RawPointer } // CHECK-LABEL: sil hidden @sink_allocation_inst sil hidden @sink_allocation_inst : $@convention(thin) (Boo, Boo) -> Bool { bb0(%0 : $Boo, %1 : $Boo): debug_value %0 : $Boo, let, name "x" // id: %2 debug_value %0 : $Boo // id: %3 debug_value %0 : $Boo, let, name "self" // id: %4 switch_enum %0 : $Boo, case #Boo.one!enumelt: bb1, case #Boo.two!enumelt: bb3 // id: %5 bb1: // Preds: bb0 %6 = alloc_ref $fuzz // users: %7, %8 debug_value %6 : $fuzz, let, name "self" // id: %7 br bb2(%6 : $fuzz) // id: %8 bb3: // Preds: bb0 %15 = alloc_ref $fuzz // users: %16, %17 debug_value %15 : $fuzz, let, name "self" // id: %16 br bb2(%15 : $fuzz) // id: %17 bb2(%9 : $fuzz): // Make sure we have a single alloc_ref instruction and not two. // CHECK: alloc_ref // CHECK-NOT: alloc_ref // CHECK: return debug_value %9 : $fuzz, let, name "lhs" // id: %10 %11 = integer_literal $Builtin.Int1, -1 // user: %12 %12 = struct $Bool (%11 : $Builtin.Int1) // user: %14 strong_release %9 : $fuzz // id: %13 return %12 : $Bool // id: %14 } // Make sure retain instruction is not sunk across copy_addr inst, as copy_addr // may decrement the refcount of %0 here. // // CHECK-LABEL: retain_blocked_by_copyaddr // CHECK: bb0 // CHECK-NEXT: retain_value sil hidden @retain_blocked_by_copyaddr : $@convention(thin) (@in T, B) -> @out T { bb0(%0 : $*T, %1 : $*T, %2 : $B): retain_value %2 : $B copy_addr [take] %1 to %0 : $*T // id: %3 %4 = tuple () // user: %5 return %4 : $() // id: %5 } // Make sure retain instruction is sunk across copy_addr inst, as copy_addr // dest is initialized. // // CHECK-LABEL: retain_not_blocked_by_copyaddrinit // CHECK: bb0 // CHECK-NEXT: copy_addr // CHECK-NEXT: tuple // CHECK-NEXT: strong_retain sil hidden @retain_not_blocked_by_copyaddrinit : $@convention(thin) (@in T, B) -> @out T { bb0(%0 : $*T, %1 : $*T, %2 : $B): retain_value %2 : $B copy_addr [take] %1 to [init] %0 : $*T // id: %3 %4 = tuple () // user: %5 return %4 : $() // id: %5 } // Make sure retain instruction is sunk across copy_addr inst, as copy_addr // dest is initialized. // // CHECK-LABEL: retain_not_blocked_by_copyaddr_notake_init // CHECK: bb0 // CHECK-NEXT: copy_addr // CHECK-NEXT: tuple // CHECK-NEXT: strong_retain sil hidden @retain_not_blocked_by_copyaddr_notake_init : $@convention(thin) (@in T, B) -> @out T { bb0(%0 : $*T, %1 : $*T, %2 : $B): retain_value %2 : $B copy_addr %1 to [init] %0 : $*T // id: %3 %4 = tuple () // user: %5 return %4 : $() // id: %5 } // Make sure we do not sink the SILArgument for bb3. // // CHECK-LABEL: no_sinkargument_on_clobbered_load // CHECK: bb3(%20 : $Int): // CHECK-NOT: load // CHECK: return sil hidden @no_sinkargument_on_clobbered_load : $@convention(thin) (Bool) -> () { // %0 // users: %2, %7 bb0(%0 : $Bool): %1 = alloc_stack $foo // users: %6, %11, %17, %24 debug_value %0 : $Bool // id: %2 // function_ref foo_init %3 = function_ref @foo_init : $@convention(thin) (@thin foo.Type) -> foo // user: %5 %4 = metatype $@thin foo.Type // user: %5 %5 = apply %3(%4) : $@convention(thin) (@thin foo.Type) -> foo // user: %6 store %5 to %1 : $*foo // id: %6 %7 = struct_extract %0 : $Bool, #Bool.value // user: %8 cond_br %7, bb1, bb2 // id: %8 bb1: // Preds: bb0 %9 = integer_literal $Builtin.Int64, 11 // user: %10 %10 = struct $Int (%9 : $Builtin.Int64) // user: %13 %11 = struct_element_addr %1 : $*foo, #foo.a // users: %12, %13 %12 = load %11 : $*Int // user: %14 store %10 to %11 : $*Int // id: %13 br bb3(%12 : $Int) // id: %14 bb2: // Preds: bb0 %15 = integer_literal $Builtin.Int64, 12 // user: %16 %16 = struct $Int (%15 : $Builtin.Int64) %17 = struct_element_addr %1 : $*foo, #foo.a // user: %18 %18 = load %17 : $*Int // user: %19 br bb3(%18 : $Int) // id: %19 // %20 // user: %22 bb3(%20 : $Int): // Preds: bb1 bb2 // function_ref user_int %21 = function_ref @user_int : $@convention(thin) (Int) -> () // user: %22 %22 = apply %21(%20) : $@convention(thin) (Int) -> () %23 = tuple () // user: %25 dealloc_stack %1 : $*foo // id: %24 return %23 : $() // id: %25 } // Make sure that we properly perform global blotting of values and do not crash // on this code. This will only fail reliably in ASAN builds of swift in such a // case. sil @test_global_blotting : $@convention(thin) () -> () { bbBegin: %0a = enum $Optional, #Optional.none!enumelt cond_br undef, bb1, bb2 bb1: br bb8(%0a : $Optional) bb2: %0 = enum $Optional, #Optional.none!enumelt cond_br undef, bb7, bb3 bb3: cond_br undef, bb4, bb5 bb4: br bb6 bb5: br bb6 bb6: br bb9 bb7: br bb8(%0 : $Optional) bb8(%result : $Optional): release_value %result : $Optional br bb9 bb9: %9999 = tuple() return %9999 : $() }