Hi!

As discussed in the PR, some packages fail to build because they use
musttail attribute on calls in functions which we inline, and if they
are inlined into a middle of the function, that results in an error
because we have a musttail call in the middle of a function and so it
can't be tail called there.

Now, guess the primary intent of the musttail attribute is ensuring
we don't get an extra stack frame in the backtrace.  Inlining itself
removes one extra stack frame from the backtrace as well (sure, not
counting virtual backtraces in gdb), so I think erroring out on that
is unnecessary.

Except when we are inlining a musttail call which has musttail calls
in it, in that case we are being asked to remove 2 stack frames from
the backtrace, inlining removes one, so we need to keep musttail
on the calls so that another stack frame is removed through a tail call.

The following patch implements that, keeping previous behavior when
id->call_stmt is NULL (i.e. when versioning/cloning etc.).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2025-03-20  Jakub Jelinek  <ja...@redhat.com>

        PR ipa/119376
        * tree-inline.cc (remap_gimple_stmt): Silently clear
        gimple_call_must_tail_p on inlined call stmts if id->call_stmt
        is a call without that flag set.

        * c-c++-common/musttail26.c: New test.

--- gcc/tree-inline.cc.jj       2025-01-20 17:58:39.244480277 +0100
+++ gcc/tree-inline.cc  2025-03-20 12:08:42.957453514 +0100
@@ -1892,6 +1892,15 @@ remap_gimple_stmt (gimple *stmt, copy_bo
            gimple_call_set_tail (call_stmt, false);
          if (gimple_call_from_thunk_p (call_stmt))
            gimple_call_set_from_thunk (call_stmt, false);
+         /* Silently clear musttail flag when inlining a function
+            with must tail call from a non-musttail call.  The inlining
+            removes one frame so acts like musttail's intent, and we
+            can be inlining a function with musttail calls in the middle
+            of caller where musttail will always error.  */
+         if (gimple_call_must_tail_p (call_stmt)
+             && id->call_stmt
+             && !gimple_call_must_tail_p (id->call_stmt))
+           gimple_call_set_must_tail (call_stmt, false);
          if (gimple_call_internal_p (call_stmt))
            switch (gimple_call_internal_fn (call_stmt))
              {
--- gcc/testsuite/c-c++-common/musttail26.c.jj  2025-03-20 12:47:53.443730606 
+0100
+++ gcc/testsuite/c-c++-common/musttail26.c     2025-03-20 12:53:26.110082182 
+0100
@@ -0,0 +1,33 @@
+/* PR ipa/119376 */
+/* { dg-do compile { target musttail } } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times "  \[^\n\r]* = foo \\\(3, \[^\n\r]*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "  \[^\n\r]* = foo \\\(4, \[^\n\r]*\\\); 
\\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "  foo \\\(\[12], \[^\n\r]*\\\); \\\[tail 
call\\\]" "optimized" } } */
+
+int foo (int, int);
+int v, w[10];
+
+static inline __attribute__((always_inline)) int
+bar (int x, int y)
+{
+  [[gnu::musttail]] return foo (x, y);
+}
+
+static int
+baz (int x, int y)
+{
+  [[gnu::musttail]] return foo (x, x + y + (v | y) * (v & y));
+}
+
+int
+qux (int x, int y)
+{
+  w[0] = bar (1, x + y);
+  w[1] = baz (2, x + y);
+  if (x == 42)
+    [[gnu::musttail]] return bar (3, x + y);
+  if (x == -42)
+    [[gnu::musttail]] return baz (4, x + y);
+  return 0;
+}

        Jakub

Reply via email to