1a7dea167SDimitry Andric //===--- InterpStack.h - Stack implementation for the VM --------*- C++ -*-===// 2a7dea167SDimitry Andric // 3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a7dea167SDimitry Andric // 7a7dea167SDimitry Andric //===----------------------------------------------------------------------===// 8a7dea167SDimitry Andric // 9a7dea167SDimitry Andric // Defines the upwards-growing stack used by the interpreter. 10a7dea167SDimitry Andric // 11a7dea167SDimitry Andric //===----------------------------------------------------------------------===// 12a7dea167SDimitry Andric 13a7dea167SDimitry Andric #ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H 14a7dea167SDimitry Andric #define LLVM_CLANG_AST_INTERP_INTERPSTACK_H 15a7dea167SDimitry Andric 16*06c3fb27SDimitry Andric #include "FunctionPointer.h" 17bdd1243dSDimitry Andric #include "PrimType.h" 18a7dea167SDimitry Andric #include <memory> 19bdd1243dSDimitry Andric #include <vector> 20a7dea167SDimitry Andric 21a7dea167SDimitry Andric namespace clang { 22a7dea167SDimitry Andric namespace interp { 23a7dea167SDimitry Andric 24a7dea167SDimitry Andric /// Stack frame storing temporaries and parameters. 25a7dea167SDimitry Andric class InterpStack final { 26a7dea167SDimitry Andric public: 27a7dea167SDimitry Andric InterpStack() {} 28a7dea167SDimitry Andric 29a7dea167SDimitry Andric /// Destroys the stack, freeing up storage. 30a7dea167SDimitry Andric ~InterpStack(); 31a7dea167SDimitry Andric 32a7dea167SDimitry Andric /// Constructs a value in place on the top of the stack. 33a7dea167SDimitry Andric template <typename T, typename... Tys> void push(Tys &&... Args) { 34a7dea167SDimitry Andric new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...); 35bdd1243dSDimitry Andric #ifndef NDEBUG 36bdd1243dSDimitry Andric ItemTypes.push_back(toPrimType<T>()); 37bdd1243dSDimitry Andric #endif 38a7dea167SDimitry Andric } 39a7dea167SDimitry Andric 40a7dea167SDimitry Andric /// Returns the value from the top of the stack and removes it. 41a7dea167SDimitry Andric template <typename T> T pop() { 42bdd1243dSDimitry Andric #ifndef NDEBUG 43bdd1243dSDimitry Andric assert(!ItemTypes.empty()); 44bdd1243dSDimitry Andric assert(ItemTypes.back() == toPrimType<T>()); 45bdd1243dSDimitry Andric ItemTypes.pop_back(); 46bdd1243dSDimitry Andric #endif 47*06c3fb27SDimitry Andric T *Ptr = &peekInternal<T>(); 48*06c3fb27SDimitry Andric T Value = std::move(*Ptr); 49a7dea167SDimitry Andric Ptr->~T(); 50a7dea167SDimitry Andric shrink(aligned_size<T>()); 51a7dea167SDimitry Andric return Value; 52a7dea167SDimitry Andric } 53a7dea167SDimitry Andric 54a7dea167SDimitry Andric /// Discards the top value from the stack. 55a7dea167SDimitry Andric template <typename T> void discard() { 56bdd1243dSDimitry Andric #ifndef NDEBUG 57*06c3fb27SDimitry Andric assert(!ItemTypes.empty()); 58bdd1243dSDimitry Andric assert(ItemTypes.back() == toPrimType<T>()); 59bdd1243dSDimitry Andric ItemTypes.pop_back(); 60bdd1243dSDimitry Andric #endif 61*06c3fb27SDimitry Andric T *Ptr = &peekInternal<T>(); 62a7dea167SDimitry Andric Ptr->~T(); 63a7dea167SDimitry Andric shrink(aligned_size<T>()); 64a7dea167SDimitry Andric } 65a7dea167SDimitry Andric 66a7dea167SDimitry Andric /// Returns a reference to the value on the top of the stack. 67bdd1243dSDimitry Andric template <typename T> T &peek() const { 68*06c3fb27SDimitry Andric #ifndef NDEBUG 69*06c3fb27SDimitry Andric assert(!ItemTypes.empty()); 70*06c3fb27SDimitry Andric assert(ItemTypes.back() == toPrimType<T>()); 71*06c3fb27SDimitry Andric #endif 72*06c3fb27SDimitry Andric return peekInternal<T>(); 73*06c3fb27SDimitry Andric } 74*06c3fb27SDimitry Andric 75*06c3fb27SDimitry Andric template <typename T> T &peek(size_t Offset) const { 76*06c3fb27SDimitry Andric assert(aligned(Offset)); 77*06c3fb27SDimitry Andric return *reinterpret_cast<T *>(peekData(Offset)); 78a7dea167SDimitry Andric } 79a7dea167SDimitry Andric 80a7dea167SDimitry Andric /// Returns a pointer to the top object. 81*06c3fb27SDimitry Andric void *top() const { return Chunk ? peekData(0) : nullptr; } 82a7dea167SDimitry Andric 83a7dea167SDimitry Andric /// Returns the size of the stack in bytes. 84a7dea167SDimitry Andric size_t size() const { return StackSize; } 85a7dea167SDimitry Andric 86a7dea167SDimitry Andric /// Clears the stack without calling any destructors. 87a7dea167SDimitry Andric void clear(); 88a7dea167SDimitry Andric 89*06c3fb27SDimitry Andric /// Returns whether the stack is empty. 90bdd1243dSDimitry Andric bool empty() const { return StackSize == 0; } 91bdd1243dSDimitry Andric 92*06c3fb27SDimitry Andric /// dump the stack contents to stderr. 93*06c3fb27SDimitry Andric void dump() const; 94*06c3fb27SDimitry Andric 95a7dea167SDimitry Andric private: 96a7dea167SDimitry Andric /// All stack slots are aligned to the native pointer alignment for storage. 97a7dea167SDimitry Andric /// The size of an object is rounded up to a pointer alignment multiple. 98a7dea167SDimitry Andric template <typename T> constexpr size_t aligned_size() const { 99a7dea167SDimitry Andric constexpr size_t PtrAlign = alignof(void *); 100a7dea167SDimitry Andric return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign; 101a7dea167SDimitry Andric } 102a7dea167SDimitry Andric 103*06c3fb27SDimitry Andric /// Like the public peek(), but without the debug type checks. 104*06c3fb27SDimitry Andric template <typename T> T &peekInternal() const { 105*06c3fb27SDimitry Andric return *reinterpret_cast<T *>(peekData(aligned_size<T>())); 106*06c3fb27SDimitry Andric } 107*06c3fb27SDimitry Andric 108349cc55cSDimitry Andric /// Grows the stack to accommodate a value and returns a pointer to it. 109a7dea167SDimitry Andric void *grow(size_t Size); 110a7dea167SDimitry Andric /// Returns a pointer from the top of the stack. 111*06c3fb27SDimitry Andric void *peekData(size_t Size) const; 112a7dea167SDimitry Andric /// Shrinks the stack. 113a7dea167SDimitry Andric void shrink(size_t Size); 114a7dea167SDimitry Andric 115a7dea167SDimitry Andric /// Allocate stack space in 1Mb chunks. 116a7dea167SDimitry Andric static constexpr size_t ChunkSize = 1024 * 1024; 117a7dea167SDimitry Andric 118a7dea167SDimitry Andric /// Metadata for each stack chunk. 119a7dea167SDimitry Andric /// 120a7dea167SDimitry Andric /// The stack is composed of a linked list of chunks. Whenever an allocation 121a7dea167SDimitry Andric /// is out of bounds, a new chunk is linked. When a chunk becomes empty, 122a7dea167SDimitry Andric /// it is not immediately freed: a chunk is deallocated only when the 123a7dea167SDimitry Andric /// predecessor becomes empty. 124a7dea167SDimitry Andric struct StackChunk { 125a7dea167SDimitry Andric StackChunk *Next; 126a7dea167SDimitry Andric StackChunk *Prev; 127a7dea167SDimitry Andric char *End; 128a7dea167SDimitry Andric 129a7dea167SDimitry Andric StackChunk(StackChunk *Prev = nullptr) 130a7dea167SDimitry Andric : Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {} 131a7dea167SDimitry Andric 132a7dea167SDimitry Andric /// Returns the size of the chunk, minus the header. 133bdd1243dSDimitry Andric size_t size() const { return End - start(); } 134a7dea167SDimitry Andric 135a7dea167SDimitry Andric /// Returns a pointer to the start of the data region. 136a7dea167SDimitry Andric char *start() { return reinterpret_cast<char *>(this + 1); } 137bdd1243dSDimitry Andric const char *start() const { 138bdd1243dSDimitry Andric return reinterpret_cast<const char *>(this + 1); 139bdd1243dSDimitry Andric } 140a7dea167SDimitry Andric }; 141a7dea167SDimitry Andric static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size"); 142a7dea167SDimitry Andric 143a7dea167SDimitry Andric /// First chunk on the stack. 144a7dea167SDimitry Andric StackChunk *Chunk = nullptr; 145a7dea167SDimitry Andric /// Total size of the stack. 146a7dea167SDimitry Andric size_t StackSize = 0; 147bdd1243dSDimitry Andric 148bdd1243dSDimitry Andric #ifndef NDEBUG 149bdd1243dSDimitry Andric /// vector recording the type of data we pushed into the stack. 150bdd1243dSDimitry Andric std::vector<PrimType> ItemTypes; 151bdd1243dSDimitry Andric 152bdd1243dSDimitry Andric template <typename T> static constexpr PrimType toPrimType() { 153bdd1243dSDimitry Andric if constexpr (std::is_same_v<T, Pointer>) 154bdd1243dSDimitry Andric return PT_Ptr; 155bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, bool> || 156bdd1243dSDimitry Andric std::is_same_v<T, Boolean>) 157bdd1243dSDimitry Andric return PT_Bool; 158bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, int8_t> || 159bdd1243dSDimitry Andric std::is_same_v<T, Integral<8, true>>) 160bdd1243dSDimitry Andric return PT_Sint8; 161bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, uint8_t> || 162bdd1243dSDimitry Andric std::is_same_v<T, Integral<8, false>>) 163bdd1243dSDimitry Andric return PT_Uint8; 164bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, int16_t> || 165bdd1243dSDimitry Andric std::is_same_v<T, Integral<16, true>>) 166bdd1243dSDimitry Andric return PT_Sint16; 167bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, uint16_t> || 168bdd1243dSDimitry Andric std::is_same_v<T, Integral<16, false>>) 169bdd1243dSDimitry Andric return PT_Uint16; 170bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, int32_t> || 171bdd1243dSDimitry Andric std::is_same_v<T, Integral<32, true>>) 172bdd1243dSDimitry Andric return PT_Sint32; 173bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, uint32_t> || 174bdd1243dSDimitry Andric std::is_same_v<T, Integral<32, false>>) 175bdd1243dSDimitry Andric return PT_Uint32; 176bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, int64_t> || 177bdd1243dSDimitry Andric std::is_same_v<T, Integral<64, true>>) 178bdd1243dSDimitry Andric return PT_Sint64; 179bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, uint64_t> || 180bdd1243dSDimitry Andric std::is_same_v<T, Integral<64, false>>) 181bdd1243dSDimitry Andric return PT_Uint64; 182*06c3fb27SDimitry Andric else if constexpr (std::is_same_v<T, Floating>) 183*06c3fb27SDimitry Andric return PT_Float; 184*06c3fb27SDimitry Andric else if constexpr (std::is_same_v<T, FunctionPointer>) 185*06c3fb27SDimitry Andric return PT_FnPtr; 186bdd1243dSDimitry Andric 187bdd1243dSDimitry Andric llvm_unreachable("unknown type push()'ed into InterpStack"); 188bdd1243dSDimitry Andric } 189bdd1243dSDimitry Andric #endif 190a7dea167SDimitry Andric }; 191a7dea167SDimitry Andric 192a7dea167SDimitry Andric } // namespace interp 193a7dea167SDimitry Andric } // namespace clang 194a7dea167SDimitry Andric 195a7dea167SDimitry Andric #endif 196