https://gcc.gnu.org/g:567684733e0c4f28158e19b72f4e7f660ad81561

commit r16-121-g567684733e0c4f28158e19b72f4e7f660ad81561
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Thu Apr 24 23:44:28 2025 +0200

    s390: Allow 5+ argument tail-calls in some special cases [PR119873]
    
    protobuf (and therefore firefox too) currently doesn't build on s390*-linux.
    The problem is that it uses [[clang::musttail]] attribute heavily, and in
    llvm (IMHO llvm bug) [[clang::musttail]] calls with 5+ arguments on
    s390*-linux are silently accepted and result in a normal non-tail call.
    In GCC we just reject those because the target hook refuses to tail call it
    (IMHO the right behavior).
    Now, the reason why that happens is as s390_function_ok_for_sibcall attempts
    to explain, the 5th argument (assuming normal <= wordsize integer or pointer
    arguments, nothing that needs 2+ registers) is passed in %r6 which is not
    call clobbered, so we can't do tail call when we'd have to change content
    of that register and then caller would assume %r6 content didn't change and
    use it again.
    In the protobuf case though, the 5th argument is always passed through
    from the caller to the musttail callee unmodified, so one can actually
    emit just jg tail_called_function or perhaps tweak some registers but
    keep %r6 untouched, and in that case I think it is just fine to tail call
    it (at least unless the stack slots used for 6+ argument can't be modified
    by the callee in the ABI and nothing checks for that).
    
    So, the following patch checks for this special case, where the argument
    which uses %r6 is passed in a single register and it is passed default
    definition of SSA_NAME of a PARM_DECL with the same DECL_INCOMING_RTL.
    
    It won't really work at -O0 but should work for -O1 and above, at least when
    one doesn't really try to modify the parameter conditionally and hope it 
will
    be optimized away in the end.
    
    2025-04-24  Jakub Jelinek  <ja...@redhat.com>
                Stefan Schulze Frielinghaus  <stefa...@gcc.gnu.org>
    
            PR target/119873
            * config/s390/s390.cc (s390_call_saved_register_used): Don't return
            true if default definition of PARM_DECL SSA_NAME of the same 
register
            is passed in call saved register.
            (s390_function_ok_for_sibcall): Adjust comment.
    
            * gcc.target/s390/pr119873-1.c: New test.
            * gcc.target/s390/pr119873-2.c: New test.
            * gcc.target/s390/pr119873-3.c: New test.
            * gcc.target/s390/pr119873-4.c: New test.

Diff:
---
 gcc/config/s390/s390.cc                    | 21 ++++++++++++++++++---
 gcc/testsuite/gcc.target/s390/pr119873-1.c | 11 +++++++++++
 gcc/testsuite/gcc.target/s390/pr119873-2.c | 17 +++++++++++++++++
 gcc/testsuite/gcc.target/s390/pr119873-3.c | 27 +++++++++++++++++++++++++++
 gcc/testsuite/gcc.target/s390/pr119873-4.c | 27 +++++++++++++++++++++++++++
 5 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc
index d82b16ea9ce0..0e9140ed5a0a 100644
--- a/gcc/config/s390/s390.cc
+++ b/gcc/config/s390/s390.cc
@@ -14496,7 +14496,21 @@ s390_call_saved_register_used (tree call_expr)
 
          for (reg = 0; reg < nregs; reg++)
            if (!call_used_or_fixed_reg_p (reg + REGNO (parm_rtx)))
-             return true;
+             {
+               rtx parm;
+               /* Allow passing through unmodified value from caller,
+                  see PR119873.  */
+               if (TREE_CODE (parameter) == SSA_NAME
+                   && SSA_NAME_IS_DEFAULT_DEF (parameter)
+                   && SSA_NAME_VAR (parameter)
+                   && TREE_CODE (SSA_NAME_VAR (parameter)) == PARM_DECL
+                   && (parm = DECL_INCOMING_RTL (SSA_NAME_VAR (parameter)))
+                   && REG_P (parm)
+                   && REGNO (parm) == REGNO (parm_rtx)
+                   && REG_NREGS (parm) == REG_NREGS (parm_rtx))
+                 break;
+               return true;
+             }
        }
       else if (GET_CODE (parm_rtx) == PARALLEL)
        {
@@ -14543,8 +14557,9 @@ s390_function_ok_for_sibcall (tree decl, tree exp)
     return false;
 
   /* Register 6 on s390 is available as an argument register but unfortunately
-     "caller saved". This makes functions needing this register for arguments
-     not suitable for sibcalls.  */
+     "caller saved".  This makes functions needing this register for arguments
+     not suitable for sibcalls, unless the same value is passed from the
+     caller.  */
   return !s390_call_saved_register_used (exp);
 }
 
