[clang] [clang][bytecode] Add `PtrView` for non-tracking pointers (PR #184129)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 2 06:09:48 PST 2026
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?= <tbaeder at redhat.com>
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.
More information about the cfe-commits
mailing list