xref: /freebsd-src/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1a7dea167SDimitry Andric //===--- InterpStack.h - Stack implementation for the VM --------*- C++ -*-===//
2a7dea167SDimitry Andric //
3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a7dea167SDimitry Andric //
7a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
8a7dea167SDimitry Andric //
9a7dea167SDimitry Andric // Defines the upwards-growing stack used by the interpreter.
10a7dea167SDimitry Andric //
11a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
12a7dea167SDimitry Andric 
13a7dea167SDimitry Andric #ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H
14a7dea167SDimitry Andric #define LLVM_CLANG_AST_INTERP_INTERPSTACK_H
15a7dea167SDimitry Andric 
1606c3fb27SDimitry Andric #include "FunctionPointer.h"
175f757f3fSDimitry Andric #include "IntegralAP.h"
18*0fca6ea1SDimitry Andric #include "MemberPointer.h"
19bdd1243dSDimitry Andric #include "PrimType.h"
20a7dea167SDimitry Andric #include <memory>
21bdd1243dSDimitry Andric #include <vector>
22a7dea167SDimitry Andric 
23a7dea167SDimitry Andric namespace clang {
24a7dea167SDimitry Andric namespace interp {
25a7dea167SDimitry Andric 
26a7dea167SDimitry Andric /// Stack frame storing temporaries and parameters.
27a7dea167SDimitry Andric class InterpStack final {
28a7dea167SDimitry Andric public:
29a7dea167SDimitry Andric   InterpStack() {}
30a7dea167SDimitry Andric 
31a7dea167SDimitry Andric   /// Destroys the stack, freeing up storage.
32a7dea167SDimitry Andric   ~InterpStack();
33a7dea167SDimitry Andric 
34a7dea167SDimitry Andric   /// Constructs a value in place on the top of the stack.
35a7dea167SDimitry Andric   template <typename T, typename... Tys> void push(Tys &&... Args) {
36a7dea167SDimitry Andric     new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...);
37bdd1243dSDimitry Andric #ifndef NDEBUG
38bdd1243dSDimitry Andric     ItemTypes.push_back(toPrimType<T>());
39bdd1243dSDimitry Andric #endif
40a7dea167SDimitry Andric   }
41a7dea167SDimitry Andric 
42a7dea167SDimitry Andric   /// Returns the value from the top of the stack and removes it.
43a7dea167SDimitry Andric   template <typename T> T pop() {
44bdd1243dSDimitry Andric #ifndef NDEBUG
45bdd1243dSDimitry Andric     assert(!ItemTypes.empty());
46bdd1243dSDimitry Andric     assert(ItemTypes.back() == toPrimType<T>());
47bdd1243dSDimitry Andric     ItemTypes.pop_back();
48bdd1243dSDimitry Andric #endif
4906c3fb27SDimitry Andric     T *Ptr = &peekInternal<T>();
5006c3fb27SDimitry Andric     T Value = std::move(*Ptr);
51a7dea167SDimitry Andric     shrink(aligned_size<T>());
52a7dea167SDimitry Andric     return Value;
53a7dea167SDimitry Andric   }
54a7dea167SDimitry Andric 
55a7dea167SDimitry Andric   /// Discards the top value from the stack.
56a7dea167SDimitry Andric   template <typename T> void discard() {
57bdd1243dSDimitry Andric #ifndef NDEBUG
5806c3fb27SDimitry Andric     assert(!ItemTypes.empty());
59bdd1243dSDimitry Andric     assert(ItemTypes.back() == toPrimType<T>());
60bdd1243dSDimitry Andric     ItemTypes.pop_back();
61bdd1243dSDimitry Andric #endif
6206c3fb27SDimitry Andric     T *Ptr = &peekInternal<T>();
63a7dea167SDimitry Andric     Ptr->~T();
64a7dea167SDimitry Andric     shrink(aligned_size<T>());
65a7dea167SDimitry Andric   }
66a7dea167SDimitry Andric 
67a7dea167SDimitry Andric   /// Returns a reference to the value on the top of the stack.
68bdd1243dSDimitry Andric   template <typename T> T &peek() const {
6906c3fb27SDimitry Andric #ifndef NDEBUG
7006c3fb27SDimitry Andric     assert(!ItemTypes.empty());
7106c3fb27SDimitry Andric     assert(ItemTypes.back() == toPrimType<T>());
7206c3fb27SDimitry Andric #endif
7306c3fb27SDimitry Andric     return peekInternal<T>();
7406c3fb27SDimitry Andric   }
7506c3fb27SDimitry Andric 
7606c3fb27SDimitry Andric   template <typename T> T &peek(size_t Offset) const {
7706c3fb27SDimitry Andric     assert(aligned(Offset));
7806c3fb27SDimitry Andric     return *reinterpret_cast<T *>(peekData(Offset));
79a7dea167SDimitry Andric   }
80a7dea167SDimitry Andric 
81a7dea167SDimitry Andric   /// Returns a pointer to the top object.
8206c3fb27SDimitry Andric   void *top() const { return Chunk ? peekData(0) : nullptr; }
83a7dea167SDimitry Andric 
84a7dea167SDimitry Andric   /// Returns the size of the stack in bytes.
85a7dea167SDimitry Andric   size_t size() const { return StackSize; }
86a7dea167SDimitry Andric 
87a7dea167SDimitry Andric   /// Clears the stack without calling any destructors.
88a7dea167SDimitry Andric   void clear();
89a7dea167SDimitry Andric 
9006c3fb27SDimitry Andric   /// Returns whether the stack is empty.
91bdd1243dSDimitry Andric   bool empty() const { return StackSize == 0; }
92bdd1243dSDimitry Andric 
9306c3fb27SDimitry Andric   /// dump the stack contents to stderr.
9406c3fb27SDimitry Andric   void dump() const;
9506c3fb27SDimitry Andric 
96a7dea167SDimitry Andric private:
97a7dea167SDimitry Andric   /// All stack slots are aligned to the native pointer alignment for storage.
98a7dea167SDimitry Andric   /// The size of an object is rounded up to a pointer alignment multiple.
99a7dea167SDimitry Andric   template <typename T> constexpr size_t aligned_size() const {
100a7dea167SDimitry Andric     constexpr size_t PtrAlign = alignof(void *);
101a7dea167SDimitry Andric     return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign;
102a7dea167SDimitry Andric   }
103a7dea167SDimitry Andric 
10406c3fb27SDimitry Andric   /// Like the public peek(), but without the debug type checks.
10506c3fb27SDimitry Andric   template <typename T> T &peekInternal() const {
10606c3fb27SDimitry Andric     return *reinterpret_cast<T *>(peekData(aligned_size<T>()));
10706c3fb27SDimitry Andric   }
10806c3fb27SDimitry Andric 
109349cc55cSDimitry Andric   /// Grows the stack to accommodate a value and returns a pointer to it.
110a7dea167SDimitry Andric   void *grow(size_t Size);
111a7dea167SDimitry Andric   /// Returns a pointer from the top of the stack.
11206c3fb27SDimitry Andric   void *peekData(size_t Size) const;
113a7dea167SDimitry Andric   /// Shrinks the stack.
114a7dea167SDimitry Andric   void shrink(size_t Size);
115a7dea167SDimitry Andric 
116a7dea167SDimitry Andric   /// Allocate stack space in 1Mb chunks.
117a7dea167SDimitry Andric   static constexpr size_t ChunkSize = 1024 * 1024;
118a7dea167SDimitry Andric 
119a7dea167SDimitry Andric   /// Metadata for each stack chunk.
120a7dea167SDimitry Andric   ///
121a7dea167SDimitry Andric   /// The stack is composed of a linked list of chunks. Whenever an allocation
122a7dea167SDimitry Andric   /// is out of bounds, a new chunk is linked. When a chunk becomes empty,
123a7dea167SDimitry Andric   /// it is not immediately freed: a chunk is deallocated only when the
124a7dea167SDimitry Andric   /// predecessor becomes empty.
125a7dea167SDimitry Andric   struct StackChunk {
126a7dea167SDimitry Andric     StackChunk *Next;
127a7dea167SDimitry Andric     StackChunk *Prev;
128a7dea167SDimitry Andric     char *End;
129a7dea167SDimitry Andric 
130a7dea167SDimitry Andric     StackChunk(StackChunk *Prev = nullptr)
131a7dea167SDimitry Andric         : Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {}
132a7dea167SDimitry Andric 
133a7dea167SDimitry Andric     /// Returns the size of the chunk, minus the header.
134bdd1243dSDimitry Andric     size_t size() const { return End - start(); }
135a7dea167SDimitry Andric 
136a7dea167SDimitry Andric     /// Returns a pointer to the start of the data region.
137a7dea167SDimitry Andric     char *start() { return reinterpret_cast<char *>(this + 1); }
138bdd1243dSDimitry Andric     const char *start() const {
139bdd1243dSDimitry Andric       return reinterpret_cast<const char *>(this + 1);
140bdd1243dSDimitry Andric     }
141a7dea167SDimitry Andric   };
142a7dea167SDimitry Andric   static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size");
143a7dea167SDimitry Andric 
144a7dea167SDimitry Andric   /// First chunk on the stack.
145a7dea167SDimitry Andric   StackChunk *Chunk = nullptr;
146a7dea167SDimitry Andric   /// Total size of the stack.
147a7dea167SDimitry Andric   size_t StackSize = 0;
148bdd1243dSDimitry Andric 
149bdd1243dSDimitry Andric #ifndef NDEBUG
150bdd1243dSDimitry Andric   /// vector recording the type of data we pushed into the stack.
151bdd1243dSDimitry Andric   std::vector<PrimType> ItemTypes;
152bdd1243dSDimitry Andric 
153bdd1243dSDimitry Andric   template <typename T> static constexpr PrimType toPrimType() {
154bdd1243dSDimitry Andric     if constexpr (std::is_same_v<T, Pointer>)
155bdd1243dSDimitry Andric       return PT_Ptr;
156bdd1243dSDimitry Andric     else if constexpr (std::is_same_v<T, bool> ||
157bdd1243dSDimitry Andric                        std::is_same_v<T, Boolean>)
158bdd1243dSDimitry Andric       return PT_Bool;
159bdd1243dSDimitry Andric     else if constexpr (std::is_same_v<T, int8_t> ||
160bdd1243dSDimitry Andric                        std::is_same_v<T, Integral<8, true>>)
161bdd1243dSDimitry Andric       return PT_Sint8;
162bdd1243dSDimitry Andric     else if constexpr (std::is_same_v<T, uint8_t> ||
163bdd1243dSDimitry Andric                        std::is_same_v<T, Integral<8, false>>)
164bdd1243dSDimitry Andric       return PT_Uint8;
165bdd1243dSDimitry Andric     else if constexpr (std::is_same_v<T, int16_t> ||
166bdd1243dSDimitry Andric                        std::is_same_v<T, Integral<16, true>>)
167bdd1243dSDimitry Andric       return PT_Sint16;
168bdd1243dSDimitry Andric     else if constexpr (std::is_same_v<T, uint16_t> ||
169bdd1243dSDimitry Andric                        std::is_same_v<T, Integral<16, false>>)
170bdd1243dSDimitry Andric       return PT_Uint16;
171bdd1243dSDimitry Andric     else if constexpr (std::is_same_v<T, int32_t> ||
172bdd1243dSDimitry Andric                        std::is_same_v<T, Integral<32, true>>)
173bdd1243dSDimitry Andric       return PT_Sint32;
174bdd1243dSDimitry Andric     else if constexpr (std::is_same_v<T, uint32_t> ||
175bdd1243dSDimitry Andric                        std::is_same_v<T, Integral<32, false>>)
176bdd1243dSDimitry Andric       return PT_Uint32;
177bdd1243dSDimitry Andric     else if constexpr (std::is_same_v<T, int64_t> ||
178bdd1243dSDimitry Andric                        std::is_same_v<T, Integral<64, true>>)
179bdd1243dSDimitry Andric       return PT_Sint64;
180bdd1243dSDimitry Andric     else if constexpr (std::is_same_v<T, uint64_t> ||
181bdd1243dSDimitry Andric                        std::is_same_v<T, Integral<64, false>>)
182bdd1243dSDimitry Andric       return PT_Uint64;
18306c3fb27SDimitry Andric     else if constexpr (std::is_same_v<T, Floating>)
18406c3fb27SDimitry Andric       return PT_Float;
18506c3fb27SDimitry Andric     else if constexpr (std::is_same_v<T, FunctionPointer>)
18606c3fb27SDimitry Andric       return PT_FnPtr;
1875f757f3fSDimitry Andric     else if constexpr (std::is_same_v<T, IntegralAP<true>>)
1885f757f3fSDimitry Andric       return PT_IntAP;
1895f757f3fSDimitry Andric     else if constexpr (std::is_same_v<T, IntegralAP<false>>)
1905f757f3fSDimitry Andric       return PT_IntAP;
191*0fca6ea1SDimitry Andric     else if constexpr (std::is_same_v<T, MemberPointer>)
192*0fca6ea1SDimitry Andric       return PT_MemberPtr;
193bdd1243dSDimitry Andric 
194bdd1243dSDimitry Andric     llvm_unreachable("unknown type push()'ed into InterpStack");
195bdd1243dSDimitry Andric   }
196bdd1243dSDimitry Andric #endif
197a7dea167SDimitry Andric };
198a7dea167SDimitry Andric 
199a7dea167SDimitry Andric } // namespace interp
200a7dea167SDimitry Andric } // namespace clang
201a7dea167SDimitry Andric 
202a7dea167SDimitry Andric #endif
203