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

Reply via email to