https://gcc.gnu.org/g:4a0bca2e9e919353574bef3a200229e29d435679
commit r17-4650-g4a0bca2e9e919353574bef3a200229e29d435679 Author: Oleg Tolmatcev <[email protected]> Date: Wed Sep 16 20:41:25 2026 +0200 testsuite, i386: add tests for over-aligned stack objects [PR54412] 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. 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]> Signed-off-by: Jonathan Yong <[email protected]> Diff: --- .../g++.target/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 ++++ .../gcc.target/i386/pr54412-m256-byval-param.c | 22 +++++ 5 files changed, 184 insertions(+) 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 000000000000..7c1d48c5d512 --- /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 000000000000..5f8a2409c6c9 --- /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 000000000000..72cbc1ee30d9 --- /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 000000000000..fb3c0bf534bb --- /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 000000000000..d2d1dcfcee06 --- /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; +}
