mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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
1635 lines
47 KiB
Plaintext
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 : $()
|
|
}
|