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