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