Timm =?utf-8?q?Bäder?= <[email protected]>,
Timm =?utf-8?q?Bäder?= <[email protected]>,
Timm =?utf-8?q?Bäder?= <[email protected]>,
Timm =?utf-8?q?Bäder?= <[email protected]>,
Timm =?utf-8?q?Bäder?= <[email protected]>,
Timm =?utf-8?q?Bäder?= <[email protected]>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/[email protected]>


https://github.com/tbaederr updated 
https://github.com/llvm/llvm-project/pull/173756

>From 91e1b64929800f76a031fd9a58bea87e497707c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Fri, 26 Dec 2025 09:07:13 +0100
Subject: [PATCH 1/7] 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 8618979d1eba0..72e9193d89f5d 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 8d5e63f54399bb8ef3049a0beabf67730cbec8c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Fri, 26 Dec 2025 09:06:45 +0100
Subject: [PATCH 2/7] musttail

---
 clang/lib/AST/ByteCode/Interp.cpp            | 44 +++++-----
 clang/lib/AST/ByteCode/Interp.h              |  7 +-
 clang/utils/TableGen/ClangOpcodesEmitter.cpp | 87 ++++++++++++++++++++
 3 files changed, 114 insertions(+), 24 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 889ac1e1a9a7e..af1bf76db865d 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -28,7 +28,8 @@
 using namespace clang;
 using namespace clang::interp;
 
-static bool RetValue(InterpState &S, CodePtr &Pt) {
+__attribute__((preserve_none)) static bool RetValue(InterpState &S,
+                                                    CodePtr &Pt) {
   llvm::report_fatal_error("Interpreter cannot return values");
 }
 
@@ -2324,38 +2325,39 @@ bool FinishInitGlobal(InterpState &S, CodePtr OpPC) {
   return true;
 }
 
-// https://github.com/llvm/llvm-project/issues/102513
-#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
-#pragma optimize("", off)
-#endif
+__attribute__((preserve_none)) static 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
   // to return from this function and thus terminate
   // interpretation.
-  const InterpFrame *StartFrame = S.Current;
   assert(!S.Current->isRoot());
   CodePtr PC = S.Current->getPC();
 
-  // Empty program.
-  if (!PC)
-    return true;
+  return InterpNext(S, PC);
+}
 
