diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp | 404 |
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)) |