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 "clang/AST/ComparisonCategories.h" |
18 | #include "clang/AST/Decl.h" |
19 | #include "clang/AST/DeclCXX.h" |
20 | #include "clang/AST/Expr.h" |
21 | #include "llvm/ADT/PointerUnion.h" |
22 | #include "llvm/Support/raw_ostream.h" |
23 | |
24 | namespace clang { |
25 | namespace interp { |
26 | class Block; |
27 | class DeadBlock; |
28 | class InterpState; |
29 | class Pointer; |
30 | enum PrimType : unsigned; |
31 | |
32 | /// A memory block, either on the stack or in the heap. |
33 | /// |
34 | /// The storage described by the block is immediately followed by |
35 | /// optional metadata, which is followed by the actual data. |
36 | /// |
37 | /// Block* rawData() data() |
38 | /// │ │ │ |
39 | /// │ │ │ |
40 | /// ▼ ▼ ▼ |
41 | /// ┌───────────────┬─────────────────────────┬─────────────────┐ |
42 | /// │ Block │ Metadata │ Data │ |
43 | /// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │ |
44 | /// └───────────────┴─────────────────────────┴─────────────────┘ |
45 | /// |
46 | /// Desc->getAllocSize() describes the size after the Block, i.e. |
47 | /// the data size and the metadata size. |
48 | /// |
49 | class Block final { |
50 | public: |
51 | /// Creates a new block. |
52 | Block(unsigned EvalID, const std::optional<unsigned> &DeclID, |
53 | const Descriptor *Desc, bool IsStatic = false, bool IsExtern = false, |
54 | bool IsWeak = false) |
55 | : EvalID(EvalID), DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), |
56 | IsDynamic(false), IsWeak(IsWeak), Desc(Desc) { |
57 | assert(Desc); |
58 | } |
59 | |
60 | Block(unsigned EvalID, const Descriptor *Desc, bool IsStatic = false, |
61 | bool IsExtern = false, bool IsWeak = false) |
62 | : EvalID(EvalID), DeclID((unsigned)-1), IsStatic(IsStatic), |
63 | IsExtern(IsExtern), IsDynamic(false), IsWeak(IsWeak), Desc(Desc) { |
64 | assert(Desc); |
65 | } |
66 | |
67 | /// Returns the block's descriptor. |
68 | const Descriptor *getDescriptor() const { return Desc; } |
69 | /// Checks if the block has any live pointers. |
70 | bool hasPointers() const { return Pointers; } |
71 | /// Checks if the block is extern. |
72 | bool isExtern() const { return IsExtern; } |
73 | /// Checks if the block has static storage duration. |
74 | bool isStatic() const { return IsStatic; } |
75 | /// Checks if the block is temporary. |
76 | bool isTemporary() const { return Desc->IsTemporary; } |
77 | bool isWeak() const { return IsWeak; } |
78 | bool isDynamic() const { return IsDynamic; } |
79 | /// Returns the size of the block. |
80 | unsigned getSize() const { return Desc->getAllocSize(); } |
81 | /// Returns the declaration ID. |
82 | std::optional<unsigned> getDeclID() const { return DeclID; } |
83 | /// Returns whether the data of this block has been initialized via |
84 | /// invoking the Ctor func. |
85 | bool isInitialized() const { return IsInitialized; } |
86 | /// The Evaluation ID this block was created in. |
87 | unsigned getEvalID() const { return EvalID; } |
88 | |
89 | /// Returns a pointer to the stored data. |
90 | /// You are allowed to read Desc->getSize() bytes from this address. |
91 | std::byte *data() { |
92 | // rawData might contain metadata as well. |
93 | size_t DataOffset = Desc->getMetadataSize(); |
94 | return rawData() + DataOffset; |
95 | } |
96 | const std::byte *data() const { |
97 | // rawData might contain metadata as well. |
98 | size_t DataOffset = Desc->getMetadataSize(); |
99 | return rawData() + DataOffset; |
100 | } |
101 | |
102 | /// Returns a pointer to the raw data, including metadata. |
103 | /// You are allowed to read Desc->getAllocSize() bytes from this address. |
104 | std::byte *rawData() { |
105 | return reinterpret_cast<std::byte *>(this) + sizeof(Block); |
106 | } |
107 | const std::byte *rawData() const { |
108 | return reinterpret_cast<const std::byte *>(this) + sizeof(Block); |
109 | } |
110 | |
111 | /// Invokes the constructor. |
112 | void invokeCtor() { |
113 | assert(!IsInitialized); |
114 | std::memset(s: rawData(), c: 0, n: Desc->getAllocSize()); |
115 | if (Desc->CtorFn) { |
116 | Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable, |
117 | Desc->IsVolatile, |
118 | /*isActive=*/true, /*InUnion=*/false, Desc); |
119 | } |
120 | IsInitialized = true; |
121 | } |
122 | |
123 | /// Invokes the Destructor. |
124 | void invokeDtor() { |
125 | assert(IsInitialized); |
126 | if (Desc->DtorFn) |
127 | Desc->DtorFn(this, data(), Desc); |
128 | IsInitialized = false; |
129 | } |
130 | |
131 | void dump() const { dump(OS&: llvm::errs()); } |
132 | void dump(llvm::raw_ostream &OS) const; |
133 | |
134 | private: |
135 | friend class Pointer; |
136 | friend class DeadBlock; |
137 | friend class InterpState; |
138 | friend class DynamicAllocator; |
139 | |
140 | Block(unsigned EvalID, const Descriptor *Desc, bool IsExtern, bool IsStatic, |
141 | bool IsWeak, bool IsDead) |
142 | : EvalID(EvalID), IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), |
143 | IsDynamic(false), IsWeak(IsWeak), Desc(Desc) { |
144 | assert(Desc); |
145 | } |
146 | |
147 | /// Deletes a dead block at the end of its lifetime. |
148 | void cleanup(); |
149 | |
150 | /// Pointer chain management. |
151 | void addPointer(Pointer *P); |
152 | void removePointer(Pointer *P); |
153 | void replacePointer(Pointer *Old, Pointer *New); |
154 | #ifndef NDEBUG |
155 | bool hasPointer(const Pointer *P) const; |
156 | #endif |
157 | |
158 | const unsigned EvalID = ~0u; |
159 | /// Start of the chain of pointers. |
160 | Pointer *Pointers = nullptr; |
161 | /// Unique identifier of the declaration. |
162 | std::optional<unsigned> DeclID; |
163 | /// Flag indicating if the block has static storage duration. |
164 | bool IsStatic = false; |
165 | /// Flag indicating if the block is an extern. |
166 | bool IsExtern = false; |
167 | /// Flag indicating if the pointer is dead. This is only ever |
168 | /// set once, when converting the Block to a DeadBlock. |
169 | bool IsDead = false; |
170 | /// Flag indicating if the block contents have been initialized |
171 | /// via invokeCtor. |
172 | bool IsInitialized = false; |
173 | /// Flag indicating if this block has been allocated via dynamic |
174 | /// memory allocation (e.g. malloc). |
175 | bool IsDynamic = false; |
176 | bool IsWeak = false; |
177 | /// Pointer to the stack slot descriptor. |
178 | const Descriptor *Desc; |
179 | }; |
180 | |
181 | /// Descriptor for a dead block. |
182 | /// |
183 | /// Dead blocks are chained in a double-linked list to deallocate them |
184 | /// whenever pointers become dead. |
185 | class DeadBlock final { |
186 | public: |
187 | /// Copies the block. |
188 | DeadBlock(DeadBlock *&Root, Block *Blk); |
189 | |
190 | /// Returns a pointer to the stored data. |
191 | std::byte *data() { return B.data(); } |
192 | std::byte *rawData() { return B.rawData(); } |
193 | |
194 | private: |
195 | friend class Block; |
196 | friend class InterpState; |
197 | |
198 | void free(); |
199 | |
200 | /// Root pointer of the list. |
201 | DeadBlock *&Root; |
202 | /// Previous block in the list. |
203 | DeadBlock *Prev; |
204 | /// Next block in the list. |
205 | DeadBlock *Next; |
206 | |
207 | /// Actual block storing data and tracking pointers. |
208 | Block B; |
209 | }; |
210 | |
211 | } // namespace interp |
212 | } // namespace clang |
213 | |
214 | #endif |
215 | |