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