Path: blob/main/contrib/llvm-project/clang/lib/AST/ByteCode/Pointer.cpp
213799 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/ExprCXX.h"19#include "clang/AST/RecordLayout.h"2021using namespace clang;22using namespace clang::interp;2324Pointer::Pointer(Block *Pointee)25: Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),26Pointee->getDescriptor()->getMetadataSize()) {}2728Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)29: Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}3031Pointer::Pointer(const Pointer &P)32: Offset(P.Offset), StorageKind(P.StorageKind),33PointeeStorage(P.PointeeStorage) {3435if (isBlockPointer() && PointeeStorage.BS.Pointee)36PointeeStorage.BS.Pointee->addPointer(this);37}3839Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)40: Offset(Offset), StorageKind(Storage::Block) {41assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");4243PointeeStorage.BS = {Pointee, Base};4445if (Pointee)46Pointee->addPointer(this);47}4849Pointer::Pointer(Pointer &&P)50: Offset(P.Offset), StorageKind(P.StorageKind),51PointeeStorage(P.PointeeStorage) {5253if (StorageKind == Storage::Block && PointeeStorage.BS.Pointee)54PointeeStorage.BS.Pointee->replacePointer(&P, this);55}5657Pointer::~Pointer() {58if (!isBlockPointer())59return;6061if (Block *Pointee = PointeeStorage.BS.Pointee) {62Pointee->removePointer(this);63PointeeStorage.BS.Pointee = nullptr;64Pointee->cleanup();65}66}6768void Pointer::operator=(const Pointer &P) {69// If the current storage type is Block, we need to remove70// this pointer from the block.71if (isBlockPointer()) {72if (P.isBlockPointer() && this->block() == P.block()) {73Offset = P.Offset;74PointeeStorage.BS.Base = P.PointeeStorage.BS.Base;75return;76}7778if (Block *Pointee = PointeeStorage.BS.Pointee) {79Pointee->removePointer(this);80PointeeStorage.BS.Pointee = nullptr;81Pointee->cleanup();82}83}8485StorageKind = P.StorageKind;86Offset = P.Offset;8788if (P.isBlockPointer()) {89PointeeStorage.BS = P.PointeeStorage.BS;90PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;9192if (PointeeStorage.BS.Pointee)93PointeeStorage.BS.Pointee->addPointer(this);94} else if (P.isIntegralPointer()) {95PointeeStorage.Int = P.PointeeStorage.Int;96} else if (P.isFunctionPointer()) {97PointeeStorage.Fn = P.PointeeStorage.Fn;98} else if (P.isTypeidPointer()) {99PointeeStorage.Typeid = P.PointeeStorage.Typeid;100} else {101assert(false && "Unhandled storage kind");102}103}104105void Pointer::operator=(Pointer &&P) {106// If the current storage type is Block, we need to remove107// this pointer from the block.108if (isBlockPointer()) {109if (P.isBlockPointer() && this->block() == P.block()) {110Offset = P.Offset;111PointeeStorage.BS.Base = P.PointeeStorage.BS.Base;112return;113}114115if (Block *Pointee = PointeeStorage.BS.Pointee) {116Pointee->removePointer(this);117PointeeStorage.BS.Pointee = nullptr;118Pointee->cleanup();119}120}121122StorageKind = P.StorageKind;123Offset = P.Offset;124125if (P.isBlockPointer()) {126PointeeStorage.BS = P.PointeeStorage.BS;127PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;128129if (PointeeStorage.BS.Pointee)130PointeeStorage.BS.Pointee->addPointer(this);131} else if (P.isIntegralPointer()) {132PointeeStorage.Int = P.PointeeStorage.Int;133} else if (P.isFunctionPointer()) {134PointeeStorage.Fn = P.PointeeStorage.Fn;135} else if (P.isTypeidPointer()) {136PointeeStorage.Typeid = P.PointeeStorage.Typeid;137} else {138assert(false && "Unhandled storage kind");139}140}141142APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {143llvm::SmallVector<APValue::LValuePathEntry, 5> Path;144145if (isZero())146return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,147/*IsOnePastEnd=*/false, /*IsNullPtr=*/true);148if (isIntegralPointer())149return APValue(static_cast<const Expr *>(nullptr),150CharUnits::fromQuantity(asIntPointer().Value + this->Offset),151Path,152/*IsOnePastEnd=*/false, /*IsNullPtr=*/false);153if (isFunctionPointer()) {154const FunctionPointer &FP = asFunctionPointer();155if (const FunctionDecl *FD = FP.getFunction()->getDecl())156return APValue(FD, CharUnits::fromQuantity(Offset), {},157/*OnePastTheEnd=*/false, /*IsNull=*/false);158return APValue(FP.getFunction()->getExpr(), CharUnits::fromQuantity(Offset),159{},160/*OnePastTheEnd=*/false, /*IsNull=*/false);161}162163if (isTypeidPointer()) {164TypeInfoLValue TypeInfo(PointeeStorage.Typeid.TypePtr);165return APValue(166APValue::LValueBase::getTypeInfo(167TypeInfo, QualType(PointeeStorage.Typeid.TypeInfoType, 0)),168CharUnits::Zero(), {},169/*OnePastTheEnd=*/false, /*IsNull=*/false);170}171172// Build the lvalue base from the block.173const Descriptor *Desc = getDeclDesc();174APValue::LValueBase Base;175if (const auto *VD = Desc->asValueDecl())176Base = VD;177else if (const auto *E = Desc->asExpr()) {178if (block()->isDynamic()) {179QualType AllocatedType = getDeclPtr().getFieldDesc()->getDataType(ASTCtx);180// FIXME: Suboptimal counting of dynamic allocations. Move this to Context181// or InterpState?182static int ReportedDynamicAllocs = 0;183DynamicAllocLValue DA(ReportedDynamicAllocs++);184Base = APValue::LValueBase::getDynamicAlloc(DA, AllocatedType);185} else {186Base = E;187}188} else189llvm_unreachable("Invalid allocation type");190191if (isUnknownSizeArray())192return APValue(Base, CharUnits::Zero(), Path,193/*IsOnePastEnd=*/isOnePastEnd(), /*IsNullPtr=*/false);194195CharUnits Offset = CharUnits::Zero();196197auto getFieldOffset = [&](const FieldDecl *FD) -> CharUnits {198// This shouldn't happen, but if it does, don't crash inside199// getASTRecordLayout.200if (FD->getParent()->isInvalidDecl())201return CharUnits::Zero();202const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());203unsigned FieldIndex = FD->getFieldIndex();204return ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex));205};206207bool UsePath = true;208if (const ValueDecl *VD = getDeclDesc()->asValueDecl();209VD && VD->getType()->isReferenceType())210UsePath = false;211212// Build the path into the object.213bool OnePastEnd = isOnePastEnd();214Pointer Ptr = *this;215while (Ptr.isField() || Ptr.isArrayElement()) {216217if (Ptr.isArrayRoot()) {218// An array root may still be an array element itself.219if (Ptr.isArrayElement()) {220Ptr = Ptr.expand();221const Descriptor *Desc = Ptr.getFieldDesc();222unsigned Index = Ptr.getIndex();223QualType ElemType = Desc->getElemQualType();224Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType));225if (Ptr.getArray().getType()->isArrayType())226Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index));227Ptr = Ptr.getArray();228} else {229const Descriptor *Desc = Ptr.getFieldDesc();230const auto *Dcl = Desc->asDecl();231Path.push_back(APValue::LValuePathEntry({Dcl, /*IsVirtual=*/false}));232233if (const auto *FD = dyn_cast_if_present<FieldDecl>(Dcl))234Offset += getFieldOffset(FD);235236Ptr = Ptr.getBase();237}238} else if (Ptr.isArrayElement()) {239Ptr = Ptr.expand();240const Descriptor *Desc = Ptr.getFieldDesc();241unsigned Index;242if (Ptr.isOnePastEnd()) {243Index = Ptr.getArray().getNumElems();244OnePastEnd = false;245} else246Index = Ptr.getIndex();247248QualType ElemType = Desc->getElemQualType();249if (const auto *RD = ElemType->getAsRecordDecl();250RD && !RD->getDefinition()) {251// Ignore this for the offset.252} else {253Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType));254}255if (Ptr.getArray().getType()->isArrayType())256Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index));257Ptr = Ptr.getArray();258} else {259const Descriptor *Desc = Ptr.getFieldDesc();260bool IsVirtual = false;261262// Create a path entry for the field.263if (const auto *BaseOrMember = Desc->asDecl()) {264if (const auto *FD = dyn_cast<FieldDecl>(BaseOrMember)) {265Ptr = Ptr.getBase();266Offset += getFieldOffset(FD);267} else if (const auto *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) {268IsVirtual = Ptr.isVirtualBaseClass();269Ptr = Ptr.getBase();270const Record *BaseRecord = Ptr.getRecord();271272const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(273cast<CXXRecordDecl>(BaseRecord->getDecl()));274if (IsVirtual)275Offset += Layout.getVBaseClassOffset(RD);276else277Offset += Layout.getBaseClassOffset(RD);278279} else {280Ptr = Ptr.getBase();281}282Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));283continue;284}285llvm_unreachable("Invalid field type");286}287}288289// We assemble the LValuePath starting from the innermost pointer to the290// outermost one. SO in a.b.c, the first element in Path will refer to291// the field 'c', while later code expects it to refer to 'a'.292// Just invert the order of the elements.293std::reverse(Path.begin(), Path.end());294295if (UsePath)296return APValue(Base, Offset, Path, OnePastEnd);297298return APValue(Base, Offset, APValue::NoLValuePath());299}300301void Pointer::print(llvm::raw_ostream &OS) const {302switch (StorageKind) {303case Storage::Block: {304const Block *B = PointeeStorage.BS.Pointee;305OS << "(Block) " << B << " {";306307if (isRoot())308OS << "rootptr(" << PointeeStorage.BS.Base << "), ";309else310OS << PointeeStorage.BS.Base << ", ";311312if (isElementPastEnd())313OS << "pastend, ";314else315OS << Offset << ", ";316317if (B)318OS << B->getSize();319else320OS << "nullptr";321OS << "}";322} break;323case Storage::Int:324OS << "(Int) {";325OS << PointeeStorage.Int.Value << " + " << Offset << ", "326<< PointeeStorage.Int.Desc;327OS << "}";328break;329case Storage::Fn:330OS << "(Fn) { " << asFunctionPointer().getFunction() << " + " << Offset331<< " }";332break;333case Storage::Typeid:334OS << "(Typeid) { " << (const void *)asTypeidPointer().TypePtr << ", "335<< (const void *)asTypeidPointer().TypeInfoType << " + " << Offset336<< "}";337}338}339340size_t Pointer::computeOffsetForComparison() const {341if (isIntegralPointer())342return asIntPointer().Value + Offset;343if (isTypeidPointer())344return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset;345346if (!isBlockPointer())347return Offset;348349size_t Result = 0;350Pointer P = *this;351while (true) {352353if (P.isVirtualBaseClass()) {354Result += getInlineDesc()->Offset;355P = P.getBase();356continue;357}358359if (P.isBaseClass()) {360if (P.getRecord()->getNumVirtualBases() > 0)361Result += P.getInlineDesc()->Offset;362P = P.getBase();363continue;364}365if (P.isArrayElement()) {366P = P.expand();367Result += (P.getIndex() * P.elemSize());368P = P.getArray();369continue;370}371372if (P.isRoot()) {373if (P.isOnePastEnd())374++Result;375break;376}377378if (const Record *R = P.getBase().getRecord(); R && R->isUnion()) {379// Direct child of a union - all have offset 0.380P = P.getBase();381continue;382}383384// Fields, etc.385Result += P.getInlineDesc()->Offset;386if (P.isOnePastEnd())387++Result;388389P = P.getBase();390if (P.isRoot())391break;392}393394return Result;395}396397std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {398if (isZero())399return "nullptr";400401if (isIntegralPointer())402return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();403404if (isFunctionPointer())405return asFunctionPointer().toDiagnosticString(Ctx);406407return toAPValue(Ctx).getAsString(Ctx, getType());408}409410bool Pointer::isInitialized() const {411if (!isBlockPointer())412return true;413414if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {415const GlobalInlineDescriptor &GD =416*reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData());417return GD.InitState == GlobalInitState::Initialized;418}419420assert(PointeeStorage.BS.Pointee &&421"Cannot check if null pointer was initialized");422const Descriptor *Desc = getFieldDesc();423assert(Desc);424if (Desc->isPrimitiveArray()) {425if (isStatic() && PointeeStorage.BS.Base == 0)426return true;427428InitMapPtr &IM = getInitMap();429430if (!IM)431return false;432433if (IM->first)434return true;435436return IM->second->isElementInitialized(getIndex());437}438439if (asBlockPointer().Base == 0)440return true;441442// Field has its bit in an inline descriptor.443return getInlineDesc()->IsInitialized;444}445446void Pointer::initialize() const {447if (!isBlockPointer())448return;449450assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer");451452if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) {453GlobalInlineDescriptor &GD = *reinterpret_cast<GlobalInlineDescriptor *>(454asBlockPointer().Pointee->rawData());455GD.InitState = GlobalInitState::Initialized;456return;457}458459const Descriptor *Desc = getFieldDesc();460assert(Desc);461if (Desc->isPrimitiveArray()) {462// Primitive global arrays don't have an initmap.463if (isStatic() && PointeeStorage.BS.Base == 0)464return;465466// Nothing to do for these.467if (Desc->getNumElems() == 0)468return;469470InitMapPtr &IM = getInitMap();471if (!IM)472IM =473std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems()));474475assert(IM);476477// All initialized.478if (IM->first)479return;480481if (IM->second->initializeElement(getIndex())) {482IM->first = true;483IM->second.reset();484}485return;486}487488// Field has its bit in an inline descriptor.489assert(PointeeStorage.BS.Base != 0 &&490"Only composite fields can be initialised");491getInlineDesc()->IsInitialized = true;492}493494void Pointer::activate() const {495// Field has its bit in an inline descriptor.496assert(PointeeStorage.BS.Base != 0 &&497"Only composite fields can be activated");498499if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor))500return;501if (!getInlineDesc()->InUnion)502return;503504auto activate = [](Pointer &P) -> void {505P.getInlineDesc()->IsActive = true;506};507508std::function<void(Pointer &)> deactivate;509deactivate = [&deactivate](Pointer &P) -> void {510P.getInlineDesc()->IsActive = false;511512if (const Record *R = P.getRecord()) {513for (const Record::Field &F : R->fields()) {514Pointer FieldPtr = P.atField(F.Offset);515if (FieldPtr.getInlineDesc()->IsActive)516deactivate(FieldPtr);517}518// FIXME: Bases?519}520};521522Pointer B = *this;523while (!B.isRoot() && B.inUnion()) {524activate(B);525526// When walking up the pointer chain, deactivate527// all union child pointers that aren't on our path.528Pointer Cur = B;529B = B.getBase();530if (const Record *BR = B.getRecord(); BR && BR->isUnion()) {531for (const Record::Field &F : BR->fields()) {532Pointer FieldPtr = B.atField(F.Offset);533if (FieldPtr != Cur)534deactivate(FieldPtr);535}536}537}538}539540void Pointer::deactivate() const {541// TODO: this only appears in constructors, so nothing to deactivate.542}543544bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {545// Two null pointers always have the same base.546if (A.isZero() && B.isZero())547return true;548549if (A.isIntegralPointer() && B.isIntegralPointer())550return true;551if (A.isFunctionPointer() && B.isFunctionPointer())552return true;553if (A.isTypeidPointer() && B.isTypeidPointer())554return true;555556if (A.isIntegralPointer() || B.isIntegralPointer())557return A.getSource() == B.getSource();558559if (A.StorageKind != B.StorageKind)560return false;561562return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee;563}564565bool Pointer::pointToSameBlock(const Pointer &A, const Pointer &B) {566if (!A.isBlockPointer() || !B.isBlockPointer())567return false;568return A.block() == B.block();569}570571bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {572return hasSameBase(A, B) &&573A.PointeeStorage.BS.Base == B.PointeeStorage.BS.Base &&574A.getFieldDesc()->IsArray;575}576577bool Pointer::pointsToLiteral() const {578if (isZero() || !isBlockPointer())579return false;580581if (block()->isDynamic())582return false;583584const Expr *E = block()->getDescriptor()->asExpr();585return E && !isa<MaterializeTemporaryExpr, StringLiteral>(E);586}587588bool Pointer::pointsToStringLiteral() const {589if (isZero() || !isBlockPointer())590return false;591592if (block()->isDynamic())593return false;594595const Expr *E = block()->getDescriptor()->asExpr();596return E && isa<StringLiteral>(E);597}598599std::optional<std::pair<Pointer, Pointer>>600Pointer::computeSplitPoint(const Pointer &A, const Pointer &B) {601if (!A.isBlockPointer() || !B.isBlockPointer())602return std::nullopt;603604if (A.asBlockPointer().Pointee != B.asBlockPointer().Pointee)605return std::nullopt;606if (A.isRoot() && B.isRoot())607return std::nullopt;608609if (A == B)610return std::make_pair(A, B);611612auto getBase = [](const Pointer &P) -> Pointer {613if (P.isArrayElement())614return P.expand().getArray();615return P.getBase();616};617618Pointer IterA = A;619Pointer IterB = B;620Pointer CurA = IterA;621Pointer CurB = IterB;622for (;;) {623if (IterA.asBlockPointer().Base > IterB.asBlockPointer().Base) {624CurA = IterA;625IterA = getBase(IterA);626} else {627CurB = IterB;628IterB = getBase(IterB);629}630631if (IterA == IterB)632return std::make_pair(CurA, CurB);633634if (IterA.isRoot() && IterB.isRoot())635return std::nullopt;636}637638llvm_unreachable("The loop above should've returned.");639}640641std::optional<APValue> Pointer::toRValue(const Context &Ctx,642QualType ResultType) const {643const ASTContext &ASTCtx = Ctx.getASTContext();644assert(!ResultType.isNull());645// Method to recursively traverse composites.646std::function<bool(QualType, const Pointer &, APValue &)> Composite;647Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr,648APValue &R) {649if (const auto *AT = Ty->getAs<AtomicType>())650Ty = AT->getValueType();651652// Invalid pointers.653if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||654Ptr.isPastEnd())655return false;656657// Primitive values.658if (std::optional<PrimType> T = Ctx.classify(Ty)) {659TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue(ASTCtx));660return true;661}662663if (const auto *RT = Ty->getAs<RecordType>()) {664const auto *Record = Ptr.getRecord();665assert(Record && "Missing record descriptor");666667bool Ok = true;668if (RT->getDecl()->isUnion()) {669const FieldDecl *ActiveField = nullptr;670APValue Value;671for (const auto &F : Record->fields()) {672const Pointer &FP = Ptr.atField(F.Offset);673QualType FieldTy = F.Decl->getType();674if (FP.isActive()) {675if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {676TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));677} else {678Ok &= Composite(FieldTy, FP, Value);679}680ActiveField = FP.getFieldDesc()->asFieldDecl();681break;682}683}684R = APValue(ActiveField, Value);685} else {686unsigned NF = Record->getNumFields();687unsigned NB = Record->getNumBases();688unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();689690R = APValue(APValue::UninitStruct(), NB, NF);691692for (unsigned I = 0; I < NF; ++I) {693const Record::Field *FD = Record->getField(I);694QualType FieldTy = FD->Decl->getType();695const Pointer &FP = Ptr.atField(FD->Offset);696APValue &Value = R.getStructField(I);697698if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {699TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));700} else {701Ok &= Composite(FieldTy, FP, Value);702}703}704705for (unsigned I = 0; I < NB; ++I) {706const Record::Base *BD = Record->getBase(I);707QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl);708const Pointer &BP = Ptr.atField(BD->Offset);709Ok &= Composite(BaseTy, BP, R.getStructBase(I));710}711712for (unsigned I = 0; I < NV; ++I) {713const Record::Base *VD = Record->getVirtualBase(I);714QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl);715const Pointer &VP = Ptr.atField(VD->Offset);716Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));717}718}719return Ok;720}721722if (Ty->isIncompleteArrayType()) {723R = APValue(APValue::UninitArray(), 0, 0);724return true;725}726727if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {728const size_t NumElems = Ptr.getNumElems();729QualType ElemTy = AT->getElementType();730R = APValue(APValue::UninitArray{}, NumElems, NumElems);731732bool Ok = true;733for (unsigned I = 0; I < NumElems; ++I) {734APValue &Slot = R.getArrayInitializedElt(I);735const Pointer &EP = Ptr.atIndex(I);736if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {737TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue(ASTCtx));738} else {739Ok &= Composite(ElemTy, EP.narrow(), Slot);740}741}742return Ok;743}744745// Complex types.746if (const auto *CT = Ty->getAs<ComplexType>()) {747QualType ElemTy = CT->getElementType();748749if (ElemTy->isIntegerType()) {750std::optional<PrimType> ElemT = Ctx.classify(ElemTy);751assert(ElemT);752INT_TYPE_SWITCH(*ElemT, {753auto V1 = Ptr.atIndex(0).deref<T>();754auto V2 = Ptr.atIndex(1).deref<T>();755R = APValue(V1.toAPSInt(), V2.toAPSInt());756return true;757});758} else if (ElemTy->isFloatingType()) {759R = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(),760Ptr.atIndex(1).deref<Floating>().getAPFloat());761return true;762}763return false;764}765766// Vector types.767if (const auto *VT = Ty->getAs<VectorType>()) {768assert(Ptr.getFieldDesc()->isPrimitiveArray());769QualType ElemTy = VT->getElementType();770PrimType ElemT = *Ctx.classify(ElemTy);771772SmallVector<APValue> Values;773Values.reserve(VT->getNumElements());774for (unsigned I = 0; I != VT->getNumElements(); ++I) {775TYPE_SWITCH(ElemT, {776Values.push_back(Ptr.atIndex(I).deref<T>().toAPValue(ASTCtx));777});778}779780assert(Values.size() == VT->getNumElements());781R = APValue(Values.data(), Values.size());782return true;783}784785llvm_unreachable("invalid value to return");786};787788// Invalid to read from.789if (isDummy() || !isLive() || isPastEnd())790return std::nullopt;791792// We can return these as rvalues, but we can't deref() them.793if (isZero() || isIntegralPointer())794return toAPValue(ASTCtx);795796// Just load primitive types.797if (std::optional<PrimType> T = Ctx.classify(ResultType)) {798TYPE_SWITCH(*T, return this->deref<T>().toAPValue(ASTCtx));799}800801// Return the composite type.802APValue Result;803if (!Composite(ResultType, *this, Result))804return std::nullopt;805return Result;806}807808IntPointer IntPointer::atOffset(const ASTContext &ASTCtx,809unsigned Offset) const {810if (!this->Desc)811return *this;812const Record *R = this->Desc->ElemRecord;813if (!R)814return *this;815816const Record::Field *F = nullptr;817for (auto &It : R->fields()) {818if (It.Offset == Offset) {819F = &It;820break;821}822}823if (!F)824return *this;825826const FieldDecl *FD = F->Decl;827const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());828unsigned FieldIndex = FD->getFieldIndex();829uint64_t FieldOffset =830ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex))831.getQuantity();832return IntPointer{F->Desc, this->Value + FieldOffset};833}834835IntPointer IntPointer::baseCast(const ASTContext &ASTCtx,836unsigned BaseOffset) const {837if (!Desc) {838assert(Value == 0);839return *this;840}841const Record *R = Desc->ElemRecord;842const Descriptor *BaseDesc = nullptr;843844// This iterates over bases and checks for the proper offset. That's845// potentially slow but this case really shouldn't happen a lot.846for (const Record::Base &B : R->bases()) {847if (B.Offset == BaseOffset) {848BaseDesc = B.Desc;849break;850}851}852assert(BaseDesc);853854// Adjust the offset value based on the information from the record layout.855const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(R->getDecl());856CharUnits BaseLayoutOffset =857Layout.getBaseClassOffset(cast<CXXRecordDecl>(BaseDesc->asDecl()));858859return {BaseDesc, Value + BaseLayoutOffset.getQuantity()};860}861862863