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

>From 6a3611348e9257b22ee2f8efb80432c407144378 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Tue, 1 Sep 2026 07:15:28 +0200
Subject: [PATCH] FrameAlloc

---
 clang/lib/AST/ByteCode/Context.cpp      |  22 +--
 clang/lib/AST/ByteCode/Context.h        |   3 +
 clang/lib/AST/ByteCode/EvalEmitter.cpp  |   4 +-
 clang/lib/AST/ByteCode/EvalEmitter.h    |   4 +-
 clang/lib/AST/ByteCode/FrameAllocator.h | 206 ++++++++++++++++++++++++
 clang/lib/AST/ByteCode/Interp.cpp       |  39 +----
 clang/lib/AST/ByteCode/Interp.h         |  15 +-
 clang/lib/AST/ByteCode/InterpFrame.cpp  |   8 +-
 clang/lib/AST/ByteCode/InterpFrame.h    |  14 --
 clang/lib/AST/ByteCode/InterpState.cpp  |  24 ++-
 clang/lib/AST/ByteCode/InterpState.h    |  32 +++-
 11 files changed, 277 insertions(+), 94 deletions(-)
 create mode 100644 clang/lib/AST/ByteCode/FrameAllocator.h

diff --git a/clang/lib/AST/ByteCode/Context.cpp 
b/clang/lib/AST/ByteCode/Context.cpp
index ab53ec1eda1db..d96bc68217576 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -62,7 +62,7 @@ void Context::isPotentialConstantExprUnevaluated(State 
&Parent, const Expr *E,
   assert(Stk.empty());
   ++EvalID;
   size_t StackSizeBefore = Stk.size();
-  Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
   if (!C.interpretCall(FD, E)) {
     C.cleanup();
@@ -74,7 +74,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, 
APValue &Result) {
   ++EvalID;
   bool Recursing = !Stk.empty();
   size_t StackSizeBefore = Stk.size();
-  Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
   auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue());
 
@@ -105,7 +105,7 @@ bool Context::evaluate(State &Parent, const Expr *E, 
APValue &Result,
   ++EvalID;
   bool Recursing = !Stk.empty();
   size_t StackSizeBefore = Stk.size();
-  Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
   auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/false,
                              /*DestroyToplevelScope=*/true);
@@ -134,7 +134,7 @@ bool Context::evaluateAsInitializer(State &Parent, const 
VarDecl *VD,
   ++EvalID;
   bool Recursing = !Stk.empty();
   size_t StackSizeBefore = Stk.size();
-  Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
   bool CheckGlobalInitialized =
       shouldBeGloballyIndexed(VD) &&
@@ -164,7 +164,7 @@ bool Context::evaluateAsInitializer(State &Parent, const 
VarDecl *VD,
 bool Context::evaluateDestruction(State &Parent, const VarDecl *VD,
                                   APValue Value) {
   assert(Stk.empty());
-  Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
   auto Res = C.interpretDestructor(VD, Value);
 
@@ -183,7 +183,7 @@ template <typename ResultT>
 bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr,
                                  const Expr *PtrExpr, ResultT &Result) {
   assert(Stk.empty());
-  Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
   // Evaluate size value.
   APValue SizeValue;
@@ -282,7 +282,7 @@ bool Context::evaluateCharRange(State &Parent, const Expr 
*SizeExpr,
 bool Context::evaluateString(State &Parent, const Expr *E,
                              std::string &Result) {
   assert(Stk.empty());
-  Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
   auto PtrRes = C.interpretAsPointer(E, [&](InterpState &S, CodePtr OpPC,
                                             const Pointer &Ptr) {
@@ -346,7 +346,7 @@ bool Context::evaluateString(State &Parent, const Expr *E,
 
 std::optional<uint64_t> Context::evaluateStrlen(State &Parent, const Expr *E) {
   assert(Stk.empty());
-  Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
   std::optional<uint64_t> Result;
   auto PtrRes = C.interpretAsPointer(E, [&](InterpState &S, CodePtr OpPC,
@@ -415,7 +415,7 @@ std::optional<uint64_t> 
Context::tryEvaluateObjectSize(State &Parent,
                                                        unsigned Kind,
                                                        bool IsDynamic) {
   assert(Stk.empty());
-  Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
 
   std::optional<uint64_t> Result;
   auto PtrRes = C.interpretAsLValuePointer(E, [&](InterpState &S, CodePtr OpPC,
@@ -452,7 +452,7 @@ Context::evaluateWithSubstitution(State &Parent, const 
FunctionDecl *Callee,
   }
 
   assert(Stk.empty());
-  Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+  Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc);
   std::optional<bool> Result =
       C.interpretWithSubstitutions(Callee, Args, This, Condition);
 
@@ -595,7 +595,7 @@ const llvm::fltSemantics 
&Context::getFloatSemantics(QualType T) const {
 
 bool Context::Run(State &Parent, const Function *Func) {
   auto Memory = std::make_unique<char[]>(InterpFrame::allocSize(Func));
-  InterpState State(Parent, *P, Stk, *this, Func);
+  InterpState State(Parent, *P, Stk, FrameAlloc, *this, Func);
   InterpFrame *Frame = new (Memory.get()) InterpFrame(
       State, Func, /*Caller=*/nullptr, CodePtr(), Func->getArgSize());
   State.Current = Frame;
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index 2a250ad119842..a555896a822a5 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -16,6 +16,7 @@
 #ifndef LLVM_CLANG_AST_INTERP_CONTEXT_H
 #define LLVM_CLANG_AST_INTERP_CONTEXT_H
 
+#include "FrameAllocator.h"
 #include "InterpStack.h"
 #include "clang/AST/ASTContext.h"
 
@@ -200,6 +201,8 @@ class Context final {
   ASTContext &Ctx;
   /// Interpreter stack, shared across invocations.
   InterpStack Stk;
+  /// (Function) frame allocator, also shared.
+  FrameAllocator FrameAlloc;
   /// Constexpr program.
   std::unique_ptr<Program> P;
   /// ID identifying an evaluation.
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp 
b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index 80eb593c2f6c4..d5a27e8c2a0e7 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -18,8 +18,8 @@ using namespace clang;
 using namespace clang::interp;
 
 EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
-                         InterpStack &Stk)
-    : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {}
+                         InterpStack &Stk, FrameAllocator &FA)
+    : Ctx(Ctx), P(P), S(Parent, P, Stk, FA, Ctx, this), EvalResult(&Ctx) {}
 
 EvalEmitter::~EvalEmitter() {
   for (auto &V : Locals) {
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h 
b/clang/lib/AST/ByteCode/EvalEmitter.h
index a80b573a7df8b..1818088053f71 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -24,6 +24,7 @@ namespace interp {
 class Context;
 class Function;
 class InterpStack;
+class FrameAllocator;
 class Program;
 enum Opcode : uint32_t;
 
@@ -61,7 +62,8 @@ class EvalEmitter : public SourceMapper {
   SourceInfo getSource(CodePtr PC) const override { return CurrentSource; }
 
 protected:
-  EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk);
+  EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk,
+              FrameAllocator &FrameAlloc);
 
   virtual ~EvalEmitter();
 
diff --git a/clang/lib/AST/ByteCode/FrameAllocator.h 
b/clang/lib/AST/ByteCode/FrameAllocator.h
new file mode 100644
index 0000000000000..1d7f7f203c909
--- /dev/null
+++ b/clang/lib/AST/ByteCode/FrameAllocator.h
@@ -0,0 +1,206 @@
+//===-------------------- FrameAllocator.h ----------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_AST_INTERP_FRAME_ALLOCATOR_H
+#define LLVM_CLANG_AST_INTERP_FRAME_ALLOCATOR_H
+
+#include "llvm/Support/Compiler.h"
+#include <algorithm>
+#include <cassert>
+#include <new>
+
+namespace clang {
+namespace interp {
+
+// Set this to 1 to collect some light statistics.
+// Print via printStats().
+#define COLLECT_STATS 0
+
+/// Allocator for function frames.
+///
+/// The function frame size includes the size reserved for local variables.
+/// Function frames are allocated strictly in a LIFO manner, i.e. the last
+/// created frame is the first frame that is destroyed.
+///
+/// Since the address a function frame is allocated in needs to stay stable
+/// during the lifetime of the frame, we allocate them here in chunks.
+///
+/// A chunk is of (at least) MinChunkSize size and only gets deallocated once 
it
+/// is empty AND the previous chunk is also empty.
+///
+class FrameAllocator final {
+private:
+  struct Chunk {
+    Chunk *Prev = nullptr;
+    unsigned Size;
+    unsigned Used = 0;
+    alignas(sizeof(void *)) char Memory[1];
+
+    Chunk(unsigned Size) : Size(Size) {}
+    unsigned bytesUnused() const { return Size - Used; }
+  };
+  static constexpr unsigned MinChunkSize = (4u * 1024u) - sizeof(Chunk);
+
+  Chunk *Tail = nullptr;
+#if COLLECT_STATS
+  size_t MaxSize = 0;
+  unsigned LargestFrame = 0;
+  unsigned NumFrames = 0;
+  unsigned NumAllocs = 0;
+#endif
+
+#ifndef NDEBUG
+  llvm::SmallVector<unsigned> FrameSizes;
+#endif
+
+public:
+  FrameAllocator() = default;
+  FrameAllocator(FrameAllocator &) = delete;
+  FrameAllocator(FrameAllocator &&) = delete;
+  ~FrameAllocator() {
+    while (Tail)
+      deallocTail();
+  }
+
+  char *reserve(unsigned Size) {
+    if (LLVM_UNLIKELY(!Tail))
+      allocateNewChunk(std::max(Size, MinChunkSize));
+    assert(Tail);
+
+#ifndef NDEBUG
+    FrameSizes.push_back(Size);
+#endif
+
+    char *Mem;
+    if (Chunk *C = getChunkToUse(Size); C->bytesUnused() >= Size) {
+      Mem = &C->Memory[C->Used];
+      C->Used += Size;
+    } else {
+      // We need to allocate a new chunk. If the requested size is larger than
+      // the minimum, use that.
+      allocateNewChunk(std::max(Size, MinChunkSize));
+      Tail->Used += Size;
+      Mem = Tail->Memory;
+    }
+
+#if COLLECT_STATS
+    LargestFrame = std::max(Size, LargestFrame);
+    MaxSize = std::max(MaxSize, countAllBytes());
+    ++NumFrames;
+#endif
+
+    return Mem;
+  }
+
+  /// Pop the memory of the last function frame that was added.
+  /// The passed \c FrameSize needs to match the latest size passed to
+  /// reserve(). If it doesn't, bad things will happen.
+  void pop(unsigned FrameSize) {
+#ifndef NDEBUG
+    assert(FrameSize == FrameSizes.back());
+#endif
+    // Frame destructor must've already been called.
+    assert(Tail);
+    Chunk *C = Tail->Used == 0 ? Tail->Prev : Tail;
+    assert(C);
+    assert(FrameSize <= C->Used);
+    C->Used -= FrameSize;
+
+    // Deallocate the tail chunk *if* it is empty _and_ the previous chunk is
+    // also empty.
+    // Since we create chunks specicially for large frames, we need to loop
+    // here.
+    while (Tail->Used == 0 && Tail->Prev && Tail->Prev->Used == 0)
+      deallocTail();
+
+#ifndef NDEBUG
+    FrameSizes.pop_back();
+#endif
+  }
+
+private:
+  /// Return the chunk to use to allocate a new frame into.
+  /// This is not always this->Tail, since Tail might be empty AND have a
+  /// previous chunk. In that case, we use the previous chunk, if it does have
+  /// \p Size bytes left.
+  Chunk *getChunkToUse(unsigned Size) {
+    assert(Tail);
+    if (Tail->Used == 0 && Tail->Prev && Tail->Prev->bytesUnused() >= Size)
+      return Tail->Prev;
+    return Tail;
+  }
+
+  void allocateNewChunk(unsigned Size) {
+    char *Mem = new char[sizeof(Chunk) + Size];
+    auto *C = new (Mem) Chunk(Size);
+    C->Prev = Tail;
+    Tail = C;
+
+    assert(Tail);
+
+#if COLLECT_STATS
+    ++NumAllocs;
+#endif
+  }
+
+  void deallocTail() {
+    assert(Tail);
+    Chunk *C = Tail;
+    Tail = Tail->Prev;
+    delete[] reinterpret_cast<char *>(C);
+  }
+
+#if COLLECT_STATS
+  size_t countAllBytes() const {
+    size_t Result = 0;
+    Chunk *C = Tail;
+    while (C) {
+      Result += C->Size + sizeof(Chunk);
+      C = C->Prev;
+    }
+    return Result;
+  }
+  void printStats() const {
+    llvm::errs() << "*** FrameAllocator stats ***\n";
+    if (!Tail) {
+      llvm::errs() << "empty\n";
+      return;
+    }
+
+    Chunk *C = Tail;
+    unsigned N = 0;
+    while (C) {
+      llvm::errs() << "Chunk " << N << ": " << C->Used << " / " << C->Size
+                   << " (";
+      double Percentage =
+          (static_cast<double>(C->Used) / static_cast<double>(C->Size)) * 100;
+      llvm::errs() << llvm::formatv("{0:2}", Percentage) << "%)\n";
+      ++N;
+      C = C->Prev;
+    }
+    llvm::errs() << "Max allocated bytes: " << MaxSize << '\n';
+    llvm::errs() << "Largest frame: " << LargestFrame << '\n';
+    llvm::errs() << "Frames created: " << NumFrames << '\n';
+    llvm::errs() << "Allocations: " << NumAllocs << '\n';
+
+    llvm::errs() << "Occupancy: ";
+    size_t AllUsed = 0;
+    size_t AllSize = 0;
+    for (Chunk *C = Tail; C; C = C->Prev) {
+      AllUsed += C->Used;
+      AllSize += C->Size;
+    }
+    double Occupancy =
+        (static_cast<double>(AllUsed) / static_cast<double>(AllSize)) * 100;
+    llvm::errs() << llvm::formatv("{0:2}", Occupancy) << "%\n";
+  }
+#endif
+};
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index a41cc1f5a564b..67aa62808a068 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1931,22 +1931,13 @@ bool CallVar(InterpState &S, CodePtr OpPC, const 
Function *Func,
   if (!CheckCallDepth(S, OpPC))
     return false;
 
-  auto *Memory = new char[InterpFrame::allocSize(Func)];
-  auto *NewFrame = new (Memory) InterpFrame(S, Func, S.PC, VarArgSize);
-  InterpFrame *FrameBefore = S.Current;
+  InterpFrame *NewFrame = S.allocFrame(Func, S.PC, VarArgSize);
   S.Current = NewFrame;
 
   InterpStateCCOverride CCOverride(S, Func->isImmediate());
-  if (Interpret(S)) {
-    assert(S.Current == FrameBefore);
-    return true;
-  }
-
-  InterpFrame::free(NewFrame);
-  // Interpreting the function failed somehow. Reset to
-  // previous state.
-  S.Current = FrameBefore;
-  return false;
+  bool Success = Interpret(S);
+  S.resetCurrentFrame();
+  return Success;
 }
 
 bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
@@ -2025,9 +2016,7 @@ bool Call(InterpState &S, CodePtr OpPC, const Function 
*Func,
   if (!CheckCallDepth(S, OpPC))
     return cleanup();
 
-  auto *Memory = new char[InterpFrame::allocSize(Func)];
-  auto *NewFrame = new (Memory) InterpFrame(S, Func, S.PC, VarArgSize);
-  InterpFrame *FrameBefore = S.Current;
+  InterpFrame *NewFrame = S.allocFrame(Func, S.PC, VarArgSize);
   S.Current = NewFrame;
 
   InterpStateCCOverride CCOverride(S, Func->isImmediate());
@@ -2036,16 +2025,8 @@ bool Call(InterpState &S, CodePtr OpPC, const Function 
*Func,
   if (InstancePtrTracked)
     S.InitializingPtrs.pop_back();
 
-  if (!Success) {
-    InterpFrame::free(NewFrame);
-    // Interpreting the function failed somehow. Reset to
-    // previous state.
-    S.Current = FrameBefore;
-    return false;
-  }
-
-  assert(S.Current == FrameBefore);
-  return true;
+  S.resetCurrentFrame();
+  return Success;
 }
 
 static bool getDynamicDecl(InterpState &S, CodePtr OpPC, PtrView TypePtr,
@@ -3443,11 +3424,7 @@ PRESERVE_NONE static bool InterpNext(InterpState &S) {
 #endif
 
 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.
-  assert(!S.Current->isRoot());
+  assert(S.Current->getFunction());
 
   S.PC = S.Current->getFunction()->getCodeBegin();
 
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 2aa5aee788230..d23ae37e15850 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -289,41 +289,32 @@ PRESERVE_NONE bool Ret(InterpState &S) {
   const T &Ret = S.Stk.pop<T>();
 
   assert(S.Current);
-
 #ifndef NDEBUG
   assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
 #endif
 
-  InterpFrame *Caller = S.Current->Caller;
-
   // This only happens via Context::Run().
-  if (!Caller)
+  if (S.Current->isBottomFrame())
     return true;
 
   cleanupAfterFunctionCall(S, S.Current->getFunction());
-
   S.PC = S.Current->getRetPC();
-  InterpFrame::free(S.Current);
-  S.Current = Caller;
   S.Stk.push<T>(Ret);
   return true;
 }
 
 PRESERVE_NONE inline bool RetVoid(InterpState &S) {
+  assert(S.Current);
 #ifndef NDEBUG
   assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
 #endif
 
-  InterpFrame *Caller = S.Current->Caller;
   // This only happens via Context::Run().
-  if (!Caller)
+  if (S.Current->isBottomFrame())
     return true;
 
   cleanupAfterFunctionCall(S, S.Current->getFunction());
-
   S.PC = S.Current->getRetPC();
-  InterpFrame::free(S.Current);
-  S.Current = Caller;
   return true;
 }
 
diff --git a/clang/lib/AST/ByteCode/InterpFrame.cpp 
b/clang/lib/AST/ByteCode/InterpFrame.cpp
index 4079e9718f1c3..413f2b51713b5 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.cpp
+++ b/clang/lib/AST/ByteCode/InterpFrame.cpp
@@ -76,15 +76,9 @@ InterpFrame::~InterpFrame() {
   for (unsigned I = 0, N = Func->getNumWrittenParams(); I != N; ++I)
     S.deallocate(argBlock(I));
 
-  // When destroying the InterpFrame, call the Dtor for all block
+  // When destroying the InterpFrame, call the Dtor for all blocks
   // that haven't been destroyed via a destroy() op yet.
   // This happens when the execution is interruped midway-through.
-  destroyScopes();
-}
-
-void InterpFrame::destroyScopes() {
-  if (!Func || Func->getFrameSize() == 0)
-    return;
   for (auto &Scope : Func->scopes()) {
     for (auto &Local : Scope.locals()) {
       S.deallocate(localBlock(Local.Offset));
diff --git a/clang/lib/AST/ByteCode/InterpFrame.h 
b/clang/lib/AST/ByteCode/InterpFrame.h
index c195165e6219f..acc9c337bfb1b 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.h
+++ b/clang/lib/AST/ByteCode/InterpFrame.h
@@ -56,19 +56,9 @@ class InterpFrame final : public Frame {
     return Func->getName();
   }
 
-  static void free(InterpFrame *F) {
-    if (!F->isBottomFrame()) {
-      F->~InterpFrame();
-      delete[] reinterpret_cast<char *>(F);
-    } else {
-      F->~InterpFrame();
-    }
-  }
-
   /// Invokes the destructors for a scope.
   void destroy(unsigned Idx);
   void initScope(unsigned Idx);
-  void destroyScopes();
   void enableLocal(unsigned Idx);
   bool isLocalEnabled(unsigned Idx) const {
     return localInlineDesc(Idx)->IsActive;
@@ -143,10 +133,6 @@ class InterpFrame final : public Frame {
     return stackRef<Pointer>(0);
   }
 
-  /// Checks if the frame is a root frame - return should quit the interpreter.
-  bool isRoot() const { return !Func; }
-
-  /// Returns the return address of the frame.
   CodePtr getRetPC() const { return RetPC; }
   /// Returns the return address of the opcode in the caller frame.
   CodePtr getRetOpPC() const {
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp 
b/clang/lib/AST/ByteCode/InterpState.cpp
index 4143a39dac83f..fa59b52408611 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -18,10 +18,11 @@ using namespace clang;
 using namespace clang::interp;
 
 InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
-                         Context &Ctx, SourceMapper *M)
-    : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(M), P(P), Stk(Stk),
-      Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame),
-      StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
+                         FrameAllocator &FrameAlloc, Context &Ctx,
+                         SourceMapper *M)
+    : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(M),
+      FrameAlloc(FrameAlloc), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this),
+      Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
       InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) {
   InConstantContext = Parent.InConstantContext;
   CheckingPotentialConstantExpression =
@@ -31,10 +32,12 @@ InterpState::InterpState(const State &Parent, Program &P, 
InterpStack &Stk,
 }
 
 InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
+                         FrameAllocator &FrameAlloc,
+
                          Context &Ctx, const Function *Func)
-    : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr), P(P),
-      Stk(Stk), Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame),
-      StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
+    : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr),
+      FrameAlloc(FrameAlloc), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this),
+      Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
       InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) {
   InConstantContext = Parent.InConstantContext;
   CheckingPotentialConstantExpression =
@@ -51,12 +54,7 @@ bool InterpState::inConstantContext() const {
 }
 
 InterpState::~InterpState() {
-  while (Current && !Current->isBottomFrame()) {
-    InterpFrame *Next = Current->Caller;
-    delete Current;
-    Current = Next;
-  }
-  BottomFrame.destroyScopes();
+  assert(Current->isBottomFrame());
 
   while (DeadBlocks) {
     DeadBlock *Next = DeadBlocks->Next;
diff --git a/clang/lib/AST/ByteCode/InterpState.h 
b/clang/lib/AST/ByteCode/InterpState.h
index ac7eb8060e032..897699aa1b5ea 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -16,6 +16,7 @@
 #include "Context.h"
 #include "DynamicAllocator.h"
 #include "Floating.h"
+#include "FrameAllocator.h"
 #include "Function.h"
 #include "InterpFrame.h"
 #include "InterpStack.h"
@@ -27,6 +28,7 @@ class Context;
 class SourceMapper;
 
 struct StdAllocatorCaller {
+
   const Expr *Call = nullptr;
   QualType AllocType;
   explicit operator bool() { return Call; }
@@ -42,10 +44,11 @@ enum class EvaluationKind : uint8_t {
 /// Interpreter context.
 class InterpState final : public State {
 public:
-  InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
+  InterpState(const State &Parent, Program &P, InterpStack &Stk,
+              FrameAllocator &FrameAlloc, Context &Ctx,
               SourceMapper *M = nullptr);
-  InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
-              const Function *Func);
+  InterpState(const State &Parent, Program &P, InterpStack &Stk,
+              FrameAllocator &FrameAlloc, Context &Ctx, const Function *Func);
 
   ~InterpState();
 
@@ -190,6 +193,27 @@ class InterpState final : public State {
 
   unsigned newStringID() { return StringID++; }
 
+  /// Allocate memory and create a new InterpFrame for the given function.
+  template <typename... Ts>
+  InterpFrame *allocFrame(const Function *F, Ts &&...Args) {
+    unsigned FrameSize = InterpFrame::allocSize(F);
+    InterpFrame *NewFrame = new (FrameAlloc.reserve(FrameSize))
+        InterpFrame(*this, F, std::forward<Ts>(Args)...);
+    assert(NewFrame);
+    return NewFrame;
+  }
+
+  /// Free resources associated with the current frame and set the caller to be
+  /// the new current frame.
+  void resetCurrentFrame() {
+    assert(Current);
+    unsigned CurrentSize = InterpFrame::allocSize(Current->getFunction());
+    InterpFrame *Caller = Current->Caller;
+    Current->~InterpFrame();
+    FrameAlloc.pop(CurrentSize);
+    Current = Caller;
+  }
+
 private:
   friend class EvaluationResult;
   friend class InterpStateCCOverride;
@@ -204,6 +228,8 @@ class InterpState final : public State {
   /// Diagnose that we've reached the constexpr step limit.
   bool diagnoseStepLimitExceeded(CodePtr OpPC);
 
+  FrameAllocator &FrameAlloc;
+
 public:
   CodePtr PC;
   /// Reference to the module containing all bytecode.

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

Reply via email to