xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/AST/Interp/InterpStack.h (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1*7330f729Sjoerg //===--- InterpStack.h - Stack implementation for the VM --------*- C++ -*-===//
2*7330f729Sjoerg //
3*7330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*7330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*7330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*7330f729Sjoerg //
7*7330f729Sjoerg //===----------------------------------------------------------------------===//
8*7330f729Sjoerg //
9*7330f729Sjoerg // Defines the upwards-growing stack used by the interpreter.
10*7330f729Sjoerg //
11*7330f729Sjoerg //===----------------------------------------------------------------------===//
12*7330f729Sjoerg 
13*7330f729Sjoerg #ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H
14*7330f729Sjoerg #define LLVM_CLANG_AST_INTERP_INTERPSTACK_H
15*7330f729Sjoerg 
16*7330f729Sjoerg #include <memory>
17*7330f729Sjoerg 
18*7330f729Sjoerg namespace clang {
19*7330f729Sjoerg namespace interp {
20*7330f729Sjoerg 
21*7330f729Sjoerg /// Stack frame storing temporaries and parameters.
22*7330f729Sjoerg class InterpStack final {
23*7330f729Sjoerg public:
InterpStack()24*7330f729Sjoerg   InterpStack() {}
25*7330f729Sjoerg 
26*7330f729Sjoerg   /// Destroys the stack, freeing up storage.
27*7330f729Sjoerg   ~InterpStack();
28*7330f729Sjoerg 
29*7330f729Sjoerg   /// Constructs a value in place on the top of the stack.
push(Tys &&...Args)30*7330f729Sjoerg   template <typename T, typename... Tys> void push(Tys &&... Args) {
31*7330f729Sjoerg     new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...);
32*7330f729Sjoerg   }
33*7330f729Sjoerg 
34*7330f729Sjoerg   /// Returns the value from the top of the stack and removes it.
pop()35*7330f729Sjoerg   template <typename T> T pop() {
36*7330f729Sjoerg     auto *Ptr = &peek<T>();
37*7330f729Sjoerg     auto Value = std::move(*Ptr);
38*7330f729Sjoerg     Ptr->~T();
39*7330f729Sjoerg     shrink(aligned_size<T>());
40*7330f729Sjoerg     return Value;
41*7330f729Sjoerg   }
42*7330f729Sjoerg 
43*7330f729Sjoerg   /// Discards the top value from the stack.
discard()44*7330f729Sjoerg   template <typename T> void discard() {
45*7330f729Sjoerg     auto *Ptr = &peek<T>();
46*7330f729Sjoerg     Ptr->~T();
47*7330f729Sjoerg     shrink(aligned_size<T>());
48*7330f729Sjoerg   }
49*7330f729Sjoerg 
50*7330f729Sjoerg   /// Returns a reference to the value on the top of the stack.
peek()51*7330f729Sjoerg   template <typename T> T &peek() {
52*7330f729Sjoerg     return *reinterpret_cast<T *>(peek(aligned_size<T>()));
53*7330f729Sjoerg   }
54*7330f729Sjoerg 
55*7330f729Sjoerg   /// Returns a pointer to the top object.
top()56*7330f729Sjoerg   void *top() { return Chunk ? peek(0) : nullptr; }
57*7330f729Sjoerg 
58*7330f729Sjoerg   /// Returns the size of the stack in bytes.
size()59*7330f729Sjoerg   size_t size() const { return StackSize; }
60*7330f729Sjoerg 
61*7330f729Sjoerg   /// Clears the stack without calling any destructors.
62*7330f729Sjoerg   void clear();
63*7330f729Sjoerg 
64*7330f729Sjoerg private:
65*7330f729Sjoerg   /// All stack slots are aligned to the native pointer alignment for storage.
66*7330f729Sjoerg   /// The size of an object is rounded up to a pointer alignment multiple.
aligned_size()67*7330f729Sjoerg   template <typename T> constexpr size_t aligned_size() const {
68*7330f729Sjoerg     constexpr size_t PtrAlign = alignof(void *);
69*7330f729Sjoerg     return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign;
70*7330f729Sjoerg   }
71*7330f729Sjoerg 
72*7330f729Sjoerg   /// Grows the stack to accomodate a value and returns a pointer to it.
73*7330f729Sjoerg   void *grow(size_t Size);
74*7330f729Sjoerg   /// Returns a pointer from the top of the stack.
75*7330f729Sjoerg   void *peek(size_t Size);
76*7330f729Sjoerg   /// Shrinks the stack.
77*7330f729Sjoerg   void shrink(size_t Size);
78*7330f729Sjoerg 
79*7330f729Sjoerg   /// Allocate stack space in 1Mb chunks.
80*7330f729Sjoerg   static constexpr size_t ChunkSize = 1024 * 1024;
81*7330f729Sjoerg 
82*7330f729Sjoerg   /// Metadata for each stack chunk.
83*7330f729Sjoerg   ///
84*7330f729Sjoerg   /// The stack is composed of a linked list of chunks. Whenever an allocation
85*7330f729Sjoerg   /// is out of bounds, a new chunk is linked. When a chunk becomes empty,
86*7330f729Sjoerg   /// it is not immediately freed: a chunk is deallocated only when the
87*7330f729Sjoerg   /// predecessor becomes empty.
88*7330f729Sjoerg   struct StackChunk {
89*7330f729Sjoerg     StackChunk *Next;
90*7330f729Sjoerg     StackChunk *Prev;
91*7330f729Sjoerg     char *End;
92*7330f729Sjoerg 
93*7330f729Sjoerg     StackChunk(StackChunk *Prev = nullptr)
NextStackChunk94*7330f729Sjoerg         : Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {}
95*7330f729Sjoerg 
96*7330f729Sjoerg     /// Returns the size of the chunk, minus the header.
sizeStackChunk97*7330f729Sjoerg     size_t size() { return End - start(); }
98*7330f729Sjoerg 
99*7330f729Sjoerg     /// Returns a pointer to the start of the data region.
startStackChunk100*7330f729Sjoerg     char *start() { return reinterpret_cast<char *>(this + 1); }
101*7330f729Sjoerg   };
102*7330f729Sjoerg   static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size");
103*7330f729Sjoerg 
104*7330f729Sjoerg   /// First chunk on the stack.
105*7330f729Sjoerg   StackChunk *Chunk = nullptr;
106*7330f729Sjoerg   /// Total size of the stack.
107*7330f729Sjoerg   size_t StackSize = 0;
108*7330f729Sjoerg };
109*7330f729Sjoerg 
110*7330f729Sjoerg } // namespace interp
111*7330f729Sjoerg } // namespace clang
112*7330f729Sjoerg 
113*7330f729Sjoerg #endif
114