сб, 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

Reply via email to