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