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