https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/185902
So we can do one heap allocation for the entire record (minus whatever the dense maps allocate). Also use the program allocator for the Function instances. >From 75fd945edfb01745c890dc2b401b128ab9ee66a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Wed, 11 Mar 2026 14:11:26 +0100 Subject: [PATCH] Record tail storage --- clang/lib/AST/ByteCode/Program.cpp | 70 ++++++++++++++++++++-------- clang/lib/AST/ByteCode/Program.h | 20 ++++---- clang/lib/AST/ByteCode/Record.cpp | 26 ++--------- clang/lib/AST/ByteCode/Record.h | 73 ++++++++++++++++++------------ 4 files changed, 111 insertions(+), 78 deletions(-) diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp index efef5db177e56..61df6618ce4e3 100644 --- a/clang/lib/AST/ByteCode/Program.cpp +++ b/clang/lib/AST/ByteCode/Program.cpp @@ -281,7 +281,7 @@ Function *Program::getFunction(const FunctionDecl *F) { F = F->getCanonicalDecl(); assert(F); auto It = Funcs.find(F); - return It == Funcs.end() ? nullptr : It->second.get(); + return It == Funcs.end() ? nullptr : It->second; } Record *Program::getOrCreateRecord(const RecordDecl *RD) { @@ -300,11 +300,6 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { if (!Inserted) return It->second; - // Number of bytes required by fields and base classes. - unsigned BaseSize = 0; - // Number of bytes required by virtual base. - unsigned VirtSize = 0; - // Helper to get a base descriptor. auto GetBaseDesc = [this](const RecordDecl *BD, const Record *BR) -> const Descriptor * { @@ -316,10 +311,28 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { }; // Reserve space for base classes. - Record::BaseList Bases; - Record::VirtualBaseList VirtBases; + unsigned NumRecordBases = 0; + unsigned NumRecordVBases = 0; + unsigned NumFields = RD->getNumFields(); + if (const auto *CD = dyn_cast<CXXRecordDecl>(RD)) { + NumRecordBases = CD->getNumBases(); + NumRecordVBases = CD->getNumVBases(); + } + + char *Memory = new (*this) char[Record::allocSize(NumFields, NumRecordBases, + NumRecordVBases)]; + Record *R = + new (Memory) Record(RD, NumRecordBases, NumFields, NumRecordVBases); + bool HasPtrField = false; + + // Number of bytes required by fields and base classes. + unsigned BaseSize = 0; + // Number of bytes required by virtual base. + unsigned VirtSize = 0; + if (const auto *CD = dyn_cast<CXXRecordDecl>(RD)) { - Bases.reserve(CD->getNumBases()); + unsigned BaseIndex = 0; + Record::Base *Bases = R->getBases(); for (const CXXBaseSpecifier &Spec : CD->bases()) { if (Spec.isVirtual()) continue; @@ -335,28 +348,40 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { return nullptr; BaseSize += align(sizeof(InlineDescriptor)); - Bases.emplace_back(BD, Desc, BR, BaseSize); + Record::Base *B = + new (&Bases[BaseIndex]) Record::Base(BD, Desc, BR, BaseSize); BaseSize += align(BR->getSize()); + ++BaseIndex; + R->BaseMap[B->Decl] = B; + if (!HasPtrField) + HasPtrField |= B->R->hasPtrField(); } + R->NumBases = BaseIndex; + unsigned VBaseIndex = 0; + Record::Base *VBases = R->getVBases(); for (const CXXBaseSpecifier &Spec : CD->vbases()) { const auto *BD = Spec.getType()->castAsCXXRecordDecl(); const Record *BR = getOrCreateRecord(BD); + if (!BR) + return nullptr; + const Descriptor *Desc = GetBaseDesc(BD, BR); if (!Desc) return nullptr; VirtSize += align(sizeof(InlineDescriptor)); - VirtBases.emplace_back(BD, Desc, BR, VirtSize); + new (&VBases[VBaseIndex]) Record::Base(BD, Desc, BR, VirtSize); VirtSize += align(BR->getSize()); + ++VBaseIndex; } + R->NumVBases = VBaseIndex; } // Reserve space for fields. - Record::FieldList Fields; - Fields.reserve(RD->getNumFields()); - bool HasPtrField = false; + Record::Field *Fields = R->getFields(); + unsigned FieldIndex = 0; for (const FieldDecl *FD : RD->fields()) { FD = FD->getFirstDecl(); // Note that we DO create fields and descriptors @@ -386,13 +411,22 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { } else { return nullptr; } - Fields.emplace_back(FD, Desc, BaseSize); + new (&Fields[FieldIndex]) Record::Field(FD, Desc, BaseSize); BaseSize += align(Desc->getAllocSize()); + ++FieldIndex; + } + + for (unsigned I = 0; I != R->getNumVirtualBases(); ++I) { + Record::Base *V = &R->getVBases()[I]; + V->Offset += BaseSize; + R->VirtualBaseMap[V->Decl] = V; + if (!HasPtrField) + HasPtrField |= V->R->hasPtrField(); } - Record *R = new (Allocator) - Record(RD, std::move(Bases), std::move(Fields), std::move(VirtBases), - VirtSize, BaseSize, HasPtrField); + R->HasPtrField = HasPtrField; + R->VirtualSize = VirtSize; + R->BaseSize = BaseSize; Records[RD] = R; return R; } diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h index c8795504391fa..2ad48799d1b20 100644 --- a/clang/lib/AST/ByteCode/Program.h +++ b/clang/lib/AST/ByteCode/Program.h @@ -52,6 +52,9 @@ class Program final { if (Record *R = RecordPair.second) R->~Record(); } + + for (Function *F : Funcs.values()) + F->~Function(); } /// Marshals a native pointer to an ID for embedding in bytecode. @@ -98,8 +101,9 @@ class Program final { template <typename... Ts> Function *createFunction(const FunctionDecl *Def, Ts &&...Args) { Def = Def->getCanonicalDecl(); - auto *Func = new Function(*this, Def, std::forward<Ts>(Args)...); - Funcs.insert({Def, std::unique_ptr<Function>(Func)}); + auto *Func = + new (Allocator) Function(*this, Def, std::forward<Ts>(Args)...); + Funcs.insert({Def, Func}); return Func; } /// Creates an anonymous function. @@ -171,8 +175,12 @@ class Program final { /// Reference to the VM context. Context &Ctx; + /// Custom allocator for global storage. + using PoolAllocTy = llvm::BumpPtrAllocator; + /// Allocator for globals. + mutable PoolAllocTy Allocator; /// Mapping from decls to cached bytecode functions. - llvm::DenseMap<const FunctionDecl *, std::unique_ptr<Function>> Funcs; + llvm::DenseMap<const FunctionDecl *, Function *> Funcs; /// List of anonymous functions. std::vector<std::unique_ptr<Function>> AnonFuncs; @@ -181,9 +189,6 @@ class Program final { /// Cached native pointer indices. llvm::DenseMap<const void *, unsigned> NativePointerIndices; - /// Custom allocator for global storage. - using PoolAllocTy = llvm::BumpPtrAllocator; - /// Descriptor + storage for a global object. /// /// Global objects never go out of scope, thus they do not track pointers. @@ -208,9 +213,6 @@ class Program final { Block B; }; - /// Allocator for globals. - mutable PoolAllocTy Allocator; - /// Global objects. std::vector<Global *> Globals; /// Cached global indices. diff --git a/clang/lib/AST/ByteCode/Record.cpp b/clang/lib/AST/ByteCode/Record.cpp index 13a5ffb85787c..552ba78f76ae0 100644 --- a/clang/lib/AST/ByteCode/Record.cpp +++ b/clang/lib/AST/ByteCode/Record.cpp @@ -12,27 +12,11 @@ using namespace clang; using namespace clang::interp; -Record::Record(const RecordDecl *Decl, BaseList &&SrcBases, - FieldList &&SrcFields, VirtualBaseList &&SrcVirtualBases, - unsigned VirtualSize, unsigned BaseSize, bool HasPtrField) - : Decl(Decl), Bases(std::move(SrcBases)), Fields(std::move(SrcFields)), - BaseSize(BaseSize), VirtualSize(VirtualSize), IsUnion(Decl->isUnion()), - IsAnonymousUnion(IsUnion && Decl->isAnonymousStructOrUnion()), - HasPtrField(HasPtrField) { - for (Base &V : SrcVirtualBases) - VirtualBases.emplace_back(V.Decl, V.Desc, V.R, V.Offset + BaseSize); - - for (Base &B : Bases) { - BaseMap[B.Decl] = &B; - if (!this->HasPtrField) - this->HasPtrField |= B.R->hasPtrField(); - } - for (Base &V : VirtualBases) { - VirtualBaseMap[V.Decl] = &V; - if (!this->HasPtrField) - this->HasPtrField |= V.R->hasPtrField(); - } -} +Record::Record(const RecordDecl *Decl, unsigned NumBases, unsigned NumFields, + unsigned NumVBases) + : Decl(Decl), NumBases(NumBases), NumFields(NumFields), + NumVBases(NumVBases), IsUnion(Decl->isUnion()), + IsAnonymousUnion(IsUnion && Decl->isAnonymousStructOrUnion()) {} std::string Record::getName() const { std::string Ret; diff --git a/clang/lib/AST/ByteCode/Record.h b/clang/lib/AST/ByteCode/Record.h index e43f683364e26..24be2cf02bbf9 100644 --- a/clang/lib/AST/ByteCode/Record.h +++ b/clang/lib/AST/ByteCode/Record.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_INTERP_RECORD_H #define LLVM_CLANG_AST_INTERP_RECORD_H +#include "PrimType.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" @@ -49,14 +50,13 @@ class Record final { : Decl(D), Desc(Desc), R(R), Offset(Offset) {} }; - /// Mapping from identifiers to field descriptors. - using FieldList = llvm::SmallVector<Field, 8>; - /// Mapping from identifiers to base classes. - using BaseList = llvm::SmallVector<Base, 8>; - /// List of virtual base classes. - using VirtualBaseList = llvm::SmallVector<Base, 0>; - public: + static size_t allocSize(unsigned NumFields, unsigned NumBases, + unsigned NumVBases) { + return align(sizeof(Record)) + align((NumFields * sizeof(Field))) + + align((NumBases * sizeof(Base))) + align(NumVBases * sizeof(Base)); + } + /// Returns the underlying declaration. const RecordDecl *getDecl() const { return Decl; } /// Returns the name of the underlying declaration. @@ -82,40 +82,40 @@ class Record final { /// with no destructor or for those with a trivial destructor. bool hasTrivialDtor() const; - using const_field_iter = FieldList::const_iterator; + using const_field_iter = ArrayRef<Field>::const_iterator; llvm::iterator_range<const_field_iter> fields() const { - return llvm::make_range(Fields.begin(), Fields.end()); + return llvm::make_range(getFields(), getFields() + NumFields); } - unsigned getNumFields() const { return Fields.size(); } - const Field *getField(unsigned I) const { return &Fields[I]; } + unsigned getNumFields() const { return NumFields; } + const Field *getField(unsigned I) const { return &getFields()[I]; } /// Returns a field. const Field *getField(const FieldDecl *FD) const { - return &Fields[FD->getFieldIndex()]; + return &getFields()[FD->getFieldIndex()]; } - using const_base_iter = BaseList::const_iterator; + using const_base_iter = ArrayRef<Base>::const_iterator; llvm::iterator_range<const_base_iter> bases() const { - return llvm::make_range(Bases.begin(), Bases.end()); + return llvm::make_range(getBases(), getBases() + NumBases); } - unsigned getNumBases() const { return Bases.size(); } + unsigned getNumBases() const { return NumBases; } const Base *getBase(unsigned I) const { assert(I < getNumBases()); - return &Bases[I]; + return &getBases()[I]; } /// Returns a base descriptor. const Base *getBase(QualType T) const; /// Returns a base descriptor. const Base *getBase(const RecordDecl *FD) const; - using const_virtual_iter = VirtualBaseList::const_iterator; - llvm::iterator_range<const_virtual_iter> virtual_bases() const { - return llvm::make_range(VirtualBases.begin(), VirtualBases.end()); + using const_vbase_iter = ArrayRef<Base>::const_iterator; + llvm::iterator_range<const_vbase_iter> virtual_bases() const { + return llvm::make_range(getVBases(), getVBases() + NumVBases); } - unsigned getNumVirtualBases() const { return VirtualBases.size(); } - const Base *getVirtualBase(unsigned I) const { return &VirtualBases[I]; } + unsigned getNumVirtualBases() const { return NumVBases; } + const Base *getVirtualBase(unsigned I) const { return &getVBases()[I]; } /// Returns a virtual base descriptor. const Base *getVirtualBase(const RecordDecl *RD) const; @@ -125,21 +125,34 @@ class Record final { private: /// Constructor used by Program to create record descriptors. - Record(const RecordDecl *, BaseList &&Bases, FieldList &&Fields, - VirtualBaseList &&VirtualBases, unsigned VirtualSize, - unsigned BaseSize, bool HasPtrField = true); + Record(const RecordDecl *, unsigned NumBases, unsigned NumFields, + unsigned NumVBases); private: friend class Program; + Field *getFields() const { + return reinterpret_cast<Field *>( + (reinterpret_cast<char *>(const_cast<Record *>(this))) + + align(sizeof(*this))); + } + Base *getBases() const { + return reinterpret_cast<Base *>( + (reinterpret_cast<char *>(const_cast<Record *>(this))) + + align(sizeof(*this)) + align((NumFields * sizeof(Field)))); + } + Base *getVBases() const { + return reinterpret_cast<Base *>( + (reinterpret_cast<char *>(const_cast<Record *>(this))) + + align(sizeof(*this)) + align((NumFields * sizeof(Field))) + + align(NumBases * sizeof(Base))); + } + /// Original declaration. const RecordDecl *Decl; - /// List of all base classes. - BaseList Bases; - /// List of all the fields in the record. - FieldList Fields; - /// List o fall virtual bases. - VirtualBaseList VirtualBases; + unsigned NumBases; + const unsigned NumFields; + unsigned NumVBases; /// Mapping from declarations to bases. llvm::DenseMap<const RecordDecl *, const Base *> BaseMap; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