diff --git a/gcc/testsuite/gcc.target/s390/pr119873-1.c 
b/gcc/testsuite/gcc.target/s390/pr119873-1.c
new file mode 100644
index 000000000000..7a9a988526b5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/pr119873-1.c
@@ -0,0 +1,11 @@
+/* PR target/119873 */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+const char *foo (void *, void *, void *, void *, unsigned long, unsigned long);
+
+const char *
+bar (void *a, void *b, void *c, void *d, unsigned long e, unsigned long f)
+{
+  [[gnu::musttail]] return foo (a, b, c, d, e, f);
+}
diff --git a/gcc/testsuite/gcc.target/s390/pr119873-2.c 
b/gcc/testsuite/gcc.target/s390/pr119873-2.c
new file mode 100644
index 000000000000..f275253eaa1e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/pr119873-2.c
@@ -0,0 +1,17 @@
+/* PR target/119873 */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+const char *foo (void *, void *, void *, void *, unsigned long, unsigned long);
+
+const char *
+bar (void *a, void *b, void *c, void *d, unsigned long e, unsigned long f)
+{
+  [[gnu::musttail]] return foo (a, b, c, d, e + 1, f); /* { dg-error "cannot 
tail-call: target is not able to optimize the call into a sibling call" } */
+}
+
+const char *
+baz (void *a, void *b, void *c, void *d, unsigned long e, unsigned long f)
+{
+  [[gnu::musttail]] return foo (a, b, c, d, f, e);     /* { dg-error "cannot 
tail-call: target is not able to optimize the call into a sibling call" } */
+}
diff --git a/gcc/testsuite/gcc.target/s390/pr119873-3.c 
b/gcc/testsuite/gcc.target/s390/pr119873-3.c
new file mode 100644
index 000000000000..048fcaa43dda
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/pr119873-3.c
@@ -0,0 +1,27 @@
+/* PR target/119873 */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int foo (int, int, int, long long, int);
+
+int
+bar (int u, int v, int w, long long x, int y)
+{
+  [[gnu::musttail]] return foo (u, v, w, x, y);
+}
+
+extern int baz (int, int, int, int, int);
+
+int
+qux (int u, int v, int w, int x, int y)
+{
+  [[gnu::musttail]] return baz (u, v, w, x, y);
+}
+
+extern int corge (int, int, int, int, unsigned short);
+
+int
+garply (int u, int v, int w, int x, unsigned short y)
+{
+  [[gnu::musttail]] return corge (u, v, w, x, y);
+}
diff --git a/gcc/testsuite/gcc.target/s390/pr119873-4.c 
b/gcc/testsuite/gcc.target/s390/pr119873-4.c
new file mode 100644
index 000000000000..384170cf4a98
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/pr119873-4.c
@@ -0,0 +1,27 @@
+/* PR target/119873 */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern int foo (int, int, int, long long, int);
+
+int
+bar (int u, int v, int w, long long x, int y)
+{
+  [[gnu::musttail]] return foo (u, v, w, x + 1, y - 1);        /* { dg-error 
"cannot tail-call: target is not able to optimize the call into a sibling call" 
} */
+}
+
+extern int baz (int, int, int, int, int);
+
+int
+qux (int u, int v, int w, int x, int y)
+{
+  [[gnu::musttail]] return baz (u, v, w, x, y + 1);    /* { dg-error "cannot 
tail-call: target is not able to optimize the call into a sibling call" } */
+}
+
+extern int corge (int, int, int, int, unsigned short);
+
+int
+garply (int u, int v, int w, int x, unsigned short y)
+{
+  [[gnu::musttail]] return corge (u, v, w, x, y + 1);  /* { dg-error "cannot 
tail-call: target is not able to optimize the call into a sibling call" } */
+}

Reply via email to