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