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

Reply via email to