https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/184129
>From 1307f85d38a428a5f63c8677f3b50ef924fa3c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Mon, 2 Mar 2026 11:33:57 +0100 Subject: [PATCH] Ptrview --- clang/lib/AST/ByteCode/EvaluationResult.cpp | 39 +- clang/lib/AST/ByteCode/Interp.h | 2 +- clang/lib/AST/ByteCode/Pointer.cpp | 83 ++-- clang/lib/AST/ByteCode/Pointer.h | 424 +++++++++++++------- 4 files changed, 339 insertions(+), 209 deletions(-) diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index 039848f00764e..a3b881e83b3f9 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -27,10 +27,10 @@ static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc, } static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, - const Pointer &BasePtr, const Record *R); + PtrView BasePtr, const Record *R); static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, - const Pointer &BasePtr, + PtrView BasePtr, const ConstantArrayType *CAT) { size_t NumElems = CAT->getZExtSize(); @@ -43,12 +43,12 @@ static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, if (ElemType->isRecordType()) { const Record *R = BasePtr.getElemRecord(); for (size_t I = 0; I != NumElems; ++I) { - Pointer ElemPtr = BasePtr.atIndex(I).narrow(); + PtrView ElemPtr = BasePtr.atIndex(I); Result &= CheckFieldsInitialized(S, Loc, ElemPtr, R); } } else if (const auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) { for (size_t I = 0; I != NumElems; ++I) { - Pointer ElemPtr = BasePtr.atIndex(I).narrow(); + PtrView ElemPtr = BasePtr.atIndex(I); Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT); } } else { @@ -74,12 +74,12 @@ static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, } static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, - const Pointer &BasePtr, const Record *R) { + PtrView BasePtr, const Record *R) { assert(R); bool Result = true; // Check all fields of this record are initialized. for (const Record::Field &F : R->fields()) { - Pointer FieldPtr = BasePtr.atField(F.Offset); + PtrView FieldPtr = BasePtr.atField(F.Offset); QualType FieldType = F.Decl->getType(); // Don't check inactive union members. @@ -104,7 +104,7 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, // Check Fields in all bases for (auto [I, B] : llvm::enumerate(R->bases())) { - Pointer P = BasePtr.atField(B.Offset); + PtrView P = BasePtr.atField(B.Offset); if (!P.isInitialized()) { const Descriptor *Desc = BasePtr.getDeclDesc(); if (const auto *CD = dyn_cast_if_present<CXXRecordDecl>(R->getDecl())) { @@ -122,7 +122,6 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, } // TODO: Virtual bases - return Result; } @@ -148,11 +147,11 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S, InitLoc = E->getExprLoc(); if (const Record *R = Ptr.getRecord()) - return CheckFieldsInitialized(S, InitLoc, Ptr, R); + return CheckFieldsInitialized(S, InitLoc, Ptr.view(), R); if (const auto *CAT = dyn_cast_if_present<ConstantArrayType>( Ptr.getType()->getAsArrayTypeUnsafe())) - return CheckArrayInitialized(S, InitLoc, Ptr, CAT); + return CheckArrayInitialized(S, InitLoc, Ptr.view(), CAT); return true; } @@ -166,17 +165,16 @@ static bool isOrHasPtr(const Descriptor *D) { return false; } -static void collectBlocks(const Pointer &Ptr, - llvm::SetVector<const Block *> &Blocks) { +static void collectBlocks(PtrView Ptr, llvm::SetVector<const Block *> &Blocks) { auto isUsefulPtr = [](const Pointer &P) -> bool { return P.isLive() && P.isBlockPointer() && !P.isZero() && !P.isDummy() && P.isDereferencable() && !P.isUnknownSizeArray() && !P.isOnePastEnd(); }; - if (!isUsefulPtr(Ptr)) + if (!isUsefulPtr(Pointer(Ptr))) return; - Blocks.insert(Ptr.block()); + Blocks.insert(Ptr.Pointee); const Descriptor *Desc = Ptr.getFieldDesc(); if (!Desc) @@ -187,24 +185,23 @@ static void collectBlocks(const Pointer &Ptr, for (const Record::Field &F : R->fields()) { if (!isOrHasPtr(F.Desc)) continue; - Pointer FieldPtr = Ptr.atField(F.Offset); - assert(FieldPtr.block() == Ptr.block()); + PtrView FieldPtr = Ptr.atField(F.Offset); collectBlocks(FieldPtr, Blocks); } } else if (Desc->isPrimitive() && Desc->getPrimType() == PT_Ptr) { Pointer Pointee = Ptr.deref<Pointer>(); if (isUsefulPtr(Pointee) && !Blocks.contains(Pointee.block())) - collectBlocks(Pointee, Blocks); + collectBlocks(Pointee.view(), Blocks); } else if (Desc->isPrimitiveArray() && Desc->getPrimType() == PT_Ptr) { for (unsigned I = 0; I != Desc->getNumElems(); ++I) { Pointer ElemPointee = Ptr.elem<Pointer>(I); if (isUsefulPtr(ElemPointee) && !Blocks.contains(ElemPointee.block())) - collectBlocks(ElemPointee, Blocks); + collectBlocks(ElemPointee.view(), Blocks); } } else if (Desc->isCompositeArray() && isOrHasPtr(Desc->ElemDesc)) { for (unsigned I = 0; I != Desc->getNumElems(); ++I) { - Pointer ElemPtr = Ptr.atIndex(I).narrow(); + PtrView ElemPtr = Ptr.atIndex(I); collectBlocks(ElemPtr, Blocks); } } @@ -213,11 +210,13 @@ static void collectBlocks(const Pointer &Ptr, bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx, const Pointer &Ptr, const SourceInfo &Info) { + if (!Ptr.isBlockPointer()) + return true; // Collect all blocks that this pointer (transitively) points to and // return false if any of them is a dynamic block. llvm::SetVector<const Block *> Blocks; - collectBlocks(Ptr, Blocks); + collectBlocks(Ptr.view(), Blocks); for (const Block *B : Blocks) { if (B->isDynamic()) { diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 01ea334f65cd3..0585a9cdcfbad 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -1095,7 +1095,7 @@ inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) { } // Diagnose comparisons between fields with different access specifiers. - if (std::optional<std::pair<Pointer, Pointer>> Split = + if (std::optional<std::pair<PtrView, PtrView>> Split = Pointer::computeSplitPoint(LHS, RHS)) { const FieldDecl *LF = Split->first.getField(); const FieldDecl *RF = Split->second.getField(); diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 689407e60c542..54db3e00697e4 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -34,7 +34,7 @@ Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset) Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset) : Offset(Offset), StorageKind(Storage::Block) { assert(Pointee); - assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base"); + assert(Base % alignof(void *) == 0 && "wrong base"); assert(Base >= Pointee->getDescriptor()->getMetadataSize()); BS = {Pointee, Base, nullptr, nullptr}; @@ -236,7 +236,8 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { // Build the path into the object. bool OnePastEnd = isOnePastEnd() && !isZeroSizeArray(); - Pointer Ptr = *this; + + PtrView Ptr = view(); while (Ptr.isField() || Ptr.isArrayElement()) { if (Ptr.isArrayRoot()) { @@ -380,7 +381,7 @@ size_t Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const { } size_t Result = 0; - Pointer P = *this; + PtrView P = view(); while (true) { if (P.isVirtualBaseClass()) { Result += getInlineDesc()->Offset; @@ -461,19 +462,15 @@ bool Pointer::isInitialized() const { return getInlineDesc()->IsInitialized; } -bool Pointer::isElementInitialized(unsigned Index) const { - if (!isBlockPointer()) - return true; - +bool PtrView::isElementInitialized(unsigned Index) const { const Descriptor *Desc = getFieldDesc(); assert(Desc); - if (isStatic() && BS.Base == 0) + if (Pointee->isStatic() && Base == 0) return true; - if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) && - Offset == BS.Base) { - const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>(); + if (isRoot() && Base == sizeof(GlobalInlineDescriptor) && Offset == Base) { + const auto &GD = Pointee->getBlockDesc<GlobalInlineDescriptor>(); return GD.InitState == GlobalInitState::Initialized; } @@ -597,16 +594,15 @@ void Pointer::initializeAllElements() const { getInitMap().noteAllInitialized(); } -bool Pointer::allElementsInitialized() const { +bool PtrView::allElementsInitialized() const { assert(getFieldDesc()->isPrimitiveArray()); - assert(isArrayRoot()); + // assert(isArrayRoot()); - if (isStatic() && BS.Base == 0) + if (Pointee->isStatic() && Base == 0) return true; - if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) && - Offset == BS.Base) { - const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>(); + if (isRoot() && Base == sizeof(GlobalInlineDescriptor) && Offset == Base) { + const auto &GD = Pointee->getBlockDesc<GlobalInlineDescriptor>(); return GD.InitState == GlobalInitState::Initialized; } @@ -640,12 +636,12 @@ void Pointer::activate() const { if (!getInlineDesc()->InUnion) return; - std::function<void(Pointer &)> activate; - activate = [&activate](Pointer &P) -> void { + std::function<void(PtrView P)> activate; + activate = [&activate](PtrView P) -> void { P.getInlineDesc()->IsActive = true; if (const Record *R = P.getRecord(); R && !R->isUnion()) { for (const Record::Field &F : R->fields()) { - Pointer FieldPtr = P.atField(F.Offset); + PtrView FieldPtr = P.atField(F.Offset); if (!FieldPtr.getInlineDesc()->IsActive) activate(FieldPtr); } @@ -653,13 +649,13 @@ void Pointer::activate() const { } }; - std::function<void(Pointer &)> deactivate; - deactivate = [&deactivate](Pointer &P) -> void { + std::function<void(PtrView &)> deactivate; + deactivate = [&deactivate](PtrView &P) -> void { P.getInlineDesc()->IsActive = false; if (const Record *R = P.getRecord()) { for (const Record::Field &F : R->fields()) { - Pointer FieldPtr = P.atField(F.Offset); + PtrView FieldPtr = P.atField(F.Offset); if (FieldPtr.getInlineDesc()->IsActive) deactivate(FieldPtr); } @@ -667,17 +663,17 @@ void Pointer::activate() const { } }; - Pointer B = *this; + PtrView B = view(); while (!B.isRoot() && B.inUnion()) { activate(B); // When walking up the pointer chain, deactivate // all union child pointers that aren't on our path. - Pointer Cur = B; + PtrView Cur = B; B = B.getBase(); if (const Record *BR = B.getRecord(); BR && BR->isUnion()) { for (const Record::Field &F : BR->fields()) { - Pointer FieldPtr = B.atField(F.Offset); + PtrView FieldPtr = B.atField(F.Offset); if (FieldPtr != Cur) deactivate(FieldPtr); } @@ -740,7 +736,7 @@ bool Pointer::pointsToStringLiteral() const { return isa_and_nonnull<StringLiteral>(E); } -std::optional<std::pair<Pointer, Pointer>> +std::optional<std::pair<PtrView, PtrView>> Pointer::computeSplitPoint(const Pointer &A, const Pointer &B) { if (!A.isBlockPointer() || !B.isBlockPointer()) return std::nullopt; @@ -751,20 +747,20 @@ Pointer::computeSplitPoint(const Pointer &A, const Pointer &B) { return std::nullopt; if (A == B) - return std::make_pair(A, B); + return std::make_pair(A.view(), B.view()); - auto getBase = [](const Pointer &P) -> Pointer { + auto getBase = [](PtrView P) -> PtrView { if (P.isArrayElement()) return P.expand().getArray(); return P.getBase(); }; - Pointer IterA = A; - Pointer IterB = B; - Pointer CurA = IterA; - Pointer CurB = IterB; + PtrView IterA = A.view(); + PtrView IterB = B.view(); + PtrView CurA = IterA; + PtrView CurB = IterB; for (;;) { - if (IterA.asBlockPointer().Base > IterB.asBlockPointer().Base) { + if (IterA.Base > IterB.Base) { CurA = IterA; IterA = getBase(IterA); } else { @@ -787,15 +783,14 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, const ASTContext &ASTCtx = Ctx.getASTContext(); assert(!ResultType.isNull()); // Method to recursively traverse composites. - std::function<bool(QualType, const Pointer &, APValue &)> Composite; - Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr, + std::function<bool(QualType, PtrView, APValue &)> Composite; + Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, PtrView Ptr, APValue &R) { if (const auto *AT = Ty->getAs<AtomicType>()) Ty = AT->getValueType(); // Invalid pointers. - if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() || - Ptr.isPastEnd()) + if (Ptr.isDummy() || !Ptr.isLive() || Ptr.isPastEnd()) return false; // Primitives should never end up here. @@ -810,7 +805,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, const FieldDecl *ActiveField = nullptr; APValue Value; for (const auto &F : Record->fields()) { - const Pointer &FP = Ptr.atField(F.Offset); + PtrView FP = Ptr.atField(F.Offset); if (FP.isActive()) { const Descriptor *Desc = F.Desc; if (Desc->isPrimitive()) { @@ -835,7 +830,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, for (unsigned I = 0; I != NF; ++I) { const Record::Field *FD = Record->getField(I); const Descriptor *Desc = FD->Desc; - const Pointer &FP = Ptr.atField(FD->Offset); + PtrView FP = Ptr.atField(FD->Offset); APValue &Value = R.getStructField(I); if (Desc->isPrimitive()) { TYPE_SWITCH(Desc->getPrimType(), @@ -849,7 +844,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, for (unsigned I = 0; I != NB; ++I) { const Record::Base *BD = Record->getBase(I); QualType BaseTy = Ctx.getASTContext().getCanonicalTagType(BD->Decl); - const Pointer &BP = Ptr.atField(BD->Offset); + PtrView BP = Ptr.atField(BD->Offset); Ok &= Composite(BaseTy, BP, R.getStructBase(I)); } @@ -857,7 +852,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, const Record::Base *VD = Record->getVirtualBase(I); QualType VirtBaseTy = Ctx.getASTContext().getCanonicalTagType(VD->Decl); - const Pointer &VP = Ptr.atField(VD->Offset); + PtrView VP = Ptr.atField(VD->Offset); Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I)); } } @@ -881,7 +876,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, if (ElemT) { TYPE_SWITCH(*ElemT, Slot = Ptr.elem<T>(I).toAPValue(ASTCtx)); } else { - Ok &= Composite(ElemTy, Ptr.atIndex(I).narrow(), Slot); + Ok &= Composite(ElemTy, Ptr.atIndex(I), Slot); } } return Ok; @@ -971,7 +966,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, // Return the composite type. APValue Result; - if (!Composite(ResultType, *this, Result)) + if (!Composite(ResultType, view(), Result)) return std::nullopt; return Result; } diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index ea9c7d4cb04db..849296313c4fb 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -33,6 +33,229 @@ class Context; class Pointer; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P); +struct PtrView { + static constexpr unsigned PastEndMark = ~0u; + + Block *Pointee; + unsigned Base; + uint64_t Offset; + + bool isZero() const { return !Pointee; } + bool isLive() const { return Pointee && !Pointee->isDead(); } + bool isDummy() const { return Pointee && Pointee->isDummy(); } + bool isActive() const { return isRoot() || getInlineDesc()->IsActive; } + bool isArrayRoot() const { return inArray() && Offset == Base; } + bool isElementPastEnd() const { return Offset == PastEndMark; } + bool inUnion() const { return getInlineDesc()->InUnion; }; + bool inArray() const { return getFieldDesc()->IsArray; } + bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); } + + bool isRoot() const { + return Base == Pointee->getDescriptor()->getMetadataSize(); + } + + InlineDescriptor *getInlineDesc() const { + assert(Base != sizeof(GlobalInlineDescriptor)); + assert(Base <= Pointee->getSize()); + assert(Base >= sizeof(InlineDescriptor)); + return getDescriptor(Base); + } + + InlineDescriptor *getDescriptor(unsigned Offset) const { + assert(Offset != 0 && "Not a nested pointer"); + return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) - + 1; + } + + const Descriptor *getFieldDesc() const { + if (isRoot()) + return Pointee->getDescriptor(); + return getInlineDesc()->Desc; + } + + const Descriptor *getDeclDesc() const { return Pointee->getDescriptor(); } + + size_t elemSize() const { return getFieldDesc()->getElemSize(); } + + [[nodiscard]] PtrView expand() const { + if (isElementPastEnd()) { + // Revert to an outer one-past-end pointer. + unsigned Adjust; + if (inPrimitiveArray()) + Adjust = sizeof(InitMapPtr); + else + Adjust = sizeof(InlineDescriptor); + return PtrView{Pointee, Base, Base + getSize() + Adjust}; + } + + // Do not step out of array elements. + if (Base != Offset) + return *this; + + if (isRoot()) + return PtrView{Pointee, Base, Base}; + + // Step into the containing array, if inside one. + unsigned Next = Base - getInlineDesc()->Offset; + const Descriptor *Desc = + (Next == Pointee->getDescriptor()->getMetadataSize()) + ? getDeclDesc() + : getDescriptor(Next)->Desc; + if (!Desc->IsArray) + return *this; + return PtrView{Pointee, Next, Offset}; + } + + [[nodiscard]] PtrView getArray() const { + assert(Offset != Base && "not an array element"); + return PtrView{Pointee, Base, Base}; + } + + const Record *getRecord() const { return getFieldDesc()->ElemRecord; } + const Record *getElemRecord() const { + const Descriptor *ElemDesc = getFieldDesc()->ElemDesc; + return ElemDesc ? ElemDesc->ElemRecord : nullptr; + } + const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); } + + bool isField() const { + return !isZero() && !isRoot() && getFieldDesc()->asDecl(); + } + + bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; } + bool isVirtualBaseClass() const { + return isField() && getInlineDesc()->IsVirtualBase; + } + bool isUnknownSizeArray() const { + return getFieldDesc()->isUnknownSizeArray(); + } + + bool isPastEnd() const { return Offset > Pointee->getSize(); } + + unsigned getOffset() const { + assert(Offset != PastEndMark); + + unsigned Adjust = 0; + if (Offset != Base) { + if (getFieldDesc()->ElemDesc) + Adjust = sizeof(InlineDescriptor); + else + Adjust = sizeof(InitMapPtr); + } + return Offset - Base - Adjust; + } + size_t getSize() const { return getFieldDesc()->getSize(); } + + bool isOnePastEnd() const { + if (!Pointee) + return false; + + if (isUnknownSizeArray()) + return false; + return isPastEnd() || (getSize() == getOffset()); + } + + PtrView atIndex(unsigned Idx) const { + unsigned Off = Idx * elemSize(); + if (getFieldDesc()->ElemDesc) + Off += sizeof(InlineDescriptor); + else + Off += sizeof(InitMapPtr); + return PtrView{Pointee, Base + Off, Base + Off}; + } + + int64_t getIndex() const { + if (isZero()) + return 0; + // narrow()ed element in a composite array. + if (Base > sizeof(InlineDescriptor) && Base == Offset) + return 0; + + if (auto ElemSize = elemSize()) + return getOffset() / ElemSize; + return 0; + } + + unsigned getNumElems() const { return getSize() / elemSize(); } + + bool isArrayElement() const { + if (inArray() && Base != Offset) + return true; + + // Might be a narrow()'ed element in a composite array. + // Check the inline descriptor. + if (Base >= sizeof(InlineDescriptor) && getInlineDesc()->IsArrayElement) + return true; + + return false; + } + + template <typename T> T &deref() const { + assert(isLive() && "Invalid pointer"); + assert(Pointee); + + if (isArrayRoot()) + return *reinterpret_cast<T *>(Pointee->rawData() + Base + + sizeof(InitMapPtr)); + + return *reinterpret_cast<T *>(Pointee->rawData() + Offset); + } + + template <typename T> T &elem(unsigned I) const { + assert(isLive() && "Invalid pointer"); + assert(Pointee); + assert(getFieldDesc()->isPrimitiveArray()); + assert(I < getFieldDesc()->getNumElems()); + + unsigned ElemByteOffset = I * getFieldDesc()->getElemSize(); + unsigned ReadOffset = Base + sizeof(InitMapPtr) + ElemByteOffset; + assert(ReadOffset + sizeof(T) <= Pointee->getDescriptor()->getAllocSize()); + + return *reinterpret_cast<T *>(Pointee->rawData() + ReadOffset); + } + + [[nodiscard]] PtrView getBase() const { + unsigned NewBase = Base - getInlineDesc()->Offset; + return PtrView{Pointee, NewBase, NewBase}; + } + + [[nodiscard]] PtrView atField(unsigned Offset) { + unsigned F = this->Offset + Offset; + return PtrView{Pointee, F, F}; + } + + bool isInitialized() const { + if (isRoot() && Base == sizeof(GlobalInlineDescriptor) && Offset == Base) { + const auto &GD = Pointee->getBlockDesc<GlobalInlineDescriptor>(); + return GD.InitState == GlobalInitState::Initialized; + } + + assert(Pointee && "Cannot check if null pointer was initialized"); + const Descriptor *Desc = getFieldDesc(); + assert(Desc); + if (Desc->isPrimitiveArray()) + return isElementInitialized(getIndex()); + + if (Base == 0) + return true; + // Field has its bit in an inline descriptor. + return getInlineDesc()->IsInitialized; + } + + bool allElementsInitialized() const; + bool isElementInitialized(unsigned Index) const; + InitMapPtr &getInitMap() const { + return *reinterpret_cast<InitMapPtr *>(Pointee->rawData() + Base); + } + + bool operator==(const PtrView &Other) const { + return Other.Pointee == Pointee && Base == Other.Base && + Offset == Other.Offset; + } + + bool operator!=(const PtrView &Other) const { return !(Other == *this); } +}; + struct BlockPointer { /// The block the pointer is pointing to. Block *Pointee; @@ -95,10 +318,6 @@ enum class Storage { Int, Block, Fn, Typeid }; /// Base /// \endverbatim class Pointer { -private: - static constexpr unsigned PastEndMark = ~0u; - static constexpr unsigned RootPtrMark = ~0u; - public: Pointer() : StorageKind(Storage::Int), Int{nullptr, 0} {} Pointer(IntPointer &&IntPtr) @@ -116,7 +335,9 @@ class Pointer { Typeid.TypePtr = TypePtr; Typeid.TypeInfoType = TypeInfoType; } + Pointer(Block *Pointee, unsigned Base, uint64_t Offset); + explicit Pointer(PtrView V) : Pointer(V.Pointee, V.Base, V.Offset) {} ~Pointer(); Pointer &operator=(const Pointer &P); @@ -154,6 +375,11 @@ class Pointer { return reinterpret_cast<uint64_t>(BS.Pointee) + Offset; } + PtrView view() const { + assert(isBlockPointer()); + return PtrView{BS.Pointee, BS.Base, Offset}; + } + /// Converts the pointer to an APValue that is an rvalue. std::optional<APValue> toRValue(const Context &Ctx, QualType ResultType) const; @@ -165,8 +391,6 @@ class Pointer { if (isFunctionPointer()) return Pointer(Fn.Func, Idx); - if (BS.Base == RootPtrMark) - return Pointer(BS.Pointee, RootPtrMark, getDeclDesc()->getSize()); uint64_t Off = Idx * elemSize(); if (getFieldDesc()->ElemDesc) Off += sizeof(InlineDescriptor); @@ -177,9 +401,7 @@ class Pointer { /// Creates a pointer to a field. [[nodiscard]] Pointer atField(unsigned Off) const { - assert(isBlockPointer()); - unsigned Field = Offset + Off; - return Pointer(BS.Pointee, Field, Field); + return Pointer(view().atField(Off)); } /// Subtract the given offset from the current Base and Offset @@ -200,15 +422,10 @@ class Pointer { return *this; unsigned Base = BS.Base; - // Pointer to an array of base types - enter block. - if (Base == RootPtrMark) - return Pointer(BS.Pointee, sizeof(InlineDescriptor), - Offset == 0 ? Offset : PastEndMark); - if (inArray()) { // Pointer is one past end - magic offset marks that. if (isOnePastEnd()) - return Pointer(BS.Pointee, Base, PastEndMark); + return Pointer(BS.Pointee, Base, PtrView::PastEndMark); if (Offset != Base) { // If we're pointing to a primitive array element, there's nothing to @@ -229,35 +446,8 @@ class Pointer { [[nodiscard]] Pointer expand() const { if (!isBlockPointer()) return *this; - assert(isBlockPointer()); - Block *Pointee = BS.Pointee; - - if (isElementPastEnd()) { - // Revert to an outer one-past-end pointer. - unsigned Adjust; - if (inPrimitiveArray()) - Adjust = sizeof(InitMapPtr); - else - Adjust = sizeof(InlineDescriptor); - return Pointer(Pointee, BS.Base, BS.Base + getSize() + Adjust); - } - - // Do not step out of array elements. - if (BS.Base != Offset) - return *this; - - if (isRoot()) - return Pointer(Pointee, BS.Base, BS.Base); - // Step into the containing array, if inside one. - unsigned Next = BS.Base - getInlineDesc()->Offset; - const Descriptor *Desc = - (Next == Pointee->getDescriptor()->getMetadataSize()) - ? getDeclDesc() - : getDescriptor(Next)->Desc; - if (!Desc->IsArray) - return *this; - return Pointer(Pointee, Next, Offset); + return Pointer(view().expand()); } /// Checks if the pointer is null. @@ -278,14 +468,14 @@ class Pointer { bool isLive() const { if (!isBlockPointer()) return true; - return BS.Pointee && !BS.Pointee->isDead(); + return view().isLive(); } /// Checks if the item is a field in an object. bool isField() const { if (!isBlockPointer()) return false; - return !isRoot() && getFieldDesc()->asDecl(); + return view().isField(); } /// Accessor for information about the declaration site. @@ -314,23 +504,9 @@ class Pointer { } /// Returns a pointer to the object of which this pointer is a field. - [[nodiscard]] Pointer getBase() const { - if (BS.Base == RootPtrMark) { - assert(Offset == PastEndMark && "cannot get base of a block"); - return Pointer(BS.Pointee, BS.Base, 0); - } - unsigned NewBase = BS.Base - getInlineDesc()->Offset; - return Pointer(BS.Pointee, NewBase, NewBase); - } + [[nodiscard]] Pointer getBase() const { return Pointer(view().getBase()); } /// Returns the parent array. - [[nodiscard]] Pointer getArray() const { - if (BS.Base == RootPtrMark) { - assert(Offset != 0 && Offset != PastEndMark && "not an array element"); - return Pointer(BS.Pointee, BS.Base, 0); - } - assert(Offset != BS.Base && "not an array element"); - return Pointer(BS.Pointee, BS.Base, BS.Base); - } + [[nodiscard]] Pointer getArray() const { return Pointer(view().getArray()); } /// Accessors for information about the innermost field. const Descriptor *getFieldDesc() const { @@ -373,9 +549,7 @@ class Pointer { return Int.Desc->getElemSize(); } - if (BS.Base == RootPtrMark) - return getDeclDesc()->getSize(); - return getFieldDesc()->getElemSize(); + return view().elemSize(); } /// Returns the total size of the innermost field. size_t getSize() const { @@ -384,42 +558,28 @@ class Pointer { } /// Returns the offset into an array. - unsigned getOffset() const { - assert(Offset != PastEndMark && "invalid offset"); - assert(isBlockPointer()); - if (BS.Base == RootPtrMark) - return Offset; - - unsigned Adjust = 0; - if (Offset != BS.Base) { - if (getFieldDesc()->ElemDesc) - Adjust = sizeof(InlineDescriptor); - else - Adjust = sizeof(InitMapPtr); - } - return Offset - BS.Base - Adjust; - } + unsigned getOffset() const { return view().getOffset(); } /// Whether this array refers to an array, but not /// to the first element. - bool isArrayRoot() const { return inArray() && Offset == BS.Base; } + bool isArrayRoot() const { return view().isArrayRoot(); } /// Checks if the innermost field is an array. bool inArray() const { if (isBlockPointer()) - return getFieldDesc()->IsArray; + return view().inArray(); return false; } bool inUnion() const { if (isBlockPointer() && BS.Base >= sizeof(InlineDescriptor)) - return getInlineDesc()->InUnion; + return view().inUnion(); return false; }; /// Checks if the structure is a primitive array. bool inPrimitiveArray() const { if (isBlockPointer()) - return getFieldDesc()->isPrimitiveArray(); + return view().inPrimitiveArray(); return false; } /// Checks if the structure is an array of unknown size. @@ -433,23 +593,13 @@ class Pointer { if (!isBlockPointer()) return false; - const BlockPointer &BP = BS; - if (inArray() && BP.Base != Offset) - return true; - - // Might be a narrow()'ed element in a composite array. - // Check the inline descriptor. - if (BP.Base >= sizeof(InlineDescriptor) && getInlineDesc()->IsArrayElement) - return true; - - return false; + return view().isArrayElement(); } /// Pointer points directly to a block. bool isRoot() const { if (isZero() || !isBlockPointer()) return true; - return (BS.Base == BS.Pointee->getDescriptor()->getMetadataSize() || - BS.Base == 0); + return view().isRoot(); } /// If this pointer has an InlineDescriptor we can use to initialize. bool canBeInitialized() const { @@ -482,12 +632,9 @@ class Pointer { bool isTypeidPointer() const { return StorageKind == Storage::Typeid; } /// Returns the record descriptor of a class. - const Record *getRecord() const { return getFieldDesc()->ElemRecord; } + const Record *getRecord() const { return view().getRecord(); } /// Returns the element record type, if this is a non-primive array. - const Record *getElemRecord() const { - const Descriptor *ElemDesc = getFieldDesc()->ElemDesc; - return ElemDesc ? ElemDesc->ElemRecord : nullptr; - } + const Record *getElemRecord() const { return view().getElemRecord(); } /// Returns the field information. const FieldDecl *getField() const { if (const Descriptor *FD = getFieldDesc()) @@ -551,21 +698,17 @@ class Pointer { bool isActive() const { if (!isBlockPointer()) return true; - return isRoot() || getInlineDesc()->IsActive; + return view().isActive(); } /// Checks if a structure is a base class. - bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; } - bool isVirtualBaseClass() const { - return isField() && getInlineDesc()->IsVirtualBase; - } + bool isBaseClass() const { return view().isBaseClass(); } + bool isVirtualBaseClass() const { return view().isVirtualBaseClass(); } + /// Checks if the pointer points to a dummy value. bool isDummy() const { if (!isBlockPointer()) return false; - - if (const Block *Pointee = BS.Pointee) - return Pointee->isDummy(); - return false; + return view().isDummy(); } /// Checks if an object or a subfield is mutable. @@ -603,7 +746,7 @@ class Pointer { if (isTypeidPointer()) return reinterpret_cast<uintptr_t>(Typeid.TypePtr) + Offset; if (isOnePastEnd()) - return PastEndMark; + return PtrView::PastEndMark; return Offset; } @@ -611,7 +754,7 @@ class Pointer { unsigned getNumElems() const { if (!isBlockPointer()) return ~0u; - return getSize() / elemSize(); + return view().getNumElems(); } const Block *block() const { return BS.Pointee; } @@ -628,16 +771,7 @@ class Pointer { if (!isBlockPointer()) return getIntegerRepresentation(); - if (isZero()) - return 0; - - // narrow()ed element in a composite array. - if (BS.Base > sizeof(InlineDescriptor) && BS.Base == Offset) - return 0; - - if (auto ElemSize = elemSize()) - return getOffset() / ElemSize; - return 0; + return view().getIndex(); } /// Checks if the index is one past end. @@ -663,7 +797,11 @@ class Pointer { } /// Checks if the pointer is an out-of-bounds element pointer. - bool isElementPastEnd() const { return Offset == PastEndMark; } + bool isElementPastEnd() const { + if (!isBlockPointer()) + return false; + return view().isElementPastEnd(); + } /// Checks if the pointer is pointing to a zero-size array. bool isZeroSizeArray() const { @@ -691,11 +829,7 @@ class Pointer { assert(isDereferencable()); assert(Offset + sizeof(T) <= BS.Pointee->getDescriptor()->getAllocSize()); - if (isArrayRoot()) - return *reinterpret_cast<T *>(BS.Pointee->rawData() + BS.Base + - sizeof(InitMapPtr)); - - return *reinterpret_cast<T *>(BS.Pointee->rawData() + Offset); + return view().deref<T>(); } /// Dereferences the element at index \p I. @@ -708,12 +842,7 @@ class Pointer { assert(getFieldDesc()->isPrimitiveArray()); assert(I < getFieldDesc()->getNumElems()); - unsigned ElemByteOffset = I * getFieldDesc()->getElemSize(); - unsigned ReadOffset = BS.Base + sizeof(InitMapPtr) + ElemByteOffset; - assert(ReadOffset + sizeof(T) <= - BS.Pointee->getDescriptor()->getAllocSize()); - - return *reinterpret_cast<T *>(BS.Pointee->rawData() + ReadOffset); + return view().elem<T>(I); } /// Whether this block can be read from at all. This is only true for @@ -738,8 +867,17 @@ class Pointer { /// Checks if an object was initialized. bool isInitialized() const; /// Like isInitialized(), but for primitive arrays. - bool isElementInitialized(unsigned Index) const; - bool allElementsInitialized() const; + bool isElementInitialized(unsigned Index) const { + if (!isBlockPointer()) + return true; + + return view().isElementInitialized(Index); + } + bool allElementsInitialized() const { + assert(getFieldDesc()->isPrimitiveArray()); + assert(isArrayRoot()); + return view().allElementsInitialized(); + } bool allElementsAlive() const; bool isElementAlive(unsigned Index) const; @@ -784,10 +922,10 @@ class Pointer { /// The result is either a root pointer or something /// that isn't a base class anymore. [[nodiscard]] Pointer stripBaseCasts() const { - Pointer P = *this; - while (P.isBaseClass()) - P = P.getBase(); - return P; + PtrView V = view(); + while (V.isBaseClass()) + V = V.getBase(); + return Pointer(V); } /// Compare two pointers. @@ -810,7 +948,7 @@ class Pointer { /// Checks if both given pointers point to the same block. static bool pointToSameBlock(const Pointer &A, const Pointer &B); - static std::optional<std::pair<Pointer, Pointer>> + static std::optional<std::pair<PtrView, PtrView>> computeSplitPoint(const Pointer &A, const Pointer &B); /// Whether this points to a block that's been created for a "literal lvalue", @@ -848,16 +986,14 @@ class Pointer { assert(Offset != 0 && "Not a nested pointer"); assert(isBlockPointer()); assert(!isZero()); - return reinterpret_cast<InlineDescriptor *>(BS.Pointee->rawData() + - Offset) - - 1; + return view().getDescriptor(Offset); } /// Returns a reference to the InitMapPtr which stores the initialization map. InitMapPtr &getInitMap() const { assert(isBlockPointer()); assert(!isZero()); - return *reinterpret_cast<InitMapPtr *>(BS.Pointee->rawData() + BS.Base); + return view().getInitMap(); } /// Offset into the storage. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
