// RUN: %target-sil-opt -sil-print-types -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 : $() }