On 3/20/25 12:28 PM, Jakub Jelinek wrote:
On Thu, Mar 20, 2025 at 09:19:02AM -0700, Andi Kleen wrote:
The inlining was just one of the issue, there are some related to
different semantics of escaped locals. gcc always errors out while
LLVM declares it undefined.

But maybe we can fix that one too.

I have 3 patches to be tested, the inline one, fnsplit one and ipa-icf one.
For the escaped locals, I guess we need to decide if [[clang::musttail]]
will be something different from [[gnu::musttail]] in GCC or not (and if
yes, what __attribute__((musttail)) will be).
The current difference in behavior is on
int foo (void);
void bar (int *);
int
baz (void)
{
   int a;
   bar (&a);
   [[clang::musttail]] return foo ();
}
Clang makes the attribute not just a request to tail call it or fail,
but also changes behavior and says if the code ever accesses a from the
above during foo () (which without the attribute is completely valid), then
it is UB.
So, clang can compile this into a tail call, while GCC takes the safer
approach and says it is invalid.  Users can of course adjust their code to
make it explicitly end lifetime before the tailcall, like with
int
qux (void)
{
   {
     int a;
     bar (&a);
   }
   [[clang::musttail]] return foo ();
}
and then gcc should tail call it too.

Yes, I guess Clang is effectively doing that implicitly.

Strangely, it there is a var with
non-trivial destructor which ends lifetime after the call, clang rejects it
like gcc:
struct S { S (); ~S (); int s; };
int
corge (void)
{
   S a;
   bar (&a.s);
   [[clang::musttail]] return foo ();
}

This doesn't seem so strange to me; the destructor call is sequenced after the call to foo, which is impossible if we turned foo into a tailcall. This is a more visible side-effect than ending the storage duration of a.

Not sure I like this, but if others (e.g. Richi, Joseph, Jason) are ok with
it I can live with it.  But we'd need a good documentation, ideally some
some new warning about it (perhaps enabled in -Wextra) and testcase
coverage.
Looking around, clang warns for
int foo (int *);
int bar (int *x)
{
  *x = 42;
  int a = 0;
  [[clang::musttail]] return foo (&a);
}
by default (without -Wall/-Wextra) with
warning: address of stack memory associated with local variable 'a' passed to 
musttail function [-Wreturn-stack-address]

This could even be added to -Wreturn-local-addr.

Dunno if that warning is about other stuff or just that.
If just that, perhaps we'd need multiple levels for it, diagnose passing
those to musttail arguments by default and have in -Wextra stronger variant
that would diagnose even the
int baz (int *x)
{
  *x = 42;
  int a = 0;
  foo (&a);
  [[clang::musttail]] return foo (nullptr);
}
case, i.e. escaped variables that are still in scope.
That variant of the warning perhaps should suggest to users to end the
lifetime of those variables before the musttail call.

I agree that it would be good to warn about this case, perhaps even by default, but that it should be possible to disable this warning without also disabling the diagnostic about foo(&a).

Jason

Reply via email to