Files
swift-mirror/test/SILOptimizer/earlycodemotion.sil
Roman Levenstein 8ad61d5cd6 Use function signatures for SILDeclRefs in witness_tables, vtables and witness_method instructions.
Textual SIL was sometimes ambiguous when SILDeclRefs were used, because the textual representation of SILDeclRefs was the same for functions that have the same name, but different signatures.
2017-01-27 12:16:14 -08:00

1596 lines
46 KiB
Plaintext

// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -module-name Swift -enable-sil-verify-all %s -early-codemotion -retain-sinking -disable-with-critical-edge=1 | %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<T> {
case none
case some(T)
}
sil @user : $@convention(thin) (Builtin.NativeObject) -> ()
sil @user_int : $@convention(thin) (Int) -> ()
sil @optional_user : $@convention(thin) (Optional<Builtin.Int32>) -> ()
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:
%2 = strong_retain %1 : $B
br bb3
bb2:
%3 = 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:
%2 = strong_retain %1 : $B
%3 = integer_literal $Builtin.Int32, 9
br bb3(%3 : $Builtin.Int32)
bb2:
%4 = strong_retain %1 : $B
%5 = integer_literal $Builtin.Int32, 7
br bb3(%5 : $Builtin.Int32)
bb3(%6 : $Builtin.Int32):
return %6 : $Builtin.Int32
}
// CHECK-LABEL: sil @deep_sink2
// CHECK-NOT: integer_literal
// CHECK: br bb3
// CHECK-NOT: integer_literal
// CHECK: br bb3
// CHECK: bb3
// CHECK: integer_literal
// CHECK: strong_retain
// CHECK-NEXT: return
sil @deep_sink2 : $@convention(thin) (Builtin.Int1, B) -> Builtin.Int32 {
bb0(%0 : $Builtin.Int1, %1 : $B):
cond_br %0, bb1, bb2
bb1:
%2 = strong_retain %1 : $B
%3 = integer_literal $Builtin.Int32, 7
br bb3(%3 : $Builtin.Int32)
bb2:
%4 = strong_retain %1 : $B
%5 = integer_literal $Builtin.Int32, 7
br bb3(%5 : $Builtin.Int32)
bb3(%6 : $Builtin.Int32):
return %6 : $Builtin.Int32
}
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_1 : $@convention(thin) (Optional<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) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
%1 = function_ref @blocker : $@convention(thin) () -> ()
retain_value %0 : $Optional<Builtin.NativeObject>
apply %1() : $@convention(thin) () -> ()
%3 = alloc_stack $Builtin.Int32
retain_value %0 : $Optional<Builtin.NativeObject>
dealloc_stack %3 : $*Builtin.Int32
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: 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 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) (Optional<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) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
%1 = function_ref @blocker : $@convention(thin) () -> ()
cond_br undef, bb1, bb2
bb1:
retain_value %0 : $Optional<Builtin.NativeObject>
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb2, case #Optional.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) (Optional<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) (Optional<Builtin.Int32>) -> () {
bb0(%0 : $Optional<Builtin.Int32>):
%1 = function_ref @blocker : $@convention(thin) () -> ()
cond_br undef, bb1, bb3
bb1:
retain_value %0 : $Optional<Builtin.Int32>
switch_enum %0 : $Optional<Builtin.Int32>, case #Optional.some!enumelt.1: bb2, case #Optional.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]+}} : $Optional<Builtin.Int32>, [[ARG1:%[0-9]+]] : $Optional<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) (Optional<Builtin.Int32>, Optional<Builtin.NativeObject>) -> Optional<Builtin.NativeObject> {
bb0(%0 : $Optional<Builtin.Int32>, %1 : $Optional<Builtin.NativeObject>):
%2 = function_ref @blocker : $@convention(thin) () -> ()
retain_value %1 : $Optional<Builtin.NativeObject>
%3 = alloc_stack $Optional<Builtin.Int32>
retain_value %0 : $Optional<Builtin.Int32>
switch_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
bb1:
apply %2() : $@convention(thin) () -> ()
br bb3
bb2:
br bb3
bb3:
dealloc_stack %3 : $*Optional<Builtin.Int32>
return %1 : $Optional<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) (Optional<Builtin.Int32>) -> () {
// CHECK: bb0({{.*}}):
// CHECK: bb1:
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: retain_value
// CHECK-NEXT: release_value
// CHECK-NEXT: switch_enum
// CHECK: bb2:
// CHECK-NOT: retain_value
// CHECK: bb3:
// CHECK-NOT: retain_value
// CHECK: bb4:
// CHECK-NOT: retain_value
sil @sink_ref_count_ops_enum_over_switch_enum_5 : $@convention(thin) (Optional<Builtin.Int32>) -> () {
bb0(%0 : $Optional<Builtin.Int32>):
br bb10
bb10:
%1 = function_ref @blocker : $@convention(thin) () -> ()
retain_value %0 : $Optional<Builtin.Int32>
%3 = alloc_stack $Builtin.Int32
dealloc_stack %3 : $*Builtin.Int32
release_value %0 : $Optional<Builtin.Int32>
switch_enum %0 : $Optional<Builtin.Int32>, case #Optional.some!enumelt.1: 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<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: 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<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%1 = function_ref @blocker : $@convention(thin) () -> ()
retain_value %0 : $Optional<Builtin.NativeObject>
apply %1() : $@convention(thin) () -> ()
%3 = alloc_stack $Builtin.Int32
retain_value %0 : $Optional<Builtin.NativeObject>
dealloc_stack %3 : $*Builtin.Int32
%100 = select_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: %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<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) (Optional<Builtin.Int32>) -> () {
bb0(%0 : $Optional<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 : $Optional<Builtin.Int32>
%100 = select_enum %0 : $Optional<Builtin.Int32>, case #Optional.some!enumelt.1: %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<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) (Optional<Builtin.Int32>) -> () {
bb0(%0 : $Optional<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 : $Optional<Builtin.Int32>
%100 = select_enum %0 : $Optional<Builtin.Int32>, case #Optional.some!enumelt.1: %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<Builtin.Int32>, Optional<Builtin.NativeObject>) -> Optional<Builtin.NativeObject> {
// CHECK: bb0({{%[0-9]+}} : $Optional<Builtin.Int32>, [[ARG1:%[0-9]+]] : $Optional<Builtin.NativeObject>):
// 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<Builtin.Int32>, Optional<Builtin.NativeObject>) -> Optional<Builtin.NativeObject> {
bb0(%0 : $Optional<Builtin.Int32>, %1 : $Optional<Builtin.NativeObject>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%2 = function_ref @blocker : $@convention(thin) () -> ()
retain_value %1 : $Optional<Builtin.NativeObject>
retain_value %0 : $Optional<Builtin.Int32>
%100 = select_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: %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<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) (Optional<Builtin.Int32>) -> () {
// CHECK: bb0({{.*}}):
// CHECK: bb1:
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: select_enum
// CHECK-NEXT: retain_value
// CHECK-NEXT: release_value
// CHECK-NEXT: cond_br
// CHECK: bb2:
// CHECK-NOT: retain_value
// CHECK: bb3:
// CHECK-NOT: retain_value
// CHECK: bb4:
// CHECK-NOT: retain_value
sil @sink_ref_count_ops_enum_over_select_enum_5 : $@convention(thin) (Optional<Builtin.Int32>) -> () {
bb0(%0 : $Optional<Builtin.Int32>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
br bb10
bb10:
%1 = function_ref @blocker : $@convention(thin) () -> ()
retain_value %0 : $Optional<Builtin.Int32>
%3 = alloc_stack $Builtin.Int32
dealloc_stack %3 : $*Builtin.Int32
%100 = select_enum %0 : $Optional<Builtin.Int32>, case #Optional.some!enumelt.1: %t, case #Optional.none!enumelt: %f : $Builtin.Int1
release_value %0 : $Optional<Builtin.Int32>
cond_br %100, bb1, bb2
bb1:
br bb3
bb2:
br bb3
bb3:
%4 = tuple()
return %4 : $()
}
// Sink the struct instruction
// CHECK-LABEL: sil @sink_struct : $@convention(thin) (Optional<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) (Optional<Builtin.Int32>) -> Bool {
bb0(%0 : $Optional<Builtin.Int32>):
switch_enum %0 : $Optional<Builtin.Int32>, case #Optional.some!enumelt.1: 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
}
// 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
}
// 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<Builtin.NativeObject>) -> () {
// CHECK: bb0
// CHECK-NEXT: retain_value
// CHECK-NEXT: release_value
sil @enum_simplification_test1 : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
retain_value %0 : $Optional<Builtin.NativeObject>
release_value %0 : $Optional<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 $Optional<Builtin.NativeObject>, #Optional.none!enumelt
retain_value %0 : $Optional<Builtin.NativeObject>
release_value %0 : $Optional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_test4 : $@convention(thin) () -> () {
// CHECK: bb0
// CHECK-NEXT: get_object
// CHECK-NEXT: function_ref @get_object
// CHECK-NEXT: apply
// CHECK-NEXT: enum
// CHECK: strong_retain
// CHECK: strong_release
sil @enum_simplification_test4 : $@convention(thin) () -> () {
bb0:
%0 = function_ref @get_object : $@convention(thin) () -> Builtin.NativeObject
%1 = apply %0() : $@convention(thin) () -> Builtin.NativeObject
%2 = enum $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1, %1 : $Builtin.NativeObject
retain_value %2 : $Optional<Builtin.NativeObject>
release_value %2 : $Optional<Builtin.NativeObject>
%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<Builtin.NativeObject>, #Optional.some!enumelt.1, %1 : $Builtin.NativeObject
%3 = function_ref @blocker : $@convention(thin) () -> ()
cond_br undef, bb1, bb2
bb1:
retain_value %2 : $Optional<Builtin.NativeObject>
apply %3() : $@convention(thin) () -> ()
br bb3
bb2:
retain_value %2 : $Optional<Builtin.NativeObject>
apply %3() : $@convention(thin) () -> ()
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// This test is testing that we can properly simplify unchecked_enum_data if we
// know the tag. We also sink it.
//
// CHECK-LABEL: sil @enum_simplification_test6a : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
// CHECK: bb1:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: strong_retain
// CHECK-NOT: retain_value
// CHECK: bb2:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: strong_retain
// CHECK-NOT: retain_value
// CHECK: bb3:
// CHECK: retain_value
sil @enum_simplification_test6a : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
%1 = unchecked_enum_data %0 : $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1
cond_br undef, bb1, bb2
bb1:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb3
bb2:
retain_value %0 : $Optional<Builtin.NativeObject>
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<Builtin.NativeObject>) -> () {
// CHECK: bb1:
// CHECK: strong_retain
// CHECK: bb2:
// CHECK: strong_retain
// CHECK: bb3:
// CHECK-NOT: retain_value
sil @enum_simplification_test6b : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
%1 = unchecked_enum_data %0 : $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1
%2 = function_ref @blocker : $@convention(thin) () -> ()
cond_br undef, bb1, bb2
bb1:
retain_value %0 : $Optional<Builtin.NativeObject>
apply %2() : $@convention(thin) () -> ()
br bb3
bb2:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_test7 : $@convention(thin) () -> () {
// CHECK-NOT: retain
// CHECK-NOT: retain
// CHECK: return
sil @enum_simplification_test7 : $@convention(thin) () -> () {
bb0:
%0 = enum $Optional<Builtin.NativeObject>, #Optional.none!enumelt
cond_br undef, bb1, bb2
bb1:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb3
bb2:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_test8 : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
// CHECK: bb0([[INPUT:%[0-9]+]] : $Optional<Builtin.NativeObject>):
// CHECK: bb2:
// CHECK-NEXT: retain_value [[INPUT]]
// CHECK: bb3:
// CHECK-NEXT: [[PAYLOAD:%[0-9]+]] = unchecked_enum_data [[INPUT]]
// CHECK-NEXT: strong_retain [[PAYLOAD]]
// CHECK: bb4:
// CHECK: retain_value [[INPUT]]
sil @enum_simplification_test8 : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
cond_br undef, bb1, bb2
bb1:
%1 = unchecked_enum_data %0 : $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1
cond_br undef, bb2, bb3
bb2:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb4
bb3:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb4
bb4:
retain_value %0 : $Optional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_test9 : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
// CHECK: bb0([[INPUT:%[0-9]+]] : $Optional<Builtin.NativeObject>):
// CHECK: bb1:
// CHECK: strong_retain
// CHECK: bb2:
// CHECK-NOT: retain
// CHECK: bb3:
// CHECK: retain_value [[INPUT]]
sil @enum_simplification_test9 : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
%1 = function_ref @blocker : $@convention(thin) () -> ()
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
bb1:
retain_value %0 : $Optional<Builtin.NativeObject>
apply %1() : $@convention(thin) () -> ()
br bb3
bb2:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb3
bb3:
retain_value %0 : $Optional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// Note, this is the same as test9, but for enums
// CHECK-LABEL: sil @enum_simplification_test9_enums : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
// CHECK: bb0([[INPUT:%[0-9]+]] : $Optional<Builtin.NativeObject>):
// CHECK: bb1:
// CHECK: strong_retain
// CHECK: bb2:
// CHECK-NOT: retain
// CHECK: bb3:
// CHECK: retain_value [[INPUT]]
sil @enum_simplification_test9_enums : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%2 = function_ref @blocker : $@convention(thin) () -> ()
%1 = select_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: %t, case #Optional.none!enumelt: %f : $Builtin.Int1
cond_br %1, bb1, bb2
bb1:
retain_value %0 : $Optional<Builtin.NativeObject>
apply %2() : $@convention(thin) () -> ()
br bb3
bb2:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb3
bb3:
retain_value %0 : $Optional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
////////////////
// Loop Tests //
////////////////
// CHECK-LABEL: sil @enum_simplification_test10 : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
// CHECK: bb0([[INPUT:%[0-9]+]] : $Optional<Builtin.NativeObject>):
// CHECK: retain_value [[INPUT]]
// CHECK: retain_value [[INPUT]]
// CHECK: retain_value [[INPUT]]
// CHECK: retain_value [[INPUT]]
sil @enum_simplification_test10 : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
// Single BB Loop
bb1:
retain_value %0 : $Optional<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 : $Optional<Builtin.NativeObject>
br bb3
bb3:
retain_value %0 : $Optional<Builtin.NativeObject>
cond_br undef, bb2, bb9999
bb9999:
retain_value %0 : $Optional<Builtin.NativeObject>
%9999 = tuple()
return %9999 : $()
}
// Make sure we don't propagate state out of loops.
// CHECK-LABEL: sil @enum_simplification_test11 : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
// CHECK: bb0([[INPUT:%[0-9]+]] : $Optional<Builtin.NativeObject>):
// CHECK: retain_value [[INPUT]]
// CHECK: retain_value [[INPUT]]
sil @enum_simplification_test11 : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
bb1:
retain_value %0 : $Optional<Builtin.NativeObject>
cond_br undef, bb1, bb2
bb2:
retain_value %0 : $Optional<Builtin.NativeObject>
%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 @sink_retains_out_of_switch_regions : $@convention(thin) (Optional<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) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
bb1:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb3
bb2:
br bb3
bb3:
%1 = tuple()
return %1 : $()
}
// CHECK-LABEL: sil @sink_retains_through_multiple_switches : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
// CHECK-NOT: retain_value
// CHECK: bb9:
// CHECK: retain_value
sil @sink_retains_through_multiple_switches : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
retain_value %0 : $Optional<Builtin.NativeObject>
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
bb1:
br bb3
bb2:
br bb3
bb3:
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb4, case #Optional.none!enumelt: bb5
bb4:
br bb6
bb5:
br bb6
bb6:
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: 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<Builtin.NativeObject>) -> () {
// CHECK-NOT: retain_value
// CHECK: bb5:
// CHECK: retain_value
sil @sink_retains_through_switch_with_body : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
retain_value %0 : $Optional<Builtin.NativeObject>
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: 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<Builtin.NativeObject>) -> () {
// 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<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
retain_value %0 : $Optional<Builtin.NativeObject>
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: 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<Builtin.NativeObject>) -> () {
// CHECK: bb3:
// CHECK: retain_value
sil @enumbbtoenumbbcaselist_invalidation_test : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
bb1:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb3
bb2:
%1 = enum $Optional<Builtin.NativeObject>, #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<Builtin.NativeObject>) -> () {
// CHECK: bb0(
// CHECK-NOT: retain_value
// CHECK: bb1:
// CHECK: retain_value
// CHECK: bb2:
// CHECK: retain_value
sil @delete_instead_of_sinkretainsintounreachable_bb : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
retain_value %0 : $Optional<Builtin.NativeObject>
cond_br undef, bb1, bb2
bb1:
unreachable
bb2:
%1 = tuple()
return %1 : $()
}
// CHECK-LABEL: sil @sink_retain_over_switch_enum_cast
// CHECK: bb0(
// CHECK-NOT: retain_value
// CHECK: switch_enum
// CHECK: bb1:
// CHECK-NOT: retain_value
// CHECK: br bb3
// CHECK: bb2:
// CHECK-NOT: retain_value
// CHECK: br bb3
// CHECK: bb3:
// CHECK: 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<Builtin.NativeObject>
switch_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt.1: 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 take_always F in %1 : $*F to G in %1 : $*F
strong_release %0 : $F
dealloc_stack %1 : $*F
%2 = tuple ()
return %2 : $()
}
// Make sure that is_unique stops code motion.
// CHECK-LABEL: sil @is_unique_stops_codemotion : $@convention(thin) (@inout Builtin.NativeObject) -> () {
// CHECK: bb0([[IN:%[0-9]+]] : $*Builtin.NativeObject):
// CHECK: [[LD:%[0-9]+]] = load [[IN]] : $*Builtin.NativeObject
// CHECK: strong_retain [[LD]] : $Builtin.NativeObject
// CHECK: is_unique [[IN]] : $*Builtin.NativeObject
sil @is_unique_stops_codemotion : $@convention(thin) (@inout Builtin.NativeObject) -> () {
bb0(%0 : $*Builtin.NativeObject):
%1 = load %0 : $*Builtin.NativeObject
strong_retain %1 : $Builtin.NativeObject
is_unique %0 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
sil @no_return_func : $@convention(thin) () -> Never
// Make sure that we do not move retains over noreturn functions.
// CHECK-LABEL: sil @no_return_stops_codemotion : $@convention(thin) (Builtin.NativeObject) -> () {
// CHECK: apply
// CHECK-NEXT: unreachable
sil @no_return_stops_codemotion : $@convention(thin) (Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject):
%1 = alloc_ref $C
strong_retain %1 : $C
%9999 = function_ref @no_return_func : $@convention(thin) () -> Never
apply %9999() : $@convention(thin) () -> Never
unreachable
}
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<X>) -> () {
bb0(%0 : $Optional<X>):
debug_value %0 : $Optional<X>, let, name "x" // id: %1
switch_enum %0 : $Optional<X>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb3 // id: %2
bb1: // Preds: bb0
%3 = unchecked_enum_data %0 : $Optional<X>, #Optional.some!enumelt.1 // users: %4, %13, %14, %15
checked_cast_br [exact] %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!1 : (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 %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!1 : (X) -> () -> (), $@convention(method) (@guaranteed X) -> () // users: %21, %23
checked_cast_br [exact] %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(%5 : $X):
//CHECK: strong_release %5
strong_release %5 : $X
br bb2(%5 : $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 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) <T> (@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) <T> (@in T, B) -> @out T {
bb0(%0 : $*T, %1 : $*T, %2 : $B):
retain_value %2 : $B
copy_addr [take] %1 to [initialization] %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) <T> (@in T, B) -> @out T {
bb0(%0 : $*T, %1 : $*T, %2 : $B):
retain_value %2 : $B
copy_addr %1 to [initialization] %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
}