| //===--- Pointer.h - Types for the constexpr VM -----------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Defines the classes responsible for pointer tracking. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_AST_INTERP_POINTER_H |
| #define LLVM_CLANG_AST_INTERP_POINTER_H |
| |
| #include "Block.h" |
| #include "Descriptor.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/ComparisonCategories.h" |
| #include "llvm/ADT/PointerUnion.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| namespace clang { |
| namespace interp { |
| class Block; |
| class DeadBlock; |
| class Context; |
| class InterpState; |
| class Pointer; |
| class Function; |
| enum PrimType : unsigned; |
| |
| /// A pointer to a memory block, live or dead. |
| /// |
| /// This object can be allocated into interpreter stack frames. If pointing to |
| /// a live block, it is a link in the chain of pointers pointing to the block. |
| class Pointer { |
| private: |
| static constexpr unsigned PastEndMark = (unsigned)-1; |
| static constexpr unsigned RootPtrMark = (unsigned)-1; |
| |
| public: |
| Pointer() {} |
| Pointer(Block *B); |
| Pointer(const Pointer &P); |
| Pointer(Pointer &&P); |
| ~Pointer(); |
| |
| void operator=(const Pointer &P); |
| void operator=(Pointer &&P); |
| |
| /// Converts the pointer to an APValue. |
| APValue toAPValue() const; |
| |
| /// Offsets a pointer inside an array. |
| Pointer atIndex(unsigned Idx) const { |
| if (Base == RootPtrMark) |
| return Pointer(Pointee, RootPtrMark, getDeclDesc()->getSize()); |
| unsigned Off = Idx * elemSize(); |
| if (getFieldDesc()->ElemDesc) |
| Off += sizeof(InlineDescriptor); |
| else |
| Off += sizeof(InitMap *); |
| return Pointer(Pointee, Base, Base + Off); |
| } |
| |
| /// Creates a pointer to a field. |
| Pointer atField(unsigned Off) const { |
| unsigned Field = Offset + Off; |
| return Pointer(Pointee, Field, Field); |
| } |
| |
| /// Restricts the scope of an array element pointer. |
| Pointer narrow() const { |
| // Null pointers cannot be narrowed. |
| if (isZero() || isUnknownSizeArray()) |
| return *this; |
| |
| // Pointer to an array of base types - enter block. |
| if (Base == RootPtrMark) |
| return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark); |
| |
| // Pointer is one past end - magic offset marks that. |
| if (isOnePastEnd()) |
| return Pointer(Pointee, Base, PastEndMark); |
| |
| // Primitive arrays are a bit special since they do not have inline |
| // descriptors. If Offset != Base, then the pointer already points to |
| // an element and there is nothing to do. Otherwise, the pointer is |
| // adjusted to the first element of the array. |
| if (inPrimitiveArray()) { |
| if (Offset != Base) |
| return *this; |
| return Pointer(Pointee, Base, Offset + sizeof(InitMap *)); |
| } |
| |
| // Pointer is to a field or array element - enter it. |
| if (Offset != Base) |
| return Pointer(Pointee, Offset, Offset); |
| |
| // Enter the first element of an array. |
| if (!getFieldDesc()->isArray()) |
| return *this; |
| |
| const unsigned NewBase = Base + sizeof(InlineDescriptor); |
| return Pointer(Pointee, NewBase, NewBase); |
| } |
| |
| /// Expands a pointer to the containing array, undoing narrowing. |
| Pointer expand() const { |
| if (isElementPastEnd()) { |
| // Revert to an outer one-past-end pointer. |
| unsigned Adjust; |
| if (inPrimitiveArray()) |
| Adjust = sizeof(InitMap *); |
| else |
| Adjust = sizeof(InlineDescriptor); |
| return Pointer(Pointee, Base, Base + getSize() + Adjust); |
| } |
| |
| // Do not step out of array elements. |
| if (Base != Offset) |
| return *this; |
| |
| // If at base, point to an array of base types. |
| if (Base == 0) |
| return Pointer(Pointee, RootPtrMark, 0); |
| |
| // Step into the containing array, if inside one. |
| unsigned Next = Base - getInlineDesc()->Offset; |
| Descriptor *Desc = Next == 0 ? getDeclDesc() : getDescriptor(Next)->Desc; |
| if (!Desc->IsArray) |
| return *this; |
| return Pointer(Pointee, Next, Offset); |
| } |
| |
| /// Checks if the pointer is null. |
| bool isZero() const { return Pointee == nullptr; } |
| /// Checks if the pointer is live. |
| bool isLive() const { return Pointee && !Pointee->IsDead; } |
| /// Checks if the item is a field in an object. |
| bool isField() const { return Base != 0 && Base != RootPtrMark; } |
| |
| /// Accessor for information about the declaration site. |
| Descriptor *getDeclDesc() const { return Pointee->Desc; } |
| SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); } |
| |
| /// Returns a pointer to the object of which this pointer is a field. |
| Pointer getBase() const { |
| if (Base == RootPtrMark) { |
| assert(Offset == PastEndMark && "cannot get base of a block"); |
| return Pointer(Pointee, Base, 0); |
| } |
| assert(Offset == Base && "not an inner field"); |
| unsigned NewBase = Base - getInlineDesc()->Offset; |
| return Pointer(Pointee, NewBase, NewBase); |
| } |
| /// Returns the parent array. |
| Pointer getArray() const { |
| if (Base == RootPtrMark) { |
| assert(Offset != 0 && Offset != PastEndMark && "not an array element"); |
| return Pointer(Pointee, Base, 0); |
| } |
| assert(Offset != Base && "not an array element"); |
| return Pointer(Pointee, Base, Base); |
| } |
| |
| /// Accessors for information about the innermost field. |
| Descriptor *getFieldDesc() const { |
| if (Base == 0 || Base == RootPtrMark) |
| return getDeclDesc(); |
| return getInlineDesc()->Desc; |
| } |
| |
| /// Returns the type of the innermost field. |
| QualType getType() const { return getFieldDesc()->getType(); } |
| |
| /// Returns the element size of the innermost field. |
| size_t elemSize() const { |
| if (Base == RootPtrMark) |
| return getDeclDesc()->getSize(); |
| return getFieldDesc()->getElemSize(); |
| } |
| /// Returns the total size of the innermost field. |
| size_t getSize() const { return getFieldDesc()->getSize(); } |
| |
| /// Returns the offset into an array. |
| unsigned getOffset() const { |
| assert(Offset != PastEndMark && "invalid offset"); |
| if (Base == RootPtrMark) |
| return Offset; |
| |
| unsigned Adjust = 0; |
| if (Offset != Base) { |
| if (getFieldDesc()->ElemDesc) |
| Adjust = sizeof(InlineDescriptor); |
| else |
| Adjust = sizeof(InitMap *); |
| } |
| return Offset - Base - Adjust; |
| } |
| |
| /// Checks if the innermost field is an array. |
| bool inArray() const { return getFieldDesc()->IsArray; } |
| /// Checks if the structure is a primitive array. |
| bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); } |
| /// Checks if the structure is an array of unknown size. |
| bool isUnknownSizeArray() const { |
| return getFieldDesc()->isUnknownSizeArray(); |
| } |
| /// Checks if the pointer points to an array. |
| bool isArrayElement() const { return Base != Offset; } |
| /// Pointer points directly to a block. |
| bool isRoot() const { |
| return (Base == 0 || Base == RootPtrMark) && Offset == 0; |
| } |
| |
| /// Returns the record descriptor of a class. |
| Record *getRecord() const { return getFieldDesc()->ElemRecord; } |
| /// Returns the field information. |
| const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); } |
| |
| /// Checks if the object is a union. |
| bool isUnion() const; |
| |
| /// Checks if the storage is extern. |
| bool isExtern() const { return Pointee->isExtern(); } |
| /// Checks if the storage is static. |
| bool isStatic() const { return Pointee->isStatic(); } |
| /// Checks if the storage is temporary. |
| bool isTemporary() const { return Pointee->isTemporary(); } |
| /// Checks if the storage is a static temporary. |
| bool isStaticTemporary() const { return isStatic() && isTemporary(); } |
| |
| /// Checks if the field is mutable. |
| bool isMutable() const { return Base != 0 && getInlineDesc()->IsMutable; } |
| /// Checks if an object was initialized. |
| bool isInitialized() const; |
| /// Checks if the object is active. |
| bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; } |
| /// Checks if a structure is a base class. |
| bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; } |
| |
| /// Checks if an object or a subfield is mutable. |
| bool isConst() const { |
| return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst; |
| } |
| |
| /// Returns the declaration ID. |
| llvm::Optional<unsigned> getDeclID() const { return Pointee->getDeclID(); } |
| |
| /// Returns the byte offset from the start. |
| unsigned getByteOffset() const { |
| return Offset; |
| } |
| |
| /// Returns the number of elements. |
| unsigned getNumElems() const { return getSize() / elemSize(); } |
| |
| /// Returns the index into an array. |
| int64_t getIndex() const { |
| if (isElementPastEnd()) |
| return 1; |
| if (auto ElemSize = elemSize()) |
| return getOffset() / ElemSize; |
| return 0; |
| } |
| |
| /// Checks if the index is one past end. |
| bool isOnePastEnd() const { |
| return isElementPastEnd() || getSize() == getOffset(); |
| } |
| |
| /// Checks if the pointer is an out-of-bounds element pointer. |
| bool isElementPastEnd() const { return Offset == PastEndMark; } |
| |
| /// Dereferences the pointer, if it's live. |
| template <typename T> T &deref() const { |
| assert(isLive() && "Invalid pointer"); |
| return *reinterpret_cast<T *>(Pointee->data() + Offset); |
| } |
| |
| /// Dereferences a primitive element. |
| template <typename T> T &elem(unsigned I) const { |
| return reinterpret_cast<T *>(Pointee->data())[I]; |
| } |
| |
| /// Initializes a field. |
| void initialize() const; |
| /// Activats a field. |
| void activate() const; |
| /// Deactivates an entire strurcutre. |
| void deactivate() const; |
| |
| /// Checks if two pointers are comparable. |
| static bool hasSameBase(const Pointer &A, const Pointer &B); |
| /// Checks if two pointers can be subtracted. |
| static bool hasSameArray(const Pointer &A, const Pointer &B); |
| |
| /// Prints the pointer. |
| void print(llvm::raw_ostream &OS) const { |
| OS << "{" << Base << ", " << Offset << ", "; |
| if (Pointee) |
| OS << Pointee->getSize(); |
| else |
| OS << "nullptr"; |
| OS << "}"; |
| } |
| |
| private: |
| friend class Block; |
| friend class DeadBlock; |
| |
| Pointer(Block *Pointee, unsigned Base, unsigned Offset); |
| |
| /// Returns the embedded descriptor preceding a field. |
| InlineDescriptor *getInlineDesc() const { return getDescriptor(Base); } |
| |
| /// Returns a descriptor at a given offset. |
| InlineDescriptor *getDescriptor(unsigned Offset) const { |
| assert(Offset != 0 && "Not a nested pointer"); |
| return reinterpret_cast<InlineDescriptor *>(Pointee->data() + Offset) - 1; |
| } |
| |
| /// Returns a reference to the pointer which stores the initialization map. |
| InitMap *&getInitMap() const { |
| return *reinterpret_cast<InitMap **>(Pointee->data() + Base); |
| } |
| |
| /// The block the pointer is pointing to. |
| Block *Pointee = nullptr; |
| /// Start of the current subfield. |
| unsigned Base = 0; |
| /// Offset into the block. |
| unsigned Offset = 0; |
| |
| /// Previous link in the pointer chain. |
| Pointer *Prev = nullptr; |
| /// Next link in the pointer chain. |
| Pointer *Next = nullptr; |
| }; |
| |
| inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) { |
| P.print(OS); |
| return OS; |
| } |
| |
| } // namespace interp |
| } // namespace clang |
| |
| #endif |