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 Pointer; 30 class Context; 31 enum PrimType : unsigned; 32 33 class Pointer; 34 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P); 35 36 /// A pointer to a memory block, live or dead. 37 /// 38 /// This object can be allocated into interpreter stack frames. If pointing to 39 /// a live block, it is a link in the chain of pointers pointing to the block. 40 /// 41 /// In the simplest form, a Pointer has a Block* (the pointee) and both Base 42 /// and Offset are 0, which means it will point to raw data. 43 /// 44 /// The Base field is used to access metadata about the data. For primitive 45 /// arrays, the Base is followed by an InitMap. In a variety of cases, the 46 /// Base is preceded by an InlineDescriptor, which is used to track the 47 /// initialization state, among other things. 48 /// 49 /// The Offset field is used to access the actual data. In other words, the 50 /// data the pointer decribes can be found at 51 /// Pointee->rawData() + Pointer.Offset. 52 /// 53 /// 54 /// Pointee Offset 55 /// │ │ 56 /// │ │ 57 /// ▼ ▼ 58 /// ┌───────┬────────────┬─────────┬────────────────────────────┐ 59 /// │ Block │ InlineDesc │ InitMap │ Actual Data │ 60 /// └───────┴────────────┴─────────┴────────────────────────────┘ 61 /// ▲ 62 /// │ 63 /// │ 64 /// Base 65 class Pointer { 66 private: 67 static constexpr unsigned PastEndMark = ~0u; 68 static constexpr unsigned RootPtrMark = ~0u; 69 70 public: 71 Pointer() {} 72 Pointer(Block *B); 73 Pointer(Block *B, unsigned BaseAndOffset); 74 Pointer(const Pointer &P); 75 Pointer(Pointer &&P); 76 ~Pointer(); 77 78 void operator=(const Pointer &P); 79 void operator=(Pointer &&P); 80 81 /// Equality operators are just for tests. 82 bool operator==(const Pointer &P) const { 83 return Pointee == P.Pointee && Base == P.Base && Offset == P.Offset; 84 } 85 86 bool operator!=(const Pointer &P) const { 87 return Pointee != P.Pointee || Base != P.Base || Offset != P.Offset; 88 } 89 90 /// Converts the pointer to an APValue. 91 APValue toAPValue() const; 92 93 /// Converts the pointer to a string usable in diagnostics. 94 std::string toDiagnosticString(const ASTContext &Ctx) const; 95 96 unsigned getIntegerRepresentation() const { 97 return reinterpret_cast<uintptr_t>(Pointee) + Offset; 98 } 99 100 /// Converts the pointer to an APValue that is an rvalue. 101 std::optional<APValue> toRValue(const Context &Ctx) const; 102 103 /// Offsets a pointer inside an array. 104 [[nodiscard]] Pointer atIndex(unsigned Idx) const { 105 if (Base == RootPtrMark) 106 return Pointer(Pointee, RootPtrMark, getDeclDesc()->getSize()); 107 unsigned Off = Idx * elemSize(); 108 if (getFieldDesc()->ElemDesc) 109 Off += sizeof(InlineDescriptor); 110 else 111 Off += sizeof(InitMapPtr); 112 return Pointer(Pointee, Base, Base + Off); 113 } 114 115 /// Creates a pointer to a field. 116 [[nodiscard]] Pointer atField(unsigned Off) const { 117 unsigned Field = Offset + Off; 118 return Pointer(Pointee, Field, Field); 119 } 120 121 /// Subtract the given offset from the current Base and Offset 122 /// of the pointer. 123 [[nodiscard]] Pointer atFieldSub(unsigned Off) const { 124 assert(Offset >= Off); 125 unsigned O = Offset - Off; 126 return Pointer(Pointee, O, O); 127 } 128 129 /// Restricts the scope of an array element pointer. 130 [[nodiscard]] Pointer narrow() const { 131 // Null pointers cannot be narrowed. 132 if (isZero() || isUnknownSizeArray()) 133 return *this; 134 135 // Pointer to an array of base types - enter block. 136 if (Base == RootPtrMark) 137 return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark); 138 139 // Pointer is one past end - magic offset marks that. 140 if (isOnePastEnd()) 141 return Pointer(Pointee, Base, PastEndMark); 142 143 // Primitive arrays are a bit special since they do not have inline 144 // descriptors. If Offset != Base, then the pointer already points to 145 // an element and there is nothing to do. Otherwise, the pointer is 146 // adjusted to the first element of the array. 147 if (inPrimitiveArray()) { 148 if (Offset != Base) 149 return *this; 150 return Pointer(Pointee, Base, Offset + sizeof(InitMapPtr)); 151 } 152 153 // Pointer is to a field or array element - enter it. 154 if (Offset != Base) 155 return Pointer(Pointee, Offset, Offset); 156 157 // Enter the first element of an array. 158 if (!getFieldDesc()->isArray()) 159 return *this; 160 161 const unsigned NewBase = Base + sizeof(InlineDescriptor); 162 return Pointer(Pointee, NewBase, NewBase); 163 } 164 165 /// Expands a pointer to the containing array, undoing narrowing. 166 [[nodiscard]] Pointer expand() const { 167 if (isElementPastEnd()) { 168 // Revert to an outer one-past-end pointer. 169 unsigned Adjust; 170 if (inPrimitiveArray()) 171 Adjust = sizeof(InitMapPtr); 172 else 173 Adjust = sizeof(InlineDescriptor); 174 return Pointer(Pointee, Base, Base + getSize() + Adjust); 175 } 176 177 // Do not step out of array elements. 178 if (Base != Offset) 179 return *this; 180 181 // If at base, point to an array of base types. 182 if (Base == 0) 183 return Pointer(Pointee, RootPtrMark, 0); 184 185 // Step into the containing array, if inside one. 186 unsigned Next = Base - getInlineDesc()->Offset; 187 const Descriptor *Desc = 188 Next == 0 ? getDeclDesc() : getDescriptor(Next)->Desc; 189 if (!Desc->IsArray) 190 return *this; 191 return Pointer(Pointee, Next, Offset); 192 } 193 194 /// Checks if the pointer is null. 195 bool isZero() const { return Pointee == nullptr; } 196 /// Checks if the pointer is live. 197 bool isLive() const { return Pointee && !Pointee->IsDead; } 198 /// Checks if the item is a field in an object. 199 bool isField() const { return Base != 0 && Base != RootPtrMark; } 200 201 /// Accessor for information about the declaration site. 202 const Descriptor *getDeclDesc() const { 203 assert(Pointee); 204 return Pointee->Desc; 205 } 206 SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); } 207 208 /// Returns a pointer to the object of which this pointer is a field. 209 [[nodiscard]] Pointer getBase() const { 210 if (Base == RootPtrMark) { 211 assert(Offset == PastEndMark && "cannot get base of a block"); 212 return Pointer(Pointee, Base, 0); 213 } 214 assert(Offset == Base && "not an inner field"); 215 unsigned NewBase = Base - getInlineDesc()->Offset; 216 return Pointer(Pointee, NewBase, NewBase); 217 } 218 /// Returns the parent array. 219 [[nodiscard]] Pointer getArray() const { 220 if (Base == RootPtrMark) { 221 assert(Offset != 0 && Offset != PastEndMark && "not an array element"); 222 return Pointer(Pointee, Base, 0); 223 } 224 assert(Offset != Base && "not an array element"); 225 return Pointer(Pointee, Base, Base); 226 } 227 228 /// Accessors for information about the innermost field. 229 const Descriptor *getFieldDesc() const { 230 if (Base == 0 || Base == RootPtrMark) 231 return getDeclDesc(); 232 return getInlineDesc()->Desc; 233 } 234 235 /// Returns the type of the innermost field. 236 QualType getType() const { 237 if (inPrimitiveArray() && Offset != Base) 238 return getFieldDesc()->getType()->getAsArrayTypeUnsafe()->getElementType(); 239 return getFieldDesc()->getType(); 240 } 241 242 [[nodiscard]] Pointer getDeclPtr() const { return Pointer(Pointee); } 243 244 /// Returns the element size of the innermost field. 245 size_t elemSize() const { 246 if (Base == RootPtrMark) 247 return getDeclDesc()->getSize(); 248 return getFieldDesc()->getElemSize(); 249 } 250 /// Returns the total size of the innermost field. 251 size_t getSize() const { return getFieldDesc()->getSize(); } 252 253 /// Returns the offset into an array. 254 unsigned getOffset() const { 255 assert(Offset != PastEndMark && "invalid offset"); 256 if (Base == RootPtrMark) 257 return Offset; 258 259 unsigned Adjust = 0; 260 if (Offset != Base) { 261 if (getFieldDesc()->ElemDesc) 262 Adjust = sizeof(InlineDescriptor); 263 else 264 Adjust = sizeof(InitMapPtr); 265 } 266 return Offset - Base - Adjust; 267 } 268 269 /// Whether this array refers to an array, but not 270 /// to the first element. 271 bool isArrayRoot() const { return inArray() && Offset == Base; } 272 273 /// Checks if the innermost field is an array. 274 bool inArray() const { return getFieldDesc()->IsArray; } 275 /// Checks if the structure is a primitive array. 276 bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); } 277 /// Checks if the structure is an array of unknown size. 278 bool isUnknownSizeArray() const { 279 return getFieldDesc()->isUnknownSizeArray(); 280 } 281 /// Checks if the pointer points to an array. 282 bool isArrayElement() const { return inArray() && Base != Offset; } 283 /// Pointer points directly to a block. 284 bool isRoot() const { 285 return (Base == 0 || Base == RootPtrMark) && Offset == 0; 286 } 287 288 /// Returns the record descriptor of a class. 289 const Record *getRecord() const { return getFieldDesc()->ElemRecord; } 290 /// Returns the element record type, if this is a non-primive array. 291 const Record *getElemRecord() const { 292 const Descriptor *ElemDesc = getFieldDesc()->ElemDesc; 293 return ElemDesc ? ElemDesc->ElemRecord : nullptr; 294 } 295 /// Returns the field information. 296 const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); } 297 298 /// Checks if the object is a union. 299 bool isUnion() const; 300 301 /// Checks if the storage is extern. 302 bool isExtern() const { return Pointee && Pointee->isExtern(); } 303 /// Checks if the storage is static. 304 bool isStatic() const { 305 assert(Pointee); 306 return Pointee->isStatic(); 307 } 308 /// Checks if the storage is temporary. 309 bool isTemporary() const { 310 assert(Pointee); 311 return Pointee->isTemporary(); 312 } 313 /// Checks if the storage is a static temporary. 314 bool isStaticTemporary() const { return isStatic() && isTemporary(); } 315 316 /// Checks if the field is mutable. 317 bool isMutable() const { 318 return Base != 0 && getInlineDesc()->IsFieldMutable; 319 } 320 /// Checks if an object was initialized. 321 bool isInitialized() const; 322 /// Checks if the object is active. 323 bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; } 324 /// Checks if a structure is a base class. 325 bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; } 326 /// Checks if the pointer pointers to a dummy value. 327 bool isDummy() const { return getDeclDesc()->isDummy(); } 328 329 /// Checks if an object or a subfield is mutable. 330 bool isConst() const { 331 return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst; 332 } 333 334 /// Returns the declaration ID. 335 std::optional<unsigned> getDeclID() const { 336 assert(Pointee); 337 return Pointee->getDeclID(); 338 } 339 340 /// Returns the byte offset from the start. 341 unsigned getByteOffset() const { 342 return Offset; 343 } 344 345 /// Returns the number of elements. 346 unsigned getNumElems() const { return getSize() / elemSize(); } 347 348 const Block *block() const { return Pointee; } 349 350 /// Returns the index into an array. 351 int64_t getIndex() const { 352 if (isElementPastEnd()) 353 return 1; 354 355 // narrow()ed element in a composite array. 356 if (Base > 0 && Base == Offset) 357 return 0; 358 359 if (auto ElemSize = elemSize()) 360 return getOffset() / ElemSize; 361 return 0; 362 } 363 364 /// Checks if the index is one past end. 365 bool isOnePastEnd() const { 366 if (!Pointee) 367 return false; 368 return isElementPastEnd() || getSize() == getOffset(); 369 } 370 371 /// Checks if the pointer is an out-of-bounds element pointer. 372 bool isElementPastEnd() const { return Offset == PastEndMark; } 373 374 /// Dereferences the pointer, if it's live. 375 template <typename T> T &deref() const { 376 assert(isLive() && "Invalid pointer"); 377 assert(Pointee); 378 if (isArrayRoot()) 379 return *reinterpret_cast<T *>(Pointee->rawData() + Base + 380 sizeof(InitMapPtr)); 381 382 assert(Offset + sizeof(T) <= Pointee->getDescriptor()->getAllocSize()); 383 return *reinterpret_cast<T *>(Pointee->rawData() + Offset); 384 } 385 386 /// Dereferences a primitive element. 387 template <typename T> T &elem(unsigned I) const { 388 assert(I < getNumElems()); 389 assert(Pointee); 390 return reinterpret_cast<T *>(Pointee->data() + sizeof(InitMapPtr))[I]; 391 } 392 393 /// Initializes a field. 394 void initialize() const; 395 /// Activats a field. 396 void activate() const; 397 /// Deactivates an entire strurcutre. 398 void deactivate() const; 399 400 /// Compare two pointers. 401 ComparisonCategoryResult compare(const Pointer &Other) const { 402 if (!hasSameBase(*this, Other)) 403 return ComparisonCategoryResult::Unordered; 404 405 if (Offset < Other.Offset) 406 return ComparisonCategoryResult::Less; 407 else if (Offset > Other.Offset) 408 return ComparisonCategoryResult::Greater; 409 410 return ComparisonCategoryResult::Equal; 411 } 412 413 /// Checks if two pointers are comparable. 414 static bool hasSameBase(const Pointer &A, const Pointer &B); 415 /// Checks if two pointers can be subtracted. 416 static bool hasSameArray(const Pointer &A, const Pointer &B); 417 418 /// Prints the pointer. 419 void print(llvm::raw_ostream &OS) const { 420 OS << Pointee << " {"; 421 if (Base == RootPtrMark) 422 OS << "rootptr, "; 423 else 424 OS << Base << ", "; 425 426 if (Offset == PastEndMark) 427 OS << "pastend, "; 428 else 429 OS << Offset << ", "; 430 431 if (Pointee) 432 OS << Pointee->getSize(); 433 else 434 OS << "nullptr"; 435 OS << "}"; 436 } 437 438 private: 439 friend class Block; 440 friend class DeadBlock; 441 friend struct InitMap; 442 443 Pointer(Block *Pointee, unsigned Base, unsigned Offset); 444 445 /// Returns the embedded descriptor preceding a field. 446 InlineDescriptor *getInlineDesc() const { return getDescriptor(Base); } 447 448 /// Returns a descriptor at a given offset. 449 InlineDescriptor *getDescriptor(unsigned Offset) const { 450 assert(Offset != 0 && "Not a nested pointer"); 451 assert(Pointee); 452 return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) - 453 1; 454 } 455 456 /// Returns a reference to the InitMapPtr which stores the initialization map. 457 InitMapPtr &getInitMap() const { 458 assert(Pointee); 459 return *reinterpret_cast<InitMapPtr *>(Pointee->rawData() + Base); 460 } 461 462 /// The block the pointer is pointing to. 463 Block *Pointee = nullptr; 464 /// Start of the current subfield. 465 unsigned Base = 0; 466 /// Offset into the block. 467 unsigned Offset = 0; 468 469 /// Previous link in the pointer chain. 470 Pointer *Prev = nullptr; 471 /// Next link in the pointer chain. 472 Pointer *Next = nullptr; 473 }; 474 475 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) { 476 P.print(OS); 477 return OS; 478 } 479 480 } // namespace interp 481 } // namespace clang 482 483 #endif 484