1e5dd7070Spatrick //===--- Pointer.h - Types for the constexpr VM -----------------*- C++ -*-===// 2e5dd7070Spatrick // 3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information. 5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e5dd7070Spatrick // 7e5dd7070Spatrick //===----------------------------------------------------------------------===// 8e5dd7070Spatrick // 9e5dd7070Spatrick // Defines the classes responsible for pointer tracking. 10e5dd7070Spatrick // 11e5dd7070Spatrick //===----------------------------------------------------------------------===// 12e5dd7070Spatrick 13e5dd7070Spatrick #ifndef LLVM_CLANG_AST_INTERP_POINTER_H 14e5dd7070Spatrick #define LLVM_CLANG_AST_INTERP_POINTER_H 15e5dd7070Spatrick 16e5dd7070Spatrick #include "Descriptor.h" 17ec727ea7Spatrick #include "InterpBlock.h" 18ec727ea7Spatrick #include "clang/AST/ComparisonCategories.h" 19e5dd7070Spatrick #include "clang/AST/Decl.h" 20e5dd7070Spatrick #include "clang/AST/DeclCXX.h" 21e5dd7070Spatrick #include "clang/AST/Expr.h" 22e5dd7070Spatrick #include "llvm/ADT/PointerUnion.h" 23e5dd7070Spatrick #include "llvm/Support/raw_ostream.h" 24e5dd7070Spatrick 25e5dd7070Spatrick namespace clang { 26e5dd7070Spatrick namespace interp { 27e5dd7070Spatrick class Block; 28e5dd7070Spatrick class DeadBlock; 29e5dd7070Spatrick class Pointer; 30e5dd7070Spatrick enum PrimType : unsigned; 31e5dd7070Spatrick 32e5dd7070Spatrick /// A pointer to a memory block, live or dead. 33e5dd7070Spatrick /// 34e5dd7070Spatrick /// This object can be allocated into interpreter stack frames. If pointing to 35e5dd7070Spatrick /// a live block, it is a link in the chain of pointers pointing to the block. 36*12c85518Srobert /// 37*12c85518Srobert /// In the simplest form, a Pointer has a Block* (the pointee) and both Base 38*12c85518Srobert /// and Offset are 0, which means it will point to raw data. 39*12c85518Srobert /// 40*12c85518Srobert /// The Base field is used to access metadata about the data. For primitive 41*12c85518Srobert /// arrays, the Base is followed by an InitMap. In a variety of cases, the 42*12c85518Srobert /// Base is preceded by an InlineDescriptor, which is used to track the 43*12c85518Srobert /// initialization state, among other things. 44*12c85518Srobert /// 45*12c85518Srobert /// The Offset field is used to access the actual data. In other words, the 46*12c85518Srobert /// data the pointer decribes can be found at 47*12c85518Srobert /// Pointee->rawData() + Pointer.Offset. 48*12c85518Srobert /// 49*12c85518Srobert /// 50*12c85518Srobert /// Pointee Offset 51*12c85518Srobert /// │ │ 52*12c85518Srobert /// │ │ 53*12c85518Srobert /// ▼ ▼ 54*12c85518Srobert /// ┌───────┬────────────┬─────────┬────────────────────────────┐ 55*12c85518Srobert /// │ Block │ InlineDesc │ InitMap │ Actual Data │ 56*12c85518Srobert /// └───────┴────────────┴─────────┴────────────────────────────┘ 57*12c85518Srobert /// ▲ 58*12c85518Srobert /// │ 59*12c85518Srobert /// │ 60*12c85518Srobert /// Base 61e5dd7070Spatrick class Pointer { 62e5dd7070Spatrick private: 63*12c85518Srobert static constexpr unsigned PastEndMark = ~0u; 64*12c85518Srobert static constexpr unsigned RootPtrMark = ~0u; 65e5dd7070Spatrick 66e5dd7070Spatrick public: Pointer()67e5dd7070Spatrick Pointer() {} 68e5dd7070Spatrick Pointer(Block *B); 69*12c85518Srobert Pointer(Block *B, unsigned BaseAndOffset); 70e5dd7070Spatrick Pointer(const Pointer &P); 71e5dd7070Spatrick Pointer(Pointer &&P); 72e5dd7070Spatrick ~Pointer(); 73e5dd7070Spatrick 74e5dd7070Spatrick void operator=(const Pointer &P); 75e5dd7070Spatrick void operator=(Pointer &&P); 76e5dd7070Spatrick 77e5dd7070Spatrick /// Converts the pointer to an APValue. 78e5dd7070Spatrick APValue toAPValue() const; 79e5dd7070Spatrick 80e5dd7070Spatrick /// Offsets a pointer inside an array. atIndex(unsigned Idx)81e5dd7070Spatrick Pointer atIndex(unsigned Idx) const { 82e5dd7070Spatrick if (Base == RootPtrMark) 83e5dd7070Spatrick return Pointer(Pointee, RootPtrMark, getDeclDesc()->getSize()); 84e5dd7070Spatrick unsigned Off = Idx * elemSize(); 85e5dd7070Spatrick if (getFieldDesc()->ElemDesc) 86e5dd7070Spatrick Off += sizeof(InlineDescriptor); 87e5dd7070Spatrick else 88e5dd7070Spatrick Off += sizeof(InitMap *); 89e5dd7070Spatrick return Pointer(Pointee, Base, Base + Off); 90e5dd7070Spatrick } 91e5dd7070Spatrick 92e5dd7070Spatrick /// Creates a pointer to a field. atField(unsigned Off)93e5dd7070Spatrick Pointer atField(unsigned Off) const { 94e5dd7070Spatrick unsigned Field = Offset + Off; 95e5dd7070Spatrick return Pointer(Pointee, Field, Field); 96e5dd7070Spatrick } 97e5dd7070Spatrick 98e5dd7070Spatrick /// Restricts the scope of an array element pointer. narrow()99e5dd7070Spatrick Pointer narrow() const { 100e5dd7070Spatrick // Null pointers cannot be narrowed. 101e5dd7070Spatrick if (isZero() || isUnknownSizeArray()) 102e5dd7070Spatrick return *this; 103e5dd7070Spatrick 104e5dd7070Spatrick // Pointer to an array of base types - enter block. 105e5dd7070Spatrick if (Base == RootPtrMark) 106e5dd7070Spatrick return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark); 107e5dd7070Spatrick 108e5dd7070Spatrick // Pointer is one past end - magic offset marks that. 109e5dd7070Spatrick if (isOnePastEnd()) 110e5dd7070Spatrick return Pointer(Pointee, Base, PastEndMark); 111e5dd7070Spatrick 112e5dd7070Spatrick // Primitive arrays are a bit special since they do not have inline 113e5dd7070Spatrick // descriptors. If Offset != Base, then the pointer already points to 114e5dd7070Spatrick // an element and there is nothing to do. Otherwise, the pointer is 115e5dd7070Spatrick // adjusted to the first element of the array. 116e5dd7070Spatrick if (inPrimitiveArray()) { 117e5dd7070Spatrick if (Offset != Base) 118e5dd7070Spatrick return *this; 119e5dd7070Spatrick return Pointer(Pointee, Base, Offset + sizeof(InitMap *)); 120e5dd7070Spatrick } 121e5dd7070Spatrick 122e5dd7070Spatrick // Pointer is to a field or array element - enter it. 123e5dd7070Spatrick if (Offset != Base) 124e5dd7070Spatrick return Pointer(Pointee, Offset, Offset); 125e5dd7070Spatrick 126e5dd7070Spatrick // Enter the first element of an array. 127e5dd7070Spatrick if (!getFieldDesc()->isArray()) 128e5dd7070Spatrick return *this; 129e5dd7070Spatrick 130e5dd7070Spatrick const unsigned NewBase = Base + sizeof(InlineDescriptor); 131e5dd7070Spatrick return Pointer(Pointee, NewBase, NewBase); 132e5dd7070Spatrick } 133e5dd7070Spatrick 134e5dd7070Spatrick /// Expands a pointer to the containing array, undoing narrowing. expand()135e5dd7070Spatrick Pointer expand() const { 136e5dd7070Spatrick if (isElementPastEnd()) { 137e5dd7070Spatrick // Revert to an outer one-past-end pointer. 138e5dd7070Spatrick unsigned Adjust; 139e5dd7070Spatrick if (inPrimitiveArray()) 140e5dd7070Spatrick Adjust = sizeof(InitMap *); 141e5dd7070Spatrick else 142e5dd7070Spatrick Adjust = sizeof(InlineDescriptor); 143e5dd7070Spatrick return Pointer(Pointee, Base, Base + getSize() + Adjust); 144e5dd7070Spatrick } 145e5dd7070Spatrick 146e5dd7070Spatrick // Do not step out of array elements. 147e5dd7070Spatrick if (Base != Offset) 148e5dd7070Spatrick return *this; 149e5dd7070Spatrick 150e5dd7070Spatrick // If at base, point to an array of base types. 151e5dd7070Spatrick if (Base == 0) 152e5dd7070Spatrick return Pointer(Pointee, RootPtrMark, 0); 153e5dd7070Spatrick 154e5dd7070Spatrick // Step into the containing array, if inside one. 155e5dd7070Spatrick unsigned Next = Base - getInlineDesc()->Offset; 156e5dd7070Spatrick Descriptor *Desc = Next == 0 ? getDeclDesc() : getDescriptor(Next)->Desc; 157e5dd7070Spatrick if (!Desc->IsArray) 158e5dd7070Spatrick return *this; 159e5dd7070Spatrick return Pointer(Pointee, Next, Offset); 160e5dd7070Spatrick } 161e5dd7070Spatrick 162e5dd7070Spatrick /// Checks if the pointer is null. isZero()163e5dd7070Spatrick bool isZero() const { return Pointee == nullptr; } 164e5dd7070Spatrick /// Checks if the pointer is live. isLive()165e5dd7070Spatrick bool isLive() const { return Pointee && !Pointee->IsDead; } 166e5dd7070Spatrick /// Checks if the item is a field in an object. isField()167e5dd7070Spatrick bool isField() const { return Base != 0 && Base != RootPtrMark; } 168e5dd7070Spatrick 169e5dd7070Spatrick /// Accessor for information about the declaration site. getDeclDesc()170e5dd7070Spatrick Descriptor *getDeclDesc() const { return Pointee->Desc; } getDeclLoc()171e5dd7070Spatrick SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); } 172e5dd7070Spatrick 173e5dd7070Spatrick /// Returns a pointer to the object of which this pointer is a field. getBase()174e5dd7070Spatrick Pointer getBase() const { 175e5dd7070Spatrick if (Base == RootPtrMark) { 176e5dd7070Spatrick assert(Offset == PastEndMark && "cannot get base of a block"); 177e5dd7070Spatrick return Pointer(Pointee, Base, 0); 178e5dd7070Spatrick } 179e5dd7070Spatrick assert(Offset == Base && "not an inner field"); 180e5dd7070Spatrick unsigned NewBase = Base - getInlineDesc()->Offset; 181e5dd7070Spatrick return Pointer(Pointee, NewBase, NewBase); 182e5dd7070Spatrick } 183e5dd7070Spatrick /// Returns the parent array. getArray()184e5dd7070Spatrick Pointer getArray() const { 185e5dd7070Spatrick if (Base == RootPtrMark) { 186e5dd7070Spatrick assert(Offset != 0 && Offset != PastEndMark && "not an array element"); 187e5dd7070Spatrick return Pointer(Pointee, Base, 0); 188e5dd7070Spatrick } 189e5dd7070Spatrick assert(Offset != Base && "not an array element"); 190e5dd7070Spatrick return Pointer(Pointee, Base, Base); 191e5dd7070Spatrick } 192e5dd7070Spatrick 193e5dd7070Spatrick /// Accessors for information about the innermost field. getFieldDesc()194e5dd7070Spatrick Descriptor *getFieldDesc() const { 195e5dd7070Spatrick if (Base == 0 || Base == RootPtrMark) 196e5dd7070Spatrick return getDeclDesc(); 197e5dd7070Spatrick return getInlineDesc()->Desc; 198e5dd7070Spatrick } 199e5dd7070Spatrick 200e5dd7070Spatrick /// Returns the type of the innermost field. getType()201e5dd7070Spatrick QualType getType() const { return getFieldDesc()->getType(); } 202e5dd7070Spatrick 203e5dd7070Spatrick /// Returns the element size of the innermost field. elemSize()204e5dd7070Spatrick size_t elemSize() const { 205e5dd7070Spatrick if (Base == RootPtrMark) 206e5dd7070Spatrick return getDeclDesc()->getSize(); 207e5dd7070Spatrick return getFieldDesc()->getElemSize(); 208e5dd7070Spatrick } 209e5dd7070Spatrick /// Returns the total size of the innermost field. getSize()210e5dd7070Spatrick size_t getSize() const { return getFieldDesc()->getSize(); } 211e5dd7070Spatrick 212e5dd7070Spatrick /// Returns the offset into an array. getOffset()213e5dd7070Spatrick unsigned getOffset() const { 214e5dd7070Spatrick assert(Offset != PastEndMark && "invalid offset"); 215e5dd7070Spatrick if (Base == RootPtrMark) 216e5dd7070Spatrick return Offset; 217e5dd7070Spatrick 218e5dd7070Spatrick unsigned Adjust = 0; 219e5dd7070Spatrick if (Offset != Base) { 220e5dd7070Spatrick if (getFieldDesc()->ElemDesc) 221e5dd7070Spatrick Adjust = sizeof(InlineDescriptor); 222e5dd7070Spatrick else 223e5dd7070Spatrick Adjust = sizeof(InitMap *); 224e5dd7070Spatrick } 225e5dd7070Spatrick return Offset - Base - Adjust; 226e5dd7070Spatrick } 227e5dd7070Spatrick 228e5dd7070Spatrick /// Checks if the innermost field is an array. inArray()229e5dd7070Spatrick bool inArray() const { return getFieldDesc()->IsArray; } 230e5dd7070Spatrick /// Checks if the structure is a primitive array. inPrimitiveArray()231e5dd7070Spatrick bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); } 232e5dd7070Spatrick /// Checks if the structure is an array of unknown size. isUnknownSizeArray()233e5dd7070Spatrick bool isUnknownSizeArray() const { 234e5dd7070Spatrick return getFieldDesc()->isUnknownSizeArray(); 235e5dd7070Spatrick } 236e5dd7070Spatrick /// Checks if the pointer points to an array. isArrayElement()237e5dd7070Spatrick bool isArrayElement() const { return Base != Offset; } 238e5dd7070Spatrick /// Pointer points directly to a block. isRoot()239e5dd7070Spatrick bool isRoot() const { 240e5dd7070Spatrick return (Base == 0 || Base == RootPtrMark) && Offset == 0; 241e5dd7070Spatrick } 242e5dd7070Spatrick 243e5dd7070Spatrick /// Returns the record descriptor of a class. getRecord()244e5dd7070Spatrick Record *getRecord() const { return getFieldDesc()->ElemRecord; } 245*12c85518Srobert // Returns the element record type, if this is a non-primive array. getElemRecord()246*12c85518Srobert Record *getElemRecord() const { return getFieldDesc()->ElemDesc->ElemRecord; } 247e5dd7070Spatrick /// Returns the field information. getField()248e5dd7070Spatrick const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); } 249e5dd7070Spatrick 250e5dd7070Spatrick /// Checks if the object is a union. 251e5dd7070Spatrick bool isUnion() const; 252e5dd7070Spatrick 253e5dd7070Spatrick /// Checks if the storage is extern. isExtern()254e5dd7070Spatrick bool isExtern() const { return Pointee->isExtern(); } 255e5dd7070Spatrick /// Checks if the storage is static. isStatic()256e5dd7070Spatrick bool isStatic() const { return Pointee->isStatic(); } 257e5dd7070Spatrick /// Checks if the storage is temporary. isTemporary()258e5dd7070Spatrick bool isTemporary() const { return Pointee->isTemporary(); } 259e5dd7070Spatrick /// Checks if the storage is a static temporary. isStaticTemporary()260e5dd7070Spatrick bool isStaticTemporary() const { return isStatic() && isTemporary(); } 261e5dd7070Spatrick 262e5dd7070Spatrick /// Checks if the field is mutable. isMutable()263*12c85518Srobert bool isMutable() const { 264*12c85518Srobert return Base != 0 && getInlineDesc()->IsFieldMutable; 265*12c85518Srobert } 266e5dd7070Spatrick /// Checks if an object was initialized. 267e5dd7070Spatrick bool isInitialized() const; 268e5dd7070Spatrick /// Checks if the object is active. isActive()269e5dd7070Spatrick bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; } 270e5dd7070Spatrick /// Checks if a structure is a base class. isBaseClass()271e5dd7070Spatrick bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; } 272e5dd7070Spatrick 273e5dd7070Spatrick /// Checks if an object or a subfield is mutable. isConst()274e5dd7070Spatrick bool isConst() const { 275e5dd7070Spatrick return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst; 276e5dd7070Spatrick } 277e5dd7070Spatrick 278e5dd7070Spatrick /// Returns the declaration ID. getDeclID()279*12c85518Srobert std::optional<unsigned> getDeclID() const { return Pointee->getDeclID(); } 280e5dd7070Spatrick 281e5dd7070Spatrick /// Returns the byte offset from the start. getByteOffset()282e5dd7070Spatrick unsigned getByteOffset() const { 283e5dd7070Spatrick return Offset; 284e5dd7070Spatrick } 285e5dd7070Spatrick 286e5dd7070Spatrick /// Returns the number of elements. getNumElems()287e5dd7070Spatrick unsigned getNumElems() const { return getSize() / elemSize(); } 288e5dd7070Spatrick 289e5dd7070Spatrick /// Returns the index into an array. getIndex()290e5dd7070Spatrick int64_t getIndex() const { 291e5dd7070Spatrick if (isElementPastEnd()) 292e5dd7070Spatrick return 1; 293e5dd7070Spatrick if (auto ElemSize = elemSize()) 294e5dd7070Spatrick return getOffset() / ElemSize; 295e5dd7070Spatrick return 0; 296e5dd7070Spatrick } 297e5dd7070Spatrick 298e5dd7070Spatrick /// Checks if the index is one past end. isOnePastEnd()299e5dd7070Spatrick bool isOnePastEnd() const { 300e5dd7070Spatrick return isElementPastEnd() || getSize() == getOffset(); 301e5dd7070Spatrick } 302e5dd7070Spatrick 303e5dd7070Spatrick /// Checks if the pointer is an out-of-bounds element pointer. isElementPastEnd()304e5dd7070Spatrick bool isElementPastEnd() const { return Offset == PastEndMark; } 305e5dd7070Spatrick 306e5dd7070Spatrick /// Dereferences the pointer, if it's live. deref()307e5dd7070Spatrick template <typename T> T &deref() const { 308e5dd7070Spatrick assert(isLive() && "Invalid pointer"); 309*12c85518Srobert return *reinterpret_cast<T *>(Pointee->rawData() + Offset); 310e5dd7070Spatrick } 311e5dd7070Spatrick 312e5dd7070Spatrick /// Dereferences a primitive element. elem(unsigned I)313e5dd7070Spatrick template <typename T> T &elem(unsigned I) const { 314*12c85518Srobert return reinterpret_cast<T *>(Pointee->rawData())[I]; 315e5dd7070Spatrick } 316e5dd7070Spatrick 317e5dd7070Spatrick /// Initializes a field. 318e5dd7070Spatrick void initialize() const; 319e5dd7070Spatrick /// Activats a field. 320e5dd7070Spatrick void activate() const; 321e5dd7070Spatrick /// Deactivates an entire strurcutre. 322e5dd7070Spatrick void deactivate() const; 323e5dd7070Spatrick 324e5dd7070Spatrick /// Checks if two pointers are comparable. 325e5dd7070Spatrick static bool hasSameBase(const Pointer &A, const Pointer &B); 326e5dd7070Spatrick /// Checks if two pointers can be subtracted. 327e5dd7070Spatrick static bool hasSameArray(const Pointer &A, const Pointer &B); 328e5dd7070Spatrick 329e5dd7070Spatrick /// Prints the pointer. print(llvm::raw_ostream & OS)330e5dd7070Spatrick void print(llvm::raw_ostream &OS) const { 331*12c85518Srobert OS << Pointee << " {" << Base << ", " << Offset << ", "; 332e5dd7070Spatrick if (Pointee) 333e5dd7070Spatrick OS << Pointee->getSize(); 334e5dd7070Spatrick else 335e5dd7070Spatrick OS << "nullptr"; 336e5dd7070Spatrick OS << "}"; 337e5dd7070Spatrick } 338e5dd7070Spatrick 339e5dd7070Spatrick private: 340e5dd7070Spatrick friend class Block; 341e5dd7070Spatrick friend class DeadBlock; 342e5dd7070Spatrick 343e5dd7070Spatrick Pointer(Block *Pointee, unsigned Base, unsigned Offset); 344e5dd7070Spatrick 345e5dd7070Spatrick /// Returns the embedded descriptor preceding a field. getInlineDesc()346e5dd7070Spatrick InlineDescriptor *getInlineDesc() const { return getDescriptor(Base); } 347e5dd7070Spatrick 348e5dd7070Spatrick /// Returns a descriptor at a given offset. getDescriptor(unsigned Offset)349e5dd7070Spatrick InlineDescriptor *getDescriptor(unsigned Offset) const { 350e5dd7070Spatrick assert(Offset != 0 && "Not a nested pointer"); 351*12c85518Srobert return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) - 352*12c85518Srobert 1; 353e5dd7070Spatrick } 354e5dd7070Spatrick 355e5dd7070Spatrick /// Returns a reference to the pointer which stores the initialization map. getInitMap()356e5dd7070Spatrick InitMap *&getInitMap() const { 357*12c85518Srobert return *reinterpret_cast<InitMap **>(Pointee->rawData() + Base); 358e5dd7070Spatrick } 359e5dd7070Spatrick 360e5dd7070Spatrick /// The block the pointer is pointing to. 361e5dd7070Spatrick Block *Pointee = nullptr; 362e5dd7070Spatrick /// Start of the current subfield. 363e5dd7070Spatrick unsigned Base = 0; 364e5dd7070Spatrick /// Offset into the block. 365e5dd7070Spatrick unsigned Offset = 0; 366e5dd7070Spatrick 367e5dd7070Spatrick /// Previous link in the pointer chain. 368e5dd7070Spatrick Pointer *Prev = nullptr; 369e5dd7070Spatrick /// Next link in the pointer chain. 370e5dd7070Spatrick Pointer *Next = nullptr; 371e5dd7070Spatrick }; 372e5dd7070Spatrick 373e5dd7070Spatrick inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) { 374e5dd7070Spatrick P.print(OS); 375e5dd7070Spatrick return OS; 376e5dd7070Spatrick } 377e5dd7070Spatrick 378e5dd7070Spatrick } // namespace interp 379e5dd7070Spatrick } // namespace clang 380e5dd7070Spatrick 381e5dd7070Spatrick #endif 382