[clang] [clang][bytecode] Add `PtrView` for non-tracking pointers (PR #184129)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 23 02:57:43 PDT 2026
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/184129
>From 636ad9ddb2f3f14b87bc6a58ab8b174d53069b76 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 | 39 +-
clang/lib/AST/ByteCode/Interp.cpp | 11 +-
clang/lib/AST/ByteCode/Interp.h | 2 +-
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 44 +-
.../lib/AST/ByteCode/InterpBuiltinBitCast.cpp | 21 +-
clang/lib/AST/ByteCode/InterpHelpers.h | 12 +-
clang/lib/AST/ByteCode/Pointer.cpp | 107 ++--
clang/lib/AST/ByteCode/Pointer.h | 546 +++++++++++-------
clang/test/AST/ByteCode/cxx20.cpp | 1 -
9 files changed, 464 insertions(+), 319 deletions(-)
diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp
index 039848f00764e..9fa63996e8ae6 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).narrow();
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).narrow();
Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT);
}
} else {
@@ -74,12 +74,12 @@ static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc,
}
static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
- const Pointer &BasePtr, const Record *R) {
+ PtrView BasePtr, const Record *R) {
assert(R);
bool Result = true;
// Check all fields of this record are initialized.
for (const Record::Field &F : R->fields()) {
- Pointer FieldPtr = BasePtr.atField(F.Offset);
+ PtrView FieldPtr = BasePtr.atField(F.Offset);
QualType FieldType = F.Decl->getType();
// Don't check inactive union members.
@@ -104,7 +104,7 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
// Check Fields in all bases
for (auto [I, B] : llvm::enumerate(R->bases())) {
- Pointer P = BasePtr.atField(B.Offset);
+ PtrView P = BasePtr.atField(B.Offset);
if (!P.isInitialized()) {
const Descriptor *Desc = BasePtr.getDeclDesc();
if (const auto *CD = dyn_cast_if_present<CXXRecordDecl>(R->getDecl())) {
@@ -122,7 +122,6 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
}
// TODO: Virtual bases
-
return Result;
}
@@ -148,11 +147,11 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S,
InitLoc = E->getExprLoc();
if (const Record *R = Ptr.getRecord())
- return CheckFieldsInitialized(S, InitLoc, Ptr, R);
+ return CheckFieldsInitialized(S, InitLoc, Ptr.view(), R);
if (const auto *CAT = dyn_cast_if_present<ConstantArrayType>(
Ptr.getType()->getAsArrayTypeUnsafe()))
- return CheckArrayInitialized(S, InitLoc, Ptr, CAT);
+ return CheckArrayInitialized(S, InitLoc, Ptr.view(), CAT);
return true;
}
@@ -166,17 +165,16 @@ static bool isOrHasPtr(const Descriptor *D) {
return false;
}
-static void collectBlocks(const Pointer &Ptr,
- llvm::SetVector<const Block *> &Blocks) {
+static void collectBlocks(PtrView Ptr, llvm::SetVector<const Block *> &Blocks) {
auto isUsefulPtr = [](const Pointer &P) -> bool {
return P.isLive() && P.isBlockPointer() && !P.isZero() && !P.isDummy() &&
P.isDereferencable() && !P.isUnknownSizeArray() && !P.isOnePastEnd();
};
- if (!isUsefulPtr(Ptr))
+ if (!isUsefulPtr(Pointer(Ptr)))
return;
- Blocks.insert(Ptr.block());
+ Blocks.insert(Ptr.Pointee);
const Descriptor *Desc = Ptr.getFieldDesc();
if (!Desc)
@@ -187,24 +185,23 @@ static void collectBlocks(const Pointer &Ptr,
for (const Record::Field &F : R->fields()) {
if (!isOrHasPtr(F.Desc))
continue;
- Pointer FieldPtr = Ptr.atField(F.Offset);
- assert(FieldPtr.block() == Ptr.block());
+ PtrView FieldPtr = Ptr.atField(F.Offset);
collectBlocks(FieldPtr, Blocks);
}
} else if (Desc->isPrimitive() && Desc->getPrimType() == PT_Ptr) {
Pointer Pointee = Ptr.deref<Pointer>();
if (isUsefulPtr(Pointee) && !Blocks.contains(Pointee.block()))
- collectBlocks(Pointee, Blocks);
+ collectBlocks(Pointee.view(), Blocks);
} else if (Desc->isPrimitiveArray() && Desc->getPrimType() == PT_Ptr) {
for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
Pointer ElemPointee = Ptr.elem<Pointer>(I);
if (isUsefulPtr(ElemPointee) && !Blocks.contains(ElemPointee.block()))
- collectBlocks(ElemPointee, Blocks);
+ collectBlocks(ElemPointee.view(), Blocks);
}
} else if (Desc->isCompositeArray() && isOrHasPtr(Desc->ElemDesc)) {
for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
- Pointer ElemPtr = Ptr.atIndex(I).narrow();
+ PtrView ElemPtr = Ptr.atIndex(I).narrow();
collectBlocks(ElemPtr, Blocks);
}
}
@@ -213,11 +210,13 @@ static void collectBlocks(const Pointer &Ptr,
bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx,
const Pointer &Ptr,
const SourceInfo &Info) {
+ if (!Ptr.isBlockPointer())
+ return true;
// Collect all blocks that this pointer (transitively) points to and
// return false if any of them is a dynamic block.
llvm::SetVector<const Block *> Blocks;
- collectBlocks(Ptr, Blocks);
+ collectBlocks(Ptr.view(), Blocks);
for (const Block *B : Blocks) {
if (B->isDynamic()) {
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 1b6b785b58757..9bb9682ecacdb 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -549,8 +549,7 @@ bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return false;
}
-bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
- AccessKinds AK) {
+bool CheckRange(InterpState &S, CodePtr OpPC, PtrView Ptr, AccessKinds AK) {
if (!Ptr.isOnePastEnd() && !Ptr.isZeroSizeArray())
return true;
if (S.getLangOpts().CPlusPlus) {
@@ -624,14 +623,14 @@ bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return false;
}
-bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+bool CheckMutable(InterpState &S, CodePtr OpPC, PtrView Ptr) {
assert(Ptr.isLive() && "Pointer is not live");
if (!Ptr.isMutable())
return true;
// In C++14 onwards, it is permitted to read a mutable member whose
// lifetime began within the evaluation.
- if (S.getLangOpts().CPlusPlus14 && Ptr.block()->getEvalID() == S.EvalID)
+ if (S.getLangOpts().CPlusPlus14 && Ptr.getEvalID() == S.EvalID)
return true;
const SourceInfo &Loc = S.Current->getSource(OpPC);
@@ -1807,6 +1806,10 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
size_t ArgSize = Func->getArgSize() + VarArgSize;
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
+
+ if (!ThisPtr.isBlockPointer())
+ return false;
+
const FunctionDecl *Callee = Func->getDecl();
const CXXRecordDecl *DynamicDecl = nullptr;
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index eb41021bcf7cf..d5777e7d056b6 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1095,7 +1095,7 @@ inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
}
// Diagnose comparisons between fields with different access specifiers.
- if (std::optional<std::pair<Pointer, Pointer>> Split =
+ if (std::optional<std::pair<PtrView, PtrView>> Split =
Pointer::computeSplitPoint(LHS, RHS)) {
const FieldDecl *LF = Split->first.getField();
const FieldDecl *RF = Split->second.getField();
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 5f8bfcc452387..9e298bdeea918 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -308,8 +308,8 @@ static bool interp__builtin_strcmp(InterpState &S, CodePtr OpPC,
if (Steps >= Limit)
break;
- const Pointer &PA = A.atIndex(IndexA);
- const Pointer &PB = B.atIndex(IndexB);
+ PtrView PA = A.view().atIndex(IndexA);
+ PtrView PB = B.view().atIndex(IndexB);
if (!CheckRange(S, OpPC, PA, AK_Read) ||
!CheckRange(S, OpPC, PB, AK_Read)) {
return false;
@@ -380,7 +380,7 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC,
size_t Len = 0;
for (size_t I = StrPtr.getIndex();; ++I, ++Len) {
- const Pointer &ElemPtr = StrPtr.atIndex(I);
+ PtrView ElemPtr = StrPtr.view().atIndex(I);
if (!CheckRange(S, OpPC, ElemPtr, AK_Read))
return false;
@@ -6120,8 +6120,8 @@ bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
assert(R->getNumFields() == 1);
unsigned FieldOffset = R->getField(0u)->Offset;
- const Pointer &FieldPtr = Ptr.atField(FieldOffset);
- PrimType FieldT = *S.getContext().classify(FieldPtr.getType());
+ PtrView FieldPtr = Ptr.view().atField(FieldOffset);
+ PrimType FieldT = FieldPtr.getFieldDesc()->getPrimType();
INT_TYPE_SWITCH(FieldT,
FieldPtr.deref<T>() = T::from(IntValue.getSExtValue()));
@@ -6129,7 +6129,7 @@ bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
return true;
}
-static void zeroAll(Pointer &Dest) {
+static void zeroAll(PtrView Dest) {
const Descriptor *Desc = Dest.getFieldDesc();
if (Desc->isPrimitive()) {
@@ -6143,7 +6143,7 @@ static void zeroAll(Pointer &Dest) {
if (Desc->isRecord()) {
const Record *R = Desc->ElemRecord;
for (const Record::Field &F : R->fields()) {
- Pointer FieldPtr = Dest.atField(F.Offset);
+ PtrView FieldPtr = Dest.atField(F.Offset);
zeroAll(FieldPtr);
}
return;
@@ -6161,22 +6161,22 @@ static void zeroAll(Pointer &Dest) {
if (Desc->isCompositeArray()) {
for (unsigned I = 0, N = Desc->getNumElems(); I != N; ++I) {
- Pointer ElemPtr = Dest.atIndex(I).narrow();
+ PtrView ElemPtr = Dest.atIndex(I).narrow();
zeroAll(ElemPtr);
}
return;
}
}
-static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src,
- Pointer &Dest, bool Activate);
-static bool copyRecord(InterpState &S, CodePtr OpPC, const Pointer &Src,
- Pointer &Dest, bool Activate = false) {
+static bool copyComposite(InterpState &S, CodePtr OpPC, PtrView Src,
+ PtrView Dest, bool Activate);
+static bool copyRecord(InterpState &S, CodePtr OpPC, PtrView Src, PtrView Dest,
+ bool Activate = false) {
[[maybe_unused]] const Descriptor *SrcDesc = Src.getFieldDesc();
const Descriptor *DestDesc = Dest.getFieldDesc();
auto copyField = [&](const Record::Field &F, bool Activate) -> bool {
- Pointer DestField = Dest.atField(F.Offset);
+ PtrView DestField = Dest.atField(F.Offset);
if (OptPrimType FT = S.Ctx.classify(F.Decl->getType())) {
TYPE_SWITCH(*FT, {
DestField.deref<T>() = Src.atField(F.Offset).deref<T>();
@@ -6197,14 +6197,14 @@ static bool copyRecord(InterpState &S, CodePtr OpPC, const Pointer &Src,
for (const Record::Field &F : R->fields()) {
if (R->isUnion()) {
// For unions, only copy the active field. Zero all others.
- const Pointer &SrcField = Src.atField(F.Offset);
+ PtrView SrcField = Src.atField(F.Offset);
if (SrcField.isActive()) {
if (!copyField(F, /*Activate=*/true))
return false;
} else {
if (!CheckMutable(S, OpPC, Src.atField(F.Offset)))
return false;
- Pointer DestField = Dest.atField(F.Offset);
+ PtrView DestField = Dest.atField(F.Offset);
zeroAll(DestField);
}
} else {
@@ -6214,7 +6214,7 @@ static bool copyRecord(InterpState &S, CodePtr OpPC, const Pointer &Src,
}
for (const Record::Base &B : R->bases()) {
- Pointer DestBase = Dest.atField(B.Offset);
+ PtrView DestBase = Dest.atField(B.Offset);
if (!copyRecord(S, OpPC, Src.atField(B.Offset), DestBase, Activate))
return false;
}
@@ -6223,8 +6223,8 @@ static bool copyRecord(InterpState &S, CodePtr OpPC, const Pointer &Src,
return true;
}
-static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src,
- Pointer &Dest, bool Activate = false) {
+static bool copyComposite(InterpState &S, CodePtr OpPC, PtrView Src,
+ PtrView Dest, bool Activate = false) {
assert(Src.isLive() && Dest.isLive());
[[maybe_unused]] const Descriptor *SrcDesc = Src.getFieldDesc();
@@ -6237,7 +6237,7 @@ static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src,
assert(SrcDesc->getNumElems() == DestDesc->getNumElems());
PrimType ET = DestDesc->getPrimType();
for (unsigned I = 0, N = DestDesc->getNumElems(); I != N; ++I) {
- Pointer DestElem = Dest.atIndex(I);
+ PtrView DestElem = Dest.atIndex(I);
TYPE_SWITCH(ET, {
DestElem.deref<T>() = Src.elem<T>(I);
DestElem.initialize();
@@ -6250,8 +6250,8 @@ static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src,
assert(SrcDesc->isCompositeArray());
assert(SrcDesc->getNumElems() == DestDesc->getNumElems());
for (unsigned I = 0, N = DestDesc->getNumElems(); I != N; ++I) {
- const Pointer &SrcElem = Src.atIndex(I).narrow();
- Pointer DestElem = Dest.atIndex(I).narrow();
+ PtrView SrcElem = Src.atIndex(I).narrow();
+ PtrView DestElem = Dest.atIndex(I).narrow();
if (!copyComposite(S, OpPC, SrcElem, DestElem, Activate))
return false;
}
@@ -6269,7 +6269,7 @@ bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
if (!Dest.isBlockPointer() || Dest.getFieldDesc()->isPrimitive())
return false;
- return copyComposite(S, OpPC, Src, Dest);
+ return copyComposite(S, OpPC, Src.view(), Dest.view());
}
} // namespace interp
diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
index 4bd9c66fc9974..782803dfbce6a 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
@@ -35,9 +35,8 @@ using namespace clang::interp;
// bytes to/from the buffer.
/// Used to iterate over pointer fields.
-using DataFunc =
- llvm::function_ref<bool(const Pointer &P, PrimType Ty, Bits BitOffset,
- Bits FullBitWidth, bool PackedBools)>;
+using DataFunc = llvm::function_ref<bool(PtrView P, PrimType Ty, Bits BitOffset,
+ Bits FullBitWidth, bool PackedBools)>;
#define BITCAST_TYPE_SWITCH(Expr, B) \
do { \
@@ -77,7 +76,7 @@ using DataFunc =
/// We use this to recursively iterate over all fields and elements of a pointer
/// and extract relevant data for a bitcast.
-static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
+static bool enumerateData(PtrView P, const Context &Ctx, Bits Offset,
Bits BitsToRead, DataFunc F) {
const Descriptor *FieldDesc = P.getFieldDesc();
assert(FieldDesc);
@@ -132,13 +131,13 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
for (const Record::Field &Fi : R->fields()) {
if (Fi.isUnnamedBitField())
continue;
- Pointer Elem = P.atField(Fi.Offset);
+ PtrView Elem = P.atField(Fi.Offset);
Bits BitOffset =
Offset + Bits(Layout.getFieldOffset(Fi.Decl->getFieldIndex()));
Ok = Ok && enumerateData(Elem, Ctx, BitOffset, BitsToRead, F);
}
for (const Record::Base &B : R->bases()) {
- Pointer Elem = P.atField(B.Offset);
+ PtrView Elem = P.atField(B.Offset);
CharUnits ByteOffset =
Layout.getBaseClassOffset(cast<CXXRecordDecl>(B.Decl));
Bits BitOffset = Offset + Bits(Ctx.getASTContext().toBits(ByteOffset));
@@ -157,7 +156,7 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
static bool enumeratePointerFields(const Pointer &P, const Context &Ctx,
Bits BitsToRead, DataFunc F) {
- return enumerateData(P, Ctx, Bits::zero(), BitsToRead, F);
+ return enumerateData(P.view(), Ctx, Bits::zero(), BitsToRead, F);
}
// This function is constexpr if and only if To, From, and the types of
@@ -267,7 +266,7 @@ bool clang::interp::readPointerToBuffer(const Context &Ctx,
return enumeratePointerFields(
FromPtr, Ctx, Buffer.size(),
- [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,
+ [&](PtrView P, PrimType T, Bits BitOffset, Bits FullBitWidth,
bool PackedBools) -> bool {
Bits BitWidth = FullBitWidth;
@@ -389,7 +388,7 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
ASTCtx.getTargetInfo().isLittleEndian() ? Endian::Little : Endian::Big;
bool Success = enumeratePointerFields(
ToPtr, S.getContext(), Buffer.size(),
- [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,
+ [&](PtrView P, PrimType T, Bits BitOffset, Bits FullBitWidth,
bool PackedBools) -> bool {
QualType PtrType = P.getType();
if (T == PT_Float) {
@@ -490,7 +489,7 @@ bool clang::interp::DoMemcpy(InterpState &S, CodePtr OpPC,
llvm::SmallVector<PrimTypeVariant> Values;
enumeratePointerFields(SrcPtr, S.getContext(), Size,
- [&](const Pointer &P, PrimType T, Bits BitOffset,
+ [&](PtrView P, PrimType T, Bits BitOffset,
Bits FullBitWidth, bool PackedBools) -> bool {
TYPE_SWITCH(T, { Values.push_back(P.deref<T>()); });
return true;
@@ -498,7 +497,7 @@ bool clang::interp::DoMemcpy(InterpState &S, CodePtr OpPC,
unsigned ValueIndex = 0;
enumeratePointerFields(DestPtr, S.getContext(), Size,
- [&](const Pointer &P, PrimType T, Bits BitOffset,
+ [&](PtrView P, PrimType T, Bits BitOffset,
Bits FullBitWidth, bool PackedBools) -> bool {
TYPE_SWITCH(T, {
P.deref<T>() = std::get<T>(Values[ValueIndex]);
diff --git a/clang/lib/AST/ByteCode/InterpHelpers.h b/clang/lib/AST/ByteCode/InterpHelpers.h
index 905bf1b43bfab..2e32a0e97a828 100644
--- a/clang/lib/AST/ByteCode/InterpHelpers.h
+++ b/clang/lib/AST/ByteCode/InterpHelpers.h
@@ -43,15 +43,21 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool CheckDummy(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK);
/// Checks if a pointer is in range.
-bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
- AccessKinds AK);
+bool CheckRange(InterpState &S, CodePtr OpPC, PtrView Ptr, AccessKinds AK);
+inline bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK) {
+ return CheckRange(S, OpPC, Ptr.view(), AK);
+}
/// Checks if a field from which a pointer is going to be derived is valid.
bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
CheckSubobjectKind CSK);
/// Checks if a pointer points to a mutable field.
-bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+bool CheckMutable(InterpState &S, CodePtr OpPC, PtrView Ptr);
+inline bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ return CheckMutable(S, OpPC, Ptr.view());
+}
/// Checks if a value can be loaded from a block.
bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index 689407e60c542..b97fbd6f01113 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -34,7 +34,7 @@ Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)
Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)
: Offset(Offset), StorageKind(Storage::Block) {
assert(Pointee);
- assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
+ assert(Base % alignof(void *) == 0 && "wrong base");
assert(Base >= Pointee->getDescriptor()->getMetadataSize());
BS = {Pointee, Base, nullptr, nullptr};
@@ -236,7 +236,8 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {
// Build the path into the object.
bool OnePastEnd = isOnePastEnd() && !isZeroSizeArray();
- Pointer Ptr = *this;
+
+ PtrView Ptr = view();
while (Ptr.isField() || Ptr.isArrayElement()) {
if (Ptr.isArrayRoot()) {
@@ -380,7 +381,7 @@ size_t Pointer::computeOffsetForComparison(const ASTContext &ASTCtx) const {
}
size_t Result = 0;
- Pointer P = *this;
+ PtrView P = view();
while (true) {
if (P.isVirtualBaseClass()) {
Result += getInlineDesc()->Offset;
@@ -461,19 +462,15 @@ bool Pointer::isInitialized() const {
return getInlineDesc()->IsInitialized;
}
-bool Pointer::isElementInitialized(unsigned Index) const {
- if (!isBlockPointer())
- return true;
-
+bool PtrView::isElementInitialized(unsigned Index) const {
const Descriptor *Desc = getFieldDesc();
assert(Desc);
- if (isStatic() && BS.Base == 0)
+ if (Pointee->isStatic() && Base == 0)
return true;
- if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
- Offset == BS.Base) {
- const auto &GD = block()->getBlockDesc<GlobalInlineDescriptor>();
+ if (isRoot() && Base == sizeof(GlobalInlineDescriptor) && Offset == Base) {
+ const auto &GD = Pointee->getBlockDesc<GlobalInlineDescriptor>();
return GD.InitState == GlobalInitState::Initialized;
}
@@ -543,15 +540,9 @@ void Pointer::endLifetime() const {
getInlineDesc()->LifeState = Lifetime::Ended;
}
-void Pointer::initialize() const {
- if (!isBlockPointer())
- return;
-
- assert(BS.Pointee && "Cannot initialize null pointer");
-
- if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&
- Offset == BS.Base) {
- auto &GD = BS.Pointee->getBlockDesc<GlobalInlineDescriptor>();
+void PtrView::initialize() const {
+ if (isRoot() && Base == sizeof(GlobalInlineDescriptor) && Offset == Base) {
+ auto &GD = Pointee->getBlockDesc<GlobalInlineDescriptor>();
GD.InitState = GlobalInitState::Initialized;
return;
}
@@ -565,13 +556,13 @@ void Pointer::initialize() const {
}
// Field has its bit in an inline descriptor.
- assert(BS.Base != 0 && "Only composite fields can be initialised");
+ assert(Base != 0 && "Only composite fields can be initialised");
getInlineDesc()->IsInitialized = true;
}
-void Pointer::initializeElement(unsigned Index) const {
+void PtrView::initializeElement(unsigned Index) const {
// Primitive global arrays don't have an initmap.
- if (isStatic() && BS.Base == 0)
+ if (Pointee->isStatic() && Base == 0)
return;
assert(Index < getFieldDesc()->getNumElems());
@@ -597,16 +588,15 @@ void Pointer::initializeAllElements() const {
getInitMap().noteAllInitialized();
}
-bool Pointer::allElementsInitialized() const {
+bool PtrView::allElementsInitialized() const {
assert(getFieldDesc()->isPrimitiveArray());
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;
}
@@ -631,21 +621,21 @@ bool Pointer::allElementsAlive() const {
return IM.allInitialized() || (IM.hasInitMap() && IM->allElementsAlive());
}
-void Pointer::activate() const {
+void PtrView::activate() const {
// Field has its bit in an inline descriptor.
- assert(BS.Base != 0 && "Only composite fields can be activated");
+ assert(Base != 0 && "Only composite fields can be activated");
- if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor))
+ if (isRoot() && Base == sizeof(GlobalInlineDescriptor))
return;
if (!getInlineDesc()->InUnion)
return;
- std::function<void(Pointer &)> activate;
- activate = [&activate](Pointer &P) -> void {
+ std::function<void(PtrView P)> activate;
+ activate = [&activate](PtrView P) -> void {
P.getInlineDesc()->IsActive = true;
if (const Record *R = P.getRecord(); R && !R->isUnion()) {
for (const Record::Field &F : R->fields()) {
- Pointer FieldPtr = P.atField(F.Offset);
+ PtrView FieldPtr = P.atField(F.Offset);
if (!FieldPtr.getInlineDesc()->IsActive)
activate(FieldPtr);
}
@@ -653,13 +643,13 @@ void Pointer::activate() const {
}
};
- std::function<void(Pointer &)> deactivate;
- deactivate = [&deactivate](Pointer &P) -> void {
+ std::function<void(PtrView &)> deactivate;
+ deactivate = [&deactivate](PtrView &P) -> void {
P.getInlineDesc()->IsActive = false;
if (const Record *R = P.getRecord()) {
for (const Record::Field &F : R->fields()) {
- Pointer FieldPtr = P.atField(F.Offset);
+ PtrView FieldPtr = P.atField(F.Offset);
if (FieldPtr.getInlineDesc()->IsActive)
deactivate(FieldPtr);
}
@@ -667,17 +657,17 @@ void Pointer::activate() const {
}
};
- Pointer B = *this;
+ PtrView B = *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);
}
@@ -685,10 +675,6 @@ void Pointer::activate() const {
}
}
-void Pointer::deactivate() const {
- // TODO: this only appears in constructors, so nothing to deactivate.
-}
-
bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
// Two null pointers always have the same base.
if (A.isZero() && B.isZero())
@@ -740,7 +726,7 @@ bool Pointer::pointsToStringLiteral() const {
return isa_and_nonnull<StringLiteral>(E);
}
-std::optional<std::pair<Pointer, Pointer>>
+std::optional<std::pair<PtrView, PtrView>>
Pointer::computeSplitPoint(const Pointer &A, const Pointer &B) {
if (!A.isBlockPointer() || !B.isBlockPointer())
return std::nullopt;
@@ -751,20 +737,20 @@ Pointer::computeSplitPoint(const Pointer &A, const Pointer &B) {
return std::nullopt;
if (A == B)
- return std::make_pair(A, B);
+ return std::make_pair(A.view(), B.view());
- auto getBase = [](const Pointer &P) -> Pointer {
+ auto getBase = [](PtrView P) -> PtrView {
if (P.isArrayElement())
return P.expand().getArray();
return P.getBase();
};
- Pointer IterA = A;
- Pointer IterB = B;
- Pointer CurA = IterA;
- Pointer CurB = IterB;
+ PtrView IterA = A.view();
+ PtrView IterB = B.view();
+ PtrView CurA = IterA;
+ PtrView CurB = IterB;
for (;;) {
- if (IterA.asBlockPointer().Base > IterB.asBlockPointer().Base) {
+ if (IterA.Base > IterB.Base) {
CurA = IterA;
IterA = getBase(IterA);
} else {
@@ -787,15 +773,14 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx,
const ASTContext &ASTCtx = Ctx.getASTContext();
assert(!ResultType.isNull());
// Method to recursively traverse composites.
- std::function<bool(QualType, const Pointer &, APValue &)> Composite;
- Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr,
+ std::function<bool(QualType, PtrView, APValue &)> Composite;
+ Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, PtrView Ptr,
APValue &R) {
if (const auto *AT = Ty->getAs<AtomicType>())
Ty = AT->getValueType();
// Invalid pointers.
- if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||
- Ptr.isPastEnd())
+ if (Ptr.isDummy() || !Ptr.isLive() || Ptr.isPastEnd())
return false;
// Primitives should never end up here.
@@ -810,7 +795,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx,
const FieldDecl *ActiveField = nullptr;
APValue Value;
for (const auto &F : Record->fields()) {
- const Pointer &FP = Ptr.atField(F.Offset);
+ PtrView FP = Ptr.atField(F.Offset);
if (FP.isActive()) {
const Descriptor *Desc = F.Desc;
if (Desc->isPrimitive()) {
@@ -835,7 +820,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx,
for (unsigned I = 0; I != NF; ++I) {
const Record::Field *FD = Record->getField(I);
const Descriptor *Desc = FD->Desc;
- const Pointer &FP = Ptr.atField(FD->Offset);
+ PtrView FP = Ptr.atField(FD->Offset);
APValue &Value = R.getStructField(I);
if (Desc->isPrimitive()) {
TYPE_SWITCH(Desc->getPrimType(),
@@ -849,7 +834,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx,
for (unsigned I = 0; I != NB; ++I) {
const Record::Base *BD = Record->getBase(I);
QualType BaseTy = Ctx.getASTContext().getCanonicalTagType(BD->Decl);
- const Pointer &BP = Ptr.atField(BD->Offset);
+ PtrView BP = Ptr.atField(BD->Offset);
Ok &= Composite(BaseTy, BP, R.getStructBase(I));
}
@@ -857,7 +842,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));
}
}
@@ -971,7 +956,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx,
// Return the composite type.
APValue Result;
- if (!Composite(ResultType, *this, Result))
+ if (!Composite(ResultType, view(), Result))
return std::nullopt;
return Result;
}
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index ea9c7d4cb04db..e5c4ce5733854 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -33,6 +33,277 @@ class Context;
class Pointer;
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P);
+struct PtrView {
+ static constexpr unsigned PastEndMark = ~0u;
+
+ Block *Pointee;
+ unsigned Base;
+ uint64_t Offset;
+
+ bool isZero() const { return !Pointee; }
+ bool isLive() const { return Pointee && !Pointee->isDead(); }
+ bool isDummy() const { return Pointee && Pointee->isDummy(); }
+ bool isActive() const { return isRoot() || getInlineDesc()->IsActive; }
+ bool isArrayRoot() const { return inArray() && Offset == Base; }
+ bool isElementPastEnd() const { return Offset == PastEndMark; }
+ bool isZeroSizeArray() const { return getFieldDesc()->isZeroSizeArray(); }
+ bool isMutable() const {
+ return !isRoot() && getInlineDesc()->IsFieldMutable;
+ }
+ bool inUnion() const { return getInlineDesc()->InUnion; };
+ bool inArray() const { return getFieldDesc()->IsArray; }
+ bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); }
+
+ unsigned getEvalID() { return Pointee->getEvalID(); }
+
+ bool isRoot() const {
+ return Base == Pointee->getDescriptor()->getMetadataSize();
+ }
+
+ InlineDescriptor *getInlineDesc() const {
+ assert(Base != sizeof(GlobalInlineDescriptor));
+ assert(Base <= Pointee->getSize());
+ assert(Base >= sizeof(InlineDescriptor));
+ return getDescriptor(Base);
+ }
+
+ InlineDescriptor *getDescriptor(unsigned Offset) const {
+ assert(Offset != 0 && "Not a nested pointer");
+ return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) -
+ 1;
+ }
+
+ const Descriptor *getFieldDesc() const {
+ if (isRoot())
+ return Pointee->getDescriptor();
+ return getInlineDesc()->Desc;
+ }
+
+ const Descriptor *getDeclDesc() const { return Pointee->getDescriptor(); }
+
+ size_t elemSize() const { return getFieldDesc()->getElemSize(); }
+
+ [[nodiscard]] PtrView narrow() const {
+ // Null pointers cannot be narrowed.
+ if (isZero() || isUnknownSizeArray())
+ return *this;
+
+ if (inArray()) {
+ // Pointer is one past end - magic offset marks that.
+ if (isOnePastEnd())
+ return PtrView{Pointee, Base, PastEndMark};
+
+ if (Offset != Base) {
+ // If we're pointing to a primitive array element, there's nothing to
+ // do.
+ if (inPrimitiveArray())
+ return *this;
+ // Pointer is to a composite array element - enter it.
+ return PtrView{Pointee, static_cast<unsigned>(Offset), Offset};
+ }
+ }
+ // Otherwise, we're pointing to a non-array element or
+ // are already narrowed to a composite array element. Nothing to do.
+ return *this;
+ }
+
+ [[nodiscard]] PtrView expand() const {
+ if (isElementPastEnd()) {
+ // Revert to an outer one-past-end pointer.
+ unsigned Adjust;
+ if (inPrimitiveArray())
+ Adjust = sizeof(InitMapPtr);
+ else
+ Adjust = sizeof(InlineDescriptor);
+ return PtrView{Pointee, Base, Base + getSize() + Adjust};
+ }
+
+ // Do not step out of array elements.
+ if (Base != Offset)
+ return *this;
+
+ if (isRoot())
+ return PtrView{Pointee, Base, Base};
+
+ // Step into the containing array, if inside one.
+ unsigned Next = Base - getInlineDesc()->Offset;
+ const Descriptor *Desc =
+ (Next == Pointee->getDescriptor()->getMetadataSize())
+ ? getDeclDesc()
+ : getDescriptor(Next)->Desc;
+ if (!Desc->IsArray)
+ return *this;
+ return PtrView{Pointee, Next, Offset};
+ }
+
+ [[nodiscard]] PtrView getArray() const {
+ assert(Offset != Base && "not an array element");
+ return PtrView{Pointee, Base, Base};
+ }
+
+ const Record *getRecord() const { return getFieldDesc()->ElemRecord; }
+ const Record *getElemRecord() const {
+ const Descriptor *ElemDesc = getFieldDesc()->ElemDesc;
+ return ElemDesc ? ElemDesc->ElemRecord : nullptr;
+ }
+ const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); }
+
+ bool isField() const {
+ return !isZero() && !isRoot() && getFieldDesc()->asDecl();
+ }
+
+ bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
+ bool isVirtualBaseClass() const {
+ return isField() && getInlineDesc()->IsVirtualBase;
+ }
+ bool isUnknownSizeArray() const {
+ return getFieldDesc()->isUnknownSizeArray();
+ }
+
+ bool isPastEnd() const { return Offset > Pointee->getSize(); }
+
+ unsigned getOffset() const {
+ assert(Offset != PastEndMark);
+
+ unsigned Adjust = 0;
+ if (Offset != Base) {
+ if (getFieldDesc()->ElemDesc)
+ Adjust = sizeof(InlineDescriptor);
+ else
+ Adjust = sizeof(InitMapPtr);
+ }
+ return Offset - Base - Adjust;
+ }
+ size_t getSize() const { return getFieldDesc()->getSize(); }
+
+ bool isOnePastEnd() const {
+ if (!Pointee)
+ return false;
+
+ if (isUnknownSizeArray())
+ return false;
+ return isPastEnd() || (getSize() == getOffset());
+ }
+
+ PtrView atIndex(unsigned Idx) const {
+ unsigned Off = Idx * elemSize();
+ if (getFieldDesc()->ElemDesc)
+ Off += sizeof(InlineDescriptor);
+ else
+ Off += sizeof(InitMapPtr);
+ return PtrView{Pointee, Base, Base + Off};
+ }
+
+ int64_t getIndex() const {
+ if (isZero())
+ return 0;
+ // narrow()ed element in a composite array.
+ if (Base > sizeof(InlineDescriptor) && Base == Offset)
+ return 0;
+
+ if (auto ElemSize = elemSize())
+ return getOffset() / ElemSize;
+ return 0;
+ }
+
+ unsigned getNumElems() const { return getSize() / elemSize(); }
+
+ bool isArrayElement() const {
+ if (inArray() && Base != Offset)
+ return true;
+
+ // Might be a narrow()'ed element in a composite array.
+ // Check the inline descriptor.
+ if (Base >= sizeof(InlineDescriptor) && getInlineDesc()->IsArrayElement)
+ return true;
+
+ return false;
+ }
+
+ template <typename T> T &deref() const {
+ assert(isLive() && "Invalid pointer");
+ assert(Pointee);
+
+ if (isArrayRoot())
+ return *reinterpret_cast<T *>(Pointee->rawData() + Base +
+ sizeof(InitMapPtr));
+
+ return *reinterpret_cast<T *>(Pointee->rawData() + Offset);
+ }
+
+ template <typename T> T &elem(unsigned I) const {
+ assert(isLive() && "Invalid pointer");
+ assert(Pointee);
+ assert(getFieldDesc()->isPrimitiveArray());
+ assert(I < getFieldDesc()->getNumElems());
+
+ unsigned ElemByteOffset = I * getFieldDesc()->getElemSize();
+ unsigned ReadOffset = Base + sizeof(InitMapPtr) + ElemByteOffset;
+ assert(ReadOffset + sizeof(T) <= Pointee->getDescriptor()->getAllocSize());
+
+ return *reinterpret_cast<T *>(Pointee->rawData() + ReadOffset);
+ }
+
+ [[nodiscard]] PtrView getBase() const {
+ unsigned NewBase = Base - getInlineDesc()->Offset;
+ return PtrView{Pointee, NewBase, NewBase};
+ }
+
+ [[nodiscard]] PtrView atField(unsigned Offset) const {
+ unsigned F = this->Offset + Offset;
+ return PtrView{Pointee, F, F};
+ }
+
+ QualType getType() const {
+ if (inPrimitiveArray() && Offset != Base) {
+ // Unfortunately, complex and vector types are not array types in clang,
+ // but they are for us.
+ if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe())
+ return AT->getElementType();
+ if (const auto *CT = getFieldDesc()->getType()->getAs<ComplexType>())
+ return CT->getElementType();
+ if (const auto *CT = getFieldDesc()->getType()->getAs<VectorType>())
+ return CT->getElementType();
+ }
+
+ return getFieldDesc()->getDataElemType();
+ }
+
+ bool isInitialized() const {
+ if (isRoot() && Base == sizeof(GlobalInlineDescriptor) && Offset == Base) {
+ const auto &GD = Pointee->getBlockDesc<GlobalInlineDescriptor>();
+ return GD.InitState == GlobalInitState::Initialized;
+ }
+
+ assert(Pointee && "Cannot check if null pointer was initialized");
+ const Descriptor *Desc = getFieldDesc();
+ assert(Desc);
+ if (Desc->isPrimitiveArray())
+ return isElementInitialized(getIndex());
+
+ if (Base == 0)
+ return true;
+ // Field has its bit in an inline descriptor.
+ return getInlineDesc()->IsInitialized;
+ }
+
+ void initializeElement(unsigned Index) const;
+ bool allElementsInitialized() const;
+ bool isElementInitialized(unsigned Index) const;
+ InitMapPtr &getInitMap() const {
+ return *reinterpret_cast<InitMapPtr *>(Pointee->rawData() + Base);
+ }
+ void initialize() const;
+ void activate() const;
+
+ bool operator==(const PtrView &Other) const {
+ return Other.Pointee == Pointee && Base == Other.Base &&
+ Offset == Other.Offset;
+ }
+
+ bool operator!=(const PtrView &Other) const { return !(Other == *this); }
+};
+
struct BlockPointer {
/// The block the pointer is pointing to.
Block *Pointee;
@@ -95,10 +366,6 @@ enum class Storage { Int, Block, Fn, Typeid };
/// Base
/// \endverbatim
class Pointer {
-private:
- static constexpr unsigned PastEndMark = ~0u;
- static constexpr unsigned RootPtrMark = ~0u;
-
public:
Pointer() : StorageKind(Storage::Int), Int{nullptr, 0} {}
Pointer(IntPointer &&IntPtr)
@@ -116,7 +383,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);
@@ -133,9 +402,7 @@ class Pointer {
if (isFunctionPointer())
return P.Fn.Func == Fn.Func && P.Offset == Offset;
- assert(isBlockPointer());
- return P.BS.Pointee == BS.Pointee && P.BS.Base == BS.Base &&
- P.Offset == Offset;
+ return P.view() == view();
}
bool operator!=(const Pointer &P) const { return !(P == *this); }
@@ -154,6 +421,11 @@ class Pointer {
return reinterpret_cast<uint64_t>(BS.Pointee) + Offset;
}
+ PtrView view() const {
+ assert(isBlockPointer());
+ return PtrView{BS.Pointee, BS.Base, Offset};
+ }
+
/// Converts the pointer to an APValue that is an rvalue.
std::optional<APValue> toRValue(const Context &Ctx,
QualType ResultType) const;
@@ -165,21 +437,12 @@ class Pointer {
if (isFunctionPointer())
return Pointer(Fn.Func, Idx);
- if (BS.Base == RootPtrMark)
- return Pointer(BS.Pointee, RootPtrMark, getDeclDesc()->getSize());
- uint64_t Off = Idx * elemSize();
- if (getFieldDesc()->ElemDesc)
- Off += sizeof(InlineDescriptor);
- else
- Off += sizeof(InitMapPtr);
- return Pointer(BS.Pointee, BS.Base, BS.Base + Off);
+ return Pointer(view().atIndex(Idx));
}
/// 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
@@ -194,70 +457,14 @@ class Pointer {
[[nodiscard]] Pointer narrow() const {
if (!isBlockPointer())
return *this;
- assert(isBlockPointer());
- // Null pointers cannot be narrowed.
- if (isZero() || isUnknownSizeArray())
- return *this;
-
- unsigned Base = BS.Base;
- // Pointer to an array of base types - enter block.
- if (Base == RootPtrMark)
- return Pointer(BS.Pointee, sizeof(InlineDescriptor),
- Offset == 0 ? Offset : PastEndMark);
-
- if (inArray()) {
- // Pointer is one past end - magic offset marks that.
- if (isOnePastEnd())
- return Pointer(BS.Pointee, Base, PastEndMark);
-
- if (Offset != Base) {
- // If we're pointing to a primitive array element, there's nothing to
- // do.
- if (inPrimitiveArray())
- return *this;
- // Pointer is to a composite array element - enter it.
- return Pointer(BS.Pointee, Offset, Offset);
- }
- }
-
- // Otherwise, we're pointing to a non-array element or
- // are already narrowed to a composite array element. Nothing to do.
- return *this;
+ return Pointer(view().narrow());
}
/// Expands a pointer to the containing array, undoing narrowing.
[[nodiscard]] Pointer expand() const {
if (!isBlockPointer())
return *this;
- assert(isBlockPointer());
- Block *Pointee = BS.Pointee;
-
- if (isElementPastEnd()) {
- // Revert to an outer one-past-end pointer.
- unsigned Adjust;
- if (inPrimitiveArray())
- Adjust = sizeof(InitMapPtr);
- else
- Adjust = sizeof(InlineDescriptor);
- return Pointer(Pointee, BS.Base, BS.Base + getSize() + Adjust);
- }
-
- // Do not step out of array elements.
- if (BS.Base != Offset)
- return *this;
-
- if (isRoot())
- return Pointer(Pointee, BS.Base, BS.Base);
-
- // Step into the containing array, if inside one.
- unsigned Next = BS.Base - getInlineDesc()->Offset;
- const Descriptor *Desc =
- (Next == Pointee->getDescriptor()->getMetadataSize())
- ? getDeclDesc()
- : getDescriptor(Next)->Desc;
- if (!Desc->IsArray)
- return *this;
- return Pointer(Pointee, Next, Offset);
+ return Pointer(view().expand());
}
/// Checks if the pointer is null.
@@ -278,14 +485,14 @@ class Pointer {
bool isLive() const {
if (!isBlockPointer())
return true;
- return BS.Pointee && !BS.Pointee->isDead();
+ return view().isLive();
}
/// Checks if the item is a field in an object.
bool isField() const {
if (!isBlockPointer())
return false;
- return !isRoot() && getFieldDesc()->asDecl();
+ return view().isField();
}
/// Accessor for information about the declaration site.
@@ -314,23 +521,9 @@ class Pointer {
}
/// Returns a pointer to the object of which this pointer is a field.
- [[nodiscard]] Pointer getBase() const {
- if (BS.Base == RootPtrMark) {
- assert(Offset == PastEndMark && "cannot get base of a block");
- return Pointer(BS.Pointee, BS.Base, 0);
- }
- unsigned NewBase = BS.Base - getInlineDesc()->Offset;
- return Pointer(BS.Pointee, NewBase, NewBase);
- }
+ [[nodiscard]] Pointer getBase() const { return Pointer(view().getBase()); }
/// Returns the parent array.
- [[nodiscard]] Pointer getArray() const {
- if (BS.Base == RootPtrMark) {
- assert(Offset != 0 && Offset != PastEndMark && "not an array element");
- return Pointer(BS.Pointee, BS.Base, 0);
- }
- assert(Offset != BS.Base && "not an array element");
- return Pointer(BS.Pointee, BS.Base, BS.Base);
- }
+ [[nodiscard]] Pointer getArray() const { return Pointer(view().getArray()); }
/// Accessors for information about the innermost field.
const Descriptor *getFieldDesc() const {
@@ -344,23 +537,17 @@ class Pointer {
/// Returns the type of the innermost field.
QualType getType() const {
- if (isTypeidPointer())
- return QualType(Typeid.TypeInfoType, 0);
- if (isFunctionPointer())
+ switch (StorageKind) {
+ case Storage::Int:
+ return Int.Desc ? Int.Desc->getType() : QualType();
+ case Storage::Block:
+ return view().getType();
+ case Storage::Fn:
return Fn.Func->getDecl()->getType();
-
- if (inPrimitiveArray() && Offset != BS.Base) {
- // Unfortunately, complex and vector types are not array types in clang,
- // but they are for us.
- if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe())
- return AT->getElementType();
- if (const auto *CT = getFieldDesc()->getType()->getAs<ComplexType>())
- return CT->getElementType();
- if (const auto *CT = getFieldDesc()->getType()->getAs<VectorType>())
- return CT->getElementType();
+ case Storage::Typeid:
+ return QualType(Typeid.TypeInfoType, 0);
}
-
- return getFieldDesc()->getDataElemType();
+ llvm_unreachable("Unhandled StorageKind");
}
[[nodiscard]] Pointer getDeclPtr() const { return Pointer(BS.Pointee); }
@@ -373,9 +560,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 {
@@ -385,41 +570,30 @@ 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;
+ assert(Offset != PtrView::PastEndMark && "invalid offset");
+ return view().getOffset();
}
/// Whether this array refers to an array, but not
/// to the first element.
- bool isArrayRoot() const { return inArray() && Offset == BS.Base; }
+ bool isArrayRoot() const { return view().isArrayRoot(); }
/// Checks if the innermost field is an array.
bool inArray() const {
if (isBlockPointer())
- return getFieldDesc()->IsArray;
+ return view().inArray();
return false;
}
bool inUnion() const {
if (isBlockPointer() && BS.Base >= sizeof(InlineDescriptor))
- return getInlineDesc()->InUnion;
+ return view().inUnion();
return false;
};
/// Checks if the structure is a primitive array.
bool inPrimitiveArray() const {
if (isBlockPointer())
- return getFieldDesc()->isPrimitiveArray();
+ return view().inPrimitiveArray();
return false;
}
/// Checks if the structure is an array of unknown size.
@@ -433,23 +607,13 @@ class Pointer {
if (!isBlockPointer())
return false;
- const BlockPointer &BP = BS;
- if (inArray() && BP.Base != Offset)
- return true;
-
- // Might be a narrow()'ed element in a composite array.
- // Check the inline descriptor.
- if (BP.Base >= sizeof(InlineDescriptor) && getInlineDesc()->IsArrayElement)
- return true;
-
- return false;
+ return view().isArrayElement();
}
/// Pointer points directly to a block.
bool isRoot() const {
if (isZero() || !isBlockPointer())
return true;
- return (BS.Base == BS.Pointee->getDescriptor()->getMetadataSize() ||
- BS.Base == 0);
+ return view().isRoot();
}
/// If this pointer has an InlineDescriptor we can use to initialize.
bool canBeInitialized() const {
@@ -482,12 +646,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())
@@ -531,7 +692,7 @@ class Pointer {
bool isMutable() const {
if (!isBlockPointer())
return false;
- return !isRoot() && getInlineDesc()->IsFieldMutable;
+ return view().isMutable();
}
bool isWeak() const {
@@ -551,21 +712,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 +760,7 @@ class Pointer {
if (isTypeidPointer())
return reinterpret_cast<uintptr_t>(Typeid.TypePtr) + Offset;
if (isOnePastEnd())
- return PastEndMark;
+ return PtrView::PastEndMark;
return Offset;
}
@@ -611,7 +768,7 @@ class Pointer {
unsigned getNumElems() const {
if (!isBlockPointer())
return ~0u;
- return getSize() / elemSize();
+ return view().getNumElems();
}
const Block *block() const { return BS.Pointee; }
@@ -628,16 +785,7 @@ class Pointer {
if (!isBlockPointer())
return getIntegerRepresentation();
- if (isZero())
- return 0;
-
- // narrow()ed element in a composite array.
- if (BS.Base > sizeof(InlineDescriptor) && BS.Base == Offset)
- return 0;
-
- if (auto ElemSize = elemSize())
- return getOffset() / ElemSize;
- return 0;
+ return view().getIndex();
}
/// Checks if the index is one past end.
@@ -663,7 +811,7 @@ class Pointer {
}
/// Checks if the pointer is an out-of-bounds element pointer.
- bool isElementPastEnd() const { return Offset == PastEndMark; }
+ bool isElementPastEnd() const { return Offset == PtrView::PastEndMark; }
/// Checks if the pointer is pointing to a zero-size array.
bool isZeroSizeArray() const {
@@ -691,11 +839,7 @@ class Pointer {
assert(isDereferencable());
assert(Offset + sizeof(T) <= BS.Pointee->getDescriptor()->getAllocSize());
- if (isArrayRoot())
- return *reinterpret_cast<T *>(BS.Pointee->rawData() + BS.Base +
- sizeof(InitMapPtr));
-
- return *reinterpret_cast<T *>(BS.Pointee->rawData() + Offset);
+ return view().deref<T>();
}
/// Dereferences the element at index \p I.
@@ -708,12 +852,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
@@ -728,9 +867,15 @@ class Pointer {
}
/// Initializes a field.
- void initialize() const;
+ void initialize() const {
+ if (!isBlockPointer())
+ return;
+ view().initialize();
+ }
/// Initialized the given element of a primitive array.
- void initializeElement(unsigned Index) const;
+ void initializeElement(unsigned Index) const {
+ view().initializeElement(Index);
+ }
/// Initialize all elements of a primitive array at once. This can be
/// used in situations where we *know* we have initialized *all* elements
/// of a primtive array.
@@ -738,15 +883,26 @@ class Pointer {
/// Checks if an object was initialized.
bool isInitialized() const;
/// Like isInitialized(), but for primitive arrays.
- bool isElementInitialized(unsigned Index) const;
- bool allElementsInitialized() const;
+ bool isElementInitialized(unsigned Index) const {
+ if (!isBlockPointer())
+ return true;
+
+ return view().isElementInitialized(Index);
+ }
+ bool allElementsInitialized() const {
+ assert(getFieldDesc()->isPrimitiveArray());
+ assert(isArrayRoot());
+ return view().allElementsInitialized();
+ }
bool allElementsAlive() const;
bool isElementAlive(unsigned Index) const;
- /// Activats a field.
- void activate() const;
+ /// Activates a field.
+ void activate() const { view().activate(); }
/// Deactivates an entire strurcutre.
- void deactivate() const;
+ void deactivate() const {
+ // TODO: this only appears in constructors, so nothing to deactivate.
+ }
Lifetime getLifetime() const {
if (!isBlockPointer())
@@ -784,10 +940,10 @@ class Pointer {
/// The result is either a root pointer or something
/// that isn't a base class anymore.
[[nodiscard]] Pointer stripBaseCasts() const {
- Pointer P = *this;
- while (P.isBaseClass())
- P = P.getBase();
- return P;
+ PtrView V = view();
+ while (V.isBaseClass())
+ V = V.getBase();
+ return Pointer(V);
}
/// Compare two pointers.
@@ -810,7 +966,7 @@ class Pointer {
/// Checks if both given pointers point to the same block.
static bool pointToSameBlock(const Pointer &A, const Pointer &B);
- static std::optional<std::pair<Pointer, Pointer>>
+ static std::optional<std::pair<PtrView, PtrView>>
computeSplitPoint(const Pointer &A, const Pointer &B);
/// Whether this points to a block that's been created for a "literal lvalue",
@@ -848,16 +1004,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.
diff --git a/clang/test/AST/ByteCode/cxx20.cpp b/clang/test/AST/ByteCode/cxx20.cpp
index 9800fe01fcaf5..4d8f7cbcb5838 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1306,7 +1306,6 @@ namespace PointerCmp {
}
namespace ExpandOnOPTEPointers {
-
template <class _BidirectionalIterator>
constexpr void inplace_merge(_BidirectionalIterator __first,
_BidirectionalIterator __middle) {
More information about the cfe-commits
mailing list