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