https://github.com/tbaederr created
https://github.com/llvm/llvm-project/pull/185835
Instead of heap-allocating an `InterpFrame` and then immediately
heap-allocating more space for the local variables, do only one heap-allocation
and use tail storage for the local variables.
We already know how many bytes we need to for the tail storage after all.
This also makes `InterpFrame` a little smaller since we don't need to save an
explicit pointer for the local variable memory.
For an artificial test case doing lots of function calls with local variables
like:
```c++
constexpr int plus(int a, int b) {
int x = a;
int y = b;
int z = x + y;
return z;
}
constexpr int minus(int a, int b) {
int x = a;
int y = b;
int z = x - y;
return z;
}
constexpr int foo() {
int a = 0;
for (unsigned I = 0; I != 1'000'000; ++I) {
int b = I;
a = plus(a,b );
a = minus(a,I);
}
return a;
}
static_assert(foo() == 0);
```
this saves us over 6%.
>From a693bbae79178ffa163e2d1db9959c1feb1327c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Tue, 10 Mar 2026 13:52:16 +0100
Subject: [PATCH] locals
---
clang/lib/AST/ByteCode/Context.cpp | 8 ++++++++
clang/lib/AST/ByteCode/Interp.cpp | 14 ++++++++------
clang/lib/AST/ByteCode/InterpFrame.cpp | 10 +++-------
clang/lib/AST/ByteCode/InterpFrame.h | 23 +++++++++++++++++------
clang/lib/AST/ByteCode/InterpState.cpp | 5 ++---
5 files changed, 38 insertions(+), 22 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Context.cpp
b/clang/lib/AST/ByteCode/Context.cpp
index 7d4534a5da5c6..9a4c1f3d520a5 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -498,11 +498,19 @@ const llvm::fltSemantics
&Context::getFloatSemantics(QualType T) const {
bool Context::Run(State &Parent, const Function *Func) {
InterpState State(Parent, *P, Stk, *this, Func);
+ auto Memory = std::make_unique<char[]>(InterpFrame::allocSize(Func));
+ InterpFrame *Frame = new (Memory.get()) InterpFrame(
+ State, Func, /*Caller=*/nullptr, CodePtr(), Func->getArgSize());
+ State.Current = Frame;
+
if (Interpret(State)) {
assert(Stk.empty());
return true;
}
+
Stk.clear();
+ Frame->~InterpFrame();
+ State.Current = &State.BottomFrame;
return false;
}
diff --git a/clang/lib/AST/ByteCode/Interp.cpp
b/clang/lib/AST/ByteCode/Interp.cpp
index ebc7220aa5671..2cb1cd7289d13 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1656,19 +1656,20 @@ bool CallVar(InterpState &S, CodePtr OpPC, const
Function *Func,
if (!CheckCallDepth(S, OpPC))
return false;
- auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
+ auto Memory = new char[InterpFrame::allocSize(Func)];
+ auto NewFrame = new (Memory) InterpFrame(S, Func, OpPC, VarArgSize);
InterpFrame *FrameBefore = S.Current;
- S.Current = NewFrame.get();
+ S.Current = NewFrame;
// Note that we cannot assert(CallResult.hasValue()) here since
// Ret() above only sets the APValue if the curent frame doesn't
// have a caller set.
if (Interpret(S)) {
- NewFrame.release(); // Frame was delete'd already.
assert(S.Current == FrameBefore);
return true;
}
+ InterpFrame::free(NewFrame);
// Interpreting the function failed somehow. Reset to
// previous state.
S.Current = FrameBefore;
@@ -1739,9 +1740,10 @@ bool Call(InterpState &S, CodePtr OpPC, const Function
*Func,
if (!CheckCallDepth(S, OpPC))
return cleanup();
- auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
+ auto Memory = new char[InterpFrame::allocSize(Func)];
+ auto NewFrame = new (Memory) InterpFrame(S, Func, OpPC, VarArgSize);
InterpFrame *FrameBefore = S.Current;
- S.Current = NewFrame.get();
+ S.Current = NewFrame;
InterpStateCCOverride CCOverride(S, Func->isImmediate());
// Note that we cannot assert(CallResult.hasValue()) here since
@@ -1753,13 +1755,13 @@ bool Call(InterpState &S, CodePtr OpPC, const Function
*Func,
S.InitializingBlocks.pop_back();
if (!Success) {
+ InterpFrame::free(NewFrame);
// Interpreting the function failed somehow. Reset to
// previous state.
S.Current = FrameBefore;
return false;
}
- NewFrame.release(); // Frame was delete'd already.
assert(S.Current == FrameBefore);
return true;
}
diff --git a/clang/lib/AST/ByteCode/InterpFrame.cpp
b/clang/lib/AST/ByteCode/InterpFrame.cpp
index 3c185a0ad661a..3a175707774f4 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.cpp
+++ b/clang/lib/AST/ByteCode/InterpFrame.cpp
@@ -31,14 +31,9 @@ InterpFrame::InterpFrame(InterpState &S, const Function
*Func,
: Caller(Caller), S(S), Depth(Caller ? Caller->Depth + 1 : 0), Func(Func),
RetPC(RetPC), ArgSize(ArgSize), Args(static_cast<char *>(S.Stk.top())),
FrameOffset(S.Stk.size()) {
- if (!Func)
- return;
-
- unsigned FrameSize = Func->getFrameSize();
- if (FrameSize == 0)
+ if (!Func || Func->getFrameSize() == 0)
return;
- Locals = std::make_unique<char[]>(FrameSize);
for (auto &Scope : Func->scopes()) {
for (auto &Local : Scope.locals()) {
new (localBlock(Local.Offset)) Block(S.Ctx.getEvalID(), Local.Desc);
@@ -77,8 +72,9 @@ InterpFrame::~InterpFrame() {
}
void InterpFrame::destroyScopes() {
- if (!Func)
+ 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 0879260695d3e..d56196845b560 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.h
+++ b/clang/lib/AST/ByteCode/InterpFrame.h
@@ -46,6 +46,12 @@ class InterpFrame final : public Frame {
/// Destroys the frame, killing all live pointers to stack slots.
~InterpFrame();
+ /// Returns the number of bytes needed to allocate an InterpFrame for the
+ /// given function.
+ static size_t allocSize(const Function *F) {
+ return sizeof(InterpFrame) + F->getFrameSize();
+ }
+
std::string getName() const {
if (!Func)
return "Bottom frame";
@@ -53,8 +59,10 @@ class InterpFrame final : public Frame {
}
static void free(InterpFrame *F) {
- if (!F->isBottomFrame())
- delete F;
+ if (!F->isBottomFrame()) {
+ F->~InterpFrame();
+ delete[] reinterpret_cast<char *>(F);
+ }
}
/// Invokes the destructors for a scope.
@@ -167,14 +175,19 @@ class InterpFrame final : public Frame {
return localBlock(Offset)->deref<T>();
}
+ char *locals() const {
+ return (reinterpret_cast<char *>(const_cast<InterpFrame *>(this))) +
+ sizeof(InterpFrame);
+ }
+
/// Returns a pointer to a local's block.
Block *localBlock(unsigned Offset) const {
- return reinterpret_cast<Block *>(Locals.get() + Offset - sizeof(Block));
+ return reinterpret_cast<Block *>(locals() + Offset - sizeof(Block));
}
/// Returns the inline descriptor of the local.
InlineDescriptor *localInlineDesc(unsigned Offset) const {
- return reinterpret_cast<InlineDescriptor *>(Locals.get() + Offset);
+ return reinterpret_cast<InlineDescriptor *>(locals() + Offset);
}
private:
@@ -192,8 +205,6 @@ class InterpFrame final : public Frame {
const unsigned ArgSize;
/// Pointer to the arguments in the callee's frame.
char *Args = nullptr;
- /// Fixed, initial storage for known local variables.
- std::unique_ptr<char[]> Locals;
/// Offset on the stack at entry.
const size_t FrameOffset;
/// Mapping from arg offsets to their argument blocks.
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp
b/clang/lib/AST/ByteCode/InterpState.cpp
index fd69559af5917..2d6ed98e6b52c 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -33,9 +33,8 @@ InterpState::InterpState(const State &Parent, Program &P,
InterpStack &Stk,
InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
Context &Ctx, const Function *Func)
: State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr), P(P),
- Stk(Stk), Ctx(Ctx),
- BottomFrame(*this, Func, nullptr, CodePtr(), Func->getArgSize()),
- Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
+ Stk(Stk), Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame),
+ StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) {
InConstantContext = Parent.InConstantContext;
CheckingPotentialConstantExpression =
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits