Skip to content

Commit 5676847

Browse files
committed
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 <[email protected]> Stefan Schulze Frielinghaus <[email protected]> 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.
1 parent a4e8d18 commit 5676847

File tree

5 files changed

+100
-3
lines changed

5 files changed

+100
-3
lines changed

gcc/config/s390/s390.cc

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14496,7 +14496,21 @@ s390_call_saved_register_used (tree call_expr)
1449614496

1449714497
for (reg = 0; reg < nregs; reg++)
1449814498
if (!call_used_or_fixed_reg_p (reg + REGNO (parm_rtx)))
14499-
return true;
14499+
{
14500+
rtx parm;
14501+
/* Allow passing through unmodified value from caller,
14502+
see PR119873. */
14503+
if (TREE_CODE (parameter) == SSA_NAME
14504+
&& SSA_NAME_IS_DEFAULT_DEF (parameter)
14505+
&& SSA_NAME_VAR (parameter)
14506+
&& TREE_CODE (SSA_NAME_VAR (parameter)) == PARM_DECL
14507+
&& (parm = DECL_INCOMING_RTL (SSA_NAME_VAR (parameter)))
14508+
&& REG_P (parm)
14509+
&& REGNO (parm) == REGNO (parm_rtx)
14510+
&& REG_NREGS (parm) == REG_NREGS (parm_rtx))
14511+
break;
14512+
return true;
14513+
}
1450014514
}
1450114515
else if (GET_CODE (parm_rtx) == PARALLEL)
1450214516
{
@@ -14543,8 +14557,9 @@ s390_function_ok_for_sibcall (tree decl, tree exp)
1454314557
return false;
1454414558

1454514559
/* Register 6 on s390 is available as an argument register but unfortunately
14546-
"caller saved". This makes functions needing this register for arguments
14547-
not suitable for sibcalls. */
14560+
"caller saved". This makes functions needing this register for arguments
14561+
not suitable for sibcalls, unless the same value is passed from the
14562+
caller. */
1454814563
return !s390_call_saved_register_used (exp);
1454914564
}
1455014565

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* PR target/119873 */
2+
/* { dg-do compile } */
3+
/* { dg-options "-O2" } */
4+
5+
const char *foo (void *, void *, void *, void *, unsigned long, unsigned long);
6+
7+
const char *
8+
bar (void *a, void *b, void *c, void *d, unsigned long e, unsigned long f)
9+
{
10+
[[gnu::musttail]] return foo (a, b, c, d, e, f);
11+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* PR target/119873 */
2+
/* { dg-do compile } */
3+
/* { dg-options "-O2" } */
4+
5+
const char *foo (void *, void *, void *, void *, unsigned long, unsigned long);
6+
7+
const char *
8+
bar (void *a, void *b, void *c, void *d, unsigned long e, unsigned long f)
9+
{
10+
[[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" } */
11+
}
12+
13+
const char *
14+
baz (void *a, void *b, void *c, void *d, unsigned long e, unsigned long f)
15+
{
16+
[[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" } */
17+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* PR target/119873 */
2+
/* { dg-do compile } */
3+
/* { dg-options "-O2" } */
4+
5+
extern int foo (int, int, int, long long, int);
6+
7+
int
8+
bar (int u, int v, int w, long long x, int y)
9+
{
10+
[[gnu::musttail]] return foo (u, v, w, x, y);
11+
}
12+
13+
extern int baz (int, int, int, int, int);
14+
15+
int
16+
qux (int u, int v, int w, int x, int y)
17+
{
18+
[[gnu::musttail]] return baz (u, v, w, x, y);
19+
}
20+
21+
extern int corge (int, int, int, int, unsigned short);
22+
23+
int
24+
garply (int u, int v, int w, int x, unsigned short y)
25+
{
26+
[[gnu::musttail]] return corge (u, v, w, x, y);
27+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* PR target/119873 */
2+
/* { dg-do compile } */
3+
/* { dg-options "-O2" } */
4+
5+
extern int foo (int, int, int, long long, int);
6+
7+
int
8+
bar (int u, int v, int w, long long x, int y)
9+
{
10+
[[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" } */
11+
}
12+
13+
extern int baz (int, int, int, int, int);
14+
15+
int
16+
qux (int u, int v, int w, int x, int y)
17+
{
18+
[[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" } */
19+
}
20+
21+
extern int corge (int, int, int, int, unsigned short);
22+
23+
int
24+
garply (int u, int v, int w, int x, unsigned short y)
25+
{
26+
[[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" } */
27+
}

0 commit comments

Comments
 (0)