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