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 "FunctionPointer.h" 18 #include "InterpBlock.h" 19 #include "clang/AST/ComparisonCategories.h" 20 #include "clang/AST/Decl.h" 21 #include "clang/AST/DeclCXX.h" 22 #include "clang/AST/Expr.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 template <unsigned A, bool B> class Integral; 32 enum PrimType : unsigned; 33 34 class Pointer; 35 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P); 36 37 struct BlockPointer { 38 /// The block the pointer is pointing to. 39 Block *Pointee; 40 /// Start of the current subfield. 41 unsigned Base; 42 }; 43 44 struct IntPointer { 45 const Descriptor *Desc; 46 uint64_t Value; 47 48 IntPointer atOffset(const ASTContext &ASTCtx, unsigned Offset) const; 49 IntPointer baseCast(const ASTContext &ASTCtx, unsigned BaseOffset) const; 50 }; 51 52 struct TypeidPointer { 53 const Type *TypePtr; 54 const Type *TypeInfoType; 55 }; 56 57 enum class Storage { Block, Int, Fn, Typeid }; 58 59 /// A pointer to a memory block, live or dead. 60 /// 61 /// This object can be allocated into interpreter stack frames. If pointing to 62 /// a live block, it is a link in the chain of pointers pointing to the block. 63 /// 64 /// In the simplest form, a Pointer has a Block* (the pointee) and both Base 65 /// and Offset are 0, which means it will point to raw data. 66 /// 67 /// The Base field is used to access metadata about the data. For primitive 68 /// arrays, the Base is followed by an InitMap. In a variety of cases, the 69 /// Base is preceded by an InlineDescriptor, which is used to track the 70 /// initialization state, among other things. 71 /// 72 /// The Offset field is used to access the actual data. In other words, the 73 /// data the pointer decribes can be found at 74 /// Pointee->rawData() + Pointer.Offset. 75 /// 76 /// 77 /// Pointee Offset 78 /// │ │ 79 /// │ │ 80 /// ▼ ▼ 81 /// ┌───────┬────────────┬─────────┬────────────────────────────┐ 82 /// │ Block │ InlineDesc │ InitMap │ Actual Data │ 83 /// └───────┴────────────┴─────────┴────────────────────────────┘ 84 /// ▲ 85 /// │ 86 /// │ 87 /// Base 88 class Pointer { 89 private: 90 static constexpr unsigned PastEndMark = ~0u; 91 static constexpr unsigned RootPtrMark = ~0u; 92 93 public: Pointer()94 Pointer() { 95 StorageKind = Storage::Int; 96 PointeeStorage.Int.Value = 0; 97 PointeeStorage.Int.Desc = nullptr; 98 } Pointer(IntPointer && IntPtr)99 Pointer(IntPointer &&IntPtr) : StorageKind(Storage::Int) { 100 PointeeStorage.Int = std::move(IntPtr); 101 } 102 Pointer(Block *B); 103 Pointer(Block *B, uint64_t BaseAndOffset); 104 Pointer(const Pointer &P); 105 Pointer(Pointer &&P); 106 Pointer(uint64_t Address, const Descriptor *Desc, uint64_t Offset = 0) Offset(Offset)107 : Offset(Offset), StorageKind(Storage::Int) { 108 PointeeStorage.Int.Value = Address; 109 PointeeStorage.Int.Desc = Desc; 110 } 111 Pointer(const Function *F, uint64_t Offset = 0) Offset(Offset)112 : Offset(Offset), StorageKind(Storage::Fn) { 113 PointeeStorage.Fn = FunctionPointer(F); 114 } 115 Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset = 0) Offset(Offset)116 : Offset(Offset), StorageKind(Storage::Typeid) { 117 PointeeStorage.Typeid.TypePtr = TypePtr; 118 PointeeStorage.Typeid.TypeInfoType = TypeInfoType; 119 } 120 Pointer(Block *Pointee, unsigned Base, uint64_t Offset); 121 ~Pointer(); 122 123 void operator=(const Pointer &P); 124 void operator=(Pointer &&P); 125 126 /// Equality operators are just for tests. 127 bool operator==(const Pointer &P) const { 128 if (P.StorageKind != StorageKind) 129 return false; 130 if (isIntegralPointer()) 131 return P.asIntPointer().Value == asIntPointer().Value && 132 P.asIntPointer().Desc == asIntPointer().Desc && P.Offset == Offset; 133 134 if (isFunctionPointer()) 135 return P.asFunctionPointer().getFunction() == 136 asFunctionPointer().getFunction() && 137 P.Offset == Offset; 138 139 assert(isBlockPointer()); 140 return P.asBlockPointer().Pointee == asBlockPointer().Pointee && 141 P.asBlockPointer().Base == asBlockPointer().Base && 142 P.Offset == Offset; 143 } 144 145 bool operator!=(const Pointer &P) const { return !(P == *this); } 146 147 /// Converts the pointer to an APValue. 148 APValue toAPValue(const ASTContext &ASTCtx) const; 149 150 /// Converts the pointer to a string usable in diagnostics. 151 std::string toDiagnosticString(const ASTContext &Ctx) const; 152 getIntegerRepresentation()153 uint64_t getIntegerRepresentation() const { 154 if (isIntegralPointer()) 155 return asIntPointer().Value + (Offset * elemSize()); 156 if (isFunctionPointer()) 157 return asFunctionPointer().getIntegerRepresentation() + Offset; 158 return reinterpret_cast<uint64_t>(asBlockPointer().Pointee) + Offset; 159 } 160 161 /// Converts the pointer to an APValue that is an rvalue. 162 std::optional<APValue> toRValue(const Context &Ctx, 163 QualType ResultType) const; 164 165 /// Offsets a pointer inside an array. atIndex(uint64_t Idx)166 [[nodiscard]] Pointer atIndex(uint64_t Idx) const { 167 if (isIntegralPointer()) 168 return Pointer(asIntPointer().Value, asIntPointer().Desc, Idx); 169 if (isFunctionPointer()) 170 return Pointer(asFunctionPointer().getFunction(), Idx); 171 172 if (asBlockPointer().Base == RootPtrMark) 173 return Pointer(asBlockPointer().Pointee, RootPtrMark, 174 getDeclDesc()->getSize()); 175 uint64_t Off = Idx * elemSize(); 176 if (getFieldDesc()->ElemDesc) 177 Off += sizeof(InlineDescriptor); 178 else 179 Off += sizeof(InitMapPtr); 180 return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 181 asBlockPointer().Base + Off); 182 } 183 184 /// Creates a pointer to a field. atField(unsigned Off)185 [[nodiscard]] Pointer atField(unsigned Off) const { 186 assert(isBlockPointer()); 187 unsigned Field = Offset + Off; 188 return Pointer(asBlockPointer().Pointee, Field, Field); 189 } 190 191 /// Subtract the given offset from the current Base and Offset 192 /// of the pointer. atFieldSub(unsigned Off)193 [[nodiscard]] Pointer atFieldSub(unsigned Off) const { 194 assert(Offset >= Off); 195 unsigned O = Offset - Off; 196 return Pointer(asBlockPointer().Pointee, O, O); 197 } 198 199 /// Restricts the scope of an array element pointer. narrow()200 [[nodiscard]] Pointer narrow() const { 201 if (!isBlockPointer()) 202 return *this; 203 assert(isBlockPointer()); 204 // Null pointers cannot be narrowed. 205 if (isZero() || isUnknownSizeArray()) 206 return *this; 207 208 unsigned Base = asBlockPointer().Base; 209 // Pointer to an array of base types - enter block. 210 if (Base == RootPtrMark) 211 return Pointer(asBlockPointer().Pointee, sizeof(InlineDescriptor), 212 Offset == 0 ? Offset : PastEndMark); 213 214 // Pointer is one past end - magic offset marks that. 215 if (isOnePastEnd()) 216 return Pointer(asBlockPointer().Pointee, Base, PastEndMark); 217 218 if (Offset != Base) { 219 // If we're pointing to a primitive array element, there's nothing to do. 220 if (inPrimitiveArray()) 221 return *this; 222 // Pointer is to a composite array element - enter it. 223 if (Offset != Base) 224 return Pointer(asBlockPointer().Pointee, Offset, Offset); 225 } 226 227 // Otherwise, we're pointing to a non-array element or 228 // are already narrowed to a composite array element. Nothing to do. 229 return *this; 230 } 231 232 /// Expands a pointer to the containing array, undoing narrowing. expand()233 [[nodiscard]] Pointer expand() const { 234 assert(isBlockPointer()); 235 Block *Pointee = asBlockPointer().Pointee; 236 237 if (isElementPastEnd()) { 238 // Revert to an outer one-past-end pointer. 239 unsigned Adjust; 240 if (inPrimitiveArray()) 241 Adjust = sizeof(InitMapPtr); 242 else 243 Adjust = sizeof(InlineDescriptor); 244 return Pointer(Pointee, asBlockPointer().Base, 245 asBlockPointer().Base + getSize() + Adjust); 246 } 247 248 // Do not step out of array elements. 249 if (asBlockPointer().Base != Offset) 250 return *this; 251 252 if (isRoot()) 253 return Pointer(Pointee, asBlockPointer().Base, asBlockPointer().Base); 254 255 // Step into the containing array, if inside one. 256 unsigned Next = asBlockPointer().Base - getInlineDesc()->Offset; 257 const Descriptor *Desc = 258 (Next == Pointee->getDescriptor()->getMetadataSize()) 259 ? getDeclDesc() 260 : getDescriptor(Next)->Desc; 261 if (!Desc->IsArray) 262 return *this; 263 return Pointer(Pointee, Next, Offset); 264 } 265 266 /// Checks if the pointer is null. isZero()267 bool isZero() const { 268 if (isBlockPointer()) 269 return asBlockPointer().Pointee == nullptr; 270 if (isFunctionPointer()) 271 return asFunctionPointer().isZero(); 272 if (isTypeidPointer()) 273 return false; 274 assert(isIntegralPointer()); 275 return asIntPointer().Value == 0 && Offset == 0; 276 } 277 /// Checks if the pointer is live. isLive()278 bool isLive() const { 279 if (!isBlockPointer()) 280 return true; 281 return asBlockPointer().Pointee && !asBlockPointer().Pointee->IsDead; 282 } 283 /// Checks if the item is a field in an object. isField()284 bool isField() const { 285 if (!isBlockPointer()) 286 return false; 287 288 return !isRoot() && getFieldDesc()->asDecl(); 289 } 290 291 /// Accessor for information about the declaration site. getDeclDesc()292 const Descriptor *getDeclDesc() const { 293 if (isIntegralPointer()) 294 return asIntPointer().Desc; 295 if (isFunctionPointer() || isTypeidPointer()) 296 return nullptr; 297 298 assert(isBlockPointer()); 299 assert(asBlockPointer().Pointee); 300 return asBlockPointer().Pointee->Desc; 301 } getDeclLoc()302 SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); } 303 304 /// Returns the expression or declaration the pointer has been created for. getSource()305 DeclTy getSource() const { 306 if (isBlockPointer()) 307 return getDeclDesc()->getSource(); 308 if (isFunctionPointer()) { 309 const Function *F = asFunctionPointer().getFunction(); 310 return F ? F->getDecl() : DeclTy(); 311 } 312 assert(isIntegralPointer()); 313 return asIntPointer().Desc ? asIntPointer().Desc->getSource() : DeclTy(); 314 } 315 316 /// Returns a pointer to the object of which this pointer is a field. getBase()317 [[nodiscard]] Pointer getBase() const { 318 if (asBlockPointer().Base == RootPtrMark) { 319 assert(Offset == PastEndMark && "cannot get base of a block"); 320 return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0); 321 } 322 unsigned NewBase = asBlockPointer().Base - getInlineDesc()->Offset; 323 return Pointer(asBlockPointer().Pointee, NewBase, NewBase); 324 } 325 /// Returns the parent array. getArray()326 [[nodiscard]] Pointer getArray() const { 327 if (asBlockPointer().Base == RootPtrMark) { 328 assert(Offset != 0 && Offset != PastEndMark && "not an array element"); 329 return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0); 330 } 331 assert(Offset != asBlockPointer().Base && "not an array element"); 332 return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 333 asBlockPointer().Base); 334 } 335 336 /// Accessors for information about the innermost field. getFieldDesc()337 const Descriptor *getFieldDesc() const { 338 if (isIntegralPointer()) 339 return asIntPointer().Desc; 340 341 if (isRoot()) 342 return getDeclDesc(); 343 return getInlineDesc()->Desc; 344 } 345 346 /// Returns the type of the innermost field. getType()347 QualType getType() const { 348 if (isTypeidPointer()) 349 return QualType(PointeeStorage.Typeid.TypeInfoType, 0); 350 351 if (inPrimitiveArray() && Offset != asBlockPointer().Base) { 352 // Unfortunately, complex and vector types are not array types in clang, 353 // but they are for us. 354 if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe()) 355 return AT->getElementType(); 356 if (const auto *CT = getFieldDesc()->getType()->getAs<ComplexType>()) 357 return CT->getElementType(); 358 if (const auto *CT = getFieldDesc()->getType()->getAs<VectorType>()) 359 return CT->getElementType(); 360 } 361 return getFieldDesc()->getType(); 362 } 363 getDeclPtr()364 [[nodiscard]] Pointer getDeclPtr() const { 365 return Pointer(asBlockPointer().Pointee); 366 } 367 368 /// Returns the element size of the innermost field. elemSize()369 size_t elemSize() const { 370 if (isIntegralPointer()) { 371 if (!asIntPointer().Desc) 372 return 1; 373 return asIntPointer().Desc->getElemSize(); 374 } 375 376 if (asBlockPointer().Base == RootPtrMark) 377 return getDeclDesc()->getSize(); 378 return getFieldDesc()->getElemSize(); 379 } 380 /// Returns the total size of the innermost field. getSize()381 size_t getSize() const { 382 assert(isBlockPointer()); 383 return getFieldDesc()->getSize(); 384 } 385 386 /// Returns the offset into an array. getOffset()387 unsigned getOffset() const { 388 assert(Offset != PastEndMark && "invalid offset"); 389 assert(isBlockPointer()); 390 if (asBlockPointer().Base == RootPtrMark) 391 return Offset; 392 393 unsigned Adjust = 0; 394 if (Offset != asBlockPointer().Base) { 395 if (getFieldDesc()->ElemDesc) 396 Adjust = sizeof(InlineDescriptor); 397 else 398 Adjust = sizeof(InitMapPtr); 399 } 400 return Offset - asBlockPointer().Base - Adjust; 401 } 402 403 /// Whether this array refers to an array, but not 404 /// to the first element. isArrayRoot()405 bool isArrayRoot() const { 406 return inArray() && Offset == asBlockPointer().Base; 407 } 408 409 /// Checks if the innermost field is an array. inArray()410 bool inArray() const { 411 if (isBlockPointer()) 412 return getFieldDesc()->IsArray; 413 return false; 414 } inUnion()415 bool inUnion() const { 416 if (isBlockPointer() && asBlockPointer().Base >= sizeof(InlineDescriptor)) 417 return getInlineDesc()->InUnion; 418 return false; 419 }; 420 421 /// Checks if the structure is a primitive array. inPrimitiveArray()422 bool inPrimitiveArray() const { 423 if (isBlockPointer()) 424 return getFieldDesc()->isPrimitiveArray(); 425 return false; 426 } 427 /// Checks if the structure is an array of unknown size. isUnknownSizeArray()428 bool isUnknownSizeArray() const { 429 if (!isBlockPointer()) 430 return false; 431 return getFieldDesc()->isUnknownSizeArray(); 432 } 433 /// Checks if the pointer points to an array. isArrayElement()434 bool isArrayElement() const { 435 if (!isBlockPointer()) 436 return false; 437 438 const BlockPointer &BP = asBlockPointer(); 439 if (inArray() && BP.Base != Offset) 440 return true; 441 442 // Might be a narrow()'ed element in a composite array. 443 // Check the inline descriptor. 444 if (BP.Base >= sizeof(InlineDescriptor) && getInlineDesc()->IsArrayElement) 445 return true; 446 447 return false; 448 } 449 /// Pointer points directly to a block. isRoot()450 bool isRoot() const { 451 if (isZero() || !isBlockPointer()) 452 return true; 453 return (asBlockPointer().Base == 454 asBlockPointer().Pointee->getDescriptor()->getMetadataSize() || 455 asBlockPointer().Base == 0); 456 } 457 /// If this pointer has an InlineDescriptor we can use to initialize. canBeInitialized()458 bool canBeInitialized() const { 459 if (!isBlockPointer()) 460 return false; 461 462 return asBlockPointer().Pointee && asBlockPointer().Base > 0; 463 } 464 asBlockPointer()465 [[nodiscard]] const BlockPointer &asBlockPointer() const { 466 assert(isBlockPointer()); 467 return PointeeStorage.BS; 468 } asIntPointer()469 [[nodiscard]] const IntPointer &asIntPointer() const { 470 assert(isIntegralPointer()); 471 return PointeeStorage.Int; 472 } asFunctionPointer()473 [[nodiscard]] const FunctionPointer &asFunctionPointer() const { 474 assert(isFunctionPointer()); 475 return PointeeStorage.Fn; 476 } asTypeidPointer()477 [[nodiscard]] const TypeidPointer &asTypeidPointer() const { 478 assert(isTypeidPointer()); 479 return PointeeStorage.Typeid; 480 } 481 isBlockPointer()482 bool isBlockPointer() const { return StorageKind == Storage::Block; } isIntegralPointer()483 bool isIntegralPointer() const { return StorageKind == Storage::Int; } isFunctionPointer()484 bool isFunctionPointer() const { return StorageKind == Storage::Fn; } isTypeidPointer()485 bool isTypeidPointer() const { return StorageKind == Storage::Typeid; } 486 487 /// Returns the record descriptor of a class. getRecord()488 const Record *getRecord() const { return getFieldDesc()->ElemRecord; } 489 /// Returns the element record type, if this is a non-primive array. getElemRecord()490 const Record *getElemRecord() const { 491 const Descriptor *ElemDesc = getFieldDesc()->ElemDesc; 492 return ElemDesc ? ElemDesc->ElemRecord : nullptr; 493 } 494 /// Returns the field information. getField()495 const FieldDecl *getField() const { 496 if (const Descriptor *FD = getFieldDesc()) 497 return FD->asFieldDecl(); 498 return nullptr; 499 } 500 501 /// Checks if the storage is extern. isExtern()502 bool isExtern() const { 503 if (isBlockPointer()) 504 return asBlockPointer().Pointee && asBlockPointer().Pointee->isExtern(); 505 return false; 506 } 507 /// Checks if the storage is static. isStatic()508 bool isStatic() const { 509 if (!isBlockPointer()) 510 return true; 511 assert(asBlockPointer().Pointee); 512 return asBlockPointer().Pointee->isStatic(); 513 } 514 /// Checks if the storage is temporary. isTemporary()515 bool isTemporary() const { 516 if (isBlockPointer()) { 517 assert(asBlockPointer().Pointee); 518 return asBlockPointer().Pointee->isTemporary(); 519 } 520 return false; 521 } 522 /// Checks if the storage has been dynamically allocated. isDynamic()523 bool isDynamic() const { 524 if (isBlockPointer()) { 525 assert(asBlockPointer().Pointee); 526 return asBlockPointer().Pointee->isDynamic(); 527 } 528 return false; 529 } 530 /// Checks if the storage is a static temporary. isStaticTemporary()531 bool isStaticTemporary() const { return isStatic() && isTemporary(); } 532 533 /// Checks if the field is mutable. isMutable()534 bool isMutable() const { 535 if (!isBlockPointer()) 536 return false; 537 return !isRoot() && getInlineDesc()->IsFieldMutable; 538 } 539 isWeak()540 bool isWeak() const { 541 if (isFunctionPointer()) 542 return asFunctionPointer().isWeak(); 543 if (!isBlockPointer()) 544 return false; 545 546 assert(isBlockPointer()); 547 return asBlockPointer().Pointee->isWeak(); 548 } 549 /// Checks if an object was initialized. 550 bool isInitialized() const; 551 /// Checks if the object is active. isActive()552 bool isActive() const { 553 if (!isBlockPointer()) 554 return true; 555 return isRoot() || getInlineDesc()->IsActive; 556 } 557 /// Checks if a structure is a base class. isBaseClass()558 bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; } isVirtualBaseClass()559 bool isVirtualBaseClass() const { 560 return isField() && getInlineDesc()->IsVirtualBase; 561 } 562 /// Checks if the pointer points to a dummy value. isDummy()563 bool isDummy() const { 564 if (!isBlockPointer()) 565 return false; 566 567 if (!asBlockPointer().Pointee) 568 return false; 569 570 return getDeclDesc()->isDummy(); 571 } 572 573 /// Checks if an object or a subfield is mutable. isConst()574 bool isConst() const { 575 if (isIntegralPointer()) 576 return true; 577 return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst; 578 } 579 580 /// Checks if an object or a subfield is volatile. isVolatile()581 bool isVolatile() const { 582 if (!isBlockPointer()) 583 return false; 584 return isRoot() ? getDeclDesc()->IsVolatile : getInlineDesc()->IsVolatile; 585 } 586 587 /// Returns the declaration ID. getDeclID()588 std::optional<unsigned> getDeclID() const { 589 if (isBlockPointer()) { 590 assert(asBlockPointer().Pointee); 591 return asBlockPointer().Pointee->getDeclID(); 592 } 593 return std::nullopt; 594 } 595 596 /// Returns the byte offset from the start. getByteOffset()597 uint64_t getByteOffset() const { 598 if (isIntegralPointer()) 599 return asIntPointer().Value + Offset; 600 if (isTypeidPointer()) 601 return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset; 602 if (isOnePastEnd()) 603 return PastEndMark; 604 return Offset; 605 } 606 607 /// Returns the number of elements. getNumElems()608 unsigned getNumElems() const { 609 if (!isBlockPointer()) 610 return ~0u; 611 return getSize() / elemSize(); 612 } 613 block()614 const Block *block() const { return asBlockPointer().Pointee; } 615 616 /// If backed by actual data (i.e. a block pointer), return 617 /// an address to that data. getRawAddress()618 const std::byte *getRawAddress() const { 619 assert(isBlockPointer()); 620 return asBlockPointer().Pointee->rawData() + Offset; 621 } 622 623 /// Returns the index into an array. getIndex()624 int64_t getIndex() const { 625 if (!isBlockPointer()) 626 return getIntegerRepresentation(); 627 628 if (isZero()) 629 return 0; 630 631 // narrow()ed element in a composite array. 632 if (asBlockPointer().Base > sizeof(InlineDescriptor) && 633 asBlockPointer().Base == Offset) 634 return 0; 635 636 if (auto ElemSize = elemSize()) 637 return getOffset() / ElemSize; 638 return 0; 639 } 640 641 /// Checks if the index is one past end. isOnePastEnd()642 bool isOnePastEnd() const { 643 if (!isBlockPointer()) 644 return false; 645 646 if (!asBlockPointer().Pointee) 647 return false; 648 649 if (isUnknownSizeArray()) 650 return false; 651 652 return isPastEnd() || (getSize() == getOffset() && !isZeroSizeArray()); 653 } 654 655 /// Checks if the pointer points past the end of the object. isPastEnd()656 bool isPastEnd() const { 657 if (isIntegralPointer()) 658 return false; 659 660 return !isZero() && Offset > PointeeStorage.BS.Pointee->getSize(); 661 } 662 663 /// Checks if the pointer is an out-of-bounds element pointer. isElementPastEnd()664 bool isElementPastEnd() const { return Offset == PastEndMark; } 665 666 /// Checks if the pointer is pointing to a zero-size array. isZeroSizeArray()667 bool isZeroSizeArray() const { 668 if (isFunctionPointer()) 669 return false; 670 if (const auto *Desc = getFieldDesc()) 671 return Desc->isZeroSizeArray(); 672 return false; 673 } 674 675 /// Dereferences the pointer, if it's live. deref()676 template <typename T> T &deref() const { 677 assert(isLive() && "Invalid pointer"); 678 assert(isBlockPointer()); 679 assert(asBlockPointer().Pointee); 680 assert(isDereferencable()); 681 assert(Offset + sizeof(T) <= 682 asBlockPointer().Pointee->getDescriptor()->getAllocSize()); 683 684 if (isArrayRoot()) 685 return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + 686 asBlockPointer().Base + sizeof(InitMapPtr)); 687 688 return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + Offset); 689 } 690 691 /// Whether this block can be read from at all. This is only true for 692 /// block pointers that point to a valid location inside that block. isDereferencable()693 bool isDereferencable() const { 694 if (!isBlockPointer()) 695 return false; 696 if (isPastEnd()) 697 return false; 698 699 return true; 700 } 701 702 /// Initializes a field. 703 void initialize() const; 704 /// Activats a field. 705 void activate() const; 706 /// Deactivates an entire strurcutre. 707 void deactivate() const; 708 getLifetime()709 Lifetime getLifetime() const { 710 if (!isBlockPointer()) 711 return Lifetime::Started; 712 if (asBlockPointer().Base < sizeof(InlineDescriptor)) 713 return Lifetime::Started; 714 return getInlineDesc()->LifeState; 715 } 716 endLifetime()717 void endLifetime() const { 718 if (!isBlockPointer()) 719 return; 720 if (asBlockPointer().Base < sizeof(InlineDescriptor)) 721 return; 722 getInlineDesc()->LifeState = Lifetime::Ended; 723 } 724 startLifetime()725 void startLifetime() const { 726 if (!isBlockPointer()) 727 return; 728 if (asBlockPointer().Base < sizeof(InlineDescriptor)) 729 return; 730 getInlineDesc()->LifeState = Lifetime::Started; 731 } 732 733 /// Compare two pointers. compare(const Pointer & Other)734 ComparisonCategoryResult compare(const Pointer &Other) const { 735 if (!hasSameBase(*this, Other)) 736 return ComparisonCategoryResult::Unordered; 737 738 if (Offset < Other.Offset) 739 return ComparisonCategoryResult::Less; 740 else if (Offset > Other.Offset) 741 return ComparisonCategoryResult::Greater; 742 743 return ComparisonCategoryResult::Equal; 744 } 745 746 /// Checks if two pointers are comparable. 747 static bool hasSameBase(const Pointer &A, const Pointer &B); 748 /// Checks if two pointers can be subtracted. 749 static bool hasSameArray(const Pointer &A, const Pointer &B); 750 /// Checks if both given pointers point to the same block. 751 static bool pointToSameBlock(const Pointer &A, const Pointer &B); 752 753 static std::optional<std::pair<Pointer, Pointer>> 754 computeSplitPoint(const Pointer &A, const Pointer &B); 755 756 /// Whether this points to a block that's been created for a "literal lvalue", 757 /// i.e. a non-MaterializeTemporaryExpr Expr. 758 bool pointsToLiteral() const; 759 bool pointsToStringLiteral() const; 760 761 /// Prints the pointer. 762 void print(llvm::raw_ostream &OS) const; 763 764 /// Compute an integer that can be used to compare this pointer to 765 /// another one. This is usually NOT the same as the pointer offset 766 /// regarding the AST record layout. 767 size_t computeOffsetForComparison() const; 768 769 private: 770 friend class Block; 771 friend class DeadBlock; 772 friend class MemberPointer; 773 friend class InterpState; 774 friend struct InitMap; 775 friend class DynamicAllocator; 776 777 /// Returns the embedded descriptor preceding a field. getInlineDesc()778 InlineDescriptor *getInlineDesc() const { 779 assert(isBlockPointer()); 780 assert(asBlockPointer().Base != sizeof(GlobalInlineDescriptor)); 781 assert(asBlockPointer().Base <= asBlockPointer().Pointee->getSize()); 782 assert(asBlockPointer().Base >= sizeof(InlineDescriptor)); 783 return getDescriptor(asBlockPointer().Base); 784 } 785 786 /// Returns a descriptor at a given offset. getDescriptor(unsigned Offset)787 InlineDescriptor *getDescriptor(unsigned Offset) const { 788 assert(Offset != 0 && "Not a nested pointer"); 789 assert(isBlockPointer()); 790 assert(!isZero()); 791 return reinterpret_cast<InlineDescriptor *>( 792 asBlockPointer().Pointee->rawData() + Offset) - 793 1; 794 } 795 796 /// Returns a reference to the InitMapPtr which stores the initialization map. getInitMap()797 InitMapPtr &getInitMap() const { 798 assert(isBlockPointer()); 799 assert(!isZero()); 800 return *reinterpret_cast<InitMapPtr *>(asBlockPointer().Pointee->rawData() + 801 asBlockPointer().Base); 802 } 803 804 /// Offset into the storage. 805 uint64_t Offset = 0; 806 807 /// Previous link in the pointer chain. 808 Pointer *Prev = nullptr; 809 /// Next link in the pointer chain. 810 Pointer *Next = nullptr; 811 812 Storage StorageKind = Storage::Int; 813 union { 814 BlockPointer BS; 815 IntPointer Int; 816 FunctionPointer Fn; 817 TypeidPointer Typeid; 818 } PointeeStorage; 819 }; 820 821 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) { 822 P.print(OS); 823 return OS; 824 } 825 826 } // namespace interp 827 } // namespace clang 828 829 #endif 830