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