[clang] [clang][bytecode] Allocate local variables in `InterpFrame` tail storage (PR #185835)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 15 06:48:51 PDT 2026
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/185835
>From ad516c285661a1d8d2931f9bd54b3e0150a6b42b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 10 Mar 2026 13:52:16 +0100
Subject: [PATCH] locals
---
clang/lib/AST/ByteCode/ByteCodeEmitter.cpp | 13 ++---
clang/lib/AST/ByteCode/ByteCodeEmitter.h | 2 +-
clang/lib/AST/ByteCode/Compiler.cpp | 24 ++++-----
clang/lib/AST/ByteCode/Context.cpp | 22 ++++++---
clang/lib/AST/ByteCode/Context.h | 5 ++
clang/lib/AST/ByteCode/DynamicAllocator.cpp | 10 +---
clang/lib/AST/ByteCode/EvalEmitter.h | 2 +-
clang/lib/AST/ByteCode/Function.cpp | 6 ---
clang/lib/AST/ByteCode/Function.h | 16 ++++--
clang/lib/AST/ByteCode/Interp.cpp | 19 +++++---
clang/lib/AST/ByteCode/Interp.h | 8 +--
clang/lib/AST/ByteCode/InterpBlock.cpp | 12 +++++
clang/lib/AST/ByteCode/InterpBlock.h | 2 +
clang/lib/AST/ByteCode/InterpFrame.cpp | 49 +++++++++++--------
clang/lib/AST/ByteCode/InterpFrame.h | 54 +++++++++++++++------
clang/lib/AST/ByteCode/InterpState.cpp | 5 +-
16 files changed, 147 insertions(+), 102 deletions(-)
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
index 35821085ff49b..c08ccf69aef85 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
@@ -55,18 +55,13 @@ void ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl,
}
bool IsValid = !FuncDecl->isInvalidDecl();
- // Register parameters with their offset.
- unsigned ParamIndex = 0;
- unsigned Drop = Func->hasRVO() +
- (Func->hasThisPointer() && !Func->isThisPointerExplicit());
-
- for (const auto &ParamDesc : llvm::drop_begin(Func->ParamDescriptors, Drop)) {
+ // Register parameters and their index.
+ for (unsigned ParamIndex = 0, N = Func->getNumWrittenParams();
+ ParamIndex != N; ++ParamIndex) {
const ParmVarDecl *PD = FuncDecl->getParamDecl(ParamIndex);
if (PD->isInvalidDecl())
IsValid = false;
- this->Params.insert(
- {PD, {ParamDesc.Offset, Ctx.canClassify(PD->getType())}});
- ++ParamIndex;
+ this->Params.insert({PD, {ParamIndex, Ctx.canClassify(PD->getType())}});
}
Func->setDefined(true);
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
index dd18341d52a09..873edeea71d96 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
@@ -67,7 +67,7 @@ class ByteCodeEmitter {
Local createLocal(Descriptor *D);
/// Parameter indices.
- llvm::DenseMap<const ParmVarDecl *, ParamOffset> Params;
+ llvm::DenseMap<const ParmVarDecl *, FuncParam> Params;
/// Lambda captures.
llvm::DenseMap<const ValueDecl *, ParamOffset> LambdaCaptures;
/// Offset of the This parameter in a lambda record.
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 8bbdf284b313d..e725260fb9f94 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -240,7 +240,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
return this->emitGetLocal(*SubExprT, It->second.Offset, E);
} else if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
if (auto It = this->Params.find(PVD); It != this->Params.end()) {
- return this->emitGetParam(*SubExprT, It->second.Offset, E);
+ return this->emitGetParam(*SubExprT, It->second.Index, E);
}
}
}
@@ -3852,13 +3852,13 @@ bool Compiler<Emitter>::VisitCXXInheritedCtorInitExpr(
// This is necessary because the calling code has pushed the pointer
// of the correct base for us already, but the arguments need
// to come after.
- unsigned Offset = align(primSize(PT_Ptr)); // instance pointer.
+ unsigned ParamIndex = 0;
for (const ParmVarDecl *PD : Ctor->parameters()) {
PrimType PT = this->classify(PD->getType()).value_or(PT_Ptr);
- if (!this->emitGetParam(PT, Offset, E))
+ if (!this->emitGetParam(PT, ParamIndex, E))
return false;
- Offset += align(primSize(PT));
+ ++ParamIndex;
}
return this->emitCall(F, 0, E);
@@ -6601,7 +6601,7 @@ bool Compiler<Emitter>::emitLambdaStaticInvokerBody(const CXXMethodDecl *MD) {
// We do the lvalue-to-rvalue conversion manually here, so no need
// to care about references.
PrimType ParamType = this->classify(PVD->getType()).value_or(PT_Ptr);
- if (!this->emitGetParam(ParamType, It->second.Offset, MD))
+ if (!this->emitGetParam(ParamType, It->second.Index, MD))
return false;
}
@@ -6698,10 +6698,7 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
if (!this->emitThis(Ctor))
return false;
- const ParmVarDecl *PVD = Ctor->getParamDecl(0);
- ParamOffset PO = this->Params[PVD]; // Must exist.
-
- if (!this->emitGetParam(PT_Ptr, PO.Offset, Ctor))
+ if (!this->emitGetParam(PT_Ptr, /*ParamIndex=*/0, Ctor))
return false;
return this->emitMemcpy(Ctor) && this->emitPopPtr(Ctor) &&
@@ -6874,10 +6871,7 @@ bool Compiler<Emitter>::compileUnionAssignmentOperator(
if (!this->emitThis(MD))
return false;
- const ParmVarDecl *PVD = MD->getParamDecl(0);
- ParamOffset PO = this->Params[PVD]; // Must exist.
-
- if (!this->emitGetParam(PT_Ptr, PO.Offset, MD))
+ if (!this->emitGetParam(PT_Ptr, /*ParamIndex=*/0, MD))
return false;
return this->emitMemcpy(MD) && this->emitRet(PT_Ptr, MD);
@@ -7449,9 +7443,9 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
}
if (auto It = this->Params.find(PVD); It != this->Params.end()) {
if (IsReference || !It->second.IsPtr)
- return this->emitGetParam(classifyPrim(E), It->second.Offset, E);
+ return this->emitGetParam(classifyPrim(E), It->second.Index, E);
- return this->emitGetPtrParam(It->second.Offset, E);
+ return this->emitGetPtrParam(It->second.Index, E);
}
if (!Ctx.getLangOpts().CPlusPlus23 && IsReference)
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index 7d4534a5da5c6..2b32161fceba0 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;
}
@@ -575,7 +583,6 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FuncDecl) {
bool HasRVO = false;
if (!Ty->isVoidType() && !canClassify(Ty)) {
HasRVO = true;
- ParamDescriptors.emplace_back(nullptr, ParamOffset, PT_Ptr);
ParamOffset += align(primSize(PT_Ptr));
}
@@ -586,10 +593,8 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FuncDecl) {
if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl)) {
if (!IsLambdaStaticInvoker) {
HasThisPointer = MD->isInstance();
- if (MD->isImplicitObjectMemberFunction()) {
- ParamDescriptors.emplace_back(nullptr, ParamOffset, PT_Ptr);
+ if (MD->isImplicitObjectMemberFunction())
ParamOffset += align(primSize(PT_Ptr));
- }
}
if (isLambdaCallOperator(MD)) {
@@ -613,6 +618,7 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FuncDecl) {
// Assign descriptors to all parameters.
// Composite objects are lowered to pointers.
const auto *FuncProto = FuncDecl->getType()->getAs<FunctionProtoType>();
+ unsigned BlockOffset = 0;
for (auto [ParamIndex, PD] : llvm::enumerate(FuncDecl->parameters())) {
bool IsConst = PD->getType().isConstQualified();
bool IsVolatile = PD->getType().isVolatileQualified();
@@ -626,8 +632,10 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FuncDecl) {
Descriptor *Desc = P->createDescriptor(PD, PT, nullptr, std::nullopt,
IsConst, /*IsTemporary=*/false,
/*IsMutable=*/false, IsVolatile);
- ParamDescriptors.emplace_back(Desc, ParamOffset, PT);
- ParamOffset += align(primSize(PT));
+ unsigned PrimTSize = align(primSize(PT));
+ ParamDescriptors.emplace_back(Desc, ParamOffset, BlockOffset, PT);
+ ParamOffset += PrimTSize;
+ BlockOffset += sizeof(Block) + PrimTSize;
}
// Create a handle over the emitted code.
@@ -655,7 +663,7 @@ const Function *Context::getOrCreateObjCBlock(const BlockExpr *E) {
Descriptor *Desc = P->createDescriptor(PD, PT, nullptr, std::nullopt,
IsConst, /*IsTemporary=*/false,
/*IsMutable=*/false, IsVolatile);
- ParamDescriptors.emplace_back(Desc, ParamOffset, PT);
+ ParamDescriptors.emplace_back(Desc, ParamOffset, ~0u, PT);
ParamOffset += align(primSize(PT));
}
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index 1b8c25732a262..f69f3c3d4ca85 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -37,6 +37,11 @@ struct ParamOffset {
bool IsPtr;
};
+struct FuncParam {
+ unsigned Index;
+ bool IsPtr;
+};
+
class EvalIDScope;
/// Holds all information required to evaluate constexpr code in a module.
class Context final {
diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.cpp b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
index 4fedac667b389..5f53fac923682 100644
--- a/clang/lib/AST/ByteCode/DynamicAllocator.cpp
+++ b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
@@ -27,15 +27,7 @@ void DynamicAllocator::cleanup() {
assert(!B->isDead());
assert(B->isInitialized());
B->invokeDtor();
-
- if (B->hasPointers()) {
- while (B->Pointers) {
- Pointer *Next = B->Pointers->asBlockPointer().Next;
- B->Pointers->BS.Pointee = nullptr;
- B->Pointers = Next;
- }
- B->Pointers = nullptr;
- }
+ B->removePointers();
}
}
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h
index f5c51c5f3dfa0..3de12366a2b3d 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -91,7 +91,7 @@ class EvalEmitter : public SourceMapper {
}
/// Parameter indices.
- llvm::DenseMap<const ParmVarDecl *, ParamOffset> Params;
+ llvm::DenseMap<const ParmVarDecl *, FuncParam> Params;
/// Local descriptors.
llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors;
std::optional<SourceInfo> LocOverride = std::nullopt;
diff --git a/clang/lib/AST/ByteCode/Function.cpp b/clang/lib/AST/ByteCode/Function.cpp
index 56d08a64d1024..5d6ad1383d896 100644
--- a/clang/lib/AST/ByteCode/Function.cpp
+++ b/clang/lib/AST/ByteCode/Function.cpp
@@ -56,12 +56,6 @@ Function::Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,
}
}
-Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const {
- auto It = Params.find(Offset);
- assert(It != Params.end() && "Invalid parameter offset");
- return It->second;
-}
-
SourceInfo Function::getSource(CodePtr PC) const {
assert(PC >= getCodeBegin() && "PC does not belong to this function");
assert(PC <= getCodeEnd() && "PC Does not belong to this function");
diff --git a/clang/lib/AST/ByteCode/Function.h b/clang/lib/AST/ByteCode/Function.h
index 544172b7e0c26..fc879505d031a 100644
--- a/clang/lib/AST/ByteCode/Function.h
+++ b/clang/lib/AST/ByteCode/Function.h
@@ -110,9 +110,11 @@ class Function final {
struct ParamDescriptor {
const Descriptor *Desc;
unsigned Offset;
+ unsigned BlockOffset;
PrimType T;
- ParamDescriptor(const Descriptor *Desc, unsigned Offset, PrimType T)
- : Desc(Desc), Offset(Offset), T(T) {}
+ ParamDescriptor(const Descriptor *Desc, unsigned Offset,
+ unsigned BlockOffset, PrimType T)
+ : Desc(Desc), Offset(Offset), BlockOffset(BlockOffset), T(T) {}
};
/// Returns the size of the function's local stack.
@@ -143,7 +145,9 @@ class Function final {
}
/// Returns a parameter descriptor.
- ParamDescriptor getParamDescriptor(unsigned Offset) const;
+ ParamDescriptor getParamDescriptor(unsigned Index) const {
+ return ParamDescriptors[Index];
+ }
/// Checks if the first argument is a RVO pointer.
bool hasRVO() const { return HasRVO; }
@@ -220,13 +224,15 @@ class Function final {
bool isVariadic() const { return Variadic; }
- unsigned getNumParams() const { return ParamDescriptors.size(); }
+ unsigned getNumParams() const {
+ return ParamDescriptors.size() + hasThisPointer() + hasRVO();
+ }
/// Returns the number of parameter this function takes when it's called,
/// i.e excluding the instance pointer and the RVO pointer.
unsigned getNumWrittenParams() const {
assert(getNumParams() >= (unsigned)(hasThisPointer() + hasRVO()));
- return getNumParams() - hasThisPointer() - hasRVO();
+ return ParamDescriptors.size();
}
unsigned getWrittenArgSize() const {
return ArgSize - (align(primSize(PT_Ptr)) * (hasThisPointer() + hasRVO()));
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index ebc7220aa5671..efc656ea9b375 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -298,6 +298,11 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC,
// at the end.
for (const Function::ParamDescriptor &PDesc : Func->args_reverse())
TYPE_SWITCH(PDesc.T, S.Stk.discard<T>());
+
+ if (Func->hasThisPointer() && !Func->isThisPointerExplicit())
+ S.Stk.discard<Pointer>();
+ if (Func->hasRVO())
+ S.Stk.discard<Pointer>();
}
bool isConstexprUnknown(const Pointer &P) {
@@ -1656,19 +1661,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 +1745,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 +1760,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/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 7b8a7c80c5423..c11786e3aedf6 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1426,11 +1426,11 @@ bool SetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
}
template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool GetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
+bool GetParam(InterpState &S, CodePtr OpPC, uint32_t Index) {
if (S.checkingPotentialConstantExpression()) {
return false;
}
- S.Stk.push<T>(S.Current->getParam<T>(I));
+ S.Stk.push<T>(S.Current->getParam<T>(Index));
return true;
}
@@ -1786,10 +1786,10 @@ inline bool GetPtrLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
-inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t I) {
+inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t Index) {
if (S.Current->isBottomFrame())
return false;
- S.Stk.push<Pointer>(S.Current->getParamPointer(I));
+ S.Stk.push<Pointer>(S.Current->getParamPointer(Index));
return true;
}
diff --git a/clang/lib/AST/ByteCode/InterpBlock.cpp b/clang/lib/AST/ByteCode/InterpBlock.cpp
index dc0178afe1246..9199cb09687ec 100644
--- a/clang/lib/AST/ByteCode/InterpBlock.cpp
+++ b/clang/lib/AST/ByteCode/InterpBlock.cpp
@@ -122,6 +122,18 @@ void Block::movePointersTo(Block *B) {
assert(!this->hasPointers());
}
+void Block::removePointers() {
+ Pointer *P = Pointers;
+ while (P) {
+ Pointer *Next = P->BS.Next;
+ P->BS.Pointee = nullptr;
+ P->BS.Prev = nullptr;
+ P->BS.Next = nullptr;
+ P = Next;
+ }
+ Pointers = nullptr;
+}
+
DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
: Root(Root), B(~0u, Blk->Desc, Blk->isExtern(), Blk->IsStatic,
Blk->isWeak(), Blk->isDummy(), /*IsDead=*/true) {
diff --git a/clang/lib/AST/ByteCode/InterpBlock.h b/clang/lib/AST/ByteCode/InterpBlock.h
index 57f9e7ec3714d..4a1195ef25bbe 100644
--- a/clang/lib/AST/ByteCode/InterpBlock.h
+++ b/clang/lib/AST/ByteCode/InterpBlock.h
@@ -94,6 +94,8 @@ class Block final {
unsigned getEvalID() const { return EvalID; }
/// Move all pointers from this block to \param B.
void movePointersTo(Block *B);
+ /// Make all pointers that currently point to this block point to nullptr.
+ void removePointers();
/// Returns a pointer to the stored data.
/// You are allowed to read Desc->getSize() bytes from this address.
diff --git a/clang/lib/AST/ByteCode/InterpFrame.cpp b/clang/lib/AST/ByteCode/InterpFrame.cpp
index 3c185a0ad661a..2d9034a1f22e1 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.cpp
+++ b/clang/lib/AST/ByteCode/InterpFrame.cpp
@@ -31,17 +31,20 @@ 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;
+ // Initialize argument blocks.
+ for (unsigned I = 0, N = Func->getNumWrittenParams(); I != N; ++I)
+ new (argBlock(I)) Block(S.EvalID, Func->getParamDescriptor(I).Desc);
- unsigned FrameSize = Func->getFrameSize();
- if (FrameSize == 0)
+ if (Func->getFrameSize() == 0)
return;
- Locals = std::make_unique<char[]>(FrameSize);
+ // std::memset(locals(), 0, Func->getFrameSize());
for (auto &Scope : Func->scopes()) {
for (auto &Local : Scope.locals()) {
- new (localBlock(Local.Offset)) Block(S.Ctx.getEvalID(), Local.Desc);
+ new (localBlock(Local.Offset)) Block(S.EvalID, Local.Desc);
// Note that we are NOT calling invokeCtor() here, since that is done
// via the InitScope op.
new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);
@@ -67,8 +70,16 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC,
}
InterpFrame::~InterpFrame() {
- for (auto &Param : Params)
- S.deallocate(reinterpret_cast<Block *>(Param.second.get()));
+ if (Func) {
+ // De-initialize all argument blocks.
+ for (unsigned I = 0, N = Func->getNumWrittenParams(); I != N; ++I) {
+ Block *B = argBlock(I);
+ if (B->isInitialized()) {
+ B->removePointers();
+ B->invokeDtor();
+ }
+ }
+ }
// When destroying the InterpFrame, call the Dtor for all block
// that haven't been destroyed via a destroy() op yet.
@@ -77,7 +88,7 @@ InterpFrame::~InterpFrame() {
}
void InterpFrame::destroyScopes() {
- if (!Func)
+ if (!Func || Func->getFrameSize() == 0)
return;
for (auto &Scope : Func->scopes()) {
for (auto &Local : Scope.locals()) {
@@ -244,25 +255,21 @@ Block *InterpFrame::getLocalBlock(unsigned Offset) const {
return localBlock(Offset);
}
-Pointer InterpFrame::getParamPointer(unsigned Off) {
- // Return the block if it was created previously.
- if (auto Pt = Params.find(Off); Pt != Params.end())
- return Pointer(reinterpret_cast<Block *>(Pt->second.get()));
-
+Pointer InterpFrame::getParamPointer(unsigned Index) {
assert(!isBottomFrame());
- // Allocate memory to store the parameter and the block metadata.
- const auto &PDesc = Func->getParamDescriptor(Off);
- size_t BlockSize = sizeof(Block) + PDesc.Desc->getAllocSize();
- auto Memory = std::make_unique<char[]>(BlockSize);
- auto *B = new (Memory.get()) Block(S.Ctx.getEvalID(), PDesc.Desc);
- B->invokeCtor();
+ Block *B = argBlock(Index);
// Copy the initial value.
- TYPE_SWITCH(PDesc.T, new (B->data()) T(stackRef<T>(Off)));
+ if (!B->isInitialized()) {
+ unsigned ByteOffset = Func->getParamDescriptor(Index).Offset;
+ assert(B->getDescriptor()->isPrimitive());
+ B->invokeCtor();
+ TYPE_SWITCH(B->getDescriptor()->getPrimType(),
+ new (B->data()) T(stackRef<T>(ByteOffset)));
+ assert(B->isInitialized());
+ }
- // Record the param.
- Params.insert({Off, std::move(Memory)});
return Pointer(B);
}
diff --git a/clang/lib/AST/ByteCode/InterpFrame.h b/clang/lib/AST/ByteCode/InterpFrame.h
index 0879260695d3e..56203658264e4 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.h
+++ b/clang/lib/AST/ByteCode/InterpFrame.h
@@ -46,6 +46,13 @@ 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() +
+ (F->getArgSize() + (sizeof(Block) * F->getNumWrittenParams()));
+ }
+
std::string getName() const {
if (!Func)
return "Bottom frame";
@@ -53,8 +60,10 @@ class InterpFrame final : public Frame {
}
static void free(InterpFrame *F) {
- if (!F->isBottomFrame())
- delete F;
+ F->~InterpFrame();
+ if (!F->isBottomFrame()) {
+ delete[] reinterpret_cast<char *>(F);
+ }
}
/// Invokes the destructors for a scope.
@@ -100,22 +109,23 @@ class InterpFrame final : public Frame {
Block *getLocalBlock(unsigned Offset) const;
/// Returns the value of an argument.
- template <typename T> const T &getParam(unsigned Offset) const {
- auto Pt = Params.find(Offset);
- if (Pt == Params.end())
- return stackRef<T>(Offset);
- return reinterpret_cast<const Block *>(Pt->second.get())->deref<T>();
+ template <typename T> const T &getParam(unsigned Index) const {
+ Block *ArgBlock = argBlock(Index);
+ if (!ArgBlock->isInitialized())
+ return stackRef<T>(Func->getParamDescriptor(Index).Offset);
+ return ArgBlock->deref<T>();
}
/// Mutates a local copy of a parameter.
- template <typename T> void setParam(unsigned Offset, const T &Value) {
- getParamPointer(Offset).deref<T>() = Value;
+ template <typename T> void setParam(unsigned Index, const T &Value) {
+ argBlock(Index)->deref<T>() = Value;
}
/// Returns a pointer to an argument - lazily creates a block.
Pointer getParamPointer(unsigned Offset);
bool hasThisPointer() const { return Func && Func->hasThisPointer(); }
+
/// Returns the 'this' pointer.
const Pointer &getThis() const {
assert(hasThisPointer());
@@ -167,14 +177,32 @@ class InterpFrame final : public Frame {
return localBlock(Offset)->deref<T>();
}
+ /// Pointer to local memory.
+ char *locals() const {
+ return (reinterpret_cast<char *>(const_cast<InterpFrame *>(this))) +
+ align(sizeof(InterpFrame));
+ }
+
+ /// Pointer to argument memory.
+ char *args() const {
+ return (reinterpret_cast<char *>(const_cast<InterpFrame *>(this))) +
+ sizeof(InterpFrame) + Func->getFrameSize();
+ }
+
/// 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 a pointer to an argument block.
+ Block *argBlock(unsigned Index) const {
+ unsigned ByteOffset = Func->getParamDescriptor(Index).BlockOffset;
+ return reinterpret_cast<Block *>(args() + ByteOffset);
}
/// 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,12 +220,8 @@ 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.
- llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Params;
public:
unsigned MSVCConstexprAllowed = 0;
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 =
More information about the cfe-commits
mailing list