Files
swift-mirror/test/SILOptimizer/latecodemotion.sil
John McCall ab3f77baf2 Make SILInstruction no longer a subclass of ValueBase and
introduce a common superclass, SILNode.

This is in preparation for allowing instructions to have multiple
results.  It is also a somewhat more elegant representation for
instructions that have zero results.  Instructions that are known
to have exactly one result inherit from a class, SingleValueInstruction,
that subclasses both ValueBase and SILInstruction.  Some care must be
taken when working with SILNode pointers and testing for equality;
please see the comment on SILNode for more information.

A number of SIL passes needed to be updated in order to handle this
new distinction between SIL values and SIL instructions.

Note that the SIL parser is now stricter about not trying to assign
a result value from an instruction (like 'return' or 'strong_retain')
that does not produce any.
2017-09-25 02:06:26 -04:00

1486 lines
40 KiB
Plaintext

// 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<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: bb2:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
// CHECK-NOT: strong_retain
// CHECK: bb3:
// CHECK-NOT: retain_value
// CHECK-NOT: strong_retain
sil @sink_ref_count_ops_enum_over_switch_enum_1 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
%1 = function_ref @blocker : $@convention(thin) () -> ()
retain_value %0 : $FakeOptional<Builtin.NativeObject>
apply %1() : $@convention(thin) () -> ()
%3 = alloc_stack $Builtin.Int32
retain_value %0 : $FakeOptional<Builtin.NativeObject>
dealloc_stack %3 : $*Builtin.Int32
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2
bb1(%2 : $Builtin.NativeObject):
apply %1() : $@convention(thin) () -> ()
br bb3
bb2:
br bb3
bb3:
%4 = tuple()
return %4 : $()
}
// This test makes sure that if any of the switches targets have a predecessor
// besides the switch enum, we don't do anything.
//
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_2 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK: bb0({{.*}}):
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: retain_value
// CHECK-NEXT: switch_enum
// CHECK: bb2:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
// CHECK: bb3:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
// CHECK: bb4:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
sil @sink_ref_count_ops_enum_over_switch_enum_2 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
%1 = function_ref @blocker : $@convention(thin) () -> ()
cond_br undef, bb1, bb2
bb1:
retain_value %0 : $FakeOptional<Builtin.NativeObject>
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb2, case #FakeOptional.none!enumelt: bb3
bb2:
br bb4
bb3:
br bb4
bb4:
%2 = tuple()
return %2 : $()
}
// This test makes sure that if any of the switches targets have a predecessor
// besides the switch enum, we don't do anything.
//
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_3 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () {
// CHECK: bb0({{.*}}):
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: retain_value
// CHECK-NEXT: switch_enum
// CHECK: bb2:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
// CHECK: bb3:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
// CHECK: bb4:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
sil @sink_ref_count_ops_enum_over_switch_enum_3 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () {
bb0(%0 : $FakeOptional<Builtin.Int32>):
%1 = function_ref @blocker : $@convention(thin) () -> ()
cond_br undef, bb1, bb3
bb1:
retain_value %0 : $FakeOptional<Builtin.Int32>
switch_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.some!enumelt.1: bb2, case #FakeOptional.none!enumelt: bb3
bb2:
br bb4
bb3:
br bb4
bb4:
%2 = tuple()
return %2 : $()
}
// This test makes sure that we do move ref counts over instructions that 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.1: 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: retain_value
// CHECK-NEXT: release_value
// CHECK-NEXT: switch_enum
// CHECK: bb2:
// CHECK-NOT: retain_value
// CHECK: bb3:
// CHECK-NOT: retain_value
// CHECK: bb4:
// CHECK-NOT: retain_value
sil @sink_ref_count_ops_enum_over_switch_enum_5 : $@convention(thin) (FakeOptional<Builtin.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.1: bb1, case #FakeOptional.none!enumelt: bb2
bb1:
br bb3
bb2:
br bb3
bb3:
%4 = tuple()
return %4 : $()
}
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_1 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK: bb0({{.*}}):
// CHECK-NEXT: integer_literal
// CHECK-NEXT: integer_literal
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: retain_value
// CHECK-NEXT: apply
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: select_enum
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK: 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.1: %t, case #FakeOptional.none!enumelt: %f : $Builtin.Int1
cond_br %100, bb1, bb2
bb1:
apply %1() : $@convention(thin) () -> ()
br bb3
bb2:
br bb3
bb3:
%4 = tuple()
return %4 : $()
}
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_2 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () {
// CHECK: bb0({{.*}}):
// CHECK-NEXT: integer_literal
// CHECK-NEXT: integer_literal
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: 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<Builtin.Int32>) -> () {
bb0(%0 : $FakeOptional<Builtin.Int32>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%1 = function_ref @blocker : $@convention(thin) () -> ()
cond_br undef, bb1, bb2
bb1:
retain_value %0 : $FakeOptional<Builtin.Int32>
%100 = select_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.some!enumelt.1: %t, case #FakeOptional.none!enumelt: %f : $Builtin.Int1
cond_br %100, bb2, bb3
bb2:
br bb4
bb3:
br bb4
bb4:
%2 = tuple()
return %2 : $()
}
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_3 : $@convention(thin) (FakeOptional<Builtin.Int32>) -> () {
// CHECK: bb0({{.*}}):
// CHECK-NEXT: integer_literal
// CHECK-NEXT: integer_literal
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: 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<Builtin.Int32>) -> () {
bb0(%0 : $FakeOptional<Builtin.Int32>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%1 = function_ref @blocker : $@convention(thin) () -> ()
cond_br undef, bb1, bb3
bb1:
retain_value %0 : $FakeOptional<Builtin.Int32>
%100 = select_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.some!enumelt.1: %t, case #FakeOptional.none!enumelt: %f : $Builtin.Int1
cond_br %100, bb2, bb3
bb2:
br bb4
bb3:
br bb4
bb4:
%2 = tuple()
return %2 : $()
}
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_4 : $@convention(thin) (FakeOptional<Builtin.Int32>, FakeOptional<Builtin.NativeObject>) -> FakeOptional<Builtin.NativeObject> {
// CHECK: bb0({{%[0-9]+}} : $FakeOptional<Builtin.Int32>, [[ARG1:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>):
// CHECK-NOT: retain_value [[ARG1]]
// CHECK: select_enum
// CHECK: cond_br
// CHECK: bb1:
// CHECK: 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.1: %t, case #FakeOptional.none!enumelt: %f : $Builtin.Int1
cond_br %100, bb1, bb2
bb1:
apply %2() : $@convention(thin) () -> ()
br bb3
bb2:
br bb3
bb3:
return %1 : $FakeOptional<Builtin.NativeObject>
}
/// This version does not work since we have a release before the terminator
/// even though we have the select_enum before it.
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_5 : $@convention(thin) (FakeOptional<Builtin.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: retain_value
// CHECK-NEXT: release_value
// CHECK-NEXT: cond_br
// CHECK: bb2:
// CHECK-NOT: retain_value
// CHECK: bb3:
// CHECK-NOT: retain_value
// CHECK: bb4:
// CHECK-NOT: retain_value
sil @sink_ref_count_ops_enum_over_select_enum_5 : $@convention(thin) (FakeOptional<Builtin.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.1: %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.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<Builtin.NativeObject>) -> () {
// CHECK: bb0
// CHECK-NEXT: retain_value
// CHECK-NEXT: release_value
sil @enum_simplification_test1 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
retain_value %0 : $FakeOptional<Builtin.NativeObject>
release_value %0 : $FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_test2 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK: bb0
// CHECK: strong_retain
// CHECK: release_value
sil @enum_simplification_test2 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
%1 = unchecked_enum_data %0 : $FakeOptional<Builtin.NativeObject>, #FakeOptional.some!enumelt.1
retain_value %0 : $FakeOptional<Builtin.NativeObject>
release_value %0 : $FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_test3 : $@convention(thin) () -> () {
// CHECK: bb0
// CHECK-NEXT: enum
// CHECK-NOT: strong_retain
// CHECK-NOT: strong_release
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil @enum_simplification_test3 : $@convention(thin) () -> () {
bb0:
%0 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.none!enumelt
retain_value %0 : $FakeOptional<Builtin.NativeObject>
release_value %0 : $FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_test4 : $@convention(thin) () -> () {
// CHECK: bb0
// CHECK-NEXT: get_object
// CHECK-NEXT: function_ref @get_object
// CHECK-NEXT: apply
// CHECK: strong_retain
// CHECK: strong_release
sil @enum_simplification_test4 : $@convention(thin) () -> () {
bb0:
%0 = function_ref @get_object : $@convention(thin) () -> Builtin.NativeObject
%1 = apply %0() : $@convention(thin) () -> Builtin.NativeObject
%2 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.some!enumelt.1, %1 : $Builtin.NativeObject
retain_value %2 : $FakeOptional<Builtin.NativeObject>
release_value %2 : $FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
////////////////
// Loop Tests //
////////////////
// CHECK-LABEL: sil @enum_simplification_test10 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK: bb0([[INPUT:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>):
// CHECK: retain_value [[INPUT]]
// CHECK: retain_value [[INPUT]]
// CHECK: retain_value [[INPUT]]
// CHECK: retain_value [[INPUT]]
sil @enum_simplification_test10 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2
// Single BB Loop
bb1:
retain_value %0 : $FakeOptional<Builtin.NativeObject>
cond_br undef, bb1, bb9999
// Two BB Loop. We can use loop or domination to handle this case. But we don't
// handle it now.
bb2:
retain_value %0 : $FakeOptional<Builtin.NativeObject>
br bb3
bb3:
retain_value %0 : $FakeOptional<Builtin.NativeObject>
cond_br undef, bb2, bb9999
bb9999:
retain_value %0 : $FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// Make sure we don't propagate state out of loops.
// CHECK-LABEL: sil @enum_simplification_test11 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK: bb0([[INPUT:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>):
// CHECK: retain_value [[INPUT]]
// CHECK: retain_value [[INPUT]]
sil @enum_simplification_test11 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2
bb1:
retain_value %0 : $FakeOptional<Builtin.NativeObject>
cond_br undef, bb1, bb2
bb2:
retain_value %0 : $FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_test12 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK: bb1:
// CHECK: release
// CHECK: bb2:
// CHECK-NOT: release
// CHECK: bb3:
// CHECK-NOT: release
sil @enum_simplification_test12 : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2
bb1:
br bb3
bb2:
br bb3
bb3:
release_value %0 : $FakeOptional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_test13 : $@convention(thin) (Either) -> () {
// CHECK: 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<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.1: bb2
bb1:
br bb3
bb2:
br bb3
bb3:
%2 = integer_literal $Builtin.Word, 2
%3 = integer_literal $Builtin.Word, 3
%4 = integer_literal $Builtin.Int1, 0
%5 = builtin "sadd_with_overflow_Word"(%2 : $Builtin.Word, %3 : $Builtin.Word, %4 : $Builtin.Int1) : $(Builtin.Word, Builtin.Int1)
release_value %0 : $FakeOptional<C>
%9998 = function_ref @unknown : $@convention(thin) () -> ()
apply %9998() : $@convention(thin) () -> ()
release_value %0 : $FakeOptional<C>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_abstraction_differences : $@convention(thin) (ThreeCaseAbstractionDifference) -> () {
// CHECK: bb0(
// CHECK-NOT: release
// CHECK: bb1:
// CHECK-NOT: release
// CHECK: bb2:
// CHECK: 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<Builtin.NativeObject>) -> () {
// CHECK: bb1:
// CHECK-NOT: retain_value
// CHECK: bb2:
// CHECK-NOT: retain_value
// CHECK: bb3:
// CHECK: retain_value
sil @sink_retains_out_of_switch_regions : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2
bb1:
retain_value %0 : $FakeOptional<Builtin.NativeObject>
br bb3
bb2:
br bb3
bb3:
%1 = tuple()
return %1 : $()
}
// CHECK-LABEL: sil @sink_retains_through_multiple_switches : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK-NOT: retain_value
// CHECK: bb9:
// CHECK: retain_value
sil @sink_retains_through_multiple_switches : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
retain_value %0 : $FakeOptional<Builtin.NativeObject>
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2
bb1:
br bb3
bb2:
br bb3
bb3:
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb4, case #FakeOptional.none!enumelt: bb5
bb4:
br bb6
bb5:
br bb6
bb6:
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb7, case #FakeOptional.none!enumelt: bb8
bb7:
br bb9
bb8:
br bb9
bb9:
%1 = tuple()
return %1 : $()
}
// CHECK-LABEL: sil @sink_retains_through_switch_with_body : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK-NOT: retain_value
// CHECK: bb5:
// CHECK: retain_value
sil @sink_retains_through_switch_with_body : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
retain_value %0 : $FakeOptional<Builtin.NativeObject>
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2
bb1:
br bb3
bb2:
br bb4
bb3:
br bb5
bb4:
br bb5
bb5:
%1 = tuple()
return %1 : $()
}
// Make sure that we blot pointers from the enumbbtoenumbbcaselist map after
// merging predecessors. This ensures that the failure to merge unrelating
// values does not stop sinking out of retain, release regions.
// CHECK-LABEL: sil @enumbbtoenumbbcaselist_invalidation_test : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK: bb3:
// CHECK: retain_value
sil @enumbbtoenumbbcaselist_invalidation_test : $@convention(thin) (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
switch_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.some!enumelt.1: bb1, case #FakeOptional.none!enumelt: bb2
bb1:
retain_value %0 : $FakeOptional<Builtin.NativeObject>
br bb3
bb2:
%1 = enum $FakeOptional<Builtin.NativeObject>, #FakeOptional.none!enumelt
br bb3
bb3:
%2 = tuple()
return %2 : $()
}
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: 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 : $()
}
// CHECK: sil @dont_hoist_release_accross_cast
// CHECK: retain
// CHECK: apply
// CHECK: unconditional_checked_cast
// CHECK: release
sil @dont_hoist_release_accross_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 : $()
}
// CHECK: sil @dont_hoist_release_accross_cast_value
// CHECK: retain
// CHECK: apply
// CHECK: unconditional_checked_cast
// CHECK: release
sil @dont_hoist_release_accross_cast_value : $@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) () -> ()
%c = unconditional_checked_cast_value %0 : $Builtin.NativeObject to $B
strong_release %0: $Builtin.NativeObject
%5 = tuple()
return %5 : $()
}