https://github.com/philnik777 created 
https://github.com/llvm/llvm-project/pull/224903

Instead of storing the intormation of which types are in the stack separately, 
this information can simply be stored in the InterpStack. This makes normal 
push and pop operations more efficient. Specifically, the push only has to 
store an extra byte at the end of the allocation instead of modifying a 
completely separate stack, and the pop doesn't do anthing special. On 64 bit 
platforms thi currently this doesn't even use any more memory than not keeping 
track of the type at all, since the type information can be stored inside the 
alloation overhead for types with `sizeof <= 8` or the tail padding of larget 
objects.


>From 005721818ed263907b15b874efd7c23935b19640 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <[email protected]>
Date: Sun, 20 Sep 2026 11:58:05 +0200
Subject: [PATCH] [Clang][ByteCode] Store type information inside the custom
 stack

---
 clang/lib/AST/ByteCode/FixedPoint.h    |  2 +-
 clang/lib/AST/ByteCode/Integral.h      |  2 +-
 clang/lib/AST/ByteCode/InterpStack.cpp | 32 ++++++------
 clang/lib/AST/ByteCode/InterpStack.h   | 71 +++++++++++++++++---------
 clang/lib/AST/ByteCode/Pointer.h       |  2 +-
 5 files changed, 66 insertions(+), 43 deletions(-)

