https://gcc.gnu.org/g:ab89dc49c189796353e640a11d83b74fc40ffdb6
commit r14-12921-gab89dc49c189796353e640a11d83b74fc40ffdb6 Author: Uros Bizjak <[email protected]> Date: Tue Sep 29 11:24:01 2026 +0200 i386: Do not over-align va_arg access to an over-aligned stack argument [PR127650] ix86_function_arg_boundary computes the stack slot alignment from TYPE_MAIN_VARIANT, so for a typedef with attribute aligned the slot is only guaranteed to be 8-byte aligned and ix86_gimplify_va_arg emits no rounding of the overflow area pointer. However, the final dereference is built with build_aligned_type (type, TYPE_ALIGN (type)), claiming the alignment of the typedef. With optimization this results in aligned vector moves from a misaligned address. Clamp the alignment claimed for the access to the boundary the argument slot is actually placed at. This also covers types whose alignment exceeds MAX_SUPPORTED_STACK_ALIGNMENT. Assisted-by: Claude Sonnet 5.5 gcc/ChangeLog: PR target/127650 * config/i386/i386.cc (ix86_gimplify_va_arg): Limit type_align to arg_boundary. gcc/testsuite/ChangeLog: PR target/127650 * gcc.target/i386/pr127650.c: New test. (cherry picked from commit ef7d69b3a91c3b12a647ffca92e2553229f324dd) Diff: --- gcc/config/i386/i386.cc | 6 ++++++ gcc/testsuite/gcc.target/i386/pr127650.c | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc index a5684d0054a7..72e3b428fb7c 100644 --- a/gcc/config/i386/i386.cc +++ b/gcc/config/i386/i386.cc @@ -5103,6 +5103,12 @@ ix86_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p, if ((unsigned int) arg_boundary > MAX_SUPPORTED_STACK_ALIGNMENT) arg_boundary = MAX_SUPPORTED_STACK_ALIGNMENT; + /* ARG_BOUNDARY is computed from TYPE_MAIN_VARIANT, so it can be smaller + than the alignment of a variant type that carries a user-specified + alignment (e.g. a typedef with attribute aligned). Do not let the + access claim more alignment than the argument slot is placed at. */ + type_align = MIN (type_align, arg_boundary); + /* Care for on-stack alignment if needed. */ if (arg_boundary <= 64 || size == 0) t = ovf; diff --git a/gcc/testsuite/gcc.target/i386/pr127650.c b/gcc/testsuite/gcc.target/i386/pr127650.c new file mode 100644 index 000000000000..ce55b6b17c30 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr127650.c @@ -0,0 +1,35 @@ +/* PR target/127650 */ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#include <stdarg.h> + +struct S { long a, b, c, d, e, f, g, h; }; +typedef struct S S32 __attribute__((aligned (32))); + +struct S gg; + +/* The stack slot of an S32 argument is only 8-byte aligned, because the + ABI boundary is computed from the main variant. The va_arg access + must not assume the typedef's 32-byte alignment, or the copy may use + aligned vector moves on a misaligned address. */ + +__attribute__((noipa)) void +f (int n, int b, int c, int d, int e, int g, ...) +{ + va_list ap; + va_start (ap, g); + long l = va_arg (ap, long); + gg = va_arg (ap, S32); + va_end (ap); + if (l != 99 || gg.a != 11 || gg.h != 18) + __builtin_abort (); +} + +int +main (void) +{ + S32 s = { 11, 12, 13, 14, 15, 16, 17, 18 }; + f (0, 1, 2, 3, 4, 5, 99L, s); + return 0; +}
