diff options
author | Richard Sandiford <richard.sandiford@arm.com> | 2023-05-03 17:43:48 +0100 |
---|---|---|
committer | Richard Sandiford <richard.sandiford@arm.com> | 2023-05-03 17:43:48 +0100 |
commit | 1c26adba4b95f9a79f3aa57637d34cff7982d832 (patch) | |
tree | e809399ce03481b70537193e1e1ef8284c7f13f1 /gcc/config/aarch64/aarch64.cc | |
parent | 3a4a39b391e63e5be04cb06ee4cd5400bef63dfc (diff) | |
download | gcc-1c26adba4b95f9a79f3aa57637d34cff7982d832.zip gcc-1c26adba4b95f9a79f3aa57637d34cff7982d832.tar.gz gcc-1c26adba4b95f9a79f3aa57637d34cff7982d832.tar.bz2 |
aarch64: Fix ABI handling of aligned enums [PR109661]
aarch64_function_arg_alignment has traditionally taken the alignment
of a scalar type T from TYPE_ALIGN (TYPE_MAIN_VARIANT (T)). This is
supposed to discard any user alignment and give the alignment of the
underlying fundamental type.
PR109661 shows that this did the wrong thing for enums with
a defined underlying type, because:
(1) The enum itself could be aligned, using attributes.
(2) The enum would pick up any user alignment on the underlying type.
We get the right behaviour if we look at the TYPE_MAIN_VARIANT
of the underlying type instead.
As always, this affects register and stack arguments differently,
because:
(a) The code that handles register arguments only considers the
alignment of types that occupy two registers, whereas the
stack alignment is applied regardless of size.
(b) The code that handles register arguments tests the alignment
for equality with 16 bytes, so that (unexpected) greater alignments
are ignored. The code that handles stack arguments instead caps the
alignment to 16 bytes.
There is now (since GCC 13) an assert to trap the difference between
(a) and (b), which is how the new incompatiblity showed up.
Clang alredy handled the testcases correctly, so this patch aligns
the GCC behaviour with the Clang behaviour.
I'm planning to remove the asserts on the branches, since we don't
want to change the ABI there.
gcc/
PR target/109661
* config/aarch64/aarch64.cc (aarch64_function_arg_alignment): Add
a new ABI break parameter for GCC 14. Set it to the alignment
of enums that have an underlying type. Take the true alignment
of such enums from the TYPE_ALIGN of the underlying type's
TYPE_MAIN_VARIANT.
(aarch64_function_arg_boundary): Update accordingly.
(aarch64_layout_arg, aarch64_gimplify_va_arg_expr): Likewise.
Warn about ABI differences.
gcc/testsuite/
* g++.target/aarch64/pr109661-1.C: New test.
* g++.target/aarch64/pr109661-2.C: Likewise.
* g++.target/aarch64/pr109661-3.C: Likewise.
* g++.target/aarch64/pr109661-4.C: Likewise.
* gcc.target/aarch64/pr109661-1.c: Likewise.
Diffstat (limited to 'gcc/config/aarch64/aarch64.cc')
-rw-r--r-- | gcc/config/aarch64/aarch64.cc | 43 |
1 files changed, 38 insertions, 5 deletions
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 70916ad..546cb12 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -7467,17 +7467,21 @@ aarch64_vfp_is_call_candidate (cumulative_args_t pcum_v, machine_mode mode, 4.1). ABI_BREAK_GCC_9 is set to the old alignment if the alignment was incorrectly calculated in versions of GCC prior to GCC 9. ABI_BREAK_GCC_13 is set to the old alignment if it was incorrectly - calculated in versions between GCC 9 and GCC 13. + calculated in versions between GCC 9 and GCC 13. If the alignment + might have changed between GCC 13 and GCC 14, ABI_BREAK_GCC_14 + is the old GCC 13 alignment, otherwise it is zero. This is a helper function for local use only. */ static unsigned int aarch64_function_arg_alignment (machine_mode mode, const_tree type, unsigned int *abi_break_gcc_9, - unsigned int *abi_break_gcc_13) + unsigned int *abi_break_gcc_13, + unsigned int *abi_break_gcc_14) { *abi_break_gcc_9 = 0; *abi_break_gcc_13 = 0; + *abi_break_gcc_14 = 0; if (!type) return GET_MODE_ALIGNMENT (mode); @@ -7498,6 +7502,11 @@ aarch64_function_arg_alignment (machine_mode mode, const_tree type, gcc_assert (known_eq (POINTER_SIZE, GET_MODE_BITSIZE (mode))); return POINTER_SIZE; } + if (TREE_CODE (type) == ENUMERAL_TYPE && TREE_TYPE (type)) + { + *abi_break_gcc_14 = TYPE_ALIGN (type); + type = TYPE_MAIN_VARIANT (TREE_TYPE (type)); + } gcc_assert (!TYPE_USER_ALIGN (type)); return TYPE_ALIGN (type); } @@ -7576,6 +7585,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) HOST_WIDE_INT size; unsigned int abi_break_gcc_9; unsigned int abi_break_gcc_13; + unsigned int abi_break_gcc_14; /* We need to do this once per argument. */ if (pcum->aapcs_arg_processed) @@ -7715,7 +7725,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) unsigned int alignment = aarch64_function_arg_alignment (mode, type, &abi_break_gcc_9, - &abi_break_gcc_13); + &abi_break_gcc_13, &abi_break_gcc_14); gcc_assert ((allocate_nvrn || alignment <= 16 * BITS_PER_UNIT) && (!alignment || abi_break_gcc_9 < alignment) @@ -7799,6 +7809,13 @@ aarch64_layout_arg (cumulative_args_t pcum_v, const function_arg_info &arg) inform (input_location, "parameter passing for argument of type " "%qT changed in GCC 13.1", type); + if (warn_pcs_change + && abi_break_gcc_14 + && ((abi_break_gcc_14 == 16 * BITS_PER_UNIT) + != (alignment == 16 * BITS_PER_UNIT))) + inform (input_location, "parameter passing for argument of type " + "%qT changed in GCC 14.1", type); + /* The == 16 * BITS_PER_UNIT instead of >= 16 * BITS_PER_UNIT comparison is there because for > 16 * BITS_PER_UNIT alignment nregs should be > 2 and therefore it should be @@ -7870,6 +7887,13 @@ on_stack: inform (input_location, "parameter passing for argument of type " "%qT changed in GCC 13.1", type); + if (warn_pcs_change + && abi_break_gcc_14 + && ((abi_break_gcc_14 >= 16 * BITS_PER_UNIT) + != (alignment >= 16 * BITS_PER_UNIT))) + inform (input_location, "parameter passing for argument of type " + "%qT changed in GCC 14.1", type); + if (alignment == 16 * BITS_PER_UNIT) { int new_size = ROUND_UP (pcum->aapcs_stack_size, 16 / UNITS_PER_WORD); @@ -7994,9 +8018,11 @@ aarch64_function_arg_boundary (machine_mode mode, const_tree type) { unsigned int abi_break_gcc_9; unsigned int abi_break_gcc_13; + unsigned int abi_break_gcc_14; unsigned int alignment = aarch64_function_arg_alignment (mode, type, &abi_break_gcc_9, - &abi_break_gcc_13); + &abi_break_gcc_13, + &abi_break_gcc_14); /* We rely on aarch64_layout_arg and aarch64_gimplify_va_arg_expr to emit warnings about ABI incompatibility. */ alignment = MIN (MAX (alignment, PARM_BOUNDARY), STACK_BOUNDARY); @@ -19765,9 +19791,10 @@ aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, unsigned int abi_break_gcc_9; unsigned int abi_break_gcc_13; + unsigned int abi_break_gcc_14; align = aarch64_function_arg_alignment (mode, type, &abi_break_gcc_9, - &abi_break_gcc_13) + &abi_break_gcc_13, &abi_break_gcc_14) / BITS_PER_UNIT; dw_align = false; @@ -19815,6 +19842,12 @@ aarch64_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, inform (input_location, "parameter passing for argument of type " "%qT changed in GCC 13.1", type); + if (warn_psabi + && abi_break_gcc_14 + && (abi_break_gcc_14 > 8 * BITS_PER_UNIT) != (align > 8)) + inform (input_location, "parameter passing for argument of type " + "%qT changed in GCC 14.1", type); + if (align > 8) { if (abi_break_gcc_9 && warn_psabi) |