Path: blob/main/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp
35291 views
//===--- Pointer.cpp - Types for the constexpr VM ---------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Pointer.h"9#include "Boolean.h"10#include "Context.h"11#include "Floating.h"12#include "Function.h"13#include "Integral.h"14#include "InterpBlock.h"15#include "MemberPointer.h"16#include "PrimType.h"17#include "Record.h"18#include "clang/AST/RecordLayout.h"1920using namespace clang;21using namespace clang::interp;2223Pointer::Pointer(Block *Pointee)24: Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),25Pointee->getDescriptor()->getMetadataSize()) {}2627Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)28: Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}2930Pointer::Pointer(const Pointer &P)31: Offset(P.Offset), PointeeStorage(P.PointeeStorage),32StorageKind(P.StorageKind) {3334if (isBlockPointer() && PointeeStorage.BS.Pointee)35PointeeStorage.BS.Pointee->addPointer(this);36}3738Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)39: Offset(Offset), StorageKind(Storage::Block) {40assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");4142PointeeStorage.BS = {Pointee, Base};4344if (Pointee)45Pointee->addPointer(this);46}4748Pointer::Pointer(Pointer &&P)49: Offset(P.Offset), PointeeStorage(P.PointeeStorage),50StorageKind(P.StorageKind) {5152if (StorageKind == Storage::Block && PointeeStorage.BS.Pointee)53PointeeStorage.BS.Pointee->replacePointer(&P, this);54}5556Pointer::~Pointer() {57if (isIntegralPointer())58return;5960if (Block *Pointee = PointeeStorage.BS.Pointee) {61Pointee->removePointer(this);62Pointee->cleanup();63}64}6566void Pointer::operator=(const Pointer &P) {67// If the current storage type is Block, we need to remove68// this pointer from the block.69bool WasBlockPointer = isBlockPointer();70if (StorageKind == Storage::Block) {71Block *Old = PointeeStorage.BS.Pointee;72if (WasBlockPointer && Old) {73PointeeStorage.BS.Pointee->removePointer(this);74Old->cleanup();75}76}7778StorageKind = P.StorageKind;79Offset = P.Offset;8081if (P.isBlockPointer()) {82PointeeStorage.BS = P.PointeeStorage.BS;83PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;8485if (PointeeStorage.BS.Pointee)86PointeeStorage.BS.Pointee->addPointer(this);87} else if (P.isIntegralPointer()) {88PointeeStorage.Int = P.PointeeStorage.Int;89} else {90assert(false && "Unhandled storage kind");91}92}9394void Pointer::operator=(Pointer &&P) {95// If the current storage type is Block, we need to remove96// this pointer from the block.97bool WasBlockPointer = isBlockPointer();98if (StorageKind == Storage::Block) {99Block *Old = PointeeStorage.BS.Pointee;100if (WasBlockPointer && Old) {101PointeeStorage.BS.Pointee->removePointer(this);102Old->cleanup();103}104}105106StorageKind = P.StorageKind;107Offset = P.Offset;108109if (P.isBlockPointer()) {110PointeeStorage.BS = P.PointeeStorage.BS;111PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;112113if (PointeeStorage.BS.Pointee)114PointeeStorage.BS.Pointee->addPointer(this);115} else if (P.isIntegralPointer()) {116PointeeStorage.Int = P.PointeeStorage.Int;117} else {118assert(false && "Unhandled storage kind");119}120}121122APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {123llvm::SmallVector<APValue::LValuePathEntry, 5> Path;124125if (isZero())126return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,127/*IsOnePastEnd=*/false, /*IsNullPtr=*/true);128if (isIntegralPointer())129return APValue(static_cast<const Expr *>(nullptr),130CharUnits::fromQuantity(asIntPointer().Value + this->Offset),131Path,132/*IsOnePastEnd=*/false, /*IsNullPtr=*/false);133134// Build the lvalue base from the block.135const Descriptor *Desc = getDeclDesc();136APValue::LValueBase Base;137if (const auto *VD = Desc->asValueDecl())138Base = VD;139else if (const auto *E = Desc->asExpr())140Base = E;141else142llvm_unreachable("Invalid allocation type");143144if (isUnknownSizeArray() || Desc->asExpr())145return APValue(Base, CharUnits::Zero(), Path,146/*IsOnePastEnd=*/isOnePastEnd(), /*IsNullPtr=*/false);147148CharUnits Offset = CharUnits::Zero();149150auto getFieldOffset = [&](const FieldDecl *FD) -> CharUnits {151// This shouldn't happen, but if it does, don't crash inside152// getASTRecordLayout.153if (FD->getParent()->isInvalidDecl())154return CharUnits::Zero();155const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());156unsigned FieldIndex = FD->getFieldIndex();157return ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex));158};159160// Build the path into the object.161Pointer Ptr = *this;162while (Ptr.isField() || Ptr.isArrayElement()) {163if (Ptr.isArrayRoot()) {164Path.push_back(APValue::LValuePathEntry(165{Ptr.getFieldDesc()->asDecl(), /*IsVirtual=*/false}));166167if (const auto *FD = dyn_cast<FieldDecl>(Ptr.getFieldDesc()->asDecl()))168Offset += getFieldOffset(FD);169170Ptr = Ptr.getBase();171} else if (Ptr.isArrayElement()) {172unsigned Index;173if (Ptr.isOnePastEnd())174Index = Ptr.getArray().getNumElems();175else176Index = Ptr.getIndex();177178Offset += (Index * ASTCtx.getTypeSizeInChars(Ptr.getType()));179Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index));180Ptr = Ptr.getArray();181} else {182bool IsVirtual = false;183184// Create a path entry for the field.185const Descriptor *Desc = Ptr.getFieldDesc();186if (const auto *BaseOrMember = Desc->asDecl()) {187if (const auto *FD = dyn_cast<FieldDecl>(BaseOrMember)) {188Ptr = Ptr.getBase();189Offset += getFieldOffset(FD);190} else if (const auto *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) {191IsVirtual = Ptr.isVirtualBaseClass();192Ptr = Ptr.getBase();193const Record *BaseRecord = Ptr.getRecord();194195const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(196cast<CXXRecordDecl>(BaseRecord->getDecl()));197if (IsVirtual)198Offset += Layout.getVBaseClassOffset(RD);199else200Offset += Layout.getBaseClassOffset(RD);201202} else {203Ptr = Ptr.getBase();204}205Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));206continue;207}208llvm_unreachable("Invalid field type");209}210}211212// FIXME(perf): We compute the lvalue path above, but we can't supply it213// for dummy pointers (that causes crashes later in CheckConstantExpression).214if (isDummy())215Path.clear();216217// We assemble the LValuePath starting from the innermost pointer to the218// outermost one. SO in a.b.c, the first element in Path will refer to219// the field 'c', while later code expects it to refer to 'a'.220// Just invert the order of the elements.221std::reverse(Path.begin(), Path.end());222223return APValue(Base, Offset, Path, /*IsOnePastEnd=*/isOnePastEnd(),224/*IsNullPtr=*/false);225}226227void Pointer::print(llvm::raw_ostream &OS) const {228OS << PointeeStorage.BS.Pointee << " (";229if (isBlockPointer()) {230const Block *B = PointeeStorage.BS.Pointee;231OS << "Block) {";232233if (isRoot())234OS << "rootptr(" << PointeeStorage.BS.Base << "), ";235else236OS << PointeeStorage.BS.Base << ", ";237238if (isElementPastEnd())239OS << "pastend, ";240else241OS << Offset << ", ";242243if (B)244OS << B->getSize();245else246OS << "nullptr";247} else {248OS << "Int) {";249OS << PointeeStorage.Int.Value << ", " << PointeeStorage.Int.Desc;250}251OS << "}";252}253254std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {255if (isZero())256return "nullptr";257258if (isIntegralPointer())259return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();260261return toAPValue(Ctx).getAsString(Ctx, getType());262}263264bool Pointer::isInitialized() const {265if (isIntegralPointer())266return true;267268if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {269const GlobalInlineDescriptor &GD =270*reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData());271return GD.InitState == GlobalInitState::Initialized;272}273274assert(PointeeStorage.BS.Pointee &&275"Cannot check if null pointer was initialized");276const Descriptor *Desc = getFieldDesc();277assert(Desc);278if (Desc->isPrimitiveArray()) {279if (isStatic() && PointeeStorage.BS.Base == 0)280return true;281282InitMapPtr &IM = getInitMap();283284if (!IM)285return false;286287if (IM->first)288return true;289290return IM->second->isElementInitialized(getIndex());291}292293if (asBlockPointer().Base == 0)294return true;295296// Field has its bit in an inline descriptor.297return getInlineDesc()->IsInitialized;298}299300void Pointer::initialize() const {301if (isIntegralPointer())302return;303304assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer");305const Descriptor *Desc = getFieldDesc();306307if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {308GlobalInlineDescriptor &GD = *reinterpret_cast<GlobalInlineDescriptor *>(309asBlockPointer().Pointee->rawData());310GD.InitState = GlobalInitState::Initialized;311return;312}313314assert(Desc);315if (Desc->isPrimitiveArray()) {316// Primitive global arrays don't have an initmap.317if (isStatic() && PointeeStorage.BS.Base == 0)318return;319320// Nothing to do for these.321if (Desc->getNumElems() == 0)322return;323324InitMapPtr &IM = getInitMap();325if (!IM)326IM =327std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems()));328329assert(IM);330331// All initialized.332if (IM->first)333return;334335if (IM->second->initializeElement(getIndex())) {336IM->first = true;337IM->second.reset();338}339return;340}341342// Field has its bit in an inline descriptor.343assert(PointeeStorage.BS.Base != 0 &&344"Only composite fields can be initialised");345getInlineDesc()->IsInitialized = true;346}347348void Pointer::activate() const {349// Field has its bit in an inline descriptor.350assert(PointeeStorage.BS.Base != 0 &&351"Only composite fields can be initialised");352353if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor))354return;355356getInlineDesc()->IsActive = true;357}358359void Pointer::deactivate() const {360// TODO: this only appears in constructors, so nothing to deactivate.361}362363bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {364// Two null pointers always have the same base.365if (A.isZero() && B.isZero())366return true;367368if (A.isIntegralPointer() && B.isIntegralPointer())369return true;370371if (A.isIntegralPointer() || B.isIntegralPointer())372return A.getSource() == B.getSource();373374return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee;375}376377bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {378return hasSameBase(A, B) &&379A.PointeeStorage.BS.Base == B.PointeeStorage.BS.Base &&380A.getFieldDesc()->IsArray;381}382383std::optional<APValue> Pointer::toRValue(const Context &Ctx,384QualType ResultType) const {385const ASTContext &ASTCtx = Ctx.getASTContext();386assert(!ResultType.isNull());387// Method to recursively traverse composites.388std::function<bool(QualType, const Pointer &, APValue &)> Composite;389Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr,390APValue &R) {391if (const auto *AT = Ty->getAs<AtomicType>())392Ty = AT->getValueType();393394// Invalid pointers.395if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||396Ptr.isPastEnd())397return false;398399// Primitive values.400if (std::optional<PrimType> T = Ctx.classify(Ty)) {401TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue(ASTCtx));402return true;403}404405if (const auto *RT = Ty->getAs<RecordType>()) {406const auto *Record = Ptr.getRecord();407assert(Record && "Missing record descriptor");408409bool Ok = true;410if (RT->getDecl()->isUnion()) {411const FieldDecl *ActiveField = nullptr;412APValue Value;413for (const auto &F : Record->fields()) {414const Pointer &FP = Ptr.atField(F.Offset);415QualType FieldTy = F.Decl->getType();416if (FP.isActive()) {417if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {418TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));419} else {420Ok &= Composite(FieldTy, FP, Value);421}422ActiveField = FP.getFieldDesc()->asFieldDecl();423break;424}425}426R = APValue(ActiveField, Value);427} else {428unsigned NF = Record->getNumFields();429unsigned NB = Record->getNumBases();430unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();431432R = APValue(APValue::UninitStruct(), NB, NF);433434for (unsigned I = 0; I < NF; ++I) {435const Record::Field *FD = Record->getField(I);436QualType FieldTy = FD->Decl->getType();437const Pointer &FP = Ptr.atField(FD->Offset);438APValue &Value = R.getStructField(I);439440if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {441TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));442} else {443Ok &= Composite(FieldTy, FP, Value);444}445}446447for (unsigned I = 0; I < NB; ++I) {448const Record::Base *BD = Record->getBase(I);449QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl);450const Pointer &BP = Ptr.atField(BD->Offset);451Ok &= Composite(BaseTy, BP, R.getStructBase(I));452}453454for (unsigned I = 0; I < NV; ++I) {455const Record::Base *VD = Record->getVirtualBase(I);456QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl);457const Pointer &VP = Ptr.atField(VD->Offset);458Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));459}460}461return Ok;462}463464if (Ty->isIncompleteArrayType()) {465R = APValue(APValue::UninitArray(), 0, 0);466return true;467}468469if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {470const size_t NumElems = Ptr.getNumElems();471QualType ElemTy = AT->getElementType();472R = APValue(APValue::UninitArray{}, NumElems, NumElems);473474bool Ok = true;475for (unsigned I = 0; I < NumElems; ++I) {476APValue &Slot = R.getArrayInitializedElt(I);477const Pointer &EP = Ptr.atIndex(I);478if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {479TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue(ASTCtx));480} else {481Ok &= Composite(ElemTy, EP.narrow(), Slot);482}483}484return Ok;485}486487// Complex types.488if (const auto *CT = Ty->getAs<ComplexType>()) {489QualType ElemTy = CT->getElementType();490491if (ElemTy->isIntegerType()) {492std::optional<PrimType> ElemT = Ctx.classify(ElemTy);493assert(ElemT);494INT_TYPE_SWITCH(*ElemT, {495auto V1 = Ptr.atIndex(0).deref<T>();496auto V2 = Ptr.atIndex(1).deref<T>();497R = APValue(V1.toAPSInt(), V2.toAPSInt());498return true;499});500} else if (ElemTy->isFloatingType()) {501R = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(),502Ptr.atIndex(1).deref<Floating>().getAPFloat());503return true;504}505return false;506}507508// Vector types.509if (const auto *VT = Ty->getAs<VectorType>()) {510assert(Ptr.getFieldDesc()->isPrimitiveArray());511QualType ElemTy = VT->getElementType();512PrimType ElemT = *Ctx.classify(ElemTy);513514SmallVector<APValue> Values;515Values.reserve(VT->getNumElements());516for (unsigned I = 0; I != VT->getNumElements(); ++I) {517TYPE_SWITCH(ElemT, {518Values.push_back(Ptr.atIndex(I).deref<T>().toAPValue(ASTCtx));519});520}521522assert(Values.size() == VT->getNumElements());523R = APValue(Values.data(), Values.size());524return true;525}526527llvm_unreachable("invalid value to return");528};529530// Invalid to read from.531if (isDummy() || !isLive() || isPastEnd())532return std::nullopt;533534// We can return these as rvalues, but we can't deref() them.535if (isZero() || isIntegralPointer())536return toAPValue(ASTCtx);537538// Just load primitive types.539if (std::optional<PrimType> T = Ctx.classify(ResultType)) {540TYPE_SWITCH(*T, return this->deref<T>().toAPValue(ASTCtx));541}542543// Return the composite type.544APValue Result;545if (!Composite(getType(), *this, Result))546return std::nullopt;547return Result;548}549550551