https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/184129
Currently, when creating a `Pointer` (of block type, which I will assume here), the pointer will add itself (via its address) to its block's pointer list. This way, a block always knows what pointers point to it. That's important so we can handle the case when a block (which was e.g. created for a local variable) is destroyed and we now need to update its pointers. However, since always do this for all `Pointer` instances, it creates a weird performance problem where we do this dance all the time for no reason, e.g. consider `Pointer::stripBaseCasts()`: https://github.com/llvm/llvm-project/blob/88693c49d9ac58a33af5978d31f6c70fe1d5b45b/clang/lib/AST/ByteCode/Pointer.h#L778-L783 This will add and remove the newly created pointer from the block's pointer list every iteration. Other offenders are `Pointer::toRValue()`, `EvaluationResult::checkFullyInitialized()` or `Pointer::computeOffsetForComparison()`. This commit introduces a `PtrView` struct, which is like a `BlockPointer`, but without the prev/next next links to other `Pointer`s in the block's pointer list. It also moves a lot of the accessors from `Pointer` to `PtrView` (e.g. `isRoot()` or `getFieldDesc()`, etc.). This PR is mostly a draft but I'm looking for opinions on the approach. The downside of this is that all the accessors in `PtrView` are also duplicated in `Pointer`, since the two aren't related. I was also trying to keep both as simple as possible, i.e. without introducing any base classes or using CRTP. compile-time-tracker: https://llvm-compile-time-tracker.com/compare.php?from=4716dc8c51719cbcc82928cd00e41a29e5b9adff&to=28d69d4ec16e77370938675826b07752e108eede&stat=instructions:u >From 64a46bff07931035ac4ba75a0cced85327ec8cea 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 | 40 +- clang/lib/AST/ByteCode/Interp.h | 2 +- clang/lib/AST/ByteCode/Pointer.cpp | 86 +++-- clang/lib/AST/ByteCode/Pointer.h | 384 ++++++++++++++------ 4 files changed, 345 insertions(+), 167 deletions(-) diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index 039848f00764e..d548d3e613912 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,24 @@ 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); + // assert(FieldPtr.block() == Ptr.block()); 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 +211,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 7f30def20cc36..f3187ba29b08b 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -1083,7 +1083,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 e237013f4199c..a3a754dc2a5d0 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -237,7 +237,8 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { // Build the path into the object. bool OnePastEnd = isOnePastEnd() && !isZeroSizeArray(); - Pointer Ptr = *this; + // Pointer Ptr = *this; + PtrView Ptr = view(); while (Ptr.isField() || Ptr.isArrayElement()) { if (Ptr.isArrayRoot()) { @@ -382,7 +383,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; @@ -470,15 +471,18 @@ bool Pointer::isElementInitialized(unsigned Index) const { if (!isBlockPointer()) return true; + return view().isElementInitialized(Index); +} + +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; } @@ -602,16 +606,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; } @@ -619,6 +622,13 @@ bool Pointer::allElementsInitialized() const { return IM.allInitialized(); } +bool Pointer::allElementsInitialized() const { + assert(getFieldDesc()->isPrimitiveArray()); + assert(isArrayRoot()); + + return view().allElementsInitialized(); +} + bool Pointer::allElementsAlive() const { assert(getFieldDesc()->isPrimitiveArray()); assert(isArrayRoot()); @@ -645,12 +655,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); } @@ -658,13 +668,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); } @@ -672,17 +682,17 @@ void Pointer::activate() const { } }; - Pointer B = *this; + PtrView B = view(); //*this; 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); } @@ -745,7 +755,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; @@ -756,20 +766,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 { @@ -792,14 +802,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() || + if (Ptr.isDummy() || !Ptr.isLive() || //! Ptr.isBlockPointer() || Ptr.isPastEnd()) return false; @@ -818,7 +828,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); QualType FieldTy = F.Decl->getType(); if (FP.isActive()) { if (OptPrimType T = Ctx.classify(FieldTy)) { @@ -841,7 +851,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, for (unsigned I = 0; I < NF; ++I) { const Record::Field *FD = Record->getField(I); QualType FieldTy = FD->Decl->getType(); - const Pointer &FP = Ptr.atField(FD->Offset); + PtrView FP = Ptr.atField(FD->Offset); APValue &Value = R.getStructField(I); if (OptPrimType T = Ctx.classify(FieldTy)) { @@ -854,7 +864,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)); } @@ -862,7 +872,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)); } } @@ -886,7 +896,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; @@ -958,7 +968,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 010e917de81b2..2235c0c0594b1 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -33,6 +33,244 @@ class Context; class Pointer; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P); +struct PtrView { + Block *Pointee; // XXX const? + unsigned Base; + uint64_t Offset; + + 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(); } + + bool isArrayRoot() const { return inArray() && Offset == Base; } + + [[nodiscard]] PtrView expand() const { +#if 0 + 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); + } +#endif + + // 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 { + // if (BS.Base == RootPtrMark) { + // assert(Offset != 0 && Offset != PastEndMark && "not an array element"); + // return Pointer(BS.Pointee, BS.Base, 0); + // } + 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 isZero() const { return !Pointee; } + + 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 && "invalid offset"); + // assert(isBlockPointer()); + // if (BS.Base == RootPtrMark) + // return Offset; + + 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()); + } + + bool inUnion() const { return getInlineDesc()->InUnion; }; + + 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 { + // 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; + } + + unsigned getNumElems() const { return getSize() / elemSize(); } + + bool inArray() const { return getFieldDesc()->IsArray; } + + 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; + } + + bool isLive() const { return Pointee && !Pointee->isDead(); } + bool isDummy() const { return Pointee && Pointee->isDummy(); } + template <typename T> T &deref() const { + assert(isLive() && "Invalid pointer"); + assert(Pointee); + + // if (isArrayRoot()) + // return *reinterpret_cast<T *>(BS.Pointee->rawData() + BS.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 { + // if (BS.Base == RootPtrMark) { + // assert(Offset == PastEndMark && "cannot get base of a block"); + // return PtrView(BS.Pointee, BS.Base, 0); + // } + 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 isActive() const { return isRoot() || getInlineDesc()->IsActive; } + + // XXX + 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 true; + // 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.Pointee == Pointee && Base == Other.Base && + Offset == Other.Offset); + } +}; + struct BlockPointer { /// The block the pointer is pointing to. Block *Pointee; @@ -112,7 +350,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); @@ -150,6 +390,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; @@ -163,6 +408,7 @@ class Pointer { if (BS.Base == RootPtrMark) return Pointer(BS.Pointee, RootPtrMark, getDeclDesc()->getSize()); + uint64_t Off = Idx * elemSize(); if (getFieldDesc()->ElemDesc) Off += sizeof(InlineDescriptor); @@ -173,9 +419,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 @@ -225,35 +469,7 @@ 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. @@ -274,14 +490,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. @@ -315,8 +531,7 @@ class Pointer { 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); + return Pointer(view().getBase()); } /// Returns the parent array. [[nodiscard]] Pointer getArray() const { @@ -324,8 +539,7 @@ class Pointer { 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); + return Pointer(view().getArray()); } /// Accessors for information about the innermost field. @@ -369,9 +583,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 { @@ -382,33 +594,22 @@ 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; + 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; }; @@ -429,23 +630,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 { @@ -478,12 +669,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()) @@ -543,21 +731,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 +787,7 @@ class Pointer { unsigned getNumElems() const { if (!isBlockPointer()) return ~0u; - return getSize() / elemSize(); + return view().getNumElems(); } const Block *block() const { return BS.Pointee; } @@ -620,16 +804,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. @@ -700,12 +875,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 @@ -776,10 +946,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. @@ -802,7 +972,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", @@ -840,16 +1010,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
