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