//===--- Pointer.cpp - Types for the constexpr VM ---------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Pointer.h" #include "Boolean.h" #include "Context.h" #include "Floating.h" #include "Function.h" #include "Integral.h" #include "InterpBlock.h" #include "PrimType.h" #include "Record.h" using namespace clang; using namespace clang::interp; Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {} Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset) : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {} Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {} Pointer::Pointer(Pointer &&P) : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) { if (Pointee) Pointee->replacePointer(&P, this); } Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset) : Pointee(Pointee), Base(Base), Offset(Offset) { assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base"); if (Pointee) Pointee->addPointer(this); } Pointer::~Pointer() { if (Pointee) { Pointee->removePointer(this); Pointee->cleanup(); } } void Pointer::operator=(const Pointer &P) { Block *Old = Pointee; if (Pointee) Pointee->removePointer(this); Offset = P.Offset; Base = P.Base; Pointee = P.Pointee; if (Pointee) Pointee->addPointer(this); if (Old) Old->cleanup(); } void Pointer::operator=(Pointer &&P) { Block *Old = Pointee; if (Pointee) Pointee->removePointer(this); Offset = P.Offset; Base = P.Base; Pointee = P.Pointee; if (Pointee) Pointee->replacePointer(&P, this); if (Old) Old->cleanup(); } APValue Pointer::toAPValue() const { APValue::LValueBase Base; llvm::SmallVector Path; CharUnits Offset; bool IsNullPtr; bool IsOnePastEnd; if (isZero()) { Base = static_cast(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(); } 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(); } 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"); } } IsOnePastEnd = isOnePastEnd(); } } // 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); } std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const { if (!Pointee) return "nullptr"; return toAPValue().getAsString(Ctx, getType()); } bool Pointer::isInitialized() const { assert(Pointee && "Cannot check if null pointer was initialized"); const Descriptor *Desc = getFieldDesc(); assert(Desc); if (Desc->isPrimitiveArray()) { if (isStatic() && Base == 0) return true; InitMapPtr &IM = getInitMap(); if (!IM) return false; if (IM->first) return true; return IM->second->isElementInitialized(getIndex()); } // Field has its bit in an inline descriptor. return Base == 0 || getInlineDesc()->IsInitialized; } void Pointer::initialize() const { assert(Pointee && "Cannot initialize null pointer"); const Descriptor *Desc = getFieldDesc(); assert(Desc); if (Desc->isPrimitiveArray()) { // Primitive global arrays don't have an initmap. if (isStatic() && Base == 0) return; InitMapPtr &IM = getInitMap(); if (!IM) IM = std::make_pair(false, std::make_shared(Desc->getNumElems())); assert(IM); // All initialized. if (IM->first) return; if (IM->second->initializeElement(getIndex())) { IM->first = true; IM->second.reset(); } return; } // Field has its bit in an inline descriptor. assert(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"); getInlineDesc()->IsActive = true; } void Pointer::deactivate() const { // TODO: this only appears in constructors, so nothing to deactivate. } bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { return A.Pointee == B.Pointee; } bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) { return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray; } std::optional Pointer::toRValue(const Context &Ctx) const { // Method to recursively traverse composites. std::function Composite; Composite = [&Composite, &Ctx](QualType Ty, const Pointer &Ptr, APValue &R) { if (const auto *AT = Ty->getAs()) Ty = AT->getValueType(); // Invalid pointers. if (Ptr.isDummy() || !Ptr.isLive() || (!Ptr.isUnknownSizeArray() && Ptr.isOnePastEnd())) return false; // Primitive values. if (std::optional T = Ctx.classify(Ty)) { if (T == PT_Ptr || T == PT_FnPtr) { R = Ptr.toAPValue(); } else { TYPE_SWITCH(*T, R = Ptr.deref().toAPValue()); } return true; } if (const auto *RT = Ty->getAs()) { const auto *Record = Ptr.getRecord(); assert(Record && "Missing record descriptor"); bool Ok = true; if (RT->getDecl()->isUnion()) { const FieldDecl *ActiveField = nullptr; APValue Value; for (const auto &F : Record->fields()) { const Pointer &FP = Ptr.atField(F.Offset); QualType FieldTy = F.Decl->getType(); if (FP.isActive()) { if (std::optional T = Ctx.classify(FieldTy)) { TYPE_SWITCH(*T, Value = FP.deref().toAPValue()); } else { Ok &= Composite(FieldTy, FP, Value); } break; } } R = APValue(ActiveField, Value); } else { unsigned NF = Record->getNumFields(); unsigned NB = Record->getNumBases(); unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases(); R = APValue(APValue::UninitStruct(), NB, NF); 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); APValue &Value = R.getStructField(I); if (std::optional T = Ctx.classify(FieldTy)) { TYPE_SWITCH(*T, Value = FP.deref().toAPValue()); } else { Ok &= Composite(FieldTy, FP, Value); } } for (unsigned I = 0; I < NB; ++I) { const Record::Base *BD = Record->getBase(I); QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl); const Pointer &BP = Ptr.atField(BD->Offset); Ok &= Composite(BaseTy, BP, R.getStructBase(I)); } for (unsigned I = 0; I < NV; ++I) { const Record::Base *VD = Record->getVirtualBase(I); QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl); const Pointer &VP = Ptr.atField(VD->Offset); Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I)); } } return Ok; } if (Ty->isIncompleteArrayType()) { R = APValue(APValue::UninitArray(), 0, 0); return true; } if (const auto *AT = Ty->getAsArrayTypeUnsafe()) { const size_t NumElems = Ptr.getNumElems(); QualType ElemTy = AT->getElementType(); R = APValue(APValue::UninitArray{}, NumElems, NumElems); bool Ok = true; for (unsigned I = 0; I < NumElems; ++I) { APValue &Slot = R.getArrayInitializedElt(I); const Pointer &EP = Ptr.atIndex(I); if (std::optional T = Ctx.classify(ElemTy)) { TYPE_SWITCH(*T, Slot = EP.deref().toAPValue()); } else { Ok &= Composite(ElemTy, EP.narrow(), Slot); } } return Ok; } // Complex types. if (const auto *CT = Ty->getAs()) { QualType ElemTy = CT->getElementType(); std::optional ElemT = Ctx.classify(ElemTy); assert(ElemT); if (ElemTy->isIntegerType()) { INT_TYPE_SWITCH(*ElemT, { auto V1 = Ptr.atIndex(0).deref(); auto V2 = Ptr.atIndex(1).deref(); R = APValue(V1.toAPSInt(), V2.toAPSInt()); return true; }); } else if (ElemTy->isFloatingType()) { R = APValue(Ptr.atIndex(0).deref().getAPFloat(), Ptr.atIndex(1).deref().getAPFloat()); return true; } return false; } llvm_unreachable("invalid value to return"); }; if (isZero()) return APValue(static_cast(nullptr), CharUnits::Zero(), {}, false, true); if (isDummy() || !isLive()) return std::nullopt; // Return the composite type. APValue Result; if (!Composite(getType(), *this, Result)) return std::nullopt; return Result; }