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