1//===-- InterpBlock.h - Allocated blocks for the interpreter -*- 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 classes describing allocated blocks.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_CLANG_AST_INTERP_BLOCK_H
14#define LLVM_CLANG_AST_INTERP_BLOCK_H
15
16#include "Descriptor.h"
17#include "llvm/Support/raw_ostream.h"
18
19namespace clang {
20namespace interp {
21class Block;
22class DeadBlock;
23class InterpState;
24class Pointer;
25enum PrimType : uint8_t;
26
27/// A memory block, either on the stack or in the heap.
28///
29/// The storage described by the block is immediately followed by
30/// optional metadata, which is followed by the actual data.
31///
32/// Block* rawData() data()
33/// │ │ │
34/// │ │ │
35/// ▼ ▼ ▼
36/// ┌───────────────┬──────────────────┬─────────────────┐
37/// │ Block │ Metadata │ Data │
38/// │ sizeof(Block) │ MDSize │ Desc->getSize() │
39/// └───────────────┴──────────────────┴─────────────────┘
40///
41/// getSize() returns MDSize + Desc->getAllocSize().
42///
43class Block final {
44private:
45 static constexpr uint8_t ExternFlag = 1 << 0;
46 static constexpr uint8_t DeadFlag = 1 << 1;
47 static constexpr uint8_t WeakFlag = 1 << 2;
48
49public:
50 static constexpr uint8_t InlineDescMD = sizeof(InlineDescriptor);
51 static constexpr uint8_t GlobalMD = sizeof(GlobalInlineDescriptor);
52
53 /// Creates a new block.
54 Block(unsigned EvalID, UnsignedOrNone DeclID, const Descriptor *Desc,
55 unsigned MDSize = 0, bool IsStatic = false, bool IsExtern = false,
56 bool IsWeak = false)
57 : Desc(Desc), DeclID(DeclID), EvalID(EvalID), MDSize(MDSize),
58 IsStatic(IsStatic) {
59 assert(Desc);
60 AccessFlags |= (ExternFlag * IsExtern);
61 AccessFlags |= (WeakFlag * IsWeak);
62 }
63
64 Block(unsigned EvalID, const Descriptor *Desc, unsigned MDSize = 0,
65 bool IsStatic = false, bool IsExtern = false, bool IsWeak = false)
66 : Desc(Desc), EvalID(EvalID), MDSize(MDSize), IsStatic(IsStatic) {
67 assert(Desc);
68 AccessFlags |= (ExternFlag * IsExtern);
69 AccessFlags |= (WeakFlag * IsWeak);
70 }
71
72 /// Returns the block's descriptor.
73 const Descriptor *getDescriptor() const { return Desc; }
74 /// Checks if the block has any live pointers.
75 bool hasPointers() const { return Pointers; }
76 /// Checks if the block is extern.
77 bool isExtern() const { return AccessFlags & ExternFlag; }
78 /// Checks if the block has static storage duration.
79 bool isStatic() const { return IsStatic; }
80 /// Checks if the block is temporary.
81 bool isTemporary() const { return Desc->IsTemporary; }
82 bool isWeak() const { return AccessFlags & WeakFlag; }
83 bool isDynamic() const { return (DynAllocId != std::nullopt); }
84 bool isDead() const { return AccessFlags & DeadFlag; }
85 /// Returns the size of the block, including metadata.
86 unsigned getSize() const { return Desc->getAllocSize() + MDSize; }
87 /// Returns the size of the metadata.
88 unsigned getMetadataSize() const { return MDSize; }
89 /// Returns the declaration ID.
90 UnsignedOrNone getDeclID() const { return DeclID; }
91 /// Returns whether the data of this block has been initialized via
92 /// invoking the Ctor func.
93 bool isInitialized() const { return IsInitialized; }
94 /// The Evaluation ID this block was created in.
95 unsigned getEvalID() const { return EvalID; }
96 /// Move all pointers from this block to \param B.
97 void movePointersTo(Block *B);
98 /// Make all pointers that currently point to this block point to nullptr.
99 void removePointers();
100
101 /// Returns a pointer to the stored data.
102 /// You are allowed to read Desc->getSize() bytes from this address.
103 std::byte *data() { return rawData() + MDSize; }
104 const std::byte *data() const { return rawData() + MDSize; }
105
106 /// Returns a pointer to the raw data, including metadata.
107 /// You are allowed to read Desc->getAllocSize() bytes from this address.
108 std::byte *rawData() {
109 return reinterpret_cast<std::byte *>(this) + sizeof(Block);
110 }
111 const std::byte *rawData() const {
112 return reinterpret_cast<const std::byte *>(this) + sizeof(Block);
113 }
114
115 template <typename T> const T &deref() const {
116 return *reinterpret_cast<const T *>(data());
117 }
118 template <typename T> T &deref() { return *reinterpret_cast<T *>(data()); }
119
120 template <typename T> T &getBlockDesc() {
121 assert(sizeof(T) == MDSize);
122 return *reinterpret_cast<T *>(rawData());
123 }
124 template <typename T> const T &getBlockDesc() const {
125 return const_cast<Block *>(this)->getBlockDesc<T>();
126 }
127
128 /// Invokes the constructor.
129 void invokeCtor() {
130 assert(!IsInitialized);
131 std::memset(s: rawData(), c: 0, n: getSize());
132 invokeCtorNoMemset();
133 }
134 /// The same, but won't memset() the memory first to zero.
135 void invokeCtorNoMemset() {
136 assert(!IsInitialized);
137 if (Desc->CtorFn)
138 Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
139 Desc->IsVolatile,
140 /*isActive=*/true, /*InUnion=*/false, Desc);
141
142 IsInitialized = true;
143 }
144
145 /// Invokes the Destructor.
146 void invokeDtor() {
147 assert(IsInitialized);
148 if (Desc->DtorFn)
149 Desc->DtorFn(this, data(), Desc);
150 IsInitialized = false;
151 }
152
153 void dump() const { dump(OS&: llvm::errs()); }
154 void dump(llvm::raw_ostream &OS) const;
155
156 bool isAccessible() const { return AccessFlags == 0; }
157
158private:
159 friend class Pointer;
160 friend class DeadBlock;
161 friend class InterpState;
162 friend class DynamicAllocator;
163 friend class Program;
164
165 Block(unsigned EvalID, const Descriptor *Desc, unsigned MDSize, bool IsExtern,
166 bool IsStatic, bool IsWeak, bool IsDead)
167 : Desc(Desc), EvalID(EvalID), MDSize(MDSize), IsStatic(IsStatic) {
168 assert(Desc);
169 AccessFlags |= (ExternFlag * IsExtern);
170 AccessFlags |= (DeadFlag * IsDead);
171 AccessFlags |= (WeakFlag * IsWeak);
172 }
173
174 /// To be called by DynamicAllocator.
175 void setDynAllocId(unsigned ID) { DynAllocId = ID; }
176
177 /// Deletes a dead block at the end of its lifetime.
178 void cleanup();
179
180 /// Pointer chain management.
181 void addPointer(Pointer *P);
182 void removePointer(Pointer *P);
183 void replacePointer(Pointer *Old, Pointer *New);
184#ifndef NDEBUG
185 bool hasPointer(const Pointer *P) const;
186#endif
187
188 /// Pointer to the stack slot descriptor.
189 const Descriptor *Desc;
190 /// Start of the chain of pointers.
191 Pointer *Pointers = nullptr;
192 /// Unique identifier of the declaration.
193 UnsignedOrNone DeclID = std::nullopt;
194 const unsigned EvalID = ~0u;
195 /// Allocation ID for this dynamic allocation, if it is one.
196 UnsignedOrNone DynAllocId = std::nullopt;
197 /// AccessFlags containing IsExtern, IsDead and IsWeak bits.
198 uint8_t AccessFlags = 0;
199 /// Size of the metadata.
200 const uint8_t MDSize = 0;
201 /// Flag indicating if the block has static storage duration.
202 bool IsStatic = false;
203 /// Flag indicating if the block contents have been initialized
204 /// via invokeCtor.
205 bool IsInitialized = false;
206};
207
208/// Descriptor for a dead block.
209///
210/// Dead blocks are chained in a double-linked list to deallocate them
211/// whenever pointers become dead.
212class DeadBlock final {
213public:
214 /// Copies the block.
215 DeadBlock(DeadBlock *&Root, Block *Blk);
216
217 /// Returns a pointer to the stored data.
218 std::byte *data() { return B.data(); }
219 std::byte *rawData() { return B.rawData(); }
220
221private:
222 friend class Block;
223 friend class InterpState;
224
225 void free();
226
227 /// Root pointer of the list.
228 DeadBlock *&Root;
229 /// Previous block in the list.
230 DeadBlock *Prev;
231 /// Next block in the list.
232 DeadBlock *Next;
233
234 /// Actual block storing data and tracking pointers.
235 Block B;
236};
237
238} // namespace interp
239} // namespace clang
240
241#endif
242