https://github.com/chfast created 
https://github.com/llvm/llvm-project/pull/234580

A musttail call to a noreturn function (`__attribute__((noreturn))`, 
`_Noreturn`, `exit()`, `longjmp()`, ...) gets `unreachable` after it instead of 
`ret`, which fails the verifier with "musttail call must precede a ret". 
Release builds skip the verifier, so -O2 crashes with "failed to perform tail 
call elimination on a call site marked musttail" and -O0 silently emits a 
regular call. Sema only rejects `[[noreturn]]` callees (#77727), so all other 
spellings get through.

Implicit musttail calls produce the same invalid IR: `swiftasynccall`, and the 
`-fptrauth-calls` thunk for a pointer to a noreturn virtual member function. 
The latter crashes even at -O0, without any `musttail` in the source.

The noreturn path in `EmitCall` runs before the musttail one. This patch skips 
it for musttail calls, so they get a `ret` like any other musttail call. As a 
side effect, the musttail checks there (e.g. the noexcept mismatch error) now 
apply to noreturn callees too.

This matches GCC, which accepts musttail calls to noreturn functions, 
`[[noreturn]]` ones included: https://godbolt.org/z/q8ajhMh15

With CodeGen handling it, the Sema error for `[[noreturn]]` callees is no 
longer needed. I'll lift it in a follow-up PR.

Inspired by rnk's comment in #191198 asking whether the thunk code emitting a 
`ret` after the musttail call is dead:
https://github.com/llvm/llvm-project/pull/191198#discussion_r3414988237. Only 
noreturn callees reach it.

Fixes #60162.
Fixes #120088.
Fixes #120637.

From 43d83c3b07896ac39b68c564b8e99b03e4e6a245 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <[email protected]>
Date: Thu, 22 Oct 2026 12:46:01 +0200
Subject: [PATCH] [clang][CodeGen] Emit return after musttail call to noreturn
 function

A musttail call to a noreturn function (`__attribute__((noreturn))`,
`_Noreturn`, `exit()`, `longjmp()`, ...) gets `unreachable` after it
instead of `ret`, which fails the verifier with "musttail call must
precede a ret". Release builds skip the verifier, so -O2 crashes with
"failed to perform tail call elimination on a call site marked musttail"
and -O0 silently emits a regular call. Sema only rejects `[[noreturn]]`
callees (#77727), so all other spellings get through.

Implicit musttail calls produce the same invalid IR: `swiftasynccall`,
and the `-fptrauth-calls` thunk for a pointer to a noreturn virtual
member function. The latter crashes even at -O0, without any `musttail`
in the source.

The noreturn path in `EmitCall` runs before the musttail one. This patch
skips it for musttail calls, so they get a `ret` like any other musttail
call. As a side effect, the musttail checks there (e.g. the noexcept
mismatch error) now apply to noreturn callees too.

This matches GCC, which accepts musttail calls to noreturn functions,
`[[noreturn]]` ones included: https://godbolt.org/z/q8ajhMh15

With CodeGen handling it, the Sema error for `[[noreturn]]` callees is
no longer needed. I'll lift it in a follow-up PR.

Inspired by rnk's comment in #191198 asking whether the thunk code
emitting a `ret` after the musttail call is dead:
https://github.com/llvm/llvm-project/pull/191198#discussion_r3414988237.
Only noreturn callees reach it.

Fixes #60162.
Fixes #120088.
Fixes #120637.
---
 clang/docs/ReleaseNotes.md                    |  5 ++++
 clang/lib/CodeGen/CGCall.cpp                  |  5 ++--
 clang/test/CodeGen/musttail-noreturn.c        | 29 +++++++++++++++++++
 .../ptrauth-member-function-pointer.cpp       | 14 +++++++++
 4 files changed, 51 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGen/musttail-noreturn.c

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7fe9977a979c0d..61bb291008439d 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -873,6 +873,11 @@ documentation](CIR/index.md) for more information 
regarding ClangIR's status and
   `alias` are now correctly diagnosed as definitions when followed by an
   out-of-line definition. (#GH204762)
 
+- Fixed a crash on a `[[clang::musttail]]` call to a noreturn function, e.g.
+  one declared with `__attribute__((noreturn))`, and on taking the address of
+  a noreturn virtual member function with `-fptrauth-calls`. (#GH60162,
+  #GH120088, #GH120637)
+
 #### Bug Fixes to C++ Support
 - Instantiate constexpr functions as needed before they are evaluated. 
(#GH73232) (#GH35052) (#GH100897)
 
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 9b8b0b52b66cb5..27b7d2d3e15f9a 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -6611,8 +6611,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo 
&CallInfo,
 
   // If the call doesn't return, finish the basic block and clear the
   // insertion point; this allows the rest of IRGen to discard
-  // unreachable code.
-  if (CI->doesNotReturn()) {
+  // unreachable code. A musttail call must be followed by a return, so it is
+  // handled below even if the callee doesn't return.
+  if (CI->doesNotReturn() && !IsMustTail) {
     if (NeedSRetLifetimeEnd)
       PopCleanupBlock();
 
diff --git a/clang/test/CodeGen/musttail-noreturn.c 
b/clang/test/CodeGen/musttail-noreturn.c
new file mode 100644
index 00000000000000..676ad4d051b525
--- /dev/null
+++ b/clang/test/CodeGen/musttail-noreturn.c
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c23 -emit-llvm %s -o 
- | FileCheck %s
+
+// A musttail call to a noreturn function must still be followed by a return.
+
+__attribute__((noreturn)) void gnu_noreturn(int);
+typedef void noreturn_fn(int) __attribute__((noreturn));
+extern noreturn_fn *noreturn_ptr;
+
+// CHECK-LABEL: define {{.*}} @test_gnu(
+// CHECK: musttail call void @gnu_noreturn(
+// CHECK-NEXT: ret void
+void test_gnu(int x) { [[clang::musttail]] return gnu_noreturn(x); }
+
+// CHECK-LABEL: define {{.*}} @test_ptr(
+// CHECK: musttail call void %{{.*}}(
+// CHECK-NEXT: ret void
+void test_ptr(int x) { [[clang::musttail]] return noreturn_ptr(x); }
+
+// swiftasynccall makes the call musttail implicitly.
+__attribute__((swiftasynccall, noreturn)) void
+async_noreturn(char *__attribute__((swift_async_context)) ctx);
+
+// CHECK-LABEL: define {{.*}} @test_swiftasync(
+// CHECK: musttail call swifttailcc void @async_noreturn(
+// CHECK-NEXT: ret void
+__attribute__((swiftasynccall)) void
+test_swiftasync(char *__attribute__((swift_async_context)) ctx) {
+  return async_noreturn(ctx);
+}
diff --git a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp 
b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp
index cbfe9e625e7bdb..ddf8a2af5125f1 100644
--- a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp
+++ b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp
@@ -501,3 +501,17 @@ void test2() {
 }
 
 }
+
+namespace testNoreturn {
+
+// CHECK: define linkonce_odr hidden void 
@_ZN12testNoreturn1S16virtual_noreturnEv_vfpthunk_(
+// CHECK: musttail call void
+// CHECK-NEXT: ret void
+
+struct S {
+  __attribute__((noreturn)) virtual void virtual_noreturn();
+};
+
+void (S::*mfptr)() = &S::virtual_noreturn;
+
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to