-
Notifications
You must be signed in to change notification settings - Fork 11k
/
InterpStack.h
203 lines (174 loc) · 6.45 KB
/
InterpStack.h
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
//===--- 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 "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);
Ptr->~T();
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();
/// 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;
llvm_unreachable("unknown type push()'ed into InterpStack");
}
#endif
};
} // namespace interp
} // namespace clang
#endif