1 | //===--- Descriptor.h - Types for the constexpr 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 descriptors which characterise allocations. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef LLVM_CLANG_AST_INTERP_DESCRIPTOR_H |
14 | #define LLVM_CLANG_AST_INTERP_DESCRIPTOR_H |
15 | |
16 | #include "PrimType.h" |
17 | #include "clang/AST/Decl.h" |
18 | #include "clang/AST/Expr.h" |
19 | |
20 | namespace clang { |
21 | namespace interp { |
22 | class Block; |
23 | class Record; |
24 | class SourceInfo; |
25 | struct InitMap; |
26 | struct Descriptor; |
27 | enum PrimType : unsigned; |
28 | |
29 | using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>; |
30 | using InitMapPtr = std::optional<std::pair<bool, std::shared_ptr<InitMap>>>; |
31 | |
32 | /// Invoked whenever a block is created. The constructor method fills in the |
33 | /// inline descriptors of all fields and array elements. It also initializes |
34 | /// all the fields which contain non-trivial types. |
35 | using BlockCtorFn = void (*)(Block *Storage, std::byte *FieldPtr, bool IsConst, |
36 | bool IsMutable, bool IsVolatile, bool IsActive, |
37 | bool InUnion, const Descriptor *FieldDesc); |
38 | |
39 | /// Invoked when a block is destroyed. Invokes the destructors of all |
40 | /// non-trivial nested fields of arrays and records. |
41 | using BlockDtorFn = void (*)(Block *Storage, std::byte *FieldPtr, |
42 | const Descriptor *FieldDesc); |
43 | |
44 | /// Invoked when a block with pointers referencing it goes out of scope. Such |
45 | /// blocks are persisted: the move function copies all inline descriptors and |
46 | /// non-trivial fields, as existing pointers might need to reference those |
47 | /// descriptors. Data is not copied since it cannot be legally read. |
48 | using BlockMoveFn = void (*)(Block *Storage, std::byte *SrcFieldPtr, |
49 | std::byte *DstFieldPtr, |
50 | const Descriptor *FieldDesc); |
51 | |
52 | enum class GlobalInitState { |
53 | Initialized, |
54 | NoInitializer, |
55 | InitializerFailed, |
56 | }; |
57 | |
58 | /// Descriptor used for global variables. |
59 | struct alignas(void *) GlobalInlineDescriptor { |
60 | GlobalInitState InitState = GlobalInitState::InitializerFailed; |
61 | }; |
62 | static_assert(sizeof(GlobalInlineDescriptor) == sizeof(void *), "" ); |
63 | |
64 | enum class Lifetime : uint8_t { |
65 | Started, |
66 | Ended, |
67 | }; |
68 | |
69 | /// Inline descriptor embedded in structures and arrays. |
70 | /// |
71 | /// Such descriptors precede all composite array elements and structure fields. |
72 | /// If the base of a pointer is not zero, the base points to the end of this |
73 | /// structure. The offset field is used to traverse the pointer chain up |
74 | /// to the root structure which allocated the object. |
75 | struct InlineDescriptor { |
76 | /// Offset inside the structure/array. |
77 | unsigned Offset; |
78 | |
79 | /// Flag indicating if the storage is constant or not. |
80 | /// Relevant for primitive fields. |
81 | LLVM_PREFERRED_TYPE(bool) |
82 | unsigned IsConst : 1; |
83 | /// For primitive fields, it indicates if the field was initialized. |
84 | /// Primitive fields in static storage are always initialized. |
85 | /// Arrays are always initialized, even though their elements might not be. |
86 | /// Base classes are initialized after the constructor is invoked. |
87 | LLVM_PREFERRED_TYPE(bool) |
88 | unsigned IsInitialized : 1; |
89 | /// Flag indicating if the field is an embedded base class. |
90 | LLVM_PREFERRED_TYPE(bool) |
91 | unsigned IsBase : 1; |
92 | /// Flag inidcating if the field is a virtual base class. |
93 | LLVM_PREFERRED_TYPE(bool) |
94 | unsigned IsVirtualBase : 1; |
95 | /// Flag indicating if the field is the active member of a union. |
96 | LLVM_PREFERRED_TYPE(bool) |
97 | unsigned IsActive : 1; |
98 | /// Flag indicating if this field is in a union (even if nested). |
99 | LLVM_PREFERRED_TYPE(bool) |
100 | unsigned InUnion : 1; |
101 | /// Flag indicating if the field is mutable (if in a record). |
102 | LLVM_PREFERRED_TYPE(bool) |
103 | unsigned IsFieldMutable : 1; |
104 | /// Flag indicating if the field is an element of a composite array. |
105 | LLVM_PREFERRED_TYPE(bool) |
106 | unsigned IsArrayElement : 1; |
107 | LLVM_PREFERRED_TYPE(bool) |
108 | unsigned IsVolatile : 1; |
109 | |
110 | Lifetime LifeState; |
111 | |
112 | const Descriptor *Desc; |
113 | |
114 | InlineDescriptor(const Descriptor *D) |
115 | : Offset(sizeof(InlineDescriptor)), IsConst(false), IsInitialized(false), |
116 | IsBase(false), IsActive(false), IsFieldMutable(false), |
117 | IsArrayElement(false), IsVolatile(false), LifeState(Lifetime::Started), |
118 | Desc(D) {} |
119 | |
120 | void dump() const { dump(OS&: llvm::errs()); } |
121 | void dump(llvm::raw_ostream &OS) const; |
122 | }; |
123 | static_assert(sizeof(GlobalInlineDescriptor) != sizeof(InlineDescriptor), "" ); |
124 | |
125 | /// Describes a memory block created by an allocation site. |
126 | struct Descriptor final { |
127 | private: |
128 | /// Original declaration, used to emit the error message. |
129 | const DeclTy Source; |
130 | const Type *SourceType = nullptr; |
131 | /// Size of an element, in host bytes. |
132 | const unsigned ElemSize; |
133 | /// Size of the storage, in host bytes. |
134 | const unsigned Size; |
135 | /// Size of the metadata. |
136 | const unsigned MDSize; |
137 | /// Size of the allocation (storage + metadata), in host bytes. |
138 | const unsigned AllocSize; |
139 | |
140 | /// Value to denote arrays of unknown size. |
141 | static constexpr unsigned UnknownSizeMark = (unsigned)-1; |
142 | |
143 | public: |
144 | /// Token to denote structures of unknown size. |
145 | struct UnknownSize {}; |
146 | |
147 | using MetadataSize = std::optional<unsigned>; |
148 | static constexpr MetadataSize InlineDescMD = sizeof(InlineDescriptor); |
149 | static constexpr MetadataSize GlobalMD = sizeof(GlobalInlineDescriptor); |
150 | |
151 | /// Maximum number of bytes to be used for array elements. |
152 | static constexpr unsigned MaxArrayElemBytes = |
153 | std::numeric_limits<decltype(AllocSize)>::max() - sizeof(InitMapPtr) - |
154 | align(Size: std::max(a: *InlineDescMD, b: *GlobalMD)); |
155 | |
156 | /// Pointer to the record, if block contains records. |
157 | const Record *const ElemRecord = nullptr; |
158 | /// Descriptor of the array element. |
159 | const Descriptor *const ElemDesc = nullptr; |
160 | /// The primitive type this descriptor was created for, |
161 | /// or the primitive element type in case this is |
162 | /// a primitive array. |
163 | const std::optional<PrimType> PrimT = std::nullopt; |
164 | /// Flag indicating if the block is mutable. |
165 | const bool IsConst = false; |
166 | /// Flag indicating if a field is mutable. |
167 | const bool IsMutable = false; |
168 | /// Flag indicating if the block is a temporary. |
169 | const bool IsTemporary = false; |
170 | const bool IsVolatile = false; |
171 | /// Flag indicating if the block is an array. |
172 | const bool IsArray = false; |
173 | /// Flag indicating if this is a dummy descriptor. |
174 | bool IsDummy = false; |
175 | bool IsConstexprUnknown = false; |
176 | |
177 | /// Storage management methods. |
178 | const BlockCtorFn CtorFn = nullptr; |
179 | const BlockDtorFn DtorFn = nullptr; |
180 | const BlockMoveFn MoveFn = nullptr; |
181 | |
182 | /// Allocates a descriptor for a primitive. |
183 | Descriptor(const DeclTy &D, const Type *SourceTy, PrimType Type, |
184 | MetadataSize MD, bool IsConst, bool IsTemporary, bool IsMutable, |
185 | bool IsVolatile); |
186 | |
187 | /// Allocates a descriptor for an array of primitives. |
188 | Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems, |
189 | bool IsConst, bool IsTemporary, bool IsMutable); |
190 | |
191 | /// Allocates a descriptor for an array of primitives of unknown size. |
192 | Descriptor(const DeclTy &D, PrimType Type, MetadataSize MDSize, bool IsConst, |
193 | bool IsTemporary, UnknownSize); |
194 | |
195 | /// Allocates a descriptor for an array of composites. |
196 | Descriptor(const DeclTy &D, const Type *SourceTy, const Descriptor *Elem, |
197 | MetadataSize MD, unsigned NumElems, bool IsConst, bool IsTemporary, |
198 | bool IsMutable); |
199 | |
200 | /// Allocates a descriptor for an array of composites of unknown size. |
201 | Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, |
202 | bool IsTemporary, UnknownSize); |
203 | |
204 | /// Allocates a descriptor for a record. |
205 | Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, bool IsConst, |
206 | bool IsTemporary, bool IsMutable, bool IsVolatile); |
207 | |
208 | /// Allocates a dummy descriptor. |
209 | Descriptor(const DeclTy &D, MetadataSize MD = std::nullopt); |
210 | |
211 | /// Make this descriptor a dummy descriptor. |
212 | void makeDummy() { IsDummy = true; } |
213 | |
214 | QualType getType() const; |
215 | QualType getElemQualType() const; |
216 | QualType getDataType(const ASTContext &Ctx) const; |
217 | SourceLocation getLocation() const; |
218 | SourceInfo getLoc() const; |
219 | |
220 | const Decl *asDecl() const { return dyn_cast<const Decl *>(Val: Source); } |
221 | const Expr *asExpr() const { return dyn_cast<const Expr *>(Val: Source); } |
222 | const DeclTy &getSource() const { return Source; } |
223 | |
224 | const ValueDecl *asValueDecl() const { |
225 | return dyn_cast_if_present<ValueDecl>(Val: asDecl()); |
226 | } |
227 | |
228 | const VarDecl *asVarDecl() const { |
229 | return dyn_cast_if_present<VarDecl>(Val: asDecl()); |
230 | } |
231 | |
232 | const FieldDecl *asFieldDecl() const { |
233 | return dyn_cast_if_present<FieldDecl>(Val: asDecl()); |
234 | } |
235 | |
236 | const RecordDecl *asRecordDecl() const { |
237 | return dyn_cast_if_present<RecordDecl>(Val: asDecl()); |
238 | } |
239 | |
240 | /// Returns the size of the object without metadata. |
241 | unsigned getSize() const { |
242 | assert(!isUnknownSizeArray() && "Array of unknown size" ); |
243 | return Size; |
244 | } |
245 | |
246 | PrimType getPrimType() const { |
247 | assert(isPrimitiveArray() || isPrimitive()); |
248 | return *PrimT; |
249 | } |
250 | |
251 | /// Returns the allocated size, including metadata. |
252 | unsigned getAllocSize() const { return AllocSize; } |
253 | /// returns the size of an element when the structure is viewed as an array. |
254 | unsigned getElemSize() const { return ElemSize; } |
255 | /// Returns the size of the metadata. |
256 | unsigned getMetadataSize() const { return MDSize; } |
257 | |
258 | /// Returns the number of elements stored in the block. |
259 | unsigned getNumElems() const { |
260 | return Size == UnknownSizeMark ? 0 : (getSize() / getElemSize()); |
261 | } |
262 | |
263 | /// Checks if the descriptor is of an array of primitives. |
264 | bool isPrimitiveArray() const { return IsArray && !ElemDesc; } |
265 | /// Checks if the descriptor is of an array of composites. |
266 | bool isCompositeArray() const { return IsArray && ElemDesc; } |
267 | /// Checks if the descriptor is of an array of zero size. |
268 | bool isZeroSizeArray() const { return Size == 0; } |
269 | /// Checks if the descriptor is of an array of unknown size. |
270 | bool isUnknownSizeArray() const { return Size == UnknownSizeMark; } |
271 | |
272 | /// Checks if the descriptor is of a primitive. |
273 | bool isPrimitive() const { return !IsArray && !ElemRecord && PrimT; } |
274 | |
275 | /// Checks if the descriptor is of an array. |
276 | bool isArray() const { return IsArray; } |
277 | /// Checks if the descriptor is of a record. |
278 | bool isRecord() const { return !IsArray && ElemRecord; } |
279 | /// Checks if the descriptor is of a union. |
280 | bool isUnion() const; |
281 | /// Checks if this is a dummy descriptor. |
282 | bool isDummy() const { return IsDummy; } |
283 | |
284 | /// Whether variables of this descriptor need their destructor called or not. |
285 | bool hasTrivialDtor() const; |
286 | |
287 | void dump() const; |
288 | void dump(llvm::raw_ostream &OS) const; |
289 | void dumpFull(unsigned Offset = 0, unsigned Indent = 0) const; |
290 | }; |
291 | |
292 | /// Bitfield tracking the initialisation status of elements of primitive arrays. |
293 | struct InitMap final { |
294 | private: |
295 | /// Type packing bits. |
296 | using T = uint64_t; |
297 | /// Bits stored in a single field. |
298 | static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT; |
299 | |
300 | public: |
301 | /// Initializes the map with no fields set. |
302 | explicit InitMap(unsigned N); |
303 | |
304 | private: |
305 | friend class Pointer; |
306 | |
307 | /// Returns a pointer to storage. |
308 | T *data() { return Data.get(); } |
309 | const T *data() const { return Data.get(); } |
310 | |
311 | /// Initializes an element. Returns true when object if fully initialized. |
312 | bool initializeElement(unsigned I); |
313 | |
314 | /// Checks if an element was initialized. |
315 | bool isElementInitialized(unsigned I) const; |
316 | |
317 | static constexpr size_t numFields(unsigned N) { |
318 | return (N + PER_FIELD - 1) / PER_FIELD; |
319 | } |
320 | /// Number of fields not initialized. |
321 | unsigned UninitFields; |
322 | std::unique_ptr<T[]> Data; |
323 | }; |
324 | |
325 | } // namespace interp |
326 | } // namespace clang |
327 | |
328 | #endif |
329 | |