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