diff --git a/clang/lib/AST/ByteCode/FixedPoint.h 
b/clang/lib/AST/ByteCode/FixedPoint.h
index fcb3c79cc1097..7aab21a77c8f9 100644
--- a/clang/lib/AST/ByteCode/FixedPoint.h
+++ b/clang/lib/AST/ByteCode/FixedPoint.h
@@ -22,7 +22,7 @@ using APSInt = llvm::APSInt;
 /// Wrapper around fixed point types.
 class FixedPoint final {
 private:
-  llvm::APFixedPoint V;
+  LLVM_NO_UNIQUE_ADDRESS llvm::APFixedPoint V;
 
 public:
   FixedPoint(llvm::APFixedPoint &&V) : V(std::move(V)) {}
diff --git a/clang/lib/AST/ByteCode/Integral.h 
b/clang/lib/AST/ByteCode/Integral.h
index 543d7f7fd43a9..e2bde2a0c82f7 100644
--- a/clang/lib/AST/ByteCode/Integral.h
+++ b/clang/lib/AST/ByteCode/Integral.h
@@ -78,7 +78,6 @@ template <unsigned Bits, bool Signed> class Integral final {
   static_assert(std::is_trivially_copyable_v<ReprT>);
   template <unsigned OtherBits, bool OtherSigned> friend class Integral;
 
-  IntegralKind Kind = IntegralKind::Number;
   union {
     ReprT V;
     struct {
@@ -90,6 +89,7 @@ template <unsigned Bits, bool Signed> class Integral final {
       const AddrLabelExpr *L2;
     } AddrLabelDiff;
   };
+  IntegralKind Kind = IntegralKind::Number;
 
   /// Primitive representing limits.
   static const auto Min = std::numeric_limits<ReprT>::min();
diff --git a/clang/lib/AST/ByteCode/InterpStack.cpp 
b/clang/lib/AST/ByteCode/InterpStack.cpp
index 839540a7912f8..b85c271931052 100644
--- a/clang/lib/AST/ByteCode/InterpStack.cpp
+++ b/clang/lib/AST/ByteCode/InterpStack.cpp
@@ -25,15 +25,22 @@ InterpStack::~InterpStack() {
     std::free(Chunk->Next);
   if (Chunk)
     std::free(Chunk);
+
+#if __has_cpp_attribute(no_unique_address)
+  TYPE_SWITCH(PrimType(), {
+    using Frame = StackFrame<T>;
+    static_assert(offsetof(Frame, type) == sizeof(Frame) - 1);
+    static_assert(sizeof(void *) != 8 || sizeof(Frame) == sizeof(T) ||
+                  sizeof(T) < sizeof(void *));
+  });
+#endif
 }
 
 // We keep the last chunk around to reuse.
 void InterpStack::clear() {
-  for (PrimType Item : llvm::reverse(ItemTypes)) {
-    TYPE_SWITCH(Item, { this->discard<T>(); });
+  while (!empty()) {
+    TYPE_SWITCH(getNextObjectType(), { this->discard<T>(); });
   }
-  assert(ItemTypes.empty());
-  assert(empty());
 }
 
 void InterpStack::clearTo(size_t NewSize) {
@@ -43,12 +50,8 @@ void InterpStack::clearTo(size_t NewSize) {
     return;
 
   assert(NewSize <= size());
-  for (PrimType Item : llvm::reverse(ItemTypes)) {
-    TYPE_SWITCH(Item, { this->discard<T>(); });
-
-    if (size() == NewSize)
-      break;
-  }
+  while (size() != NewSize)
+    TYPE_SWITCH(getNextObjectType(), { this->discard<T>(); });
 
   // Note: discard() above already removed the types from ItemTypes.
   assert(size() == NewSize);
@@ -96,16 +99,13 @@ void InterpStack::shrink(size_t Size) {
 }
 
 void InterpStack::dump() const {
-  llvm::errs() << "Items: " << ItemTypes.size() << ". Size: " << size() << 
'\n';
-  if (ItemTypes.empty())
-    return;
-
   size_t Index = 0;
   size_t Offset = 0;
 
   // The type of the item on the top of the stack is inserted to the back
   // of the vector, so the iteration has to happen backwards.
-  for (PrimType Item : llvm::reverse(ItemTypes)) {
+  while (Offset != size()) {
+    PrimType Item = *static_cast<PrimType *>(peekData(Offset + 1));
     Offset += align(primSize(Item));
 
     llvm::errs() << Index << '/' << Offset << ": ";
@@ -122,5 +122,5 @@ void InterpStack::dump() const {
 void InterpStack::discardSlow() {
   assert(!empty());
 
-  TYPE_SWITCH(ItemTypes.back(), { discard<T>(); });
+  TYPE_SWITCH(getNextObjectType(), { discard<T>(); });
 }
diff --git a/clang/lib/AST/ByteCode/InterpStack.h 
b/clang/lib/AST/ByteCode/InterpStack.h
index 2c02979ee6eec..faaab371fbf59 100644
--- a/clang/lib/AST/ByteCode/InterpStack.h
+++ b/clang/lib/AST/ByteCode/InterpStack.h
@@ -21,6 +21,15 @@
 namespace clang {
 namespace interp {
 
+template <class T>
+struct datasizeof_impl {
+  LLVM_NO_UNIQUE_ADDRESS T v;
+  char first_padding_byte;
+};
+
+template <class T>
+constexpr size_t datasizeof_v = offsetof(datasizeof_impl<T>, 
first_padding_byte);
+
 /// Stack frame storing temporaries and parameters.
 class InterpStack final {
 public:
@@ -29,40 +38,63 @@ class InterpStack final {
   /// Destroys the stack, freeing up storage.
   ~InterpStack();
 
+  template <size_t N> struct Padding {
+    char padding[N];
+  };
+
+  template <> struct Padding<0> {};
+
+  template <class T> struct alignas(void *) StackFrame {
+    static_assert(alignof(T) <= alignof(void *),
+                  "Unexpected overaligned object");
+
+    template <class... Args>
+    StackFrame(Args &&...args)
+        : v(std::forward<Args>(args)...), type(toPrimType<T>()) {}
+
+    static constexpr size_t getPaddingSize() {
+      if constexpr (sizeof(T) < sizeof(void*))
+        return sizeof(void*) - datasizeof_v<T> - 1;
+      else if constexpr (sizeof(T) == datasizeof_v<T>)
+        return sizeof(void*) - 1;
+      else
+        return sizeof(T) - datasizeof_v<T> - 1;
+    }
+
+    LLVM_NO_UNIQUE_ADDRESS T v;
+    LLVM_NO_UNIQUE_ADDRESS Padding<getPaddingSize()> padding;
+    PrimType type;
+  };
+
   /// Constructs a value in place on the top of the stack.
   template <typename T, typename... Tys> void push(Tys &&...Args) {
-    new (grow<aligned_size<T>()>()) T(std::forward<Tys>(Args)...);
-    ItemTypes.push_back(toPrimType<T>());
+    using Frame = StackFrame<T>;
+    new (grow<sizeof(Frame)>()) Frame(std::forward<Tys>(Args)...);
   }
 
   /// Returns the value from the top of the stack and removes it.
   template <typename T> T pop() {
-    assert(!ItemTypes.empty());
-    assert(ItemTypes.back() == toPrimType<T>());
-    ItemTypes.pop_back();
+    assert(getNextObjectType() == toPrimType<T>());
     T *Ptr = &peekInternal<T>();
     T Value = std::move(*Ptr);
-    shrink(aligned_size<T>());
+    shrink(sizeof(StackFrame<T>));
     return Value;
   }
 
   /// Discards the top value from the stack.
   template <typename T> void discard() {
-    assert(!ItemTypes.empty());
-    assert(ItemTypes.back() == toPrimType<T>());
-    ItemTypes.pop_back();
+    assert(getNextObjectType() == toPrimType<T>());
     T *Ptr = &peekInternal<T>();
     if constexpr (!std::is_trivially_destructible_v<T>) {
       Ptr->~T();
     }
-    shrink(aligned_size<T>());
+    shrink(sizeof(StackFrame<T>));
   }
   void discardSlow();
 
   /// Returns a reference to the value on the top of the stack.
   template <typename T> T &peek() const {
-    assert(!ItemTypes.empty());
-    assert(ItemTypes.back() == toPrimType<T>());
+    assert(getNextObjectType() == toPrimType<T>());
     return peekInternal<T>();
   }
 
@@ -88,16 +120,13 @@ class InterpStack final {
   void dump() const;
 
 private:
-  /// All stack slots are aligned to the native pointer alignment for storage.
-  /// The size of an object is rounded up to a pointer alignment multiple.
-  template <typename T> static constexpr size_t aligned_size() {
-    constexpr size_t PtrAlign = alignof(void *);
-    return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign;
+  PrimType getNextObjectType() const {
+    return *static_cast<PrimType *>(peekData(1));
   }
 
   /// Like the public peek(), but without the debug type checks.
   template <typename T> T &peekInternal() const {
-    return *reinterpret_cast<T *>(peekData(aligned_size<T>()));
+    return static_cast<StackFrame<T> *>(peekData(sizeof(StackFrame<T>)))->v;
   }
 
   /// Grows the stack to accommodate a value and returns a pointer to it.
@@ -163,12 +192,6 @@ class InterpStack final {
   /// Total size of the stack.
   size_t StackSize = 0;
 
-  /// SmallVector recording the type of data we pushed into the stack.
-  /// We don't usually need this during normal code interpretation but
-  /// when aborting, we need type information to call the destructors
-  /// for what's left on the stack.
-  llvm::SmallVector<PrimType> ItemTypes;
-
   template <typename T> static constexpr PrimType toPrimType() {
     if constexpr (std::is_same_v<T, Pointer>)
       return PT_Ptr;
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 54e0f858b4fa0..975f1cc15aa58 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -1334,7 +1334,6 @@ class Pointer {
   /// Offset into the storage.
   uint64_t Offset = 0;
 
-  Storage StorageKind = Storage::Int;
   union {
     IntPointer Int;
     BlockPointer BS;
@@ -1343,6 +1342,7 @@ class Pointer {
     StringPointer Str;
     OpaquePointer Opaque;
   };
+  Storage StorageKind = Storage::Int;
 };
 
 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {

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

Reply via email to