clang 24.0.0git
InterpStack.h
Go to the documentation of this file.
1//===--- InterpStack.h - Stack implementation for the VM --------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Defines the upwards-growing stack used by the interpreter.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H
14#define LLVM_CLANG_AST_INTERP_INTERPSTACK_H
15
16#include "FixedPoint.h"
17#include "IntegralAP.h"
18#include "MemberPointer.h"
19#include "PrimType.h"
20
21namespace clang {
22namespace interp {
23
24/// Stack frame storing temporaries and parameters.
25class InterpStack final {
26public:
27 InterpStack() = default;
28
29 /// Destroys the stack, freeing up storage.
31
32 /// Constructs a value in place on the top of the stack.
33 template <typename T, typename... Tys> void push(Tys &&...Args) {
34 new (grow<aligned_size<T>()>()) T(std::forward<Tys>(Args)...);
35 ItemTypes.push_back(toPrimType<T>());
36 }
37
38 /// Returns the value from the top of the stack and removes it.
39 template <typename T> T pop() {
40 assert(!ItemTypes.empty());
41 assert(ItemTypes.back() == toPrimType<T>());
42 ItemTypes.pop_back();
43 T *Ptr = &peekInternal<T>();
44 T Value = std::move(*Ptr);
45 shrink(aligned_size<T>());
46 return Value;
47 }
48
49 /// Discards the top value from the stack.
50 template <typename T> void discard() {
51 assert(!ItemTypes.empty());
52 assert(ItemTypes.back() == toPrimType<T>());
53 ItemTypes.pop_back();
54 T *Ptr = &peekInternal<T>();
55 if constexpr (!std::is_trivially_destructible_v<T>) {
56 Ptr->~T();
57 }
58 shrink(aligned_size<T>());
59 }
60 void discardSlow();
61
62 /// Returns a reference to the value on the top of the stack.
63 template <typename T> T &peek() const {
64 assert(!ItemTypes.empty());
65 assert(ItemTypes.back() == toPrimType<T>());
66 return peekInternal<T>();
67 }
68
69 template <typename T> T &peek(size_t Offset) const {
70 assert(aligned(Offset));
71 return *reinterpret_cast<T *>(peekData(Offset));
72 }
73
74 /// Returns a pointer to the top object.
75 void *top() const { return Chunk ? peekData(0) : nullptr; }
76
77 /// Returns the size of the stack in bytes.
78 size_t size() const { return StackSize; }
79
80 /// Clears the stack.
81 void clear();
82 void clearTo(size_t NewSize);
83
84 /// Returns whether the stack is empty.
85 bool empty() const { return StackSize == 0; }
86
87 /// dump the stack contents to stderr.
88 void dump() const;
89
90private:
91 /// All stack slots are aligned to the native pointer alignment for storage.
92 /// The size of an object is rounded up to a pointer alignment multiple.
93 template <typename T> static constexpr size_t aligned_size() {
94 constexpr size_t PtrAlign = alignof(void *);
95 return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign;
96 }
97
98 /// Like the public peek(), but without the debug type checks.
99 template <typename T> T &peekInternal() const {
100 return *reinterpret_cast<T *>(peekData(aligned_size<T>()));
101 }
102
103 /// Grows the stack to accommodate a value and returns a pointer to it.
104 template <size_t Size> void *grow() {
105 assert(Size < ChunkSize - sizeof(StackChunk) && "Object too large");
106 static_assert(aligned(Size));
107
108 // Allocate a new stack chunk if necessary.
109 if (LLVM_UNLIKELY(!Chunk)) {
110 Chunk = new (std::malloc(ChunkSize)) StackChunk(Chunk);
111 } else if (LLVM_UNLIKELY(Chunk->size() >
112 ChunkSize - sizeof(StackChunk) - Size)) {
113 if (Chunk->Next) {
114 Chunk = Chunk->Next;
115 } else {
116 StackChunk *Next = new (std::malloc(ChunkSize)) StackChunk(Chunk);
117 Chunk->Next = Next;
118 Chunk = Next;
119 }
120 }
121
122 auto *Object = reinterpret_cast<void *>(Chunk->start() + Chunk->Size);
123 Chunk->Size += Size;
124 StackSize += Size;
125 return Object;
126 }
127
128 void *peekDataSlow(size_t Size) const;
129 /// Returns a pointer from the top of the stack.
130 void *peekData(size_t Size) const {
131 assert(Chunk && "Stack is empty!");
132 if (LLVM_LIKELY(Size <= Chunk->size()))
133 return reinterpret_cast<void *>(Chunk->start() + Chunk->Size - Size);
134
135 return peekDataSlow(Size);
136 }
137
138 void shrinkSlow(size_t Size);
139 /// Shrinks the stack.
140 void shrink(size_t Size) {
141 assert(Chunk && "Chunk is empty!");
142
143 // Likely case is that we simply remove something from the current chunk.
144 if (LLVM_LIKELY(Size <= Chunk->size())) {
145 Chunk->Size -= Size;
146 StackSize -= Size;
147 return;
148 }
149
150 shrinkSlow(Size);
151 }
152
153 /// Allocate stack space in 1Mb chunks.
154 static constexpr size_t ChunkSize = 1024 * 1024;
155
156 /// Metadata for each stack chunk.
157 ///
158 /// The stack is composed of a linked list of chunks. Whenever an allocation
159 /// is out of bounds, a new chunk is linked. When a chunk becomes empty,
160 /// it is not immediately freed: a chunk is deallocated only when the
161 /// predecessor becomes empty.
162 struct StackChunk {
163 StackChunk *Next;
164 StackChunk *Prev;
165 uint32_t Size;
166
167 StackChunk(StackChunk *Prev = nullptr)
168 : Next(nullptr), Prev(Prev), Size(0) {}
169
170 /// Returns the size of the chunk, minus the header.
171 size_t size() const { return Size; }
172
173 /// Returns a pointer to the start of the data region.
174 char *start() { return reinterpret_cast<char *>(this + 1); }
175 const char *start() const {
176 return reinterpret_cast<const char *>(this + 1);
177 }
178 };
179 static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size");
180
181 /// First chunk on the stack.
182 StackChunk *Chunk = nullptr;
183 /// Total size of the stack.
184 size_t StackSize = 0;
185
186 /// SmallVector recording the type of data we pushed into the stack.
187 /// We don't usually need this during normal code interpretation but
188 /// when aborting, we need type information to call the destructors
189 /// for what's left on the stack.
190 llvm::SmallVector<PrimType> ItemTypes;
191
192 template <typename T> static constexpr PrimType toPrimType() {
193 if constexpr (std::is_same_v<T, Pointer>)
194 return PT_Ptr;
195 else if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, Boolean>)
196 return PT_Bool;
197 else if constexpr (std::is_same_v<T, int8_t> ||
198 std::is_same_v<T, Char<true>>)
199 return PT_Sint8;
200 else if constexpr (std::is_same_v<T, uint8_t> ||
201 std::is_same_v<T, Char<false>>)
202 return PT_Uint8;
203 else if constexpr (std::is_same_v<T, Integral<16, true>>)
204 return PT_Sint16;
205 else if constexpr (std::is_same_v<T, Integral<16, false>>)
206 return PT_Uint16;
207 else if constexpr (std::is_same_v<T, Integral<32, true>>)
208 return PT_Sint32;
209 else if constexpr (std::is_same_v<T, Integral<32, false>>)
210 return PT_Uint32;
211 else if constexpr (std::is_same_v<T, Integral<64, true>>)
212 return PT_Sint64;
213 else if constexpr (std::is_same_v<T, Integral<64, false>>)
214 return PT_Uint64;
215
216 else if constexpr (std::is_same_v<T, Floating>)
217 return PT_Float;
218 else if constexpr (std::is_same_v<T, IntegralAP<true>>)
219 return PT_IntAP;
220 else if constexpr (std::is_same_v<T, IntegralAP<false>>)
221 return PT_IntAP;
222 else if constexpr (std::is_same_v<T, MemberPointer>)
223 return PT_MemberPtr;
224 else if constexpr (std::is_same_v<T, FixedPoint>)
225 return PT_FixedPoint;
226
227 llvm_unreachable("unknown type push()'ed into InterpStack");
228 }
229};
230
231} // namespace interp
232} // namespace clang
233
234#endif
FormatToken * Next
The next token in the unwrapped line.
llvm::json::Object Object
void clearTo(size_t NewSize)
T pop()
Returns the value from the top of the stack and removes it.
Definition InterpStack.h:39
void push(Tys &&...Args)
Constructs a value in place on the top of the stack.
Definition InterpStack.h:33
T & peek(size_t Offset) const
Definition InterpStack.h:69
void dump() const
dump the stack contents to stderr.
void * top() const
Returns a pointer to the top object.
Definition InterpStack.h:75
void clear()
Clears the stack.
size_t size() const
Returns the size of the stack in bytes.
Definition InterpStack.h:78
bool empty() const
Returns whether the stack is empty.
Definition InterpStack.h:85
void discard()
Discards the top value from the stack.
Definition InterpStack.h:50
~InterpStack()
Destroys the stack, freeing up storage.
T & peek() const
Returns a reference to the value on the top of the stack.
Definition InterpStack.h:63
constexpr bool aligned(uintptr_t Value)
Definition PrimType.h:217
PrimType
Enumeration of the primitive types of the VM.
Definition PrimType.h:34
Top level wrappers for InstallAPI frontend operations.
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
const FunctionProtoType * T
__builtin_elementwise_add_sat __builtin_elementwise_sub_sat uint32_t __packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 __packed_splat2 __packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 uint32_t