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)

Reply via email to