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