Files
swift-mirror/test/SILOptimizer/funcsig_explode_heuristic.sil
Nate Chandler 9567bd4341 [SILOptimizer] Alter FSO arg explosion heuristic.
The new rule is that an argument will be exploded if one of the
following sets of conditions hold:

(1) (a) Specializing the function will result in a thunk.  That is, the
        thunk that is generated cannot be inlined everywhere.
    (b) The argument has dead non-trivial leaves.
    (c) The argument has fewer than three live leaves.

(2) (a) Specializing the function will not result in a thunk.  That is,
        the thunk that is generated will be inlined everywhere and
        eliminated as dead code.
    (b) The argument has dead potentially trivial leaves.
    (c) The argument has fewer than six live leaves.

This change is based heavily on @gottesm's
https://github.com/apple/swift/pull/16756 .

rdar://problem/39957093
2019-09-24 15:59:28 -07:00

208 lines
12 KiB
Plaintext

// 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 occured.
//
// 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 delibrately 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 appropriatel 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 : $()
}