On Wed, 16 Sep 2026, Oleg Tolmatcev wrote: > Cover the places where an object needs more alignment than the frame can > give it: the home slot of a by-value parameter, both when nothing else sees > it and when its address escapes to a callee, the hidden copy the caller makes > when the MS ABI passes an argument by reference, and a copy that GCC merely > prefers to over-align. > > Where nothing else sees the object, it only has to be stored with a move that > matches its slot, so those tests scan for the move GCC selects. Where the > address escapes, the object must really be aligned, so those tests scan for > the mask that rounds the slot address up. None of them runs the code: a slot > that is short of the alignment the object needs still lands on a 32-byte > boundary about half the time. > > The dg-do run C++ test covers an over-aligned temporary used on both arms of > a branch.
OK. Richard. > gcc/testsuite/ChangeLog: > > PR target/54412 > * g++.target/i386/pr54412-highpart-extract-rt.C: New test. > * gcc.target/i386/pr54412-aligned-struct-byval-temp.c: New test. > * gcc.target/i386/pr54412-blkmode-temp-biggest-align.c: New test. > * gcc.target/i386/pr54412-escaping-param.c: New test. > * gcc.target/i386/pr54412-m256-byval-param.c: New test. > > Signed-off-by: Oleg Tolmatcev <[email protected]> > --- > .../i386/pr54412-highpart-extract-rt.C | 110 ++++++++++++++++++ > .../i386/pr54412-aligned-struct-byval-temp.c | 16 +++ > .../i386/pr54412-blkmode-temp-biggest-align.c | 16 +++ > .../gcc.target/i386/pr54412-escaping-param.c | 20 ++++ > .../i386/pr54412-m256-byval-param.c | 22 ++++ > 5 files changed, 184 insertions(+) > create mode 100644 > gcc/testsuite/g++.target/i386/pr54412-highpart-extract-rt.C > create mode 100644 > gcc/testsuite/gcc.target/i386/pr54412-aligned-struct-byval-temp.c > create mode 100644 > gcc/testsuite/gcc.target/i386/pr54412-blkmode-temp-biggest-align.c > create mode 100644 gcc/testsuite/gcc.target/i386/pr54412-escaping-param.c > create mode 100644 gcc/testsuite/gcc.target/i386/pr54412-m256-byval-param.c > > diff --git a/gcc/testsuite/g++.target/i386/pr54412-highpart-extract-rt.C > b/gcc/testsuite/g++.target/i386/pr54412-highpart-extract-rt.C > new file mode 100644 > index 0000000000..7c1d48c5d5 > --- /dev/null > +++ b/gcc/testsuite/g++.target/i386/pr54412-highpart-extract-rt.C > @@ -0,0 +1,110 @@ > +/* PR target/54412 */ > +/* Check that "assign_stack_temp_for_type" safely reuses an over-aligned > + temporary across control-flow paths. The stack slot used by Vec4q (z0) > + is reused for Vec4q (z1). Its dynamically aligned address is computed in > + the first branch and held in a pseudo-register that is undefined along the > + second path, so the address must be recomputed when the slot is reused. > */ > +/* { dg-do run { target { x86_64-*-mingw* && avx2 } } } */ > +/* { dg-options "-O0 -mavx2 -std=gnu++17 -fno-omit-frame-pointer" } */ > +/* { dg-require-effective-target avx2_runtime } */ > + > +#include "avx2-check.h" > + > +typedef long long v4di __attribute__ ((vector_size (32), aligned (32))); > + > +static inline v4di > +make_v4di (long long a0, long long a1, long long a2, long long a3) > +{ > + return (v4di) { a0, a1, a2, a3 }; > +} > + > +struct Vec256b > +{ > + v4di ymm; > + > + Vec256b () = default; > + explicit Vec256b (v4di x) : ymm (x) {} > + > + __attribute__ ((noinline, noclone)) > + operator v4di () const > + { > + return ymm; > + } > +}; > + > +struct Vec4q : public Vec256b > +{ > + Vec4q () = default; > + > + __attribute__ ((noinline, noclone)) > + explicit Vec4q (v4di x) > + { > + ymm = x; > + } > + > + __attribute__ ((noinline, noclone)) > + long long > + extract (int index) const > + { > + long long elems[4] __attribute__ ((aligned (32))); > + __builtin_memcpy (elems, &ymm, sizeof (elems)); > + return elems[index & 3]; > + } > +}; > + > +struct Vec8q > +{ > + Vec256b z0; > + Vec256b z1; > + > + Vec8q () = default; > + Vec8q (Vec256b a, Vec256b b) : z0 (a), z1 (b) {} > + > + __attribute__ ((noinline, noclone)) > + long long > + extract (int index) const > + { > + if ((unsigned) index < 4) > + return Vec4q (z0).extract (index); > + > + return Vec4q (z1).extract (index - 4); > + } > +}; > + > +__attribute__ ((noinline, noclone)) > +static Vec8q > +passthru (Vec8q x) > +{ > + return x; > +} > + > +__attribute__ ((noinline, noclone)) > +static long long > +call_extract (Vec8q x, int index) > +{ > + Vec8q y = passthru (x); > + > + if ((unsigned) index >= 4) > + { > + register void *p __asm__ ("rbx") = (void *) 2; > + asm volatile ("" : : "r" (p) : "memory"); > + } > + > + return y.extract (index); > +} > + > +static void > +avx2_test () > +{ > + Vec8q x (Vec256b (make_v4di (101, 102, 103, 104)), > + Vec256b (make_v4di (201, 202, 203, 204))); > + > + if (call_extract (x, 1) != 102) > + __builtin_abort (); > + > + if (call_extract (x, 5) != 202) > + __builtin_abort (); > + > + if (call_extract (x, 7) != 204) > + __builtin_abort (); > +} > diff --git > a/gcc/testsuite/gcc.target/i386/pr54412-aligned-struct-byval-temp.c > b/gcc/testsuite/gcc.target/i386/pr54412-aligned-struct-byval-temp.c > new file mode 100644 > index 0000000000..5f8a2409c6 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/pr54412-aligned-struct-byval-temp.c > @@ -0,0 +1,16 @@ > +/* PR target/54412 */ > +/* Check that "assign_stack_temp_for_type" preserves the required alignment > + of a caller-created temporary for a by-reference argument. */ > +/* { dg-do compile { target x86_64-*-mingw* } } */ > +/* { dg-options "-O2" } */ > +/* { dg-final { scan-assembler {and[lq]?\t\$-32,} } } */ > + > +struct S { char c[32]; } __attribute__((aligned (32))); > + > +void sink (struct S); > + > +void > +caller (struct S *p) > +{ > + sink (*p); > +} > diff --git > a/gcc/testsuite/gcc.target/i386/pr54412-blkmode-temp-biggest-align.c > b/gcc/testsuite/gcc.target/i386/pr54412-blkmode-temp-biggest-align.c > new file mode 100644 > index 0000000000..72cbc1ee30 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/pr54412-blkmode-temp-biggest-align.c > @@ -0,0 +1,16 @@ > +/* PR target/54412 */ > +/* Check that "assign_stack_temp_for_type" does not overstate the alignment > + of a "BLKmode" temporary. */ > +/* { dg-do compile { target x86_64-*-mingw* } } */ > +/* { dg-options "-O2 -mavx" } */ > +/* { dg-final { scan-assembler-not {vmov(aps|dqa)\t%ymm[0-9]+, > [0-9-]*\(%r[sb]p\)} } } */ > + > +struct S { char c[32]; }; > + > +void sink (struct S); > + > +void > +caller (struct S *p) > +{ > + sink (*p); > +} > diff --git a/gcc/testsuite/gcc.target/i386/pr54412-escaping-param.c > b/gcc/testsuite/gcc.target/i386/pr54412-escaping-param.c > new file mode 100644 > index 0000000000..fb3c0bf534 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/pr54412-escaping-param.c > @@ -0,0 +1,20 @@ > +/* PR target/54412 */ > +/* Check that "assign_parm_setup_reg" correctly aligns the local copy of an > + indirectly passed parameter whose address escapes. */ > +/* { dg-do compile { target x86_64-*-mingw* } } */ > +/* { dg-options "-O2 -mavx" } */ > +/* { dg-final { scan-assembler {and[lq]?\t\$-32,} } } */ > + > +typedef float v8sf __attribute__ ((vector_size (32))); > + > +__attribute__ ((noipa)) v8sf > +get (const v8sf *p) > +{ > + return *p; > +} > + > +v8sf > +f (v8sf x) > +{ > + return get (&x); > +} > diff --git a/gcc/testsuite/gcc.target/i386/pr54412-m256-byval-param.c > b/gcc/testsuite/gcc.target/i386/pr54412-m256-byval-param.c > new file mode 100644 > index 0000000000..d2d1dcfcee > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/pr54412-m256-byval-param.c > @@ -0,0 +1,22 @@ > +/* PR target/54412 */ > +/* Check that "assign_parm_setup_reg" does not emit an aligned store to an > + under-aligned local copy of an indirectly passed "__m256" parameter. > + > + MSYS2/MINGW-packages#1209: X needs 32-byte alignment, but under SEH the > + frame is only 16-byte aligned. GCC put its home slot in a plain 16-byte > + slot, still treated it as 32-byte aligned and stored X with vmovaps. > + Nothing else sees X, so the move only has to match the slot. Check the > + move instead of running: the bad slot is 32-byte aligned about half the > + time. */ > +/* { dg-do compile { target x86_64-*-mingw* } } */ > +/* { dg-options "-O0 -mavx" } */ > +/* { dg-final { scan-assembler-not {vmovaps\t%ymm0, -[0-9]+\(%rbp\)} } } */ > + > +typedef float __m256 __attribute__ ((__vector_size__ (32), > + __may_alias__)); > + > +void > +foo (__m256 x) > +{ > + (void) x; > +} > -- Richard Biener <[email protected]> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)
