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