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

Reply via email to