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