[clang][bytecode] Optimize InterpStack (#159400)

Replace `StackChunk::End` with `StackChunk::Size`, mark the allocating
code paths as unlikely and move `grow()` into the header, which allows
us to template this for the `Size` parameter. Since we only push our
primitive types on the stack and all the sizes are aligned to pointer
size multiples, this only results in a few instantiations.
This commit is contained in:
Timm Baeder
2025-09-18 15:26:02 +02:00
committed by GitHub
parent dcd0a2eecc
commit 29620d9b89
2 changed files with 43 additions and 33 deletions

View File

@@ -24,9 +24,6 @@ InterpStack::~InterpStack() {
std::free(Chunk->Next);
if (Chunk)
std::free(Chunk);
Chunk = nullptr;
StackSize = 0;
ItemTypes.clear();
}
// We keep the last chunk around to reuse.
@@ -56,29 +53,12 @@ void InterpStack::clearTo(size_t NewSize) {
assert(size() == NewSize);
}
void *InterpStack::grow(size_t Size) {
assert(Size < ChunkSize - sizeof(StackChunk) && "Object too large");
if (!Chunk || sizeof(StackChunk) + Chunk->size() + Size > ChunkSize) {
if (Chunk && Chunk->Next) {
Chunk = Chunk->Next;
} else {
StackChunk *Next = new (std::malloc(ChunkSize)) StackChunk(Chunk);
if (Chunk)
Chunk->Next = Next;
Chunk = Next;
}
}
auto *Object = reinterpret_cast<void *>(Chunk->End);
Chunk->End += Size;
StackSize += Size;
return Object;
}
void *InterpStack::peekData(size_t Size) const {
assert(Chunk && "Stack is empty!");
if (LLVM_LIKELY(Size <= Chunk->size()))
return reinterpret_cast<void *>(Chunk->start() + Chunk->Size - Size);
StackChunk *Ptr = Chunk;
while (Size > Ptr->size()) {
Size -= Ptr->size();
@@ -86,24 +66,31 @@ void *InterpStack::peekData(size_t Size) const {
assert(Ptr && "Offset too large");
}
return reinterpret_cast<void *>(Ptr->End - Size);
return reinterpret_cast<void *>(Ptr->start() + Ptr->Size - Size);
}
void InterpStack::shrink(size_t Size) {
assert(Chunk && "Chunk is empty!");
// Likely case is that we simply remove something from the current chunk.
if (LLVM_LIKELY(Size <= Chunk->size())) {
Chunk->Size -= Size;
StackSize -= Size;
return;
}
while (Size > Chunk->size()) {
Size -= Chunk->size();
if (Chunk->Next) {
std::free(Chunk->Next);
Chunk->Next = nullptr;
}
Chunk->End = Chunk->start();
Chunk->Size = 0;
Chunk = Chunk->Prev;
assert(Chunk && "Offset too large");
}
Chunk->End -= Size;
Chunk->Size -= Size;
StackSize -= Size;
}

View File

@@ -24,14 +24,14 @@ namespace interp {
/// Stack frame storing temporaries and parameters.
class InterpStack final {
public:
InterpStack() {}
InterpStack() = default;
/// Destroys the stack, freeing up storage.
~InterpStack();
/// 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)...);
new (grow<aligned_size<T>()>()) T(std::forward<Tys>(Args)...);
ItemTypes.push_back(toPrimType<T>());
}
@@ -89,7 +89,7 @@ public:
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> constexpr size_t aligned_size() const {
template <typename T> static constexpr size_t aligned_size() {
constexpr size_t PtrAlign = alignof(void *);
return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign;
}
@@ -100,7 +100,30 @@ private:
}
/// Grows the stack to accommodate a value and returns a pointer to it.
void *grow(size_t Size);
template <size_t Size> void *grow() {
assert(Size < ChunkSize - sizeof(StackChunk) && "Object too large");
static_assert(aligned(Size));
// Allocate a new stack chunk if necessary.
if (LLVM_UNLIKELY(!Chunk)) {
Chunk = new (std::malloc(ChunkSize)) StackChunk(Chunk);
} else if (LLVM_UNLIKELY(Chunk->size() >
ChunkSize - sizeof(StackChunk) - Size)) {
if (Chunk->Next) {
Chunk = Chunk->Next;
} else {
StackChunk *Next = new (std::malloc(ChunkSize)) StackChunk(Chunk);
Chunk->Next = Next;
Chunk = Next;
}
}
auto *Object = reinterpret_cast<void *>(Chunk->start() + Chunk->Size);
Chunk->Size += Size;
StackSize += Size;
return Object;
}
/// Returns a pointer from the top of the stack.
void *peekData(size_t Size) const;
/// Shrinks the stack.
@@ -118,13 +141,13 @@ private:
struct StackChunk {
StackChunk *Next;
StackChunk *Prev;
char *End;
uint32_t Size;
StackChunk(StackChunk *Prev = nullptr)
: Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {}
: Next(nullptr), Prev(Prev), Size(0) {}
/// Returns the size of the chunk, minus the header.
size_t size() const { return End - start(); }
size_t size() const { return Size; }
/// Returns a pointer to the start of the data region.
char *start() { return reinterpret_cast<char *>(this + 1); }