Files
swift-mirror/test/SILOptimizer/latecodemotion.sil
Nate Chandler e5d87f75a8 [SIL] Add source formal type to checked_cast_br.
It is necessary for opaque values where for casts that will newly start
out as checked_cast_brs and be lowered to checked_cast_addr_brs, since
the latter has the source formal type, IRGen relies on being able to
access it, and there's no way in general to obtain the source formal
type from the source lowered type.
2023-07-27 15:04:15 -07:00

1391 lines
37 KiB
Plaintext

// RUN: %target-sil-opt -enable-sil-verify-all %s -late-codemotion -release-hoisting -retain-sinking | %FileCheck %s
// REQUIRES: swift_in_compiler
import Builtin
import Swift
class B { }
class E : B { }
struct A {
var i : Builtin.Int32
}
enum FakeOptional<T> {
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<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:
strong_retain %1 : $B
br bb3
bb2:
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:
strong_retain %1 : $B
br bb3(%1 : $B)
bb2:
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:
strong_retain %1 : $B
br bb3(%1 : $B)
bb2:
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:
strong_retain %1 : $B
br bb3(%3 : $B, %1 : $B)
bb2:
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:
strong_retain %1 : $B
br bb3(%1 : $B)
bb2:
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:
strong_retain %1 : $B
br bb3(%1 : $B, %3 : $B)
bb2:
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:
strong_retain %1 : $B
br bb5(%1 : $B)
bb3:
strong_retain %2 : $B
br bb5(%2 : $B)
bb4:
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:
strong_retain %1 : $B
br bb5(%2 : $B)
bb3:
strong_retain %2 : $B
br bb5(%2 : $B)
bb4:
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:
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) (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: strong_retain
// CHECK: apply
// 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
// CHECK-LABEL: } // end sil function 'sink_ref_count_ops_enum_over_switch_enum_1'
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 : $*Builtin.Int32
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: 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 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<Builtin.NativeObject>, [[ARG1:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>):
// 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<Builtin.NativeObject>, FakeOptional<Builtin.NativeObject>) -> FakeOptional<Builtin.NativeObject> {
bb0(%0 : $FakeOptional<Builtin.NativeObject>, %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.NativeObject>
switch_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
bb1:
apply %2() : $@convention(thin) () -> ()
br bb3
bb2:
br bb3
bb3:
dealloc_stack %3 : $*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.NativeObject>) -> () {
// CHECK: bb0({{.*}}):
// CHECK: bb1:
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: dealloc_stack
// 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.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
br bb10
bb10:
%1 = function_ref @blocker : $@convention(thin) () -> ()
retain_value %0 : $FakeOptional<Builtin.NativeObject>
%3 = alloc_stack $Builtin.Int32
dealloc_stack %3 : $*Builtin.Int32
release_value %0 : $FakeOptional<Builtin.NativeObject>
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: 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: 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 : $*Builtin.Int32
%100 = select_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: %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: br bb4
// CHECK: bb2:
// 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_2'
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: %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: br bb5
// CHECK: bb2:
// 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) (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: %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: strong_retain
// 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: %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.NativeObject>) -> () {
// 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: 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.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
%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.NativeObject>
%3 = alloc_stack $Builtin.Int32
dealloc_stack %3 : $*Builtin.Int32
%100 = select_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: %t, case #FakeOptional.none!enumelt: %f : $Builtin.Int1
release_value %0 : $FakeOptional<Builtin.NativeObject>
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: 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 : (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
}
sil @arc_user : $@convention(thin) () -> ()
// CHECK-LABEL: sil @enum_simplification_test1 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK: bb0
// CHECK-NEXT: tuple
// CHECK-NEXT: return
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-NEXT: unchecked_enum_data
// CHECK-NEXT: tuple
// CHECK-NEXT: return
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
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: tuple
// CHECK-NEXT: return
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 : $Builtin.NativeObject
retain_value %2 : $FakeOptional<Builtin.NativeObject>
release_value %2 : $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: bbcase1, case #FakeOptional.none!enumelt: bbcase2
bbcase1:
br bb1
bbcase2:
br 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: bbcase1, case #FakeOptional.none!enumelt: bbcase2
bbcase1:
br bb1
bbcase2:
br 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: 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: 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
br bb3
bb2:
%2 = unchecked_enum_data %0 : $Either, #Either.Second!enumelt
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
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 @enum_simplification_test16 : $@convention(thin) (FakeOptional<C>) -> () {
// CHECK: bb1:
// CHECK-NOT: release
// CHECK: bb2:
// CHECK: release_value
// 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: 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: 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: bb1,
case #ThreeCaseAbstractionDifference.B!enumelt: bb2,
case #ThreeCaseAbstractionDifference.C!enumelt: 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: 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: bb1, case #FakeOptional.none!enumelt: bb2
bb1:
br bb3
bb2:
br bb3
bb3:
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: bb4, case #FakeOptional.none!enumelt: bb5
bb4:
br bb6
bb5:
br bb6
bb6:
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt: 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: 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<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: 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 : $()
}
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
}
// CHECK-LABEL: sil @hoist_release_partially_available_retain
// CHECK: bb0
// CHECK: cond_br undef, bb1, bb2
// CHECK: bb1:
// CHECK-NEXT: 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-NEXT: 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: strong_retain
// CHECK: apply
// CHECK: strong_release
// CHECK: br
// CHECK: bb2
// CHECK-NEXT: 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 : $()
}
// CHECK: sil @dont_hoist_release_across_cast
// CHECK: retain
// CHECK: apply
// CHECK: unconditional_checked_cast
// CHECK: release
sil @dont_hoist_release_across_cast : $@convention(thin) (Builtin.NativeObject, Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject, %1: $Builtin.NativeObject):
strong_retain %0: $Builtin.NativeObject
%2 = function_ref @blocker : $@convention(thin) () -> ()
apply %2() : $@convention(thin) () -> ()
%3 = unconditional_checked_cast %0 : $Builtin.NativeObject to B
strong_release %0: $Builtin.NativeObject
%5 = tuple()
return %5 : $()
}