Hi Segher, Thanks for the review! This is what I committed.
2021-09-23 Bill Schmidt <wschm...@linux.ibm.com> gcc/ PR target/102024 * config/rs6000/rs6000-call.c (rs6000_aggregate_candidate): Detect zero-width bit fields and return indicator. (rs6000_discover_homogeneous_aggregate): Diagnose when the presence of a zero-width bit field changes parameter passing in GCC 12. gcc/testsuite/ PR target/102024 * g++.target/powerpc/pr102024.C: New. --- gcc/config/rs6000/rs6000-call.c | 64 +++++++++++++++++++-- gcc/testsuite/g++.target/powerpc/pr102024.C | 23 ++++++++ 2 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/g++.target/powerpc/pr102024.C diff --git a/gcc/config/rs6000/rs6000-call.c b/gcc/config/rs6000/rs6000-call.c index 7d485480225..2eceb2c71c0 100644 --- a/gcc/config/rs6000/rs6000-call.c +++ b/gcc/config/rs6000/rs6000-call.c @@ -6223,11 +6223,19 @@ const struct altivec_builtin_types altivec_overloaded_builtins[] = { or vector type. If a non-floating point or vector type is found, or if a floating point or vector type that doesn't match a non-VOIDmode *MODEP is found, then return -1, otherwise return the count in the - sub-tree. */ + sub-tree. + + There have been some ABI snafus along the way with C++. Modify + EMPTY_BASE_SEEN to a nonzero value iff a C++ empty base class makes + an appearance; separate flag bits indicate whether or not such a + field is marked "no unique address". Modify ZERO_WIDTH_BF_SEEN + to 1 iff a C++ zero-length bitfield makes an appearance, but + in this case otherwise treat this as still being a homogeneous + aggregate. */ static int rs6000_aggregate_candidate (const_tree type, machine_mode *modep, - int *empty_base_seen) + int *empty_base_seen, int *zero_width_bf_seen) { machine_mode mode; HOST_WIDE_INT size; @@ -6298,7 +6306,8 @@ rs6000_aggregate_candidate (const_tree type, machine_mode *modep, return -1; count = rs6000_aggregate_candidate (TREE_TYPE (type), modep, - empty_base_seen); + empty_base_seen, + zero_width_bf_seen); if (count == -1 || !index || !TYPE_MAX_VALUE (index) @@ -6336,6 +6345,26 @@ rs6000_aggregate_candidate (const_tree type, machine_mode *modep, if (TREE_CODE (field) != FIELD_DECL) continue; + if (DECL_FIELD_CXX_ZERO_WIDTH_BIT_FIELD (field)) + { + /* GCC 11 and earlier generated incorrect code in a rare + corner case for C++. When a RECORD_TYPE looks like a + homogeneous aggregate, except that it also contains + one or more zero-width bit fields, these earlier + compilers would incorrectly pass the fields in FPRs + or VSRs. This occurred because the front end wrongly + removed these bitfields from the RECORD_TYPE. In + GCC 12 and later, the front end flaw was corrected. + We want to diagnose this case. To do this, we pretend + that we don't see the zero-width bit fields (hence + the continue statement here), but pass back a flag + indicating what happened. The caller then diagnoses + the issue and rejects the RECORD_TYPE as a homogeneous + aggregate. */ + *zero_width_bf_seen = 1; + continue; + } + if (DECL_FIELD_ABI_IGNORED (field)) { if (lookup_attribute ("no_unique_address", @@ -6347,7 +6376,8 @@ rs6000_aggregate_candidate (const_tree type, machine_mode *modep, } sub_count = rs6000_aggregate_candidate (TREE_TYPE (field), modep, - empty_base_seen); + empty_base_seen, + zero_width_bf_seen); if (sub_count < 0) return -1; count += sub_count; @@ -6381,7 +6411,8 @@ rs6000_aggregate_candidate (const_tree type, machine_mode *modep, continue; sub_count = rs6000_aggregate_candidate (TREE_TYPE (field), modep, - empty_base_seen); + empty_base_seen, + zero_width_bf_seen); if (sub_count < 0) return -1; count = count > sub_count ? count : sub_count; @@ -6423,8 +6454,10 @@ rs6000_discover_homogeneous_aggregate (machine_mode mode, const_tree type, { machine_mode field_mode = VOIDmode; int empty_base_seen = 0; + int zero_width_bf_seen = 0; int field_count = rs6000_aggregate_candidate (type, &field_mode, - &empty_base_seen); + &empty_base_seen, + &zero_width_bf_seen); if (field_count > 0) { @@ -6460,6 +6493,25 @@ rs6000_discover_homogeneous_aggregate (machine_mode mode, const_tree type, last_reported_type_uid = uid; } } + if (zero_width_bf_seen && warn_psabi) + { + static unsigned last_reported_type_uid; + unsigned uid = TYPE_UID (TYPE_MAIN_VARIANT (type)); + if (uid != last_reported_type_uid) + { + inform (input_location, + "ELFv2 parameter passing for an argument " + "containing zero-width bit fields but that is " + "otherwise a homogeneous aggregate was " + "corrected in GCC 12"); + last_reported_type_uid = uid; + } + if (elt_mode) + *elt_mode = mode; + if (n_elts) + *n_elts = 1; + return false; + } return true; } } diff --git a/gcc/testsuite/g++.target/powerpc/pr102024.C b/gcc/testsuite/g++.target/powerpc/pr102024.C new file mode 100644 index 00000000000..769585052b5 --- /dev/null +++ b/gcc/testsuite/g++.target/powerpc/pr102024.C @@ -0,0 +1,23 @@ +// PR target/102024 +// { dg-do compile { target powerpc_elfv2 } } +// { dg-options "-O2" } + +// Test that a zero-width bit field in an otherwise homogeneous aggregate +// generates a psabi warning and passes arguments in GPRs. + +// { dg-final { scan-assembler-times {\mstd\M} 4 } } + +struct a_thing +{ + double x; + double y; + double z; + int : 0; + double w; +}; + +double +foo (a_thing a) // { dg-message "ELFv2 parameter passing for an argument containing zero-width bit fields but that is otherwise a homogeneous aggregate was corrected in GCC 12" } +{ + return a.x * a.y + a.z - a.w; +} -- 2.25.1