-  for (;;) {
-    auto Op = PC.read<Opcode>();
-    CodePtr OpPC = PC;
+#define GET_INTERPFNS_
+#include "Opcodes.inc"
+#undef GET_INTERPFNS_
 
-    switch (Op) {
-#define GET_INTERP
+using InterpFn = __attribute__((preserve_none)) bool (*)(InterpState &,
+                                                         CodePtr &PC);
+
+const InterpFn InterpFunctions[] = {
+#define GET_INTERPFNS
 #include "Opcodes.inc"
-#undef GET_INTERP
-    }
-  }
+#undef GET_INTERPFNS
+};
+
+__attribute__((preserve_none)) static 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)
-#endif
 
 } // namespace interp
 } // namespace clang
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 9accbbc1605a9..c32ade5e125b0 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -221,7 +221,7 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC,
                               const Function *Func);
 
 template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool Ret(InterpState &S, CodePtr &PC) {
+__attribute__((preserve_none)) bool Ret(InterpState &S, CodePtr &PC) {
   const T &Ret = S.Stk.pop<T>();
 
   assert(S.Current);
@@ -243,7 +243,8 @@ bool Ret(InterpState &S, CodePtr &PC) {
   return true;
 }
 
-inline bool RetVoid(InterpState &S, CodePtr &PC) {
+__attribute__((preserve_none)) inline bool RetVoid(InterpState &S,
+                                                   CodePtr &PC) {
   assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
 
   if (!S.checkingPotentialConstantExpression() || S.Current->Caller)
@@ -3040,7 +3041,7 @@ static inline bool ShiftFixedPoint(InterpState &S, 
CodePtr OpPC, bool Left) {
 // NoRet
 
//===----------------------------------------------------------------------===//
 
-inline bool NoRet(InterpState &S, CodePtr OpPC) {
+__attribute__((preserve_none)) inline bool NoRet(InterpState &S, CodePtr OpPC) 
{
   SourceLocation EndLoc = S.Current->getCallee()->getEndLoc();
   S.FFDiag(EndLoc, diag::note_constexpr_no_return);
   return false;
diff --git a/clang/utils/TableGen/ClangOpcodesEmitter.cpp 
b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
index d26122aca46bd..84c4a673e133a 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,89 @@ 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 << "__attribute__((preserve_none))\nstatic bool Interp_" << ID
+       << "(InterpState &S, CodePtr &PC) {\n";
+
+    bool CanReturn = R->getValueAsBit("CanReturn");
+    const auto &Args = R->getValueAsListOfDefs("Args");
+    bool ChangesPC = R->getValueAsBit("ChangesPC");
+
+    if (Args.empty()) {
+      if (CanReturn) {
+        OS << " [[clang::musttail]] return " << N;
+        PrintTypes(OS, TS);
+        OS << "(S, PC);\n";
+        OS << "}\n";
+        return;
+      }
+
+      // OS << "llvm::errs() << \"Calling \" << \"" << N << "\\n\";";//'\n';
+      OS << "  if (!" << N;
+      PrintTypes(OS, TS);
+      OS << "(S, PC))\n";
+      OS << "  return false;\n";
+      OS << "[[clang::musttail]] return InterpNext(S, PC);\n";
+      OS << "}\n";
+      return;
+    }
+
+    OS << "{\n";
+
+    if (!ChangesPC)
+      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";

>From 63ca4e59c10b19eed4b5c2e173b33c4758d889b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Sun, 28 Dec 2025 06:37:20 +0100
Subject: [PATCH 3/7] Revert "Test"

This reverts commit 699354a4146e2ebc7e7c7aa84d14c396a4300508.
---
 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 72e9193d89f5d..8618979d1eba0 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(true),
+          EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp),
           BottomFrame(*this, SourceLocation(), /*Callee=*/nullptr,
                       /*This=*/nullptr,
                       /*CallExpr=*/nullptr, CallRef()),

>From 721741435ec7f21caacd2e6f5e48f0ef5b2a92ef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Fri, 2 Jan 2026 10:38:31 +0100
Subject: [PATCH 4/7] Use a macro for the preserve_none attribute

---
 clang/lib/AST/ByteCode/Interp.cpp            | 30 ++++++++++++--------
 clang/utils/TableGen/ClangOpcodesEmitter.cpp | 22 +++++++-------
 2 files changed, 29 insertions(+), 23 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index af1bf76db865d..dbd0cccd9ba75 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -28,8 +28,13 @@
 using namespace clang;
 using namespace clang::interp;
 
-__attribute__((preserve_none)) static bool RetValue(InterpState &S,
-                                                    CodePtr &Pt) {
+#ifdef __clang__
+#define PRESERVE_NONE [[clang::preserve_none]]
+#else
+#define PRESERVE_NONE
+#endif
+
+PRESERVE_NONE static bool RetValue(InterpState &S, CodePtr &Pt) {
   llvm::report_fatal_error("Interpreter cannot return values");
 }
 
@@ -2325,8 +2330,7 @@ bool FinishInitGlobal(InterpState &S, CodePtr OpPC) {
   return true;
 }
 
-__attribute__((preserve_none)) static bool InterpNext(InterpState &S,
-                                                      CodePtr &PC);
+PRESERVE_NONE static bool InterpNext(InterpState &S, CodePtr &PC);
 
 bool Interpret(InterpState &S) {
   // The current stack frame when we started Interpret().
@@ -2339,21 +2343,23 @@ bool Interpret(InterpState &S) {
   return InterpNext(S, PC);
 }
 
-#define GET_INTERPFNS_
+// The dispatcher functions read the opcode arguments from the
+// bytecode and call the implementation function.
+#define GET_INTERPFN_DISPATCHERS
 #include "Opcodes.inc"
-#undef GET_INTERPFNS_
+#undef GET_INTERPFN_DISPATCHERS
 
-using InterpFn = __attribute__((preserve_none)) bool (*)(InterpState &,
-                                                         CodePtr &PC);
+using InterpFn = bool (*)(InterpState &, CodePtr &PC) PRESERVE_NONE;
 
+// Array of the dispatcher functions defined above.
 const InterpFn InterpFunctions[] = {
-#define GET_INTERPFNS
+#define GET_INTERPFN_LIST
 #include "Opcodes.inc"
-#undef GET_INTERPFNS
+#undef GET_INTERPFN_LIST
 };
 
-__attribute__((preserve_none)) static bool InterpNext(InterpState &S,
-                                                      CodePtr &PC) {
+// Read the next opcode and call the dispatcher function.
+PRESERVE_NONE static bool InterpNext(InterpState &S, CodePtr &PC) {
   auto Op = PC.read<Opcode>();
   auto Fn = InterpFunctions[Op];
   [[clang::musttail]] return Fn(S, PC);
diff --git a/clang/utils/TableGen/ClangOpcodesEmitter.cpp 
b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
index 84c4a673e133a..ed6934509b4da 100644
--- a/clang/utils/TableGen/ClangOpcodesEmitter.cpp
+++ b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
@@ -36,8 +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);
+  void EmitInterpFnList(raw_ostream &OS, StringRef N, const Record *R);
+  void EmitInterpFnDispatchers(raw_ostream &OS, StringRef N, const Record *R);
 
   /// Emits the disassembler.
   void EmitDisasm(raw_ostream &OS, StringRef N, const Record *R);
@@ -94,8 +94,8 @@ void ClangOpcodesEmitter::run(raw_ostream &OS) {
 
     EmitEnum(OS, N, Opcode);
     EmitInterp(OS, N, Opcode);
-    EmitInterpFns(OS, N, Opcode);
-    EmitInterpFns_(OS, N, Opcode);
+    EmitInterpFnList(OS, N, Opcode);
+    EmitInterpFnDispatchers(OS, N, Opcode);
     EmitDisasm(OS, N, Opcode);
     EmitProto(OS, N, Opcode);
     EmitGroup(OS, N, Opcode);
@@ -113,11 +113,11 @@ 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";
+void ClangOpcodesEmitter::EmitInterpFnDispatchers(raw_ostream &OS, StringRef N,
+                                                  const Record *R) {
+  OS << "#ifdef GET_INTERPFN_DISPATCHERS\n";
   Enumerate(R, N, [&](ArrayRef<const Record *> TS, const Twine &ID) {
-    OS << "__attribute__((preserve_none))\nstatic bool Interp_" << ID
+    OS << "PRESERVE_NONE\nstatic bool Interp_" << ID
        << "(InterpState &S, CodePtr &PC) {\n";
 
     bool CanReturn = R->getValueAsBit("CanReturn");
@@ -187,9 +187,9 @@ void ClangOpcodesEmitter::EmitInterpFns_(raw_ostream &OS, 
StringRef N,
   OS << "#endif\n";
 }
 
-void ClangOpcodesEmitter::EmitInterpFns(raw_ostream &OS, StringRef N,
-                                        const Record *R) {
-  OS << "#ifdef GET_INTERPFNS\n";
+void ClangOpcodesEmitter::EmitInterpFnList(raw_ostream &OS, StringRef N,
+                                           const Record *R) {
+  OS << "#ifdef GET_INTERPFN_LIST\n";
   Enumerate(R, N, [&OS](ArrayRef<const Record *>, const Twine &ID) {
     OS << "&Interp_" << ID << ",\n";
   });

>From 6b87a71d03cbe396f6f26044e444047641b7eee5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Fri, 2 Jan 2026 12:10:49 +0100
Subject: [PATCH 5/7] Use a macro for musttail attribute

---
 clang/lib/AST/ByteCode/Interp.cpp            | 10 ++++++++
 clang/utils/TableGen/ClangOpcodesEmitter.cpp | 27 +++++++++-----------
 2 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index dbd0cccd9ba75..b391171f86682 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -34,6 +34,16 @@ using namespace clang::interp;
 #define PRESERVE_NONE
 #endif
 
+#if defined(_MSC_VER) && !defined(__clang__)
+#ifdef NDEBUG
+#define MUSTTAIL [[msvc::musttail]]
+#else
+#define MUSTTAIL
+#endif
+#else
+#define MUSTTAIL [[clang::musttail]]
+#endif
+
 PRESERVE_NONE static bool RetValue(InterpState &S, CodePtr &Pt) {
   llvm::report_fatal_error("Interpreter cannot return values");
 }
diff --git a/clang/utils/TableGen/ClangOpcodesEmitter.cpp 
b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
index ed6934509b4da..c89dc632d1137 100644
--- a/clang/utils/TableGen/ClangOpcodesEmitter.cpp
+++ b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
@@ -126,27 +126,26 @@ void 
ClangOpcodesEmitter::EmitInterpFnDispatchers(raw_ostream &OS, StringRef N,
 
     if (Args.empty()) {
       if (CanReturn) {
-        OS << " [[clang::musttail]] return " << N;
+        OS << " MUSTTAIL return " << N;
         PrintTypes(OS, TS);
         OS << "(S, PC);\n";
         OS << "}\n";
         return;
       }
 
-      // OS << "llvm::errs() << \"Calling \" << \"" << N << "\\n\";";//'\n';
       OS << "  if (!" << N;
       PrintTypes(OS, TS);
       OS << "(S, PC))\n";
-      OS << "  return false;\n";
-      OS << "[[clang::musttail]] return InterpNext(S, PC);\n";
+      OS << "    return false;\n";
+      OS << "  MUSTTAIL return InterpNext(S, PC);\n";
       OS << "}\n";
       return;
     }
 
-    OS << "{\n";
+    OS << "  {\n";
 
     if (!ChangesPC)
-      OS << "  CodePtr OpPC = PC;\n";
+      OS << "    CodePtr OpPC = PC;\n";
 
     // Emit calls to read arguments.
     for (size_t I = 0, N = Args.size(); I < N; ++I) {
@@ -154,17 +153,16 @@ void 
ClangOpcodesEmitter::EmitInterpFnDispatchers(raw_ostream &OS, StringRef N,
       bool AsRef = Arg->getValueAsBit("AsRef");
 
       if (AsRef)
-        OS << "  const auto &V" << I;
+        OS << "    const auto &V" << I;
       else
-        OS << "  const auto V" << I;
+        OS << "    const auto V" << I;
       OS << " = ";
       OS << "ReadArg<" << Arg->getValueAsString("Name") << ">(S, PC);\n";
     }
 
-    OS << "  if (!" << N;
+    OS << "    if (!" << N;
     PrintTypes(OS, TS);
     OS << "(S";
-    // OS << ", OpPC";
     if (ChangesPC)
       OS << ", PC";
     else
@@ -172,15 +170,14 @@ void 
ClangOpcodesEmitter::EmitInterpFnDispatchers(raw_ostream &OS, StringRef N,
     for (size_t I = 0, N = Args.size(); I < N; ++I)
       OS << ", V" << I;
     OS << "))\n";
-    OS << "    return false;\n";
+    OS << "      return false;\n";
 
-    OS << "}\n";
+    OS << "  }\n";
 
     if (!CanReturn)
-      OS << "[[clang::musttail]] return InterpNext(S, PC);\n";
+      OS << "  MUSTTAIL return InterpNext(S, PC);\n";
     else
-      OS << " return true;\n";
-    // OS << "  return false;\n";
+      OS << "  return true;\n";
 
     OS << "}\n";
   });

>From fc25a33c09dca74ab3869bfa0343ecc3caad23ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Fri, 2 Jan 2026 12:18:09 +0100
Subject: [PATCH 6/7] Move preserve_none macro to Interp.h

---
 clang/lib/AST/ByteCode/Interp.cpp |  6 ------
 clang/lib/AST/ByteCode/Interp.h   | 14 +++++++++-----
 2 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index b391171f86682..27d24f867bdb2 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -28,12 +28,6 @@
 using namespace clang;
 using namespace clang::interp;
 
-#ifdef __clang__
-#define PRESERVE_NONE [[clang::preserve_none]]
-#else
-#define PRESERVE_NONE
-#endif
-
 #if defined(_MSC_VER) && !defined(__clang__)
 #ifdef NDEBUG
 #define MUSTTAIL [[msvc::musttail]]
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index c32ade5e125b0..e77b389e4fb12 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -35,6 +35,12 @@
 #include "llvm/ADT/APSInt.h"
 #include <type_traits>
 
+#ifdef __clang__
+#define PRESERVE_NONE [[clang::preserve_none]]
+#else
+#define PRESERVE_NONE
+#endif
+
 namespace clang {
 namespace interp {
 
@@ -221,7 +227,7 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC,
                               const Function *Func);
 
 template <PrimType Name, class T = typename PrimConv<Name>::T>
-__attribute__((preserve_none)) bool Ret(InterpState &S, CodePtr &PC) {
+bool Ret(InterpState &S, CodePtr &PC) PRESERVE_NONE {
   const T &Ret = S.Stk.pop<T>();
 
   assert(S.Current);
@@ -243,8 +249,7 @@ __attribute__((preserve_none)) bool Ret(InterpState &S, 
CodePtr &PC) {
   return true;
 }
 
-__attribute__((preserve_none)) inline bool RetVoid(InterpState &S,
-                                                   CodePtr &PC) {
+inline bool RetVoid(InterpState &S, CodePtr &PC) PRESERVE_NONE {
   assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
 
   if (!S.checkingPotentialConstantExpression() || S.Current->Caller)
@@ -3040,8 +3045,7 @@ static inline bool ShiftFixedPoint(InterpState &S, 
CodePtr OpPC, bool Left) {
 
//===----------------------------------------------------------------------===//
 // NoRet
 
//===----------------------------------------------------------------------===//
-
-__attribute__((preserve_none)) inline bool NoRet(InterpState &S, CodePtr OpPC) 
{
+inline bool NoRet(InterpState &S, CodePtr OpPC) PRESERVE_NONE {
   SourceLocation EndLoc = S.Current->getCallee()->getEndLoc();
   S.FFDiag(EndLoc, diag::note_constexpr_no_return);
   return false;

>From 1af16b9229ab7894a501bf07694f0ce449c40abf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Fri, 2 Jan 2026 17:04:03 +0100
Subject: [PATCH 7/7] Use tailscalls in builtin_constant_p as well

---
 clang/lib/AST/ByteCode/Compiler.cpp          |  11 +-
 clang/lib/AST/ByteCode/EvalEmitter.cpp       |   5 +
 clang/lib/AST/ByteCode/Interp.cpp            | 146 ++++++++++---------
 clang/lib/AST/ByteCode/Interp.h              |  35 ++++-
 clang/lib/AST/ByteCode/InterpState.h         |   3 +
 clang/lib/AST/ByteCode/Opcodes.td            |   3 +
 clang/utils/TableGen/ClangOpcodesEmitter.cpp |  66 +--------
 7 files changed, 127 insertions(+), 142 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 4daab0702f147..e9c4539b8a2e9 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2529,7 +2529,7 @@ bool Compiler<Emitter>::VisitAbstractConditionalOperator(
   LabelTy LabelFalse = this->getLabel(); // Label for the false expr.
 
   if (IsBcpCall) {
-    if (!this->emitStartSpeculation(E))
+    if (!this->emitPushIgnoreDiags(E))
       return false;
   }
 
@@ -2560,8 +2560,11 @@ bool Compiler<Emitter>::VisitAbstractConditionalOperator(
   this->fallthrough(LabelEnd);
   this->emitLabel(LabelEnd);
 
-  if (IsBcpCall)
-    return this->emitEndSpeculation(E);
+  if (IsBcpCall) {
+    if (!this->emitPopIgnoreDiags(E))
+      return false;
+  }
+
   return true;
 }
 
@@ -5079,9 +5082,9 @@ bool Compiler<Emitter>::VisitBuiltinCallExpr(const 
CallExpr *E,
     LabelTy EndLabel = this->getLabel();
     if (!this->speculate(E, EndLabel))
       return false;
-    this->fallthrough(EndLabel);
     if (!this->emitEndSpeculation(E))
       return false;
+    this->fallthrough(EndLabel);
     if (DiscardResult)
       return this->emitPop(classifyPrim(E), E);
     return true;
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp 
b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index cf3cc1b17133c..2e7eed285a76a 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -11,6 +11,7 @@
 #include "IntegralAP.h"
 #include "Interp.h"
 #include "clang/AST/DeclCXX.h"
+#include "llvm/ADT/ScopeExit.h"
 
 using namespace clang;
 using namespace clang::interp;
@@ -157,6 +158,10 @@ bool EvalEmitter::fallthrough(const LabelTy &Label) {
 bool EvalEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) {
   if (!isActive())
     return true;
+
+  PushIgnoreDiags(S, OpPC);
+  auto _ = llvm::make_scope_exit([&]() { PopIgnoreDiags(S, OpPC); });
+
   size_t StackSizeBefore = S.Stk.size();
   const Expr *Arg = E->getArg(0);
   if (!this->visit(Arg)) {
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 27d24f867bdb2..857b2bcf372c5 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -65,76 +65,6 @@ static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
   return true;
 }
 
-// https://github.com/llvm/llvm-project/issues/102513
-#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
-#pragma optimize("", off)
-#endif
-// FIXME: We have the large switch over all opcodes here again, and in
-// Interpret().
-static bool BCP(InterpState &S, CodePtr &RealPC, int32_t Offset, PrimType PT) {
-  [[maybe_unused]] CodePtr PCBefore = RealPC;
-  size_t StackSizeBefore = S.Stk.size();
-
-  auto SpeculativeInterp = [&S, RealPC]() -> bool {
-    const InterpFrame *StartFrame = S.Current;
-    CodePtr PC = RealPC;
-
-    for (;;) {
-      auto Op = PC.read<Opcode>();
-      if (Op == OP_EndSpeculation)
-        return true;
-      CodePtr OpPC = PC;
-
-      switch (Op) {
-#define GET_INTERP
-#include "Opcodes.inc"
-#undef GET_INTERP
-      }
-    }
-    llvm_unreachable("We didn't see an EndSpeculation op?");
-  };
-
-  if (SpeculativeInterp()) {
-    if (PT == PT_Ptr) {
-      const auto &Ptr = S.Stk.pop<Pointer>();
-      assert(S.Stk.size() == StackSizeBefore);
-      S.Stk.push<Integral<32, true>>(
-          Integral<32, true>::from(CheckBCPResult(S, Ptr)));
-    } else {
-      // Pop the result from the stack and return success.
-      TYPE_SWITCH(PT, S.Stk.pop<T>(););
-      assert(S.Stk.size() == StackSizeBefore);
-      S.Stk.push<Integral<32, true>>(Integral<32, true>::from(1));
-    }
-  } else {
-    if (!S.inConstantContext())
-      return Invalid(S, RealPC);
-
-    S.Stk.clearTo(StackSizeBefore);
-    S.Stk.push<Integral<32, true>>(Integral<32, true>::from(0));
-  }
-
-  // RealPC should not have been modified.
-  assert(*RealPC == *PCBefore);
-
-  // Jump to end label. This is a little tricker than just RealPC += Offset
-  // because our usual jump instructions don't have any arguments, to the 
offset
-  // we get is a little too much and we need to subtract the size of the
-  // bool and PrimType arguments again.
-  int32_t ParamSize = align(sizeof(PrimType));
-  assert(Offset >= ParamSize);
-  RealPC += Offset - ParamSize;
-
-  [[maybe_unused]] CodePtr PCCopy = RealPC;
-  assert(PCCopy.read<Opcode>() == OP_EndSpeculation);
-
-  return true;
-}
-// https://github.com/llvm/llvm-project/issues/102513
-#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG)
-#pragma optimize("", on)
-#endif
-
 static void diagnoseMissingInitializer(InterpState &S, CodePtr OpPC,
                                        const ValueDecl *VD) {
   const SourceInfo &E = S.Current->getSource(OpPC);
@@ -263,6 +193,9 @@ static bool CheckGlobal(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr) {
 
 namespace clang {
 namespace interp {
+PRESERVE_NONE static bool BCP(InterpState &S, CodePtr &RealPC, int32_t Offset,
+                              PrimType PT);
+
 static void popArg(InterpState &S, const Expr *Arg) {
   PrimType Ty = S.getContext().classify(Arg).value_or(PT_Ptr);
   TYPE_SWITCH(Ty, S.Stk.discard<T>());
@@ -2369,5 +2302,78 @@ PRESERVE_NONE static bool InterpNext(InterpState &S, 
CodePtr &PC) {
   [[clang::musttail]] return Fn(S, PC);
 }
 
+/// This is used to implement speculative execution via __builtin_constant_p
+/// when we generate bytecode.
+/// The setup here is that we use the same tailcall mechanism for speculative
+/// evaluation that we use for the regular one.
+/// Since each speculative execution ends with an EndSpeculation opcode,
+/// that one does NOT call InterpNext() but simply returns true.
+/// This way, we return back to this function when we see an EndSpeculation,
+/// OR (of course), when we encounter an error and one of the opcodes
+/// returns false.
+PRESERVE_NONE static bool BCP(InterpState &S, CodePtr &RealPC, int32_t Offset,
+                              PrimType PT) {
+  [[maybe_unused]] CodePtr PCBefore = RealPC;
+  size_t StackSizeBefore = S.Stk.size();
+
+  // Speculation depth must be at least 1 here, since we must have
+  // passed a StartSpeculation op before.
+  [[maybe_unused]] unsigned DepthBefore = S.SpeculationDepth;
+  assert(DepthBefore >= 1);
+
+  CodePtr PC = RealPC;
+  auto SpeculativeInterp = [&S, &PC]() -> bool {
+    // Ignore diagnostics during speculative execution.
+    PushIgnoreDiags(S, PC);
+    auto _ = llvm::make_scope_exit([&]() { PopIgnoreDiags(S, PC); });
+
+    auto Op = PC.read<Opcode>();
+    auto Fn = InterpFunctions[Op];
+    return Fn(S, PC);
+  };
+
+  if (SpeculativeInterp()) {
+    // Speculation must've ended naturally via a EndSpeculation opcode.
+    assert(S.SpeculationDepth == DepthBefore - 1);
+    if (PT == PT_Ptr) {
+      const auto &Ptr = S.Stk.pop<Pointer>();
+      assert(S.Stk.size() == StackSizeBefore);
+      S.Stk.push<Integral<32, true>>(
+          Integral<32, true>::from(CheckBCPResult(S, Ptr)));
+    } else {
+      // Pop the result from the stack and return success.
+      TYPE_SWITCH(PT, S.Stk.discard<T>(););
+      assert(S.Stk.size() == StackSizeBefore);
+      S.Stk.push<Integral<32, true>>(Integral<32, true>::from(1));
+    }
+  } else {
+    // End the speculation manually since we didn't call EndSpeculation
+    // naturally.
+    EndSpeculation(S, RealPC);
+
+    if (!S.inConstantContext())
+      return Invalid(S, RealPC);
+
+    S.Stk.clearTo(StackSizeBefore);
+    S.Stk.push<Integral<32, true>>(Integral<32, true>::from(0));
+  }
+
+  // RealPC should not have been modified.
+  assert(*RealPC == *PCBefore);
+
+  // We have already evaluated this speculation's EndSpeculation opcode.
+  assert(S.SpeculationDepth == DepthBefore - 1);
+
+  // Jump to end label. This is a little tricker than just RealPC += Offset
+  // because our usual jump instructions don't have any arguments, to the 
offset
+  // we get is a little too much and we need to subtract the size of the
+  // bool and PrimType arguments again.
+  int32_t ParamSize = align(sizeof(PrimType));
+  assert(Offset >= ParamSize);
+  RealPC += Offset - ParamSize;
+
+  return true;
+}
+
 } // namespace interp
 } // namespace clang
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index e77b389e4fb12..0e63c721f12af 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -33,6 +33,7 @@
 #include "clang/AST/Expr.h"
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/ScopeExit.h"
 #include <type_traits>
 
 #ifdef __clang__
@@ -3263,26 +3264,44 @@ inline bool Unsupported(InterpState &S, CodePtr OpPC) {
   return false;
 }
 
-inline bool StartSpeculation(InterpState &S, CodePtr OpPC) {
-  ++S.SpeculationDepth;
-  if (S.SpeculationDepth != 1)
+inline bool PushIgnoreDiags(InterpState &S, CodePtr OpPC) {
+  ++S.DiagIgnoreDepth;
+  if (S.DiagIgnoreDepth != 1)
     return true;
-
   assert(S.PrevDiags == nullptr);
   S.PrevDiags = S.getEvalStatus().Diag;
   S.getEvalStatus().Diag = nullptr;
+  assert(!S.diagnosing());
   return true;
 }
-inline bool EndSpeculation(InterpState &S, CodePtr OpPC) {
-  assert(S.SpeculationDepth != 0);
-  --S.SpeculationDepth;
-  if (S.SpeculationDepth == 0) {
+
+inline bool PopIgnoreDiags(InterpState &S, CodePtr OpPC) {
+  --S.DiagIgnoreDepth;
+  if (S.DiagIgnoreDepth == 0) {
     S.getEvalStatus().Diag = S.PrevDiags;
     S.PrevDiags = nullptr;
   }
   return true;
 }
 
+inline bool StartSpeculation(InterpState &S, CodePtr OpPC) {
+#ifndef NDEBUG
+  ++S.SpeculationDepth;
+#endif
+  return true;
+}
+
+// This is special-cased in the tablegen opcode emitter.
+// Its dispatch function will NOT call InterpNext
+// and instead simply return true.
+inline bool EndSpeculation(InterpState &S, CodePtr OpPC) {
+#ifndef NDEBUG
+  assert(S.SpeculationDepth != 0);
+  --S.SpeculationDepth;
+#endif
+  return true;
+}
+
 inline bool PushCC(InterpState &S, CodePtr OpPC, bool Value) {
   S.ConstantContextOverride = Value;
   return true;
diff --git a/clang/lib/AST/ByteCode/InterpState.h 
b/clang/lib/AST/ByteCode/InterpState.h
index e2e4d5c985f93..1591d90715131 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -182,7 +182,10 @@ class InterpState final : public State, public 
SourceMapper {
   const VarDecl *EvaluatingDecl = nullptr;
   /// Things needed to do speculative execution.
   SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr;
+#ifndef NDEBUG
   unsigned SpeculationDepth = 0;
+#endif
+  unsigned DiagIgnoreDepth = 0;
   std::optional<bool> ConstantContextOverride;
 
   llvm::SmallVector<
diff --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index 6e768793fcfcf..81e7bc3813a4e 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -178,6 +178,9 @@ def Jt : JumpOpcode;
 // [Bool] -> [], jumps if false.
 def Jf : JumpOpcode;
 
+def PushIgnoreDiags : Opcode;
+def PopIgnoreDiags : Opcode;
+
 def StartSpeculation : Opcode;
 def EndSpeculation : Opcode;
 def BCP : Opcode {
diff --git a/clang/utils/TableGen/ClangOpcodesEmitter.cpp 
b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
index c89dc632d1137..4dc21f4c6c383 100644
--- a/clang/utils/TableGen/ClangOpcodesEmitter.cpp
+++ b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
@@ -34,8 +34,6 @@ class ClangOpcodesEmitter {
   /// The name is obtained by concatenating the name with the list of types.
   void EmitEnum(raw_ostream &OS, StringRef N, const Record *R);
 
-  /// Emits the switch case and the invocation in the interpreter.
-  void EmitInterp(raw_ostream &OS, StringRef N, const Record *R);
   void EmitInterpFnList(raw_ostream &OS, StringRef N, const Record *R);
   void EmitInterpFnDispatchers(raw_ostream &OS, StringRef N, const Record *R);
 
@@ -93,7 +91,6 @@ void ClangOpcodesEmitter::run(raw_ostream &OS) {
       N = Opcode->getName();
 
     EmitEnum(OS, N, Opcode);
-    EmitInterp(OS, N, Opcode);
     EmitInterpFnList(OS, N, Opcode);
     EmitInterpFnDispatchers(OS, N, Opcode);
     EmitDisasm(OS, N, Opcode);
@@ -120,6 +117,12 @@ void 
ClangOpcodesEmitter::EmitInterpFnDispatchers(raw_ostream &OS, StringRef N,
     OS << "PRESERVE_NONE\nstatic bool Interp_" << ID
        << "(InterpState &S, CodePtr &PC) {\n";
 
+    if (ID.str() == "EndSpeculation") {
+      OS << "    return EndSpeculation(S, PC);\n";
+      OS << "}\n";
+      return;
+    }
+
     bool CanReturn = R->getValueAsBit("CanReturn");
     const auto &Args = R->getValueAsListOfDefs("Args");
     bool ChangesPC = R->getValueAsBit("ChangesPC");
@@ -193,63 +196,6 @@ void ClangOpcodesEmitter::EmitInterpFnList(raw_ostream 
&OS, StringRef N,
   OS << "#endif\n";
 }
 
-void ClangOpcodesEmitter::EmitInterp(raw_ostream &OS, StringRef N,
-                                     const Record *R) {
-  OS << "#ifdef GET_INTERP\n";
-
-  Enumerate(R, N,
-            [this, R, &OS, &N](ArrayRef<const Record *> TS, const Twine &ID) {
-              bool CanReturn = R->getValueAsBit("CanReturn");
-              bool ChangesPC = R->getValueAsBit("ChangesPC");
-              const auto &Args = R->getValueAsListOfDefs("Args");
-
-              OS << "case OP_" << ID << ": {\n";
-
-              if (CanReturn)
-                OS << "  bool DoReturn = (S.Current == StartFrame);\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";
-              }
-
-              // Emit a call to the template method and pass arguments.
-              OS << "  if (!" << N;
-              PrintTypes(OS, TS);
-              OS << "(S";
-              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";
-
-              // Bail out if interpreter returned.
-              if (CanReturn) {
-                OS << "  if (!S.Current || S.Current->isRoot())\n";
-                OS << "    return true;\n";
-
-                OS << "  if (DoReturn)\n";
-                OS << "    return true;\n";
-              }
-
-              OS << "  continue;\n";
-              OS << "}\n";
-            });
-  OS << "#endif\n";
-}
-
 void ClangOpcodesEmitter::EmitDisasm(raw_ostream &OS, StringRef N,
                                      const Record *R) {
   OS << "#ifdef GET_DISASM\n";

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

Reply via email to