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