Files
swift-mirror/test/SILOptimizer/earlycodemotion.sil
Erik Eckstein b80a08ee6c CodeMotion: check the ownership of arguments when sinking arguments
So far we only checked the ownership of incoming values.
But even if the incoming instruction has no ownership, the argument may have.
This can happen with enums which are constructed with a non-payload case:

   %1 = enum $Optional<C>, #Optional.none!enumelt
   br bb3(%1)
 bb1(%3 : @owned $Optional<C>):

Fixes an ownership verification error:
rdar://142506300
2025-01-09 19:56:25 +01:00

1635 lines
47 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -module-name Swift -enable-sil-verify-all %s -early-codemotion -retain-sinking | %FileCheck %s
import Builtin
enum Never {}
struct Int {
var value : Builtin.Int64
}
struct Int32 {
var value : Builtin.Int32
}
struct Int64 {
var value : Builtin.Int64
}
struct UInt64 {
var value : Builtin.Int64
}
struct Bool {
var value : Builtin.Int1
}
class fuzz { }
enum Boo {
case one
case two
}
class B { }
class E : B { }
class C {}
enum Either {
case First(Builtin.NativeObject)
case Second(C)
}
enum ThreeCaseEnum {
case A
case B
case C
}
struct S {
var ptr : Builtin.NativeObject
}
enum ThreeCaseAbstractionDifference {
case A(Builtin.Int32)
case B(Builtin.NativeObject)
case C(S)
}
struct foo {
var a: Int
init(a: Int)
init()
}
enum Optional<T> {
case none
case some(T)
}
class Klass {}
sil @user : $@convention(thin) (Builtin.NativeObject) -> ()
sil @user_int : $@convention(thin) (Int) -> ()
sil @optional_user : $@convention(thin) (Optional<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:
strong_retain %1 : $B
br bb3
bb2:
strong_retain %1 : $B
br bb3
bb3:
%4 = tuple()
return %4 : $()
}
// CHECK-LABEL: sil @do_not_sink_local_uses
// CHECK: integer_literal
// CHECK: integer_literal
// CHECK: return
sil @do_not_sink_local_uses : $@convention(thin) (Builtin.Int1) -> Builtin.Int32 {
bb0(%0 : $Builtin.Int1):
cond_br %0, bb1, bb3
bb1:
%1 = integer_literal $Builtin.Int32, 7
br bb2(%1 : $Builtin.Int32)
bb3:
%2 = integer_literal $Builtin.Int32, 8
br bb2(%2 : $Builtin.Int32)
bb2(%3 : $Builtin.Int32):
return %3 : $Builtin.Int32
}
// CHECK-LABEL: sil @deep_sink1
// CHECK: integer_literal
// CHECK: br bb3
// CHECK: integer_literal
// CHECK: br bb3
// CHECK: bb3
// CHECK: strong_retain
// CHECK-NEXT: return
sil @deep_sink1 : $@convention(thin) (Builtin.Int1, B) -> Builtin.Int32 {
bb0(%0 : $Builtin.Int1, %1 : $B):
cond_br %0, bb1, bb2
bb1:
strong_retain %1 : $B
%3 = integer_literal $Builtin.Int32, 9
br bb3(%3 : $Builtin.Int32)
bb2:
strong_retain %1 : $B
%5 = integer_literal $Builtin.Int32, 7
br bb3(%5 : $Builtin.Int32)
bb3(%6 : $Builtin.Int32):
return %6 : $Builtin.Int32
}
// CHECK-LABEL: sil @deep_sink2
// CHECK-NOT: integer_literal
// CHECK: br bb3
// CHECK-NOT: integer_literal
// CHECK: br bb3
// CHECK: bb3
// CHECK: integer_literal
// CHECK: strong_retain
// CHECK-NEXT: return
sil @deep_sink2 : $@convention(thin) (Builtin.Int1, B) -> Builtin.Int32 {
bb0(%0 : $Builtin.Int1, %1 : $B):
cond_br %0, bb1, bb2
bb1:
strong_retain %1 : $B
%3 = integer_literal $Builtin.Int32, 7
br bb3(%3 : $Builtin.Int32)
bb2:
strong_retain %1 : $B
%5 = integer_literal $Builtin.Int32, 7
br bb3(%5 : $Builtin.Int32)
bb3(%6 : $Builtin.Int32):
return %6 : $Builtin.Int32
}
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_1 : $@convention(thin) (Optional<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: bb1, case #Optional.none!enumelt: bb2
bb1(%2 : $Builtin.NativeObject):
apply %1() : $@convention(thin) () -> ()
br bb3
bb2:
br bb3
bb3:
%4 = tuple()
return %4 : $()
}
// This test makes sure that we do move ref counts over instructions that cannot reference it.
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_4 :
// CHECK: bb0({{%[0-9]+}} : $Optional<Builtin.NativeObject>, [[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.NativeObject>, Optional<Builtin.NativeObject>) -> Optional<Builtin.NativeObject> {
bb0(%0 : $Optional<Builtin.NativeObject>, %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.NativeObject>
switch_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
bb1:
apply %2() : $@convention(thin) () -> ()
br bb3
bb2:
br bb3
bb3:
dealloc_stack %3 : $*Optional<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: 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: %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: bb2:
// CHECK-NEXT: select_enum
// CHECK-NEXT: cond_br
// CHECK: bb3:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
// CHECK-LABEL: } // end sil function 'sink_ref_count_ops_enum_over_select_enum_2'
sil @sink_ref_count_ops_enum_over_select_enum_2 : $@convention(thin) (Optional<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: %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: bb2:
// CHECK-NOT: retain_value
// CHECK-NEXT: select_enum
// CHECK-NEXT: cond_br
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
// CHECK-LABEL: } // end sil function 'sink_ref_count_ops_enum_over_select_enum_3'
sil @sink_ref_count_ops_enum_over_select_enum_3 : $@convention(thin) (Optional<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: %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.NativeObject>, Optional<Builtin.NativeObject>) -> Optional<Builtin.NativeObject> {
// CHECK: bb0({{%[0-9]+}} : $Optional<Builtin.NativeObject>, [[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.NativeObject>, Optional<Builtin.NativeObject>) -> Optional<Builtin.NativeObject> {
bb0(%0 : $Optional<Builtin.NativeObject>, %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.NativeObject>
%100 = select_enum %1 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt: %t, case #Optional.none!enumelt: %f : $Builtin.Int1
cond_br %100, bb1, bb2
bb1:
apply %2() : $@convention(thin) () -> ()
br bb3
bb2:
br bb3
bb3:
return %1 : $Optional<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: %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: bb1, case #Optional.none!enumelt: bb2
bb1:
%6 = integer_literal $Builtin.Int1, -1
%7 = struct $Bool (%6 : $Builtin.Int1)
br bb3(%7 : $Bool)
bb2:
%9 = integer_literal $Builtin.Int1, 0
%10 = struct $Bool (%9 : $Builtin.Int1)
br bb3(%10 : $Bool)
bb3(%12 : $Bool):
return %12 : $Bool
}
// CHECK-LABEL: sil [ossa] @sink_struct_ossa :
// CHECK: bb0({{.*}}):
// CHECK: switch_enum
// CHECK: bb1({{.*}}):
// CHECK: integer_literal $Builtin.Int1, -1
// CHECK: br bb3({{.*}} : $Builtin.Int1)
// CHECK: bb2:
// CHECK: integer_literal $Builtin.Int1, 0
// CHECK: br bb3({{.*}} : $Builtin.Int1)
// CHECK: bb3({{.*}} : $Builtin.Int1):
// CHECK: struct $Bool
// CHECK: } // end sil function 'sink_struct_ossa'
sil [ossa] @sink_struct_ossa : $@convention(thin) (Optional<Builtin.Int32>) -> Bool {
bb0(%0 : $Optional<Builtin.Int32>):
switch_enum %0 : $Optional<Builtin.Int32>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
bb1(%2 : $Builtin.Int32):
%6 = integer_literal $Builtin.Int1, -1
%7 = struct $Bool (%6 : $Builtin.Int1)
br bb3(%7 : $Bool)
bb2:
%9 = integer_literal $Builtin.Int1, 0
%10 = struct $Bool (%9 : $Builtin.Int1)
br bb3(%10 : $Bool)
bb3(%12 : $Bool):
return %12 : $Bool
}
// CHECK-LABEL: sil [ossa] @dont_sink_owned_trivial_enum :
// CHECK: bb1:
// CHECK-NEXT: enum
// CHECK: bb2:
// CHECK-NEXT: enum
// CHECK: bb3([[A:%.*]] : @owned $Optional<C>):
// CHECK-NEXT: return [[A]]
// CHECK: } // end sil function 'dont_sink_owned_trivial_enum'
sil [ossa] @dont_sink_owned_trivial_enum : $@convention(thin) () -> @owned Optional<C> {
bb0:
cond_br undef, bb1, bb2
bb1:
%2 = enum $Optional<C>, #Optional.none!enumelt
br bb3(%2)
bb2:
%4 = enum $Optional<C>, #Optional.none!enumelt
br bb3(%4)
bb3(%6 : @owned $Optional<C>):
return %6
}
// Sink retain down the successors so we can pair up retain with release on the
// fast path.
class Test {
func testing() -> UInt64
}
sil_global @test : $Test
sil_global @x : $UInt64
// CHECK-LABEL: @sink_refcount_to_succs
sil @sink_refcount_to_succs : $@convention(thin) () -> () {
bb0:
%0 = global_addr @test : $*Test
%1 = alloc_ref $Test
store %1 to %0 : $*Test
%7 = global_addr @x : $*UInt64
%8 = integer_literal $Builtin.Int64, 0
%9 = struct $UInt64 (%8 : $Builtin.Int64)
store %9 to %7 : $*UInt64
br bb1
bb1:
// CHECK: bb1:
// CHECK-NOT: strong_retain
%17 = load %0 : $*Test
strong_retain %17 : $Test
%19 = class_method %17 : $Test, #Test.testing : (Test) -> () -> UInt64, $@convention(method) (@guaranteed Test) -> UInt64
%20 = load %7 : $*UInt64
checked_cast_br [exact] Test in %17 : $Test to Test, bb3, bb4
bb2:
// CHECK: bb2:
// CHECK-NOT: strong_retain
%36 = tuple ()
return %36 : $()
bb3(%38 : $Test):
// CHECK: bb3(
// CHECK: strong_retain
// CHECK: strong_release
strong_release %17 : $Test
br bb2
bb4:
// CHECK: bb4:
// CHECK: strong_retain
// CHECK: apply
%65 = apply %19(%17) : $@convention(method) (@guaranteed Test) -> UInt64
br bb2
}
sil @virtual_callee : $@convention(method) (@guaranteed Test) -> UInt64
sil_vtable Test {
#Test.testing: @virtual_callee
}
// CHECK-LABEL: sil @sink_retains_from_preds : $@convention(thin) (Builtin.NativeObject) -> () {
// CHECK: bb1:
// CHECK-NOT: retain_value
// CHECK: bb2:
// CHECK-NOT: retain_value
// CHECK: bb3:
// CHECK: strong_retain
sil @sink_retains_from_preds : $@convention(thin) (Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject):
cond_br undef, bb1, bb2
bb1:
retain_value %0 : $Builtin.NativeObject
br bb3
bb2:
retain_value %0 : $Builtin.NativeObject
br bb3
bb3:
%1 = tuple()
return %1 : $()
}
// CHECK-LABEL: sil @enum_simplification_test1 : $@convention(thin) (Optional<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 : $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 : $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
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
%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-NOT: retain_value [[INPUT]]
// CHECK: bb2:
// CHECK-NEXT: [[PAYLOAD:%[0-9]+]] = unchecked_enum_data [[INPUT]]
// CHECK-NOT: strong_retain
// CHECK-NOT: retain_value
// CHECK: bb4:
// CHECK: retain_value [[INPUT]]
sil @enum_simplification_test8 : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
cond_br undef, bb1, bb2
bb1:
%1 = unchecked_enum_data %0 : $Optional<Builtin.NativeObject>, #Optional.some!enumelt
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: 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: %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: bb1, case #Optional.none!enumelt: bb3
bb1:
br bb2
// Single BB Loop
bb2:
retain_value %0 : $Optional<Builtin.NativeObject>
cond_br undef, bb2, bb9999
bb3:
br bb4
// Two BB Loop. We can use loop or domination to handle this case. But we don't
// handle it now.
bb4:
retain_value %0 : $Optional<Builtin.NativeObject>
br bb5
bb5:
retain_value %0 : $Optional<Builtin.NativeObject>
cond_br undef, bb4, 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: bb1, case #Optional.none!enumelt: bb2
bb1:
br bb3
bb2:
br bb5
bb3:
retain_value %0 : $Optional<Builtin.NativeObject>
cond_br undef, bb3, bb4
bb4:
br bb5
bb5:
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
cond_br undef, bb1, bb2
bb1:
release_value %0 : $Either
br bb3
bb2:
br bb3
bb3:
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @enum_simplification_test15 : $@convention(thin) (Either, ThreeCaseEnum) -> () {
// CHECK: bb1:
// CHECK: release
// CHECK: bb2:
// CHECK-NOT: release
// CHECK: bb3:
// CHECK-NOT: release
sil @enum_simplification_test15 : $@convention(thin) (Either, ThreeCaseEnum) -> () {
bb0(%0 : $Either, %1 : $ThreeCaseEnum):
%2 = unchecked_enum_data %0 : $Either, #Either.First!enumelt
switch_enum %1 : $ThreeCaseEnum, case #ThreeCaseEnum.A!enumelt: bb1,
case #ThreeCaseEnum.B!enumelt: bb2,
case #ThreeCaseEnum.C!enumelt: bb3
bb1:
release_value %0 : $Either
br bb4
bb2:
br bb4
bb3:
br bb4
bb4:
%9999 = tuple()
return %9999 : $()
}
sil @unknown : $@convention(thin) () -> ()
// CHECK-LABEL: sil @sink_retains_out_of_switch_regions : $@convention(thin) (Optional<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: 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: bb1, case #Optional.none!enumelt: bb2
bb1:
br bb3
bb2:
br bb3
bb3:
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5
bb4:
br bb6
bb5:
br bb6
bb6:
switch_enum %0 : $Optional<Builtin.NativeObject>, case #Optional.some!enumelt: bb7, case #Optional.none!enumelt: bb8
bb7:
br bb9
bb8:
br bb9
bb9:
%1 = tuple()
return %1 : $()
}
// CHECK-LABEL: sil @sink_retains_through_switch_with_body : $@convention(thin) (Optional<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: 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: 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: 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-NEXT: unreachable
// 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: bb1, case #Optional.none!enumelt: bb2
bb1:
br bb3
bb2:
br bb3
bb3:
%4 = tuple()
return %4 : $()
}
class F {}
class G {}
// CHECK-LABEL: sil @cast_consumption
// CHECK: store
// CHECK-NEXT: strong_retain
// CHECK-NEXT: unconditional_checked_cast_addr
// CHECK-NEXT: strong_release
sil @cast_consumption : $@convention(thin) (@owned F) -> () {
bb0(%0 : $F):
%1 = alloc_stack $F
store %0 to %1 : $*F
strong_retain %0 : $F
unconditional_checked_cast_addr F in %1 : $*F to G in %1 : $*F
strong_release %0 : $F
dealloc_stack %1 : $*F
%2 = tuple ()
return %2 : $()
}
// Make sure that is_unique stops code motion.
// CHECK-LABEL: sil @is_unique_stops_codemotion : $@convention(thin) (@inout Builtin.NativeObject) -> () {
// CHECK: bb0([[IN:%[0-9]+]] : $*Builtin.NativeObject):
// CHECK: [[LD:%[0-9]+]] = load [[IN]] : $*Builtin.NativeObject
// CHECK: strong_retain [[LD]] : $Builtin.NativeObject
// CHECK: is_unique [[IN]] : $*Builtin.NativeObject
sil @is_unique_stops_codemotion : $@convention(thin) (@inout Builtin.NativeObject) -> () {
bb0(%0 : $*Builtin.NativeObject):
%1 = load %0 : $*Builtin.NativeObject
strong_retain %1 : $Builtin.NativeObject
is_unique %0 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
sil @no_return_func : $@convention(thin) () -> Never
// Make sure that we do not move retains over noreturn functions.
// CHECK-LABEL: sil @no_return_stops_codemotion : $@convention(thin) (Builtin.NativeObject) -> () {
// CHECK: apply
// CHECK-NEXT: unreachable
sil @no_return_stops_codemotion : $@convention(thin) (Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject):
%1 = alloc_ref $C
strong_retain %1 : $C
%9999 = function_ref @no_return_func : $@convention(thin) () -> Never
apply %9999() : $@convention(thin) () -> Never
unreachable
}
class URefd {
}
class Cont {
unowned var x : URefd
unowned var y : URefd
init()
}
// CHECK-LABEL: sil @sink_unowned_to_ref_through_args
// CHECK: [[LD1:%[0-9]+]] = load
// CHECK-NEXT: br bb3([[LD1]] : $@sil_unowned URefd)
// CHECK: [[LD2:%[0-9]+]] = load
// CHECK-NEXT: br bb3([[LD2]] : $@sil_unowned URefd)
// CHECK: bb3([[ARG:%[0-9]+]] : $@sil_unowned URefd):
// CHECK-NEXT: [[R:%[0-9]+]] = unowned_to_ref [[ARG]]
// CHECK-NEXT: return [[R]]
sil @sink_unowned_to_ref_through_args : $@convention(thin) (Builtin.Int1, Cont) -> URefd {
bb0(%0 : $Builtin.Int1, %1 : $Cont):
cond_br %0, bb1, bb2
bb1:
%x1 = ref_element_addr %1 : $Cont, #Cont.x
%x2 = load %x1 : $*@sil_unowned URefd
%x3 = unowned_to_ref %x2 : $@sil_unowned URefd to $URefd
br bb3(%x3 : $URefd)
bb2:
%y1 = ref_element_addr %1 : $Cont, #Cont.y
%y2 = load %y1 : $*@sil_unowned URefd
%y3 = unowned_to_ref %y2 : $@sil_unowned URefd to $URefd
br bb3(%y3 : $URefd)
bb3(%12 : $URefd):
return %12 : $URefd
}
class X { func ping() {} }
class Y : X { }
// CHECK-LABEL: sil @canonicalize_releases
sil @canonicalize_releases : $@convention(thin) (@owned Optional<X>) -> () {
bb0(%0 : $Optional<X>):
debug_value %0 : $Optional<X>, let, name "x" // id: %1
switch_enum %0 : $Optional<X>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb3 // id: %2
bb1: // Preds: bb0
%3 = unchecked_enum_data %0 : $Optional<X>, #Optional.some!enumelt // users: %4, %13, %14, %15
checked_cast_br [exact] X in %3 : $X to X, bb4, bb5 // id: %4
// Make sure we were able to sink the release.
// CHECK: strong_release
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
bb2: // Preds: bb4 bb5
%5 = tuple () // user: %6
return %5 : $() // id: %6
bb3: // Preds: bb0
%7 = integer_literal $Builtin.Int1, -1 // user: %8
cond_fail %7 : $Builtin.Int1 // id: %8
unreachable // id: %9
// Canonicalize the re
bb4(%10 : $X): // Preds: bb1
strong_release %10 : $X // id: %11
br bb2 // id: %12
bb5: // Preds: bb1
%13 = class_method %3 : $X, #X.ping : (X) -> () -> (), $@convention(method) (@guaranteed X) -> () // user: %14
%14 = apply %13(%3) : $@convention(method) (@guaranteed X) -> ()
strong_release %3 : $X // id: %15
br bb2 // id: %16
}
// The input swift code:
// func foo(x : X) -> Int {
// if let z = x as? Y {
// z.ping()
// z.ping()
// }
// return 0
// }
//CHECK-LABEL: sil @canonicalize_casts
// Make sure we are replacing all uses of %3 with %0.
sil @canonicalize_casts: $@convention(thin) (@owned X) -> Int32 {
bb0(%0 : $X):
debug_value %0 : $X, let, name "x" // id: %1
checked_cast_br X in %0 : $X to Y, bb1, bb3 // id: %2
bb1(%3 : $Y): // Preds: bb0
debug_value %3 : $Y, let, name "z" // id: %4
%5 = upcast %3 : $Y to $X // users: %6, %7, %21, %23
%6 = class_method %5 : $X, #X.ping : (X) -> () -> (), $@convention(method) (@guaranteed X) -> () // users: %21, %23
checked_cast_br [exact] X in %5 : $X to Y, bb5, bb6 // id: %7
bb2: // Preds: bb5 bb6
//CHECK: strong_release %0
//CHECK-NEXT: strong_release %0
strong_release %0 : $X // id: %8
strong_release %3 : $Y // id: %9
br bb4 // id: %10
bb3: // Preds: bb0
br bb4 // id: %11
bb4: // Preds: bb2 bb3
%12 = integer_literal $Builtin.Int32, 0 // user: %13
%13 = struct $Int32 (%12 : $Builtin.Int32) // user: %15
strong_release %0 : $X // id: %14
return %13 : $Int32 // id: %15
bb5(%16 : $Y): // Preds: bb1
//CHECK: strong_retain %0
//CHECK-NEXT: strong_retain %0
strong_retain %0 : $X // id: %17
strong_retain %3 : $Y // id: %18
br bb2 // id: %19
bb6: // Preds: bb1
//CHECK: strong_retain %0
//CHECK-NEXT: apply
//CHECK-NEXT: strong_retain %0
//CHECK-NEXT: apply
strong_retain %3 : $Y // id: %20
%21 = apply %6(%5) : $@convention(method) (@guaranteed X) -> ()
strong_retain %0 : $X // id: %22
%23 = apply %6(%5) : $@convention(method) (@guaranteed X) -> ()
br bb2 // id: %24
}
// Make sure we are not crashing on this one:
sil @direct_branch0: $@convention(thin) (@owned X) -> Int {
bb0(%0 : $X):
br bb1(%0 : $X)
bb1(%1 : $X):
strong_release %1 : $X
br bb2(%1 : $X)
bb2(%4 : $X):
strong_release %4 : $X
br bb2(%4 : $X)
}
//CHECK-LABEL: @cond_branch0
sil @cond_branch0: $@convention(thin) (@owned X) -> Int {
bb0(%0 : $X):
cond_br undef, bb1(%0 : $X), bb2(%0 : $X)
bb1(%1 : $X):
//CHECK: strong_release %0
strong_release %1 : $X
br bb2(%1 : $X)
bb2(%4 : $X):
br bb3(%4 : $X)
bb3(%6 : $X):
//CHECK: strong_release %6
strong_release %6 : $X
br bb3(%6 : $X)
}
// CHECK-LABEL: sil @sink_literals_through_arguments
sil @sink_literals_through_arguments : $@convention(thin) (Builtin.Int1) -> Builtin.RawPointer {
bb0(%0 : $Builtin.Int1):
cond_br %0, bb1, bb2
bb1:
%x1 = string_literal utf8 "0"
br bb3(%x1 : $Builtin.RawPointer)
bb2:
%y1 = string_literal utf8 "0"
br bb3(%y1 : $Builtin.RawPointer)
bb3(%z1 : $Builtin.RawPointer):
//CHECK: string_literal utf8 "0"
//CHECK-NEXT: return
return %z1 : $Builtin.RawPointer
}
// CHECK-LABEL: sil [ossa] @sink_literals_through_arguments_ossa :
// CHECK: string_literal utf8 "0"
// CHECK-NEXT: return
// CHECK: } // end sil function 'sink_literals_through_arguments_ossa'
sil [ossa] @sink_literals_through_arguments_ossa : $@convention(thin) (Builtin.Int1) -> Builtin.RawPointer {
bb0(%0 : $Builtin.Int1):
cond_br %0, bb1, bb2
bb1:
%x1 = string_literal utf8 "0"
br bb3(%x1 : $Builtin.RawPointer)
bb2:
%y1 = string_literal utf8 "0"
br bb3(%y1 : $Builtin.RawPointer)
bb3(%z1 : $Builtin.RawPointer):
return %z1 : $Builtin.RawPointer
}
// CHECK-LABEL: sil hidden @sink_allocation_inst
sil hidden @sink_allocation_inst : $@convention(thin) (Boo, Boo) -> Bool {
bb0(%0 : $Boo, %1 : $Boo):
debug_value %0 : $Boo, let, name "x" // id: %2
debug_value %0 : $Boo // id: %3
debug_value %0 : $Boo, let, name "self" // id: %4
switch_enum %0 : $Boo, case #Boo.one!enumelt: bb1, case #Boo.two!enumelt: bb3 // id: %5
bb1: // Preds: bb0
%6 = alloc_ref $fuzz // users: %7, %8
debug_value %6 : $fuzz, let, name "self" // id: %7
br bb2(%6 : $fuzz) // id: %8
bb3: // Preds: bb0
%15 = alloc_ref $fuzz // users: %16, %17
debug_value %15 : $fuzz, let, name "self" // id: %16
br bb2(%15 : $fuzz) // id: %17
bb2(%9 : $fuzz):
// Make sure we have a single alloc_ref instruction and not two.
// CHECK: alloc_ref
// CHECK-NOT: alloc_ref
// CHECK: return
debug_value %9 : $fuzz, let, name "lhs" // id: %10
%11 = integer_literal $Builtin.Int1, -1 // user: %12
%12 = struct $Bool (%11 : $Builtin.Int1) // user: %14
strong_release %9 : $fuzz // id: %13
return %12 : $Bool // id: %14
}
// Make sure retain instruction is not sunk across copy_addr inst, as copy_addr
// may decrement the refcount of %0 here.
//
// CHECK-LABEL: retain_blocked_by_copyaddr
// CHECK: bb0
// CHECK-NEXT: retain_value
sil hidden @retain_blocked_by_copyaddr : $@convention(thin) <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 [init] %0 : $*T // id: %3
%4 = tuple () // user: %5
return %4 : $() // id: %5
}
// Make sure retain instruction is sunk across copy_addr inst, as copy_addr
// dest is initialized.
//
// CHECK-LABEL: retain_not_blocked_by_copyaddr_notake_init
// CHECK: bb0
// CHECK-NEXT: copy_addr
// CHECK-NEXT: tuple
// CHECK-NEXT: strong_retain
sil hidden @retain_not_blocked_by_copyaddr_notake_init : $@convention(thin) <T> (@in T, B) -> @out T {
bb0(%0 : $*T, %1 : $*T, %2 : $B):
retain_value %2 : $B
copy_addr %1 to [init] %0 : $*T // id: %3
%4 = tuple () // user: %5
return %4 : $() // id: %5
}
// Make sure we do not sink the SILArgument for bb3.
//
// CHECK-LABEL: no_sinkargument_on_clobbered_load
// CHECK: bb3(%20 : $Int):
// CHECK-NOT: load
// CHECK: return
sil hidden @no_sinkargument_on_clobbered_load : $@convention(thin) (Bool) -> () {
// %0 // users: %2, %7
bb0(%0 : $Bool):
%1 = alloc_stack $foo // users: %6, %11, %17, %24
debug_value %0 : $Bool // id: %2
// function_ref foo_init
%3 = function_ref @foo_init : $@convention(thin) (@thin foo.Type) -> foo // user: %5
%4 = metatype $@thin foo.Type // user: %5
%5 = apply %3(%4) : $@convention(thin) (@thin foo.Type) -> foo // user: %6
store %5 to %1 : $*foo // id: %6
%7 = struct_extract %0 : $Bool, #Bool.value // user: %8
cond_br %7, bb1, bb2 // id: %8
bb1: // Preds: bb0
%9 = integer_literal $Builtin.Int64, 11 // user: %10
%10 = struct $Int (%9 : $Builtin.Int64) // user: %13
%11 = struct_element_addr %1 : $*foo, #foo.a // users: %12, %13
%12 = load %11 : $*Int // user: %14
store %10 to %11 : $*Int // id: %13
br bb3(%12 : $Int) // id: %14
bb2: // Preds: bb0
%15 = integer_literal $Builtin.Int64, 12 // user: %16
%16 = struct $Int (%15 : $Builtin.Int64)
%17 = struct_element_addr %1 : $*foo, #foo.a // user: %18
%18 = load %17 : $*Int // user: %19
br bb3(%18 : $Int) // id: %19
// %20 // user: %22
bb3(%20 : $Int): // Preds: bb1 bb2
// function_ref user_int
%21 = function_ref @user_int : $@convention(thin) (Int) -> () // user: %22
%22 = apply %21(%20) : $@convention(thin) (Int) -> ()
%23 = tuple () // user: %25
dealloc_stack %1 : $*foo // id: %24
return %23 : $() // id: %25
}
// Make sure that we properly perform global blotting of values and do not crash
// on this code. This will only fail reliably in ASAN builds of swift in such a
// case.
sil @test_global_blotting : $@convention(thin) () -> () {
bbBegin:
%0a = enum $Optional<Klass>, #Optional.none!enumelt
cond_br undef, bb1, bb2
bb1:
br bb8(%0a : $Optional<Klass>)
bb2:
%0 = enum $Optional<Klass>, #Optional.none!enumelt
cond_br undef, bb7, bb3
bb3:
cond_br undef, bb4, bb5
bb4:
br bb6
bb5:
br bb6
bb6:
br bb9
bb7:
br bb8(%0 : $Optional<Klass>)
bb8(%result : $Optional<Klass>):
release_value %result : $Optional<Klass>
br bb9
bb9:
%9999 = tuple()
return %9999 : $()
}