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