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