> On 7 Jul 2026, at 17:12, Andrea Pinski <[email protected]> wrote:
> 
> On Wed, Jun 17, 2026 at 7:47 AM <[email protected]> wrote:
>> 
>> From: Kyrylo Tkachov <[email protected]>
>> 
>> aarch64_function_arg_alignment returns the ABI alignment of an argument.
>> For scalars and vectors this is the natural alignment of the type,
>> ignoring any user-specified alignment.  The code obtains the natural
>> alignment from the TYPE_MAIN_VARIANT, relying on the main variant having
>> no user alignment.  PR108910 showed that this does not hold for pointers,
>> and that case is handled explicitly.
>> 
>> PR124146 is another counterexample.  An attribute that affects type
>> identity, such as may_alias, makes a type its own TYPE_MAIN_VARIANT, so
>> the main variant retains the user alignment requested by the aligned
>> attribute.  TYPE_MAIN_VARIANT therefore does not strip the alignment and
>> the gcc_assert (!TYPE_USER_ALIGN (type)) fires.
>> 
>> In the testcase the may_alias+aligned type reaches the argument-passing
>> code because foo is inlined into bar and forwprop propagates the value
>> of that type directly into the recursive call to bar.
>> 
>> Fix it by ignoring user alignment explicitly: when the type still has
>> user alignment, use the natural alignment of its mode, exactly as the
>> !type path at the top of the function already does.  Behaviour is
>> unchanged for the existing (non-user-aligned) cases, since the natural
>> alignment of a scalar or vector equals its mode alignment.  This also
>> gives the natural alignment when a may_alias typedef lowers the
>> alignment of a 16-byte type, matching the AAPCS64 (and Clang).
>> 
>> Bootstrapped and tested on aarch64-none-linux-gnu.
>> 
>> Ok for trunk?
> 
> I am not sure this is correct. I think the front-end is where the
> problem is creating the type in the first place.
> Also can you add/try a C++ testcase. Because the 2 front-ends are
> different in the area of may_alias.

Thanks, I’ve pushed a C++ test case as well:
https://gcc.gnu.org/pipermail/gcc-patches/2026-July/723720.html
It ICEs in the same way without my commit and now passes
Could you elaborate a bit on what the frontend may be doing wrong here?
With this patch GCC agrees with Clang 23 on these test cases.
Kyrill

