[clang] [clang][bytecode] Use tailcalls via `[[clang::musttail]]` (PR #173756)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 14 18:46:57 PST 2026
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/173756 at github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/173756
>From 826988d6e87f79a08613ceb7cf3911cf851124ae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 15 Jan 2026 03:46:30 +0100
Subject: [PATCH 1/2] test
---
clang/lib/AST/ExprConstant.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 2a228d2896730..79d4c1c26e41a 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -931,7 +931,7 @@ namespace {
: Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr),
CallStackDepth(0), NextCallIndex(1),
StepsLeft(C.getLangOpts().ConstexprStepLimit),
- EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp),
+ EnableNewConstInterp(true),
BottomFrame(*this, SourceLocation(), /*Callee=*/nullptr,
/*This=*/nullptr,
/*CallExpr=*/nullptr, CallRef()),
>From a4d5933b47595f7dec0b06120666c30312e29b41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Fri, 26 Dec 2025 09:06:45 +0100
Subject: [PATCH 2/2] musttail
---
clang/lib/AST/ByteCode/Interp.cpp | 34 ++++++++++
clang/utils/TableGen/ClangOpcodesEmitter.cpp | 70 ++++++++++++++++++++
2 files changed, 104 insertions(+)
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 0205d840fd71e..cb63e0f723aa1 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -2374,6 +2374,11 @@ bool FinishInitGlobal(InterpState &S, CodePtr OpPC) {
#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
#pragma optimize("", off)
#endif
+
+
+
+bool InterpNext(InterpState &S, CodePtr &PC);
+
bool Interpret(InterpState &S) {
// The current stack frame when we started Interpret().
// This is being used by the ops to determine wheter
@@ -2383,10 +2388,14 @@ bool Interpret(InterpState &S) {
assert(!S.Current->isRoot());
CodePtr PC = S.Current->getPC();
+ // S.Current->dump();
+
// Empty program.
if (!PC)
return true;
+ return InterpNext(S, PC);
+
for (;;) {
auto Op = PC.read<Opcode>();
CodePtr OpPC = PC;
@@ -2398,6 +2407,31 @@ bool Interpret(InterpState &S) {
}
}
}
+
+
+
+
+#define GET_INTERPFNS_
+#include "Opcodes.inc"
+#undef GET_INTERPFNS_
+
+
+
+using InterpFn = bool (*)(InterpState &, CodePtr &PC);
+
+const InterpFn InterpFunctions[] = {
+#define GET_INTERPFNS
+#include "Opcodes.inc"
+#undef GET_INTERPFNS
+};
+
+
+bool InterpNext(InterpState &S, CodePtr &PC) {
+ auto Op = PC.read<Opcode>();
+ auto Fn = InterpFunctions[Op];
+ [[clang::musttail]] return Fn(S, PC);
+}
+
// https://github.com/llvm/llvm-project/issues/102513
#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
#pragma optimize("", on)
diff --git a/clang/utils/TableGen/ClangOpcodesEmitter.cpp b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
index d26122aca46bd..4d05d781dbda2 100644
--- a/clang/utils/TableGen/ClangOpcodesEmitter.cpp
+++ b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
@@ -36,6 +36,8 @@ class ClangOpcodesEmitter {
/// Emits the switch case and the invocation in the interpreter.
void EmitInterp(raw_ostream &OS, StringRef N, const Record *R);
+ void EmitInterpFns(raw_ostream &OS, StringRef N, const Record *R);
+ void EmitInterpFns_(raw_ostream &OS, StringRef N, const Record *R);
/// Emits the disassembler.
void EmitDisasm(raw_ostream &OS, StringRef N, const Record *R);
@@ -92,6 +94,8 @@ void ClangOpcodesEmitter::run(raw_ostream &OS) {
EmitEnum(OS, N, Opcode);
EmitInterp(OS, N, Opcode);
+ EmitInterpFns(OS, N, Opcode);
+ EmitInterpFns_(OS, N, Opcode);
EmitDisasm(OS, N, Opcode);
EmitProto(OS, N, Opcode);
EmitGroup(OS, N, Opcode);
@@ -109,6 +113,72 @@ void ClangOpcodesEmitter::EmitEnum(raw_ostream &OS, StringRef N,
OS << "#endif\n";
}
+void ClangOpcodesEmitter::EmitInterpFns_(raw_ostream &OS, StringRef N,
+ const Record *R) {
+ OS << "#ifdef GET_INTERPFNS_\n";
+ Enumerate(R, N, [&](ArrayRef<const Record *>TS, const Twine &ID) {
+ OS << "static bool Interp_" << ID << "(InterpState &S, CodePtr &PC) {\n";
+
+ bool CanReturn = R->getValueAsBit("CanReturn");
+ const auto &Args = R->getValueAsListOfDefs("Args");
+ bool ChangesPC = R->getValueAsBit("ChangesPC");
+
+ OS << "{\n";
+
+ OS << "CodePtr OpPC = PC;\n";
+
+ // Emit calls to read arguments.
+ for (size_t I = 0, N = Args.size(); I < N; ++I) {
+ const auto *Arg = Args[I];
+ bool AsRef = Arg->getValueAsBit("AsRef");
+
+ if (AsRef)
+ OS << " const auto &V" << I;
+ else
+ OS << " const auto V" << I;
+ OS << " = ";
+ OS << "ReadArg<" << Arg->getValueAsString("Name")
+ << ">(S, PC);\n";
+ }
+
+
+ OS << " if (!" << N;
+ PrintTypes(OS, TS);
+ OS << "(S";
+ // OS << ", OpPC";
+ if (ChangesPC)
+ OS << ", PC";
+ else
+ OS << ", OpPC";
+ for (size_t I = 0, N = Args.size(); I < N; ++I)
+ OS << ", V" << I;
+ OS << "))\n";
+ OS << " return false;\n";
+
+ OS << "}\n";
+
+ if (!CanReturn)
+ OS << "[[clang::musttail]] return InterpNext(S, PC);\n";
+ else
+ OS << " return true;\n";
+ // OS << " return false;\n";
+
+ OS << "}\n";
+ });
+ OS << "#endif\n";
+}
+
+
+
+void ClangOpcodesEmitter::EmitInterpFns(raw_ostream &OS, StringRef N,
+ const Record *R) {
+ OS << "#ifdef GET_INTERPFNS\n";
+ Enumerate(R, N, [&OS](ArrayRef<const Record *>, const Twine &ID) {
+ OS << "&Interp_" << ID << ",\n";
+ });
+ OS << "#endif\n";
+}
+
void ClangOpcodesEmitter::EmitInterp(raw_ostream &OS, StringRef N,
const Record *R) {
OS << "#ifdef GET_INTERP\n";
More information about the cfe-commits
mailing list