1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
// RUN: %target-sil-opt -enable-objc-interop -enable-sil-verify-all -function-signature-opts -sil-fso-disable-dead-argument -sil-fso-disable-owned-to-guaranteed -enable-expand-all -sil-fso-optimize-if-not-called %s | %FileCheck %s
// *NOTE* We turn off all other fso optimizations including dead arg so we can
// make sure that we are not exploding those.
sil_stage canonical
import Builtin
//////////////////
// Declarations //
//////////////////
struct BigTrivial {
var x1: Builtin.Int32
var x2: Builtin.Int32
var x3: Builtin.Int32
var x4: Builtin.Int32
var x5: Builtin.Int32
var x6: Builtin.Int32
}
class Klass {}
struct LargeNonTrivialStructOneNonTrivialField {
var k1: Klass
var k2: Klass
var x1: Builtin.Int32
var x2: Builtin.Int32
var x3: Builtin.Int32
var x4: Builtin.Int32
}
sil @int_user : $@convention(thin) (Builtin.Int32) -> ()
sil @consuming_user : $@convention(thin) (@owned Klass) -> ()
sil @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
///////////
// Tests //
///////////
// We should never optimize this. If we did this would become a thunk, so we
// know that just be checking NFC we have proven no optimization has occurred.
//
// CHECK-LABEL: sil @never_explode_trivial : $@convention(thin) (BigTrivial) -> () {
// CHECK: } // end sil function 'never_explode_trivial'
sil @never_explode_trivial : $@convention(thin) (BigTrivial) -> () {
bb0(%0 : $BigTrivial):
%1 = struct_extract %0 : $BigTrivial, #BigTrivial.x1
%intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> ()
apply %intfunc(%1) : $@convention(thin) (Builtin.Int32) -> ()
%9999 = tuple()
return %9999 : $()
}
// If a value is never used, do not touch it. We leave it for dead argument
// elimination. We have deliberately turned this off to test that behavior.
//
// CHECK-LABEL: sil @big_arg_with_no_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
// CHECK-NOT: apply
// CHECK: } // end sil function 'big_arg_with_no_uses'
sil @big_arg_with_no_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
%9999 = tuple()
return %9999 : $()
}
// We are using a single non-trivial field of the struct. We should explode this
// so we eliminate the second non-trivial leaf.
//
// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField):
// CHECK: [[FUNC:%.*]] = function_ref @$s31big_arg_with_one_nontrivial_useTf4x_n
// CHECK: [[FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
// CHECK: apply [[FUNC]]([[FIELD]])
// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use'
sil @big_arg_with_one_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
%2 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %2(%1) : $@convention(thin) (@guaranteed Klass) -> ()
%9999 = tuple()
return %9999 : $()
}
// We are using a single non-trivial field and a single trivial field. We are
// willing to blow this up.
//
// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_one_trivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField):
// CHECK: [[FUNC:%.*]] = function_ref @$s032big_arg_with_one_nontrivial_use_d9_trivial_F0Tf4x_n : $@convention(thin) (@guaranteed Klass, Builtin.Int32) -> ()
// CHECK: [[TRIVIAL_FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1
// CHECK: [[NON_TRIVIAL_FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
// CHECK: apply [[FUNC]]([[NON_TRIVIAL_FIELD]], [[TRIVIAL_FIELD]])
// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use_one_trivial_use'
sil @big_arg_with_one_nontrivial_use_one_trivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
%2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1
%3 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %3(%1) : $@convention(thin) (@guaranteed Klass) -> ()
%intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> ()
apply %intfunc(%2) : $@convention(thin) (Builtin.Int32) -> ()
%9999 = tuple()
return %9999 : $()
}
// We can still explode this, since our limit is 3 values.
//
// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_two_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField):
// CHECK: [[FUNC:%.*]] = function_ref @$s48big_arg_with_one_nontrivial_use_two_trivial_usesTf4x_n : $@convention(thin)
// CHECK: [[TRIVIAL_FIELD1:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x2
// CHECK: [[TRIVIAL_FIELD2:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1
// CHECK: [[NON_TRIVIAL_FIELD:%.*]] = struct_extract [[ARG]] : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
// CHECK: apply [[FUNC]]([[NON_TRIVIAL_FIELD]], [[TRIVIAL_FIELD2]], [[TRIVIAL_FIELD1]])
sil @big_arg_with_one_nontrivial_use_two_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
%2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1
%3 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x2
%4 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %4(%1) : $@convention(thin) (@guaranteed Klass) -> ()
%intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> ()
apply %intfunc(%2) : $@convention(thin) (Builtin.Int32) -> ()
apply %intfunc(%3) : $@convention(thin) (Builtin.Int32) -> ()
%9999 = tuple()
return %9999 : $()
}
// We do not blow up the struct here since we have 4 uses, not 3.
//
// CHECK-LABEL: sil @big_arg_with_one_nontrivial_use_three_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
sil @big_arg_with_one_nontrivial_use_three_trivial_uses : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
%2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x1
%3 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x2
%3a = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.x3
%4 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %4(%1) : $@convention(thin) (@guaranteed Klass) -> ()
%intfunc = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> ()
apply %intfunc(%2) : $@convention(thin) (Builtin.Int32) -> ()
apply %intfunc(%3) : $@convention(thin) (Builtin.Int32) -> ()
apply %intfunc(%3a) : $@convention(thin) (Builtin.Int32) -> ()
%9999 = tuple()
return %9999 : $()
}
// In this case, we shouldn't blow up the struct since we have not reduced the
// number of non-trivial leaf nodes used.
//
// CHECK-LABEL: sil @big_arg_with_two_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
sil @big_arg_with_two_nontrivial_use : $@convention(thin) (@guaranteed LargeNonTrivialStructOneNonTrivialField) -> () {
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
%2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k2
%3 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
apply %3(%1) : $@convention(thin) (@guaranteed Klass) -> ()
apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
%9999 = tuple()
return %9999 : $()
}
// If we have one non-trivial value that is live and only live because of a
// destroy, we can delete the argument after performing o2g.
//
// We are using a single non-trivial field of the struct. We should explode this
// so we eliminate the second non-trivial leaf.
//
// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_o2g_other_dead : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () {
// CHECK-NOT: release_value
// CHECK: apply
// CHECK-NOT: release_value
// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use_o2g_other_dead'
sil @big_arg_with_one_nontrivial_use_o2g_other_dead : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () {
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
release_value %1 : $Klass
%9999 = tuple()
return %9999 : $()
}
// If we have two non-trivial values that are live and one is always dead and
// the other is kept alive due to a release, we can get rid of both since FSO
// reruns with o2g. Test here that we explode it appropriately even though we
// aren't reducing the number of non-trivial uses. The
// funcsig_explode_heuristic_inline.sil test makes sure we in combination
// produce the appropriate SIL.
//
// We check that we can inline this correctly in the inline test.
//
// CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @big_arg_with_one_nontrivial_use_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () {
// CHECK: bb0([[ARG:%.*]] : $LargeNonTrivialStructOneNonTrivialField):
// CHECK: [[FUNC:%.*]] = function_ref @$s35big_arg_with_one_nontrivial_use_o2gTf4x_n : $@convention(thin) (@owned Klass, @owned Klass) -> ()
// CHECK: apply [[FUNC]](
// CHECK: } // end sil function 'big_arg_with_one_nontrivial_use_o2g'
sil @big_arg_with_one_nontrivial_use_o2g : $@convention(thin) (@owned LargeNonTrivialStructOneNonTrivialField) -> () {
bb0(%0 : $LargeNonTrivialStructOneNonTrivialField):
%1 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k1
%2 = struct_extract %0 : $LargeNonTrivialStructOneNonTrivialField, #LargeNonTrivialStructOneNonTrivialField.k2
%3 = function_ref @consuming_user : $@convention(thin) (@owned Klass) -> ()
apply %3(%2) : $@convention(thin) (@owned Klass) -> ()
release_value %1 : $Klass
%9999 = tuple()
return %9999 : $()
}
|