> 
> Thanks,
> Andrea
> 
>> Thanks,
>> Kyrill
>> 
>> Signed-off-by: Kyrylo Tkachov <[email protected]>
>> 
>> gcc/
>> 
>>        PR target/124146
>>        * config/aarch64/aarch64.cc (aarch64_function_arg_alignment):
>>        Ignore user alignment left on a type's main variant; use the
>>        mode's natural alignment instead.
>> 
>> gcc/testsuite/
>> 
>>        PR target/124146
>>        * gcc.target/aarch64/pr124146.c: New test.
>>        * gcc.target/aarch64/pr124146-2.c: New test.
>>        * gcc.target/aarch64/pr124146-3.c: New test.
>> ---
>> gcc/config/aarch64/aarch64.cc                 | 10 ++++-
>> gcc/testsuite/gcc.target/aarch64/pr124146-2.c | 38 ++++++++++++++++
>> gcc/testsuite/gcc.target/aarch64/pr124146-3.c | 45 +++++++++++++++++++
>> gcc/testsuite/gcc.target/aarch64/pr124146.c   | 29 ++++++++++++
>> 4 files changed, 121 insertions(+), 1 deletion(-)
>> create mode 100644 gcc/testsuite/gcc.target/aarch64/pr124146-2.c
>> create mode 100644 gcc/testsuite/gcc.target/aarch64/pr124146-3.c
>> create mode 100644 gcc/testsuite/gcc.target/aarch64/pr124146.c
>> 
>> diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
>> index 42e56512c61..924dbcea0bd 100644
>> --- a/gcc/config/aarch64/aarch64.cc
>> +++ b/gcc/config/aarch64/aarch64.cc
>> @@ -7493,7 +7493,15 @@ aarch64_function_arg_alignment (machine_mode mode, 
>> const_tree type,
>>          *abi_break_gcc_14 = TYPE_ALIGN (type);
>>          type = TYPE_MAIN_VARIANT (TREE_TYPE (type));
>>        }
>> -      gcc_assert (!TYPE_USER_ALIGN (type));
>> +      /* Ignore any user-specified alignment: the AAPCS64 uses the
>> +        type's natural alignment for scalars and vectors.  We normally
>> +        strip user alignment by taking the TYPE_MAIN_VARIANT above, but
>> +        an attribute that affects type identity (such as may_alias) can
>> +        make a type its own main variant while still recording the user
>> +        alignment, so handle that case explicitly here (PR124146).  For
>> +        a scalar or vector the natural alignment is that of its mode.  */
>> +      if (TYPE_USER_ALIGN (type))
>> +       return GET_MODE_ALIGNMENT (mode);
>>       return TYPE_ALIGN (type);
>>     }
>> 
>> diff --git a/gcc/testsuite/gcc.target/aarch64/pr124146-2.c 
>> b/gcc/testsuite/gcc.target/aarch64/pr124146-2.c
>> new file mode 100644
>> index 00000000000..3dabdc59e02
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/aarch64/pr124146-2.c
>> @@ -0,0 +1,38 @@
>> +/* PR target/124146 */
>> +/* { dg-do compile } */
>> +/* { dg-options "-O2" } */
>> +
>> +/* A type that combines an alignment attribute with an attribute that 
>> affects
>> +   type identity (may_alias) is its own TYPE_MAIN_VARIANT and keeps
>> +   TYPE_USER_ALIGN set.  aarch64_function_arg_alignment used to assert that
>> +   the main variant had no user alignment, which ICEd for such types.  
>> Verify
>> +   that a wide range of them can be passed (alone and after another 
>> argument,
>> +   over-aligned and under-aligned) and returned without an ICE.  */
>> +
>> +typedef int v4si __attribute__((__vector_size__ (16)));
>> +
>> +#define TEST(BASE, SUF)                                                     
>>    \
>> +  typedef __attribute__((__aligned__, __may_alias__)) BASE big_##SUF;  \
>> +  typedef __attribute__((__aligned__ (8), __may_alias__)) BASE al8_##SUF; \
>> +  void gbig_##SUF (big_##SUF);                                         \
>> +  void hbig_##SUF (int, big_##SUF);                                    \
>> +  void gal8_##SUF (al8_##SUF);                                         \
>> +  void hal8_##SUF (int, al8_##SUF);                                    \
>> +  void call_##SUF (big_##SUF a, al8_##SUF b)                           \
>> +  {                                                                    \
>> +    gbig_##SUF (a);                                                    \
>> +    hbig_##SUF (1, a);                                                 \
>> +    gal8_##SUF (b);                                                    \
>> +    hal8_##SUF (1, b);                                                 \
>> +  }                                                                    \
>> +  big_##SUF ret_##SUF (big_##SUF a) { return a; }
>> +
>> +TEST (unsigned char, uc)
>> +TEST (unsigned short, us)
>> +TEST (unsigned int, ui)
>> +TEST (unsigned long, ul)
>> +TEST (long long, ll)
>> +TEST (__int128, i128)
>> +TEST (float, f)
>> +TEST (double, d)
>> +TEST (v4si, v)
>> diff --git a/gcc/testsuite/gcc.target/aarch64/pr124146-3.c 
>> b/gcc/testsuite/gcc.target/aarch64/pr124146-3.c
>> new file mode 100644
>> index 00000000000..f9ca9319d77
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/aarch64/pr124146-3.c
>> @@ -0,0 +1,45 @@
>> +/* PR target/124146 */
>> +/* { dg-do compile } */
>> +/* { dg-options "-O2" } */
>> +/* { dg-final { check-function-bodies "**" "" } } */
>> +
>> +/* The AAPCS64 passes scalars and vectors using their natural alignment,
>> +   ignoring user alignment.  A 16-byte __int128 must therefore use its
>> +   natural 16-byte alignment (so the C.8 rule rounds NGRN up to an even
>> +   register), even when a may_alias typedef records a different user
>> +   alignment.  These checks would have ICEd before the PR124146 fix.  */
>> +
>> +typedef __attribute__((__aligned__, __may_alias__)) __int128 ma_i128;
>> +typedef __attribute__((__aligned__ (8), __may_alias__)) __int128 ma8_i128;
>> +typedef __attribute__((__aligned__, __may_alias__)) unsigned long ma_ul;
>> +
>> +void consume_i128 (int, __int128);
>> +void consume_ul (int, unsigned long);
>> +
>> +/* Over-aligned __int128: natural alignment 16 -> argument in x2/x3.
>> +** pass_i128:
>> +**     mov     x2, x0
>> +**     mov     x3, x1
>> +**     mov     w0, 5
>> +**     b       consume_i128
>> +*/
>> +void pass_i128 (ma_i128 y) { consume_i128 (5, y); }
>> +
>> +/* Under-aligned (aligned(8)) __int128: natural alignment is still 16, so 
>> the
>> +   argument must still land in x2/x3, not x1/x2.
>> +** pass_i128_underaligned:
>> +**     mov     x2, x0
>> +**     mov     x3, x1
>> +**     mov     w0, 5
>> +**     b       consume_i128
>> +*/
>> +void pass_i128_underaligned (ma8_i128 y) { consume_i128 (5, y); }
>> +
>> +/* Over-aligned unsigned long: a single 8-byte register, alignment is
>> +   irrelevant to placement -> argument in x1.
>> +** pass_ul:
>> +**     mov     x1, x0
>> +**     mov     w0, 5
>> +**     b       consume_ul
>> +*/
>> +void pass_ul (ma_ul y) { consume_ul (5, y); }
>> diff --git a/gcc/testsuite/gcc.target/aarch64/pr124146.c 
>> b/gcc/testsuite/gcc.target/aarch64/pr124146.c
>> new file mode 100644
>> index 00000000000..29142847ed4
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/aarch64/pr124146.c
>> @@ -0,0 +1,29 @@
>> +/* PR target/124146 */
>> +/* { dg-do compile } */
>> +/* { dg-options "-O1" } */
>> +
>> +/* The may_alias attribute makes the typedef T its own TYPE_MAIN_VARIANT
>> +   while still recording the user alignment from the aligned attribute.
>> +   Taking the main variant in aarch64_function_arg_alignment therefore did
>> +   not strip the user alignment, which used to trigger an assertion failure
>> +   (ICE) when foo was inlined into bar and the value of type T was passed
>> +   directly to bar.  */
>> +
>> +long a;
>> +void *b;
>> +char c;
>> +
>> +long
>> +foo (void *p)
>> +{
>> +  typedef __attribute__((__aligned__)) __attribute__((__may_alias__)) 
>> unsigned long T;
>> +  a = *(T *) b;
>> +  return a;
>> +}
>> +
>> +void
>> +bar (unsigned long x)
>> +{
>> +  long d = foo (&c);
>> +  bar (d);
>> +}
>> --
>> 2.50.1 (Apple Git-155)
>> 

Reply via email to