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