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 1606c3fb27SDimitry Andric #include "FunctionPointer.h" 175f757f3fSDimitry Andric #include "IntegralAP.h" 18*0fca6ea1SDimitry Andric #include "MemberPointer.h" 19bdd1243dSDimitry Andric #include "PrimType.h" 20a7dea167SDimitry Andric #include <memory> 21bdd1243dSDimitry Andric #include <vector> 22a7dea167SDimitry Andric 23a7dea167SDimitry Andric namespace clang { 24a7dea167SDimitry Andric namespace interp { 25a7dea167SDimitry Andric 26a7dea167SDimitry Andric /// Stack frame storing temporaries and parameters. 27a7dea167SDimitry Andric class InterpStack final { 28a7dea167SDimitry Andric public: 29a7dea167SDimitry Andric InterpStack() {} 30a7dea167SDimitry Andric 31a7dea167SDimitry Andric /// Destroys the stack, freeing up storage. 32a7dea167SDimitry Andric ~InterpStack(); 33a7dea167SDimitry Andric 34a7dea167SDimitry Andric /// Constructs a value in place on the top of the stack. 35a7dea167SDimitry Andric template <typename T, typename... Tys> void push(Tys &&... Args) { 36a7dea167SDimitry Andric new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...); 37bdd1243dSDimitry Andric #ifndef NDEBUG 38bdd1243dSDimitry Andric ItemTypes.push_back(toPrimType<T>()); 39bdd1243dSDimitry Andric #endif 40a7dea167SDimitry Andric } 41a7dea167SDimitry Andric 42a7dea167SDimitry Andric /// Returns the value from the top of the stack and removes it. 43a7dea167SDimitry Andric template <typename T> T pop() { 44bdd1243dSDimitry Andric #ifndef NDEBUG 45bdd1243dSDimitry Andric assert(!ItemTypes.empty()); 46bdd1243dSDimitry Andric assert(ItemTypes.back() == toPrimType<T>()); 47bdd1243dSDimitry Andric ItemTypes.pop_back(); 48bdd1243dSDimitry Andric #endif 4906c3fb27SDimitry Andric T *Ptr = &peekInternal<T>(); 5006c3fb27SDimitry Andric T Value = std::move(*Ptr); 51a7dea167SDimitry Andric shrink(aligned_size<T>()); 52a7dea167SDimitry Andric return Value; 53a7dea167SDimitry Andric } 54a7dea167SDimitry Andric 55a7dea167SDimitry Andric /// Discards the top value from the stack. 56a7dea167SDimitry Andric template <typename T> void discard() { 57bdd1243dSDimitry Andric #ifndef NDEBUG 5806c3fb27SDimitry Andric assert(!ItemTypes.empty()); 59bdd1243dSDimitry Andric assert(ItemTypes.back() == toPrimType<T>()); 60bdd1243dSDimitry Andric ItemTypes.pop_back(); 61bdd1243dSDimitry Andric #endif 6206c3fb27SDimitry Andric T *Ptr = &peekInternal<T>(); 63a7dea167SDimitry Andric Ptr->~T(); 64a7dea167SDimitry Andric shrink(aligned_size<T>()); 65a7dea167SDimitry Andric } 66a7dea167SDimitry Andric 67a7dea167SDimitry Andric /// Returns a reference to the value on the top of the stack. 68bdd1243dSDimitry Andric template <typename T> T &peek() const { 6906c3fb27SDimitry Andric #ifndef NDEBUG 7006c3fb27SDimitry Andric assert(!ItemTypes.empty()); 7106c3fb27SDimitry Andric assert(ItemTypes.back() == toPrimType<T>()); 7206c3fb27SDimitry Andric #endif 7306c3fb27SDimitry Andric return peekInternal<T>(); 7406c3fb27SDimitry Andric } 7506c3fb27SDimitry Andric 7606c3fb27SDimitry Andric template <typename T> T &peek(size_t Offset) const { 7706c3fb27SDimitry Andric assert(aligned(Offset)); 7806c3fb27SDimitry Andric return *reinterpret_cast<T *>(peekData(Offset)); 79a7dea167SDimitry Andric } 80a7dea167SDimitry Andric 81a7dea167SDimitry Andric /// Returns a pointer to the top object. 8206c3fb27SDimitry Andric void *top() const { return Chunk ? peekData(0) : nullptr; } 83a7dea167SDimitry Andric 84a7dea167SDimitry Andric /// Returns the size of the stack in bytes. 85a7dea167SDimitry Andric size_t size() const { return StackSize; } 86a7dea167SDimitry Andric 87a7dea167SDimitry Andric /// Clears the stack without calling any destructors. 88a7dea167SDimitry Andric void clear(); 89a7dea167SDimitry Andric 9006c3fb27SDimitry Andric /// Returns whether the stack is empty. 91bdd1243dSDimitry Andric bool empty() const { return StackSize == 0; } 92bdd1243dSDimitry Andric 9306c3fb27SDimitry Andric /// dump the stack contents to stderr. 9406c3fb27SDimitry Andric void dump() const; 9506c3fb27SDimitry Andric 96a7dea167SDimitry Andric private: 97a7dea167SDimitry Andric /// All stack slots are aligned to the native pointer alignment for storage. 98a7dea167SDimitry Andric /// The size of an object is rounded up to a pointer alignment multiple. 99a7dea167SDimitry Andric template <typename T> constexpr size_t aligned_size() const { 100a7dea167SDimitry Andric constexpr size_t PtrAlign = alignof(void *); 101a7dea167SDimitry Andric return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign; 102a7dea167SDimitry Andric } 103a7dea167SDimitry Andric 10406c3fb27SDimitry Andric /// Like the public peek(), but without the debug type checks. 10506c3fb27SDimitry Andric template <typename T> T &peekInternal() const { 10606c3fb27SDimitry Andric return *reinterpret_cast<T *>(peekData(aligned_size<T>())); 10706c3fb27SDimitry Andric } 10806c3fb27SDimitry Andric 109349cc55cSDimitry Andric /// Grows the stack to accommodate a value and returns a pointer to it. 110a7dea167SDimitry Andric void *grow(size_t Size); 111a7dea167SDimitry Andric /// Returns a pointer from the top of the stack. 11206c3fb27SDimitry Andric void *peekData(size_t Size) const; 113a7dea167SDimitry Andric /// Shrinks the stack. 114a7dea167SDimitry Andric void shrink(size_t Size); 115a7dea167SDimitry Andric 116a7dea167SDimitry Andric /// Allocate stack space in 1Mb chunks. 117a7dea167SDimitry Andric static constexpr size_t ChunkSize = 1024 * 1024; 118a7dea167SDimitry Andric 119a7dea167SDimitry Andric /// Metadata for each stack chunk. 120a7dea167SDimitry Andric /// 121a7dea167SDimitry Andric /// The stack is composed of a linked list of chunks. Whenever an allocation 122a7dea167SDimitry Andric /// is out of bounds, a new chunk is linked. When a chunk becomes empty, 123a7dea167SDimitry Andric /// it is not immediately freed: a chunk is deallocated only when the 124a7dea167SDimitry Andric /// predecessor becomes empty. 125a7dea167SDimitry Andric struct StackChunk { 126a7dea167SDimitry Andric StackChunk *Next; 127a7dea167SDimitry Andric StackChunk *Prev; 128a7dea167SDimitry Andric char *End; 129a7dea167SDimitry Andric 130a7dea167SDimitry Andric StackChunk(StackChunk *Prev = nullptr) 131a7dea167SDimitry Andric : Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {} 132a7dea167SDimitry Andric 133a7dea167SDimitry Andric /// Returns the size of the chunk, minus the header. 134bdd1243dSDimitry Andric size_t size() const { return End - start(); } 135a7dea167SDimitry Andric 136a7dea167SDimitry Andric /// Returns a pointer to the start of the data region. 137a7dea167SDimitry Andric char *start() { return reinterpret_cast<char *>(this + 1); } 138bdd1243dSDimitry Andric const char *start() const { 139bdd1243dSDimitry Andric return reinterpret_cast<const char *>(this + 1); 140bdd1243dSDimitry Andric } 141a7dea167SDimitry Andric }; 142a7dea167SDimitry Andric static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size"); 143a7dea167SDimitry Andric 144a7dea167SDimitry Andric /// First chunk on the stack. 145a7dea167SDimitry Andric StackChunk *Chunk = nullptr; 146a7dea167SDimitry Andric /// Total size of the stack. 147a7dea167SDimitry Andric size_t StackSize = 0; 148bdd1243dSDimitry Andric 149bdd1243dSDimitry Andric #ifndef NDEBUG 150bdd1243dSDimitry Andric /// vector recording the type of data we pushed into the stack. 151bdd1243dSDimitry Andric std::vector<PrimType> ItemTypes; 152bdd1243dSDimitry Andric 153bdd1243dSDimitry Andric template <typename T> static constexpr PrimType toPrimType() { 154bdd1243dSDimitry Andric if constexpr (std::is_same_v<T, Pointer>) 155bdd1243dSDimitry Andric return PT_Ptr; 156bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, bool> || 157bdd1243dSDimitry Andric std::is_same_v<T, Boolean>) 158bdd1243dSDimitry Andric return PT_Bool; 159bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, int8_t> || 160bdd1243dSDimitry Andric std::is_same_v<T, Integral<8, true>>) 161bdd1243dSDimitry Andric return PT_Sint8; 162bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, uint8_t> || 163bdd1243dSDimitry Andric std::is_same_v<T, Integral<8, false>>) 164bdd1243dSDimitry Andric return PT_Uint8; 165bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, int16_t> || 166bdd1243dSDimitry Andric std::is_same_v<T, Integral<16, true>>) 167bdd1243dSDimitry Andric return PT_Sint16; 168bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, uint16_t> || 169bdd1243dSDimitry Andric std::is_same_v<T, Integral<16, false>>) 170bdd1243dSDimitry Andric return PT_Uint16; 171bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, int32_t> || 172bdd1243dSDimitry Andric std::is_same_v<T, Integral<32, true>>) 173bdd1243dSDimitry Andric return PT_Sint32; 174bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, uint32_t> || 175bdd1243dSDimitry Andric std::is_same_v<T, Integral<32, false>>) 176bdd1243dSDimitry Andric return PT_Uint32; 177bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, int64_t> || 178bdd1243dSDimitry Andric std::is_same_v<T, Integral<64, true>>) 179bdd1243dSDimitry Andric return PT_Sint64; 180bdd1243dSDimitry Andric else if constexpr (std::is_same_v<T, uint64_t> || 181bdd1243dSDimitry Andric std::is_same_v<T, Integral<64, false>>) 182bdd1243dSDimitry Andric return PT_Uint64; 18306c3fb27SDimitry Andric else if constexpr (std::is_same_v<T, Floating>) 18406c3fb27SDimitry Andric return PT_Float; 18506c3fb27SDimitry Andric else if constexpr (std::is_same_v<T, FunctionPointer>) 18606c3fb27SDimitry Andric return PT_FnPtr; 1875f757f3fSDimitry Andric else if constexpr (std::is_same_v<T, IntegralAP<true>>) 1885f757f3fSDimitry Andric return PT_IntAP; 1895f757f3fSDimitry Andric else if constexpr (std::is_same_v<T, IntegralAP<false>>) 1905f757f3fSDimitry Andric return PT_IntAP; 191*0fca6ea1SDimitry Andric else if constexpr (std::is_same_v<T, MemberPointer>) 192*0fca6ea1SDimitry Andric return PT_MemberPtr; 193bdd1243dSDimitry Andric 194bdd1243dSDimitry Andric llvm_unreachable("unknown type push()'ed into InterpStack"); 195bdd1243dSDimitry Andric } 196bdd1243dSDimitry Andric #endif 197a7dea167SDimitry Andric }; 198a7dea167SDimitry Andric 199a7dea167SDimitry Andric } // namespace interp 200a7dea167SDimitry Andric } // namespace clang 201a7dea167SDimitry Andric 202a7dea167SDimitry Andric #endif 203