Skip to content

Commit 29620d9

Browse files
authored
[clang][bytecode] Optimize InterpStack (llvm#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.
1 parent dcd0a2e commit 29620d9

File tree

2 files changed

+43
-33
lines changed

2 files changed

+43
-33
lines changed

clang/lib/AST/ByteCode/InterpStack.cpp

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ InterpStack::~InterpStack() {
2424
std::free(Chunk->Next);
2525
if (Chunk)
2626
std::free(Chunk);
27-
Chunk = nullptr;
28-
StackSize = 0;
29-
ItemTypes.clear();
3027
}
3128

3229
// We keep the last chunk around to reuse.
@@ -56,54 +53,44 @@ void InterpStack::clearTo(size_t NewSize) {
5653
assert(size() == NewSize);
5754
}
5855

59-
void *InterpStack::grow(size_t Size) {
60-
assert(Size < ChunkSize - sizeof(StackChunk) && "Object too large");
61-
62-
if (!Chunk || sizeof(StackChunk) + Chunk->size() + Size > ChunkSize) {
63-
if (Chunk && Chunk->Next) {
64-
Chunk = Chunk->Next;
65-
} else {
66-
StackChunk *Next = new (std::malloc(ChunkSize)) StackChunk(Chunk);
67-
if (Chunk)
68-
Chunk->Next = Next;
69-
Chunk = Next;
70-
}
71-
}
72-
73-
auto *Object = reinterpret_cast<void *>(Chunk->End);
74-
Chunk->End += Size;
75-
StackSize += Size;
76-
return Object;
77-
}
78-
7956
void *InterpStack::peekData(size_t Size) const {
8057
assert(Chunk && "Stack is empty!");
8158

59+
if (LLVM_LIKELY(Size <= Chunk->size()))
60+
return reinterpret_cast<void *>(Chunk->start() + Chunk->Size - Size);
61+
8262
StackChunk *Ptr = Chunk;
8363
while (Size > Ptr->size()) {
8464
Size -= Ptr->size();
8565
Ptr = Ptr->Prev;
8666
assert(Ptr && "Offset too large");
8767
}
8868

89-
return reinterpret_cast<void *>(Ptr->End - Size);
69+
return reinterpret_cast<void *>(Ptr->start() + Ptr->Size - Size);
9070
}
9171

9272
void InterpStack::shrink(size_t Size) {
9373
assert(Chunk && "Chunk is empty!");
9474

75+
// Likely case is that we simply remove something from the current chunk.
76+
if (LLVM_LIKELY(Size <= Chunk->size())) {
77+
Chunk->Size -= Size;
78+
StackSize -= Size;
79+
return;
80+
}
81+
9582
while (Size > Chunk->size()) {
9683
Size -= Chunk->size();
9784
if (Chunk->Next) {
9885
std::free(Chunk->Next);
9986
Chunk->Next = nullptr;
10087
}
101-
Chunk->End = Chunk->start();
88+
Chunk->Size = 0;
10289
Chunk = Chunk->Prev;
10390
assert(Chunk && "Offset too large");
10491
}
10592

106-
Chunk->End -= Size;
93+
Chunk->Size -= Size;
10794
StackSize -= Size;
10895
}
10996

clang/lib/AST/ByteCode/InterpStack.h

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ namespace interp {
2424
/// Stack frame storing temporaries and parameters.
2525
class InterpStack final {
2626
public:
27-
InterpStack() {}
27+
InterpStack() = default;
2828

2929
/// Destroys the stack, freeing up storage.
3030
~InterpStack();
3131

3232
/// Constructs a value in place on the top of the stack.
3333
template <typename T, typename... Tys> void push(Tys &&...Args) {
34-
new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...);
34+
new (grow<aligned_size<T>()>()) T(std::forward<Tys>(Args)...);
3535
ItemTypes.push_back(toPrimType<T>());
3636
}
3737

@@ -89,7 +89,7 @@ class InterpStack final {
8989
private:
9090
/// All stack slots are aligned to the native pointer alignment for storage.
9191
/// The size of an object is rounded up to a pointer alignment multiple.
92-
template <typename T> constexpr size_t aligned_size() const {
92+
template <typename T> static constexpr size_t aligned_size() {
9393
constexpr size_t PtrAlign = alignof(void *);
9494
return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign;
9595
}
@@ -100,7 +100,30 @@ class InterpStack final {
100100
}
101101

102102
/// Grows the stack to accommodate a value and returns a pointer to it.
103-
void *grow(size_t Size);
103+
template <size_t Size> void *grow() {
104+
assert(Size < ChunkSize - sizeof(StackChunk) && "Object too large");
105+
static_assert(aligned(Size));
106+
107+
// Allocate a new stack chunk if necessary.
108+
if (LLVM_UNLIKELY(!Chunk)) {
109+
Chunk = new (std::malloc(ChunkSize)) StackChunk(Chunk);
110+
} else if (LLVM_UNLIKELY(Chunk->size() >
111+
ChunkSize - sizeof(StackChunk) - Size)) {
112+
if (Chunk->Next) {
113+
Chunk = Chunk->Next;
114+
} else {
115+
StackChunk *Next = new (std::malloc(ChunkSize)) StackChunk(Chunk);
116+
Chunk->Next = Next;
117+
Chunk = Next;
118+
}
119+
}
120+
121+
auto *Object = reinterpret_cast<void *>(Chunk->start() + Chunk->Size);
122+
Chunk->Size += Size;
123+
StackSize += Size;
124+
return Object;
125+
}
126+
104127
/// Returns a pointer from the top of the stack.
105128
void *peekData(size_t Size) const;
106129
/// Shrinks the stack.
@@ -118,13 +141,13 @@ class InterpStack final {
118141
struct StackChunk {
119142
StackChunk *Next;
120143
StackChunk *Prev;
121-
char *End;
144+
uint32_t Size;
122145

123146
StackChunk(StackChunk *Prev = nullptr)
124-
: Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {}
147+
: Next(nullptr), Prev(Prev), Size(0) {}
125148

126149
/// Returns the size of the chunk, minus the header.
127-
size_t size() const { return End - start(); }
150+
size_t size() const { return Size; }
128151

129152
/// Returns a pointer to the start of the data region.
130153
char *start() { return reinterpret_cast<char *>(this + 1); }

0 commit comments

Comments
 (0)