aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp404
1 files changed, 290 insertions, 114 deletions
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp
index 5af1d6d52e93..29579f5db40b 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp
@@ -13,156 +13,271 @@
#include "Function.h"
#include "Integral.h"
#include "InterpBlock.h"
+#include "MemberPointer.h"
#include "PrimType.h"
#include "Record.h"
+#include "clang/AST/RecordLayout.h"
using namespace clang;
using namespace clang::interp;
-Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
+Pointer::Pointer(Block *Pointee)
+ : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),
+ Pointee->getDescriptor()->getMetadataSize()) {}
-Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
+Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)
: Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
-Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
+Pointer::Pointer(const Pointer &P)
+ : Offset(P.Offset), PointeeStorage(P.PointeeStorage),
+ StorageKind(P.StorageKind) {
-Pointer::Pointer(Pointer &&P)
- : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) {
- if (Pointee)
- Pointee->replacePointer(&P, this);
+ if (isBlockPointer() && PointeeStorage.BS.Pointee)
+ PointeeStorage.BS.Pointee->addPointer(this);
}
-Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset)
- : Pointee(Pointee), Base(Base), Offset(Offset) {
+Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)
+ : Offset(Offset), StorageKind(Storage::Block) {
assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
+
+ PointeeStorage.BS = {Pointee, Base};
+
if (Pointee)
Pointee->addPointer(this);
}
+Pointer::Pointer(Pointer &&P)
+ : Offset(P.Offset), PointeeStorage(P.PointeeStorage),
+ StorageKind(P.StorageKind) {
+
+ if (StorageKind == Storage::Block && PointeeStorage.BS.Pointee)
+ PointeeStorage.BS.Pointee->replacePointer(&P, this);
+}
+
Pointer::~Pointer() {
- if (Pointee) {
+ if (isIntegralPointer())
+ return;
+
+ if (Block *Pointee = PointeeStorage.BS.Pointee) {
Pointee->removePointer(this);
Pointee->cleanup();
}
}
void Pointer::operator=(const Pointer &P) {
- Block *Old = Pointee;
-
- if (Pointee)
- Pointee->removePointer(this);
+ // If the current storage type is Block, we need to remove
+ // this pointer from the block.
+ bool WasBlockPointer = isBlockPointer();
+ if (StorageKind == Storage::Block) {
+ Block *Old = PointeeStorage.BS.Pointee;
+ if (WasBlockPointer && Old) {
+ PointeeStorage.BS.Pointee->removePointer(this);
+ Old->cleanup();
+ }
+ }
+ StorageKind = P.StorageKind;
Offset = P.Offset;
- Base = P.Base;
- Pointee = P.Pointee;
- if (Pointee)
- Pointee->addPointer(this);
+ if (P.isBlockPointer()) {
+ PointeeStorage.BS = P.PointeeStorage.BS;
+ PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;
- if (Old)
- Old->cleanup();
+ if (PointeeStorage.BS.Pointee)
+ PointeeStorage.BS.Pointee->addPointer(this);
+ } else if (P.isIntegralPointer()) {
+ PointeeStorage.Int = P.PointeeStorage.Int;
+ } else {
+ assert(false && "Unhandled storage kind");
+ }
}
void Pointer::operator=(Pointer &&P) {
- Block *Old = Pointee;
-
- if (Pointee)
- Pointee->removePointer(this);
+ // If the current storage type is Block, we need to remove
+ // this pointer from the block.
+ bool WasBlockPointer = isBlockPointer();
+ if (StorageKind == Storage::Block) {
+ Block *Old = PointeeStorage.BS.Pointee;
+ if (WasBlockPointer && Old) {
+ PointeeStorage.BS.Pointee->removePointer(this);
+ Old->cleanup();
+ }
+ }
+ StorageKind = P.StorageKind;
Offset = P.Offset;
- Base = P.Base;
- Pointee = P.Pointee;
- if (Pointee)
- Pointee->replacePointer(&P, this);
+ if (P.isBlockPointer()) {
+ PointeeStorage.BS = P.PointeeStorage.BS;
+ PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;
- if (Old)
- Old->cleanup();
+ if (PointeeStorage.BS.Pointee)
+ PointeeStorage.BS.Pointee->addPointer(this);
+ } else if (P.isIntegralPointer()) {
+ PointeeStorage.Int = P.PointeeStorage.Int;
+ } else {
+ assert(false && "Unhandled storage kind");
+ }
}
-APValue Pointer::toAPValue() const {
- APValue::LValueBase Base;
+APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {
llvm::SmallVector<APValue::LValuePathEntry, 5> Path;
- CharUnits Offset;
- bool IsNullPtr;
- bool IsOnePastEnd;
-
- if (isZero()) {
- Base = static_cast<const Expr *>(nullptr);
- IsNullPtr = true;
- IsOnePastEnd = false;
- Offset = CharUnits::Zero();
- } else {
- // Build the lvalue base from the block.
- const Descriptor *Desc = getDeclDesc();
- if (auto *VD = Desc->asValueDecl())
- Base = VD;
- else if (auto *E = Desc->asExpr())
- Base = E;
- else
- llvm_unreachable("Invalid allocation type");
-
- // Not a null pointer.
- IsNullPtr = false;
-
- if (isUnknownSizeArray()) {
- IsOnePastEnd = false;
- Offset = CharUnits::Zero();
- } else if (Desc->asExpr()) {
- // Pointer pointing to a an expression.
- IsOnePastEnd = false;
- Offset = CharUnits::Zero();
+
+ if (isZero())
+ return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,
+ /*IsOnePastEnd=*/false, /*IsNullPtr=*/true);
+ if (isIntegralPointer())
+ return APValue(static_cast<const Expr *>(nullptr),
+ CharUnits::fromQuantity(asIntPointer().Value + this->Offset),
+ Path,
+ /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
+
+ // Build the lvalue base from the block.
+ const Descriptor *Desc = getDeclDesc();
+ APValue::LValueBase Base;
+ if (const auto *VD = Desc->asValueDecl())
+ Base = VD;
+ else if (const auto *E = Desc->asExpr())
+ Base = E;
+ else
+ llvm_unreachable("Invalid allocation type");
+
+ if (isUnknownSizeArray() || Desc->asExpr())
+ return APValue(Base, CharUnits::Zero(), Path,
+ /*IsOnePastEnd=*/isOnePastEnd(), /*IsNullPtr=*/false);
+
+ CharUnits Offset = CharUnits::Zero();
+
+ auto getFieldOffset = [&](const FieldDecl *FD) -> CharUnits {
+ // This shouldn't happen, but if it does, don't crash inside
+ // getASTRecordLayout.
+ if (FD->getParent()->isInvalidDecl())
+ return CharUnits::Zero();
+ const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());
+ unsigned FieldIndex = FD->getFieldIndex();
+ return ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex));
+ };
+
+ // Build the path into the object.
+ Pointer Ptr = *this;
+ while (Ptr.isField() || Ptr.isArrayElement()) {
+ if (Ptr.isArrayRoot()) {
+ Path.push_back(APValue::LValuePathEntry(
+ {Ptr.getFieldDesc()->asDecl(), /*IsVirtual=*/false}));
+
+ if (const auto *FD = dyn_cast<FieldDecl>(Ptr.getFieldDesc()->asDecl()))
+ Offset += getFieldOffset(FD);
+
+ Ptr = Ptr.getBase();
+ } else if (Ptr.isArrayElement()) {
+ unsigned Index;
+ if (Ptr.isOnePastEnd())
+ Index = Ptr.getArray().getNumElems();
+ else
+ Index = Ptr.getIndex();
+
+ Offset += (Index * ASTCtx.getTypeSizeInChars(Ptr.getType()));
+ Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index));
+ Ptr = Ptr.getArray();
} else {
- // TODO: compute the offset into the object.
- Offset = CharUnits::Zero();
-
- // Build the path into the object.
- Pointer Ptr = *this;
- while (Ptr.isField() || Ptr.isArrayElement()) {
- if (Ptr.isArrayElement()) {
- Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
- Ptr = Ptr.getArray();
+ bool IsVirtual = false;
+
+ // Create a path entry for the field.
+ const Descriptor *Desc = Ptr.getFieldDesc();
+ if (const auto *BaseOrMember = Desc->asDecl()) {
+ if (const auto *FD = dyn_cast<FieldDecl>(BaseOrMember)) {
+ Ptr = Ptr.getBase();
+ Offset += getFieldOffset(FD);
+ } else if (const auto *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) {
+ IsVirtual = Ptr.isVirtualBaseClass();
+ Ptr = Ptr.getBase();
+ const Record *BaseRecord = Ptr.getRecord();
+
+ const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(
+ cast<CXXRecordDecl>(BaseRecord->getDecl()));
+ if (IsVirtual)
+ Offset += Layout.getVBaseClassOffset(RD);
+ else
+ Offset += Layout.getBaseClassOffset(RD);
+
} else {
- // TODO: figure out if base is virtual
- bool IsVirtual = false;
-
- // Create a path entry for the field.
- const Descriptor *Desc = Ptr.getFieldDesc();
- if (const auto *BaseOrMember = Desc->asDecl()) {
- Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
- Ptr = Ptr.getBase();
- continue;
- }
- llvm_unreachable("Invalid field type");
+ Ptr = Ptr.getBase();
}
+ Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
+ continue;
}
-
- IsOnePastEnd = isOnePastEnd();
+ llvm_unreachable("Invalid field type");
}
}
+ // FIXME(perf): We compute the lvalue path above, but we can't supply it
+ // for dummy pointers (that causes crashes later in CheckConstantExpression).
+ if (isDummy())
+ Path.clear();
+
// We assemble the LValuePath starting from the innermost pointer to the
// outermost one. SO in a.b.c, the first element in Path will refer to
// the field 'c', while later code expects it to refer to 'a'.
// Just invert the order of the elements.
std::reverse(Path.begin(), Path.end());
- return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr);
+ return APValue(Base, Offset, Path, /*IsOnePastEnd=*/isOnePastEnd(),
+ /*IsNullPtr=*/false);
+}
+
+void Pointer::print(llvm::raw_ostream &OS) const {
+ OS << PointeeStorage.BS.Pointee << " (";
+ if (isBlockPointer()) {
+ const Block *B = PointeeStorage.BS.Pointee;
+ OS << "Block) {";
+
+ if (isRoot())
+ OS << "rootptr(" << PointeeStorage.BS.Base << "), ";
+ else
+ OS << PointeeStorage.BS.Base << ", ";
+
+ if (isElementPastEnd())
+ OS << "pastend, ";
+ else
+ OS << Offset << ", ";
+
+ if (B)
+ OS << B->getSize();
+ else
+ OS << "nullptr";
+ } else {
+ OS << "Int) {";
+ OS << PointeeStorage.Int.Value << ", " << PointeeStorage.Int.Desc;
+ }
+ OS << "}";
}
std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
- if (!Pointee)
+ if (isZero())
return "nullptr";
- return toAPValue().getAsString(Ctx, getType());
+ if (isIntegralPointer())
+ return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();
+
+ return toAPValue(Ctx).getAsString(Ctx, getType());
}
bool Pointer::isInitialized() const {
- assert(Pointee && "Cannot check if null pointer was initialized");
+ if (isIntegralPointer())
+ return true;
+
+ if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {
+ const GlobalInlineDescriptor &GD =
+ *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData());
+ return GD.InitState == GlobalInitState::Initialized;
+ }
+
+ assert(PointeeStorage.BS.Pointee &&
+ "Cannot check if null pointer was initialized");
const Descriptor *Desc = getFieldDesc();
assert(Desc);
if (Desc->isPrimitiveArray()) {
- if (isStatic() && Base == 0)
+ if (isStatic() && PointeeStorage.BS.Base == 0)
return true;
InitMapPtr &IM = getInitMap();
@@ -176,18 +291,35 @@ bool Pointer::isInitialized() const {
return IM->second->isElementInitialized(getIndex());
}
+ if (asBlockPointer().Base == 0)
+ return true;
+
// Field has its bit in an inline descriptor.
- return Base == 0 || getInlineDesc()->IsInitialized;
+ return getInlineDesc()->IsInitialized;
}
void Pointer::initialize() const {
- assert(Pointee && "Cannot initialize null pointer");
+ if (isIntegralPointer())
+ return;
+
+ assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer");
const Descriptor *Desc = getFieldDesc();
+ if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {
+ GlobalInlineDescriptor &GD = *reinterpret_cast<GlobalInlineDescriptor *>(
+ asBlockPointer().Pointee->rawData());
+ GD.InitState = GlobalInitState::Initialized;
+ return;
+ }
+
assert(Desc);
if (Desc->isPrimitiveArray()) {
// Primitive global arrays don't have an initmap.
- if (isStatic() && Base == 0)
+ if (isStatic() && PointeeStorage.BS.Base == 0)
+ return;
+
+ // Nothing to do for these.
+ if (Desc->getNumElems() == 0)
return;
InitMapPtr &IM = getInitMap();
@@ -209,13 +341,19 @@ void Pointer::initialize() const {
}
// Field has its bit in an inline descriptor.
- assert(Base != 0 && "Only composite fields can be initialised");
+ assert(PointeeStorage.BS.Base != 0 &&
+ "Only composite fields can be initialised");
getInlineDesc()->IsInitialized = true;
}
void Pointer::activate() const {
// Field has its bit in an inline descriptor.
- assert(Base != 0 && "Only composite fields can be initialised");
+ assert(PointeeStorage.BS.Base != 0 &&
+ "Only composite fields can be initialised");
+
+ if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor))
+ return;
+
getInlineDesc()->IsActive = true;
}
@@ -224,32 +362,44 @@ void Pointer::deactivate() const {
}
bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
- return A.Pointee == B.Pointee;
+ // Two null pointers always have the same base.
+ if (A.isZero() && B.isZero())
+ return true;
+
+ if (A.isIntegralPointer() && B.isIntegralPointer())
+ return true;
+
+ if (A.isIntegralPointer() || B.isIntegralPointer())
+ return A.getSource() == B.getSource();
+
+ return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee;
}
bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
- return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray;
+ return hasSameBase(A, B) &&
+ A.PointeeStorage.BS.Base == B.PointeeStorage.BS.Base &&
+ A.getFieldDesc()->IsArray;
}
-std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
+std::optional<APValue> Pointer::toRValue(const Context &Ctx,
+ QualType ResultType) const {
+ const ASTContext &ASTCtx = Ctx.getASTContext();
+ assert(!ResultType.isNull());
// Method to recursively traverse composites.
std::function<bool(QualType, const Pointer &, APValue &)> Composite;
- Composite = [&Composite, &Ctx](QualType Ty, const Pointer &Ptr, APValue &R) {
+ Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr,
+ APValue &R) {
if (const auto *AT = Ty->getAs<AtomicType>())
Ty = AT->getValueType();
// Invalid pointers.
- if (Ptr.isDummy() || !Ptr.isLive() ||
- (!Ptr.isUnknownSizeArray() && Ptr.isOnePastEnd()))
+ if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||
+ Ptr.isPastEnd())
return false;
// Primitive values.
if (std::optional<PrimType> T = Ctx.classify(Ty)) {
- if (T == PT_Ptr || T == PT_FnPtr) {
- R = Ptr.toAPValue();
- } else {
- TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue());
- }
+ TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue(ASTCtx));
return true;
}
@@ -266,10 +416,11 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
QualType FieldTy = F.Decl->getType();
if (FP.isActive()) {
if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
- TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue());
+ TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));
} else {
Ok &= Composite(FieldTy, FP, Value);
}
+ ActiveField = FP.getFieldDesc()->asFieldDecl();
break;
}
}
@@ -288,7 +439,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
APValue &Value = R.getStructField(I);
if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
- TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue());
+ TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));
} else {
Ok &= Composite(FieldTy, FP, Value);
}
@@ -326,7 +477,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
APValue &Slot = R.getArrayInitializedElt(I);
const Pointer &EP = Ptr.atIndex(I);
if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
- TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue());
+ TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue(ASTCtx));
} else {
Ok &= Composite(ElemTy, EP.narrow(), Slot);
}
@@ -337,10 +488,10 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
// Complex types.
if (const auto *CT = Ty->getAs<ComplexType>()) {
QualType ElemTy = CT->getElementType();
- std::optional<PrimType> ElemT = Ctx.classify(ElemTy);
- assert(ElemT);
if (ElemTy->isIntegerType()) {
+ std::optional<PrimType> ElemT = Ctx.classify(ElemTy);
+ assert(ElemT);
INT_TYPE_SWITCH(*ElemT, {
auto V1 = Ptr.atIndex(0).deref<T>();
auto V2 = Ptr.atIndex(1).deref<T>();
@@ -355,16 +506,41 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
return false;
}
+ // Vector types.
+ if (const auto *VT = Ty->getAs<VectorType>()) {
+ assert(Ptr.getFieldDesc()->isPrimitiveArray());
+ QualType ElemTy = VT->getElementType();
+ PrimType ElemT = *Ctx.classify(ElemTy);
+
+ SmallVector<APValue> Values;
+ Values.reserve(VT->getNumElements());
+ for (unsigned I = 0; I != VT->getNumElements(); ++I) {
+ TYPE_SWITCH(ElemT, {
+ Values.push_back(Ptr.atIndex(I).deref<T>().toAPValue(ASTCtx));
+ });
+ }
+
+ assert(Values.size() == VT->getNumElements());
+ R = APValue(Values.data(), Values.size());
+ return true;
+ }
+
llvm_unreachable("invalid value to return");
};
- if (isZero())
- return APValue(static_cast<Expr *>(nullptr), CharUnits::Zero(), {}, false,
- true);
-
- if (isDummy() || !isLive())
+ // Invalid to read from.
+ if (isDummy() || !isLive() || isPastEnd())
return std::nullopt;
+ // We can return these as rvalues, but we can't deref() them.
+ if (isZero() || isIntegralPointer())
+ return toAPValue(ASTCtx);
+
+ // Just load primitive types.
+ if (std::optional<PrimType> T = Ctx.classify(ResultType)) {
+ TYPE_SWITCH(*T, return this->deref<T>().toAPValue(ASTCtx));
+ }
+
// Return the composite type.
APValue Result;
if (!Composite(getType(), *this, Result))