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