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