/src/llvm-project/clang/lib/AST/Interp/Descriptor.h
Line | Count | Source (jump to first uncovered line) |
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 | | unsigned IsConst : 1; |
63 | | /// For primitive fields, it indicates if the field was initialized. |
64 | | /// Primitive fields in static storage are always initialized. |
65 | | /// Arrays are always initialized, even though their elements might not be. |
66 | | /// Base classes are initialized after the constructor is invoked. |
67 | | unsigned IsInitialized : 1; |
68 | | /// Flag indicating if the field is an embedded base class. |
69 | | unsigned IsBase : 1; |
70 | | /// Flag indicating if the field is the active member of a union. |
71 | | unsigned IsActive : 1; |
72 | | /// Flag indicating if the field is mutable (if in a record). |
73 | | unsigned IsFieldMutable : 1; |
74 | | |
75 | | const Descriptor *Desc; |
76 | | }; |
77 | | |
78 | | /// Describes a memory block created by an allocation site. |
79 | | struct Descriptor final { |
80 | | private: |
81 | | /// Original declaration, used to emit the error message. |
82 | | const DeclTy Source; |
83 | | /// Size of an element, in host bytes. |
84 | | const unsigned ElemSize; |
85 | | /// Size of the storage, in host bytes. |
86 | | const unsigned Size; |
87 | | /// Size of the metadata. |
88 | | const unsigned MDSize; |
89 | | /// Size of the allocation (storage + metadata), in host bytes. |
90 | | const unsigned AllocSize; |
91 | | |
92 | | /// Value to denote arrays of unknown size. |
93 | | static constexpr unsigned UnknownSizeMark = (unsigned)-1; |
94 | | |
95 | | public: |
96 | | /// Token to denote structures of unknown size. |
97 | | struct UnknownSize {}; |
98 | | |
99 | | using MetadataSize = std::optional<unsigned>; |
100 | | static constexpr MetadataSize InlineDescMD = sizeof(InlineDescriptor); |
101 | | |
102 | | /// Pointer to the record, if block contains records. |
103 | | const Record *const ElemRecord = nullptr; |
104 | | /// Descriptor of the array element. |
105 | | const Descriptor *const ElemDesc = nullptr; |
106 | | /// Flag indicating if the block is mutable. |
107 | | const bool IsConst = false; |
108 | | /// Flag indicating if a field is mutable. |
109 | | const bool IsMutable = false; |
110 | | /// Flag indicating if the block is a temporary. |
111 | | const bool IsTemporary = false; |
112 | | /// Flag indicating if the block is an array. |
113 | | const bool IsArray = false; |
114 | | /// Flag indicating if this is a dummy descriptor. |
115 | | const bool IsDummy = false; |
116 | | |
117 | | /// Storage management methods. |
118 | | const BlockCtorFn CtorFn = nullptr; |
119 | | const BlockDtorFn DtorFn = nullptr; |
120 | | const BlockMoveFn MoveFn = nullptr; |
121 | | |
122 | | /// Allocates a descriptor for a primitive. |
123 | | Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsConst, |
124 | | bool IsTemporary, bool IsMutable); |
125 | | |
126 | | /// Allocates a descriptor for an array of primitives. |
127 | | Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems, |
128 | | bool IsConst, bool IsTemporary, bool IsMutable); |
129 | | |
130 | | /// Allocates a descriptor for an array of primitives of unknown size. |
131 | | Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize); |
132 | | |
133 | | /// Allocates a descriptor for an array of composites. |
134 | | Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, |
135 | | unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable); |
136 | | |
137 | | /// Allocates a descriptor for an array of composites of unknown size. |
138 | | Descriptor(const DeclTy &D, const Descriptor *Elem, bool IsTemporary, |
139 | | UnknownSize); |
140 | | |
141 | | /// Allocates a descriptor for a record. |
142 | | Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, bool IsConst, |
143 | | bool IsTemporary, bool IsMutable); |
144 | | |
145 | | Descriptor(const DeclTy &D, MetadataSize MD); |
146 | | |
147 | | QualType getType() const; |
148 | | QualType getElemQualType() const; |
149 | | SourceLocation getLocation() const; |
150 | | |
151 | 0 | const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); } |
152 | 0 | const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); } |
153 | | |
154 | 0 | const ValueDecl *asValueDecl() const { |
155 | 0 | return dyn_cast_if_present<ValueDecl>(asDecl()); |
156 | 0 | } |
157 | | |
158 | 0 | const FieldDecl *asFieldDecl() const { |
159 | 0 | return dyn_cast_if_present<FieldDecl>(asDecl()); |
160 | 0 | } |
161 | | |
162 | 0 | const RecordDecl *asRecordDecl() const { |
163 | 0 | return dyn_cast_if_present<RecordDecl>(asDecl()); |
164 | 0 | } |
165 | | |
166 | | /// Returns the size of the object without metadata. |
167 | 0 | unsigned getSize() const { |
168 | 0 | assert(!isUnknownSizeArray() && "Array of unknown size"); |
169 | 0 | return Size; |
170 | 0 | } |
171 | | |
172 | | /// Returns the allocated size, including metadata. |
173 | 0 | unsigned getAllocSize() const { return AllocSize; } |
174 | | /// returns the size of an element when the structure is viewed as an array. |
175 | 0 | unsigned getElemSize() const { return ElemSize; } |
176 | | /// Returns the size of the metadata. |
177 | 0 | unsigned getMetadataSize() const { return MDSize; } |
178 | | |
179 | | /// Returns the number of elements stored in the block. |
180 | 0 | unsigned getNumElems() const { |
181 | 0 | return Size == UnknownSizeMark ? 0 : (getSize() / getElemSize()); |
182 | 0 | } |
183 | | |
184 | | /// Checks if the descriptor is of an array of primitives. |
185 | 0 | bool isPrimitiveArray() const { return IsArray && !ElemDesc; } |
186 | | /// Checks if the descriptor is of an array of composites. |
187 | 0 | bool isCompositeArray() const { return IsArray && ElemDesc; } |
188 | | /// Checks if the descriptor is of an array of zero size. |
189 | 0 | bool isZeroSizeArray() const { return Size == 0; } |
190 | | /// Checks if the descriptor is of an array of unknown size. |
191 | 0 | bool isUnknownSizeArray() const { return Size == UnknownSizeMark; } |
192 | | |
193 | | /// Checks if the descriptor is of a primitive. |
194 | 0 | bool isPrimitive() const { return !IsArray && !ElemRecord; } |
195 | | |
196 | | /// Checks if the descriptor is of an array. |
197 | 0 | bool isArray() const { return IsArray; } |
198 | | /// Checks if the descriptor is of a record. |
199 | 0 | bool isRecord() const { return !IsArray && ElemRecord; } |
200 | | /// Checks if this is a dummy descriptor. |
201 | 0 | bool isDummy() const { return IsDummy; } |
202 | | }; |
203 | | |
204 | | /// Bitfield tracking the initialisation status of elements of primitive arrays. |
205 | | struct InitMap final { |
206 | | private: |
207 | | /// Type packing bits. |
208 | | using T = uint64_t; |
209 | | /// Bits stored in a single field. |
210 | | static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT; |
211 | | |
212 | | public: |
213 | | /// Initializes the map with no fields set. |
214 | | explicit InitMap(unsigned N); |
215 | | |
216 | | private: |
217 | | friend class Pointer; |
218 | | |
219 | | /// Returns a pointer to storage. |
220 | 0 | T *data() { return Data.get(); } |
221 | 0 | const T *data() const { return Data.get(); } |
222 | | |
223 | | /// Initializes an element. Returns true when object if fully initialized. |
224 | | bool initializeElement(unsigned I); |
225 | | |
226 | | /// Checks if an element was initialized. |
227 | | bool isElementInitialized(unsigned I) const; |
228 | | |
229 | 0 | static constexpr size_t numFields(unsigned N) { |
230 | 0 | return (N + PER_FIELD - 1) / PER_FIELD; |
231 | 0 | } |
232 | | /// Number of fields not initialized. |
233 | | unsigned UninitFields; |
234 | | std::unique_ptr<T[]> Data; |
235 | | }; |
236 | | |
237 | | } // namespace interp |
238 | | } // namespace clang |
239 | | |
240 | | #endif |