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