Re: [PATCH] i386: preserve fastcall reg slots across aggregate args [PR41013]

Oleg Tolmatcev <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <CACcXsZguTk4V-trv9CjePTn0PErhKoJxLUSaKnUCAcEWpxCEfA@mail.gmail.com>
сб, 27 июн. 2026 г. в 18:07, Oleg Tolmatcev <[email protected]>:
>
> From: oltolm <[email protected]>
>
> On x86-32 fastcall, by-value aggregate arguments are stack-passed and
> must not consume the ECX/EDX register cursor in function_arg_advance_32.
>
> Advancing the cursor for BLKmode/DImode/aggregate arguments could shift
> later eligible scalar arguments out of fastcall registers and onto the stack.
>
> gcc/ChangeLog:
>
>         PR target/41013
>         * config/i386/i386.cc (function_arg_advance_32): Do not
>         consume fastcall register slots for BLKmode/DImode/aggregate
>         arguments.
>
> gcc/testsuite/ChangeLog:
>
>         PR target/41013
>         * gcc.target/i386/fastcall-1.c: Restrict to mingw targets and
>         force -m32.
>         * gcc.target/i386/fastcall-arg-kinds-1.c: New test.
>         * gcc.target/i386/thiscall-arg-kinds-1.c: New test.
>
> Signed-off-by: oltolm <[email protected]>
> ---
>
> I don't have commit rights for the git repository.
>
>  gcc/config/i386/i386.cc                       | 12 ++++
>  gcc/testsuite/gcc.target/i386/fastcall-1.c    |  4 +-
>  .../gcc.target/i386/fastcall-arg-kinds-1.c    | 70 +++++++++++++++++++
>  .../gcc.target/i386/thiscall-arg-kinds-1.c    | 42 +++++++++++
>  4 files changed, 126 insertions(+), 2 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/i386/fastcall-arg-kinds-1.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/thiscall-arg-kinds-1.c
>
> diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
> index e66958db7a..1b019f9e1c 100644
> --- a/gcc/config/i386/i386.cc
> +++ b/gcc/config/i386/i386.cc
> @@ -3101,6 +3101,18 @@ function_arg_advance_32 (CUMULATIVE_ARGS *cum, machine_mode mode,
>      case E_HImode:
>      case E_QImode:
>  pass_in_reg:
> +  /* For fastcall, only register-eligible scalar arguments consume
> +     the ECX/EDX slots.  Non-eligible arguments are passed on stack
> +     and must not advance the fastcall register cursor.  */
> +  if (cum->fastcall
> +      && (mode == BLKmode
> +         || mode == DImode
> +         || (type && AGGREGATE_TYPE_P (type))))
> +    {
> +      cum->words += words;
> +      break;
> +    }
> +
>        cum->words += words;
>        cum->nregs -= words;
>        cum->regno += words;
> diff --git a/gcc/testsuite/gcc.target/i386/fastcall-1.c b/gcc/testsuite/gcc.target/i386/fastcall-1.c
> index 9d70123916..ce08695563 100644
> --- a/gcc/testsuite/gcc.target/i386/fastcall-1.c
> +++ b/gcc/testsuite/gcc.target/i386/fastcall-1.c
> @@ -1,5 +1,5 @@
> -/* { dg-do compile { target i?86-*-mingw32* i?86-*-cygwin* } } */
> -/* { dg-options "-std=gnu89" } */
> +/* { dg-do compile { target *-*-mingw32* i?86-*-cygwin* } } */
> +/* { dg-options "-m32 -std=gnu89" } */
>
>  void
>  __attribute__ ((fastcall))
> diff --git a/gcc/testsuite/gcc.target/i386/fastcall-arg-kinds-1.c b/gcc/testsuite/gcc.target/i386/fastcall-arg-kinds-1.c
> new file mode 100644
> index 0000000000..fadad43359
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/fastcall-arg-kinds-1.c
> @@ -0,0 +1,70 @@
> +/* { dg-do compile { target *-*-mingw32* } } */
> +/* { dg-options "-m32 -O0" } */
> +
> +enum e { E0 = 0, E1 = 1 };
> +union u { int x; };
> +struct s { int x; };
> +
> +int __attribute__((fastcall, noinline))
> +fp (int *p, int a1)
> +{
> +  return *p + a1;
> +}
> +
> +int __attribute__((fastcall, noinline))
> +fe (enum e v, int a1)
> +{
> +  return (int) v + a1;
> +}
> +
> +int __attribute__((fastcall, noinline))
> +fu (union u v, int a1)
> +{
> +  return v.x + a1;
> +}
> +
> +int __attribute__((fastcall, noinline))
> +fs (struct s v, int a1)
> +{
> +  return v.x + a1;
> +}
> +
> +int __attribute__((fastcall, noinline))
> +f2 (struct s v, int a1, int a2)
> +{
> +  return v.x + a1 + a2;
> +}
> +
> +int
> +main (void)
> +{
> +  int a = 7;
> +  union u uv = { 11 };
> +  struct s sv = { 13 };
> +
> +  return (fp (&a, 1)
> +         + fe (E1, 2)
> +         + fu (uv, 3)
> +         + fs (sv, 4)
> +         + f2 (sv, 5, 6));
> +}
> +
> +/* Pointer and enum arguments are register-eligible in fastcall.  */
> +/* { dg-final { scan-assembler {call[ \t]+@fp@8} } } */
> +/* { dg-final { scan-assembler {call[ \t]+@fe@8} } } */
> +/* { dg-final { scan-assembler {movl\t\$1, %edx} } } */
> +/* { dg-final { scan-assembler {movl\t\$2, %edx} } } */
> +/* { dg-final { scan-assembler {movl\t\$1, %ecx} } } */
> +
> +/* Small aggregates remain stack-passed, and the integer argument stays in ECX.  */
> +/* { dg-final { scan-assembler {call[ \t]+@fu@8} } } */
> +/* { dg-final { scan-assembler {call[ \t]+@fs@8} } } */
> +/* { dg-final { scan-assembler-times {movl\t%eax, \(%esp\)} 3 } } */
> +/* { dg-final { scan-assembler {movl\t\$3, %ecx} } } */
> +/* { dg-final { scan-assembler {movl\t\$4, %ecx} } } */
> +
> +/* Aggregate arg must not consume fastcall register slots; both ints stay
> +   register-passed in ECX/EDX.  */
> +/* { dg-final { scan-assembler {call[ \t]+@f2@12} } } */
> +/* { dg-final { scan-assembler {movl\t\$5, %ecx} } } */
> +/* { dg-final { scan-assembler {movl\t\$6, %edx} } } */
> diff --git a/gcc/testsuite/gcc.target/i386/thiscall-arg-kinds-1.c b/gcc/testsuite/gcc.target/i386/thiscall-arg-kinds-1.c
> new file mode 100644
> index 0000000000..2b5450aae3
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/thiscall-arg-kinds-1.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile { target *-*-mingw32* } } */
> +/* { dg-options "-m32 -O0" } */
> +
> +struct s { int x; };
> +
> +int __attribute__((thiscall, noinline))
> +fp (int *p, int a1)
> +{
> +  return *p + a1;
> +}
> +
> +int __attribute__((thiscall, noinline))
> +fs (struct s v, int a1)
> +{
> +  return v.x + a1;
> +}
> +
> +int __attribute__((thiscall, noinline))
> +f2 (int a0, int a1)
> +{
> +  return a0 + a1;
> +}
> +
> +int
> +main (void)
> +{
> +  int a = 7;
> +  struct s sv = { 11 };
> +
> +  return fp (&a, 1) + fs (sv, 2) + f2 (3, 4);
> +}
> +
> +/* Pointer arguments remain register-eligible in thiscall.  */
> +/* { dg-final { scan-assembler {movl\t\$1, \(%esp\)} } } */
> +
> +/* Small aggregates remain stack-passed, and the later integer stays in ECX.  */
> +/* { dg-final { scan-assembler-times {movl\t%eax, \(%esp\)} 1 } } */
> +/* { dg-final { scan-assembler {movl\t\$2, %ecx} } } */
> +
> +/* Control case: only the first scalar gets the ECX slot.  */
> +/* { dg-final { scan-assembler {movl\t\$3, %ecx} } } */
> +/* { dg-final { scan-assembler {movl\t\$4, \(%esp\)} } } */
> --
> 2.54.0.windows.1
>

I am dropping this patch. i386 is obsolete, so fixing this bug is not worth it.

Oleg
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.