diff options
Diffstat (limited to 'clang/lib/AST/Interp/Pointer.h')
| -rw-r--r-- | clang/lib/AST/Interp/Pointer.h | 353 | 
1 files changed, 353 insertions, 0 deletions
| diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h new file mode 100644 index 000000000000..b8fa98e24faa --- /dev/null +++ b/clang/lib/AST/Interp/Pointer.h @@ -0,0 +1,353 @@ +//===--- 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 | 
