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