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