954 lines · cpp
1//===--- Pointer.cpp - Types for the constexpr VM ---------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Pointer.h"10#include "Boolean.h"11#include "Context.h"12#include "Floating.h"13#include "Function.h"14#include "Integral.h"15#include "InterpBlock.h"16#include "MemberPointer.h"17#include "PrimType.h"18#include "Record.h"19#include "clang/AST/Expr.h"20#include "clang/AST/ExprCXX.h"21#include "clang/AST/RecordLayout.h"22 23using namespace clang;24using namespace clang::interp;25 26Pointer::Pointer(Block *Pointee)27 : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),28 Pointee->getDescriptor()->getMetadataSize()) {}29 30Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)31 : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}32 33Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)34 : Offset(Offset), StorageKind(Storage::Block) {35 assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");36 assert(Base >= Pointee->getDescriptor()->getMetadataSize());37 38 BS = {Pointee, Base, nullptr, nullptr};39 40 if (Pointee)41 Pointee->addPointer(this);42}43 44Pointer::Pointer(const Pointer &P)45 : Offset(P.Offset), StorageKind(P.StorageKind) {46 switch (StorageKind) {47 case Storage::Int:48 Int = P.Int;49 break;50 case Storage::Block:51 BS = P.BS;52 if (BS.Pointee)53 BS.Pointee->addPointer(this);54 break;55 case Storage::Fn:56 Fn = P.Fn;57 break;58 case Storage::Typeid:59 Typeid = P.Typeid;60 break;61 }62}63 64Pointer::Pointer(Pointer &&P) : Offset(P.Offset), StorageKind(P.StorageKind) {65 switch (StorageKind) {66 case Storage::Int:67 Int = P.Int;68 break;69 case Storage::Block:70 BS = P.BS;71 if (BS.Pointee)72 BS.Pointee->replacePointer(&P, this);73 break;74 case Storage::Fn:75 Fn = P.Fn;76 break;77 case Storage::Typeid:78 Typeid = P.Typeid;79 break;80 }81}82 83Pointer::~Pointer() {84 if (!isBlockPointer())85 return;86 87 if (Block *Pointee = BS.Pointee) {88 Pointee->removePointer(this);89 BS.Pointee = nullptr;90 Pointee->cleanup();91 }92}93 94Pointer &Pointer::operator=(const Pointer &P) {95 // If the current storage type is Block, we need to remove96 // this pointer from the block.97 if (isBlockPointer()) {98 if (P.isBlockPointer() && this->block() == P.block()) {99 Offset = P.Offset;100 BS.Base = P.BS.Base;101 return *this;102 }103 104 if (Block *Pointee = BS.Pointee) {105 Pointee->removePointer(this);106 BS.Pointee = nullptr;107 Pointee->cleanup();108 }109 }110 111 StorageKind = P.StorageKind;112 Offset = P.Offset;113 114 switch (StorageKind) {115 case Storage::Int:116 Int = P.Int;117 break;118 case Storage::Block:119 BS = P.BS;120 121 if (BS.Pointee)122 BS.Pointee->addPointer(this);123 break;124 case Storage::Fn:125 Fn = P.Fn;126 break;127 case Storage::Typeid:128 Typeid = P.Typeid;129 }130 return *this;131}132 133Pointer &Pointer::operator=(Pointer &&P) {134 // If the current storage type is Block, we need to remove135 // this pointer from the block.136 if (isBlockPointer()) {137 if (P.isBlockPointer() && this->block() == P.block()) {138 Offset = P.Offset;139 BS.Base = P.BS.Base;140 return *this;141 }142 143 if (Block *Pointee = BS.Pointee) {144 Pointee->removePointer(this);145 BS.Pointee = nullptr;146 Pointee->cleanup();147 }148 }149 150 StorageKind = P.StorageKind;151 Offset = P.Offset;152 153 switch (StorageKind) {154 case Storage::Int:155 Int = P.Int;156 break;157 case Storage::Block:158 BS = P.BS;159 160 if (BS.Pointee)161 BS.Pointee->addPointer(this);162 break;163 case Storage::Fn:164 Fn = P.Fn;165 break;166 case Storage::Typeid:167 Typeid = P.Typeid;168 }169 return *this;170}171 172APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {173 llvm::SmallVector<APValue::LValuePathEntry, 5> Path;174 175 if (isZero())176 return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,177 /*IsOnePastEnd=*/false, /*IsNullPtr=*/true);178 if (isIntegralPointer())179 return APValue(static_cast<const Expr *>(nullptr),180 CharUnits::fromQuantity(asIntPointer().Value + this->Offset),181 Path,182 /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);183 if (isFunctionPointer()) {184 const FunctionPointer &FP = asFunctionPointer();185 if (const FunctionDecl *FD = FP.getFunction()->getDecl())186 return APValue(FD, CharUnits::fromQuantity(Offset), {},187 /*OnePastTheEnd=*/false, /*IsNull=*/false);188 return APValue(FP.getFunction()->getExpr(), CharUnits::fromQuantity(Offset),189 {},190 /*OnePastTheEnd=*/false, /*IsNull=*/false);191 }192 193 if (isTypeidPointer()) {194 TypeInfoLValue TypeInfo(Typeid.TypePtr);195 return APValue(APValue::LValueBase::getTypeInfo(196 TypeInfo, QualType(Typeid.TypeInfoType, 0)),197 CharUnits::Zero(), {},198 /*OnePastTheEnd=*/false, /*IsNull=*/false);199 }200 201 // Build the lvalue base from the block.202 const Descriptor *Desc = getDeclDesc();203 APValue::LValueBase Base;204 if (const auto *VD = Desc->asValueDecl())205 Base = VD;206 else if (const auto *E = Desc->asExpr()) {207 if (block()->isDynamic()) {208 QualType AllocatedType = getDeclPtr().getFieldDesc()->getDataType(ASTCtx);209 DynamicAllocLValue DA(*block()->DynAllocId);210 Base = APValue::LValueBase::getDynamicAlloc(DA, AllocatedType);211 } else {212 Base = E;213 }214 } else215 llvm_unreachable("Invalid allocation type");216 217 if (isUnknownSizeArray())218 return APValue(Base, CharUnits::Zero(), Path,219 /*IsOnePastEnd=*/isOnePastEnd(), /*IsNullPtr=*/false);220 221 CharUnits Offset = CharUnits::Zero();222 223 auto getFieldOffset = [&](const FieldDecl *FD) -> CharUnits {224 // This shouldn't happen, but if it does, don't crash inside225 // getASTRecordLayout.226 if (FD->getParent()->isInvalidDecl())227 return CharUnits::Zero();228 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());229 unsigned FieldIndex = FD->getFieldIndex();230 return ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex));231 };232 233 bool UsePath = true;234 if (const ValueDecl *VD = getDeclDesc()->asValueDecl();235 VD && VD->getType()->isReferenceType())236 UsePath = false;237 238 // Build the path into the object.239 bool OnePastEnd = isOnePastEnd() && !isZeroSizeArray();240 Pointer Ptr = *this;241 while (Ptr.isField() || Ptr.isArrayElement()) {242 243 if (Ptr.isArrayRoot()) {244 // An array root may still be an array element itself.245 if (Ptr.isArrayElement()) {246 Ptr = Ptr.expand();247 const Descriptor *Desc = Ptr.getFieldDesc();248 unsigned Index = Ptr.getIndex();249 QualType ElemType = Desc->getElemQualType();250 Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType));251 if (Ptr.getArray().getType()->isArrayType())252 Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index));253 Ptr = Ptr.getArray();254 } else {255 const Descriptor *Desc = Ptr.getFieldDesc();256 const auto *Dcl = Desc->asDecl();257 Path.push_back(APValue::LValuePathEntry({Dcl, /*IsVirtual=*/false}));258 259 if (const auto *FD = dyn_cast_if_present<FieldDecl>(Dcl))260 Offset += getFieldOffset(FD);261 262 Ptr = Ptr.getBase();263 }264 } else if (Ptr.isArrayElement()) {265 Ptr = Ptr.expand();266 const Descriptor *Desc = Ptr.getFieldDesc();267 unsigned Index;268 if (Ptr.isOnePastEnd()) {269 Index = Ptr.getArray().getNumElems();270 OnePastEnd = false;271 } else272 Index = Ptr.getIndex();273 274 QualType ElemType = Desc->getElemQualType();275 if (const auto *RD = ElemType->getAsRecordDecl();276 RD && !RD->getDefinition()) {277 // Ignore this for the offset.278 } else {279 Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType));280 }281 if (Ptr.getArray().getType()->isArrayType())282 Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index));283 Ptr = Ptr.getArray();284 } else {285 const Descriptor *Desc = Ptr.getFieldDesc();286 287 // Create a path entry for the field.288 if (const auto *BaseOrMember = Desc->asDecl()) {289 bool IsVirtual = false;290 if (const auto *FD = dyn_cast<FieldDecl>(BaseOrMember)) {291 Ptr = Ptr.getBase();292 Offset += getFieldOffset(FD);293 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) {294 IsVirtual = Ptr.isVirtualBaseClass();295 Ptr = Ptr.getBase();296 const Record *BaseRecord = Ptr.getRecord();297 298 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(299 cast<CXXRecordDecl>(BaseRecord->getDecl()));300 if (IsVirtual)301 Offset += Layout.getVBaseClassOffset(RD);302 else303 Offset += Layout.getBaseClassOffset(RD);304 305 } else {306 Ptr = Ptr.getBase();307 }308 Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));309 continue;310 }311 llvm_unreachable("Invalid field type");312 }313 }314 315 // We assemble the LValuePath starting from the innermost pointer to the316 // outermost one. SO in a.b.c, the first element in Path will refer to317 // the field 'c', while later code expects it to refer to 'a'.318 // Just invert the order of the elements.319 std::reverse(Path.begin(), Path.end());320 321 if (UsePath)322 return APValue(Base, Offset, Path, OnePastEnd);323 324 return APValue(Base, Offset, APValue::NoLValuePath());325}326 327void Pointer::print(llvm::raw_ostream &OS) const {328 switch (StorageKind) {329 case Storage::Block: {330 const Block *B = BS.Pointee;331 OS << "(Block) " << B << " {";332 333 if (isRoot())334 OS << "rootptr(" << BS.Base << "), ";335 else336 OS << BS.Base << ", ";337 338 if (isElementPastEnd())339 OS << "pastend, ";340 else341 OS << Offset << ", ";342 343 if (B)344 OS << B->getSize();345 else346 OS << "nullptr";347 OS << "}";348 } break;349 case Storage::Int:350 OS << "(Int) {";351 OS << Int.Value << " + " << Offset << ", " << Int.Desc;352 OS << "}";353 break;354 case Storage::Fn:355 OS << "(Fn) { " << asFunctionPointer().getFunction() << " + " << Offset356 << " }";357 break;358 case Storage::Typeid:359 OS << "(Typeid) { " << (const void *)asTypeidPointer().TypePtr << ", "360 << (const void *)asTypeidPointer().TypeInfoType << " + " << Offset361 << "}";362 }363}364 365size_t Pointer::computeOffsetForComparison() const {366 switch (StorageKind) {367 case Storage::Int:368 return Int.Value + Offset;369 case Storage::Block:370 // See below.371 break;372 case Storage::Fn:373 return Fn.getIntegerRepresentation() + Offset;374 case Storage::Typeid:375 return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset;376 }377 378 size_t Result = 0;379 Pointer P = *this;380 while (true) {381 382 if (P.isVirtualBaseClass()) {383 Result += getInlineDesc()->Offset;384 P = P.getBase();385 continue;386 }387 388 if (P.isBaseClass()) {389 if (P.getRecord()->getNumVirtualBases() > 0)390 Result += P.getInlineDesc()->Offset;391 P = P.getBase();392 continue;393 }394 if (P.isArrayElement()) {395 P = P.expand();396 Result += (P.getIndex() * P.elemSize());397 P = P.getArray();398 continue;399 }400 401 if (P.isRoot()) {402 if (P.isOnePastEnd())403 ++Result;404 break;405 }406 407 if (const Record *R = P.getBase().getRecord(); R && R->isUnion()) {408 if (P.isOnePastEnd())409 ++Result;410 // Direct child of a union - all have offset 0.411 P = P.getBase();412 continue;413 }414 415 // Fields, etc.416 Result += P.getInlineDesc()->Offset;417 if (P.isOnePastEnd())418 ++Result;419 420 P = P.getBase();421 if (P.isRoot())422 break;423 }424 425 return Result;426}427 428std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {429 if (isZero())430 return "nullptr";431 432 if (isIntegralPointer())433 return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();434 435 if (isFunctionPointer())436 return asFunctionPointer().toDiagnosticString(Ctx);437 438 return toAPValue(Ctx).getAsString(Ctx, getType());439}440 441bool Pointer::isInitialized() const {442 if (!isBlockPointer())443 return true;444 445 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&446 Offset == BS.Base) {447 const GlobalInlineDescriptor &GD =448 *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData());449 return GD.InitState == GlobalInitState::Initialized;450 }451 452 assert(BS.Pointee && "Cannot check if null pointer was initialized");453 const Descriptor *Desc = getFieldDesc();454 assert(Desc);455 if (Desc->isPrimitiveArray())456 return isElementInitialized(getIndex());457 458 if (asBlockPointer().Base == 0)459 return true;460 // Field has its bit in an inline descriptor.461 return getInlineDesc()->IsInitialized;462}463 464bool Pointer::isElementInitialized(unsigned Index) const {465 if (!isBlockPointer())466 return true;467 468 const Descriptor *Desc = getFieldDesc();469 assert(Desc);470 471 if (isStatic() && BS.Base == 0)472 return true;473 474 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&475 Offset == BS.Base) {476 const GlobalInlineDescriptor &GD =477 *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData());478 return GD.InitState == GlobalInitState::Initialized;479 }480 481 if (Desc->isPrimitiveArray()) {482 InitMapPtr &IM = getInitMap();483 if (!IM)484 return false;485 486 if (IM->first)487 return true;488 489 return IM->second->isElementInitialized(Index);490 }491 return isInitialized();492}493 494void Pointer::initialize() const {495 if (!isBlockPointer())496 return;497 498 assert(BS.Pointee && "Cannot initialize null pointer");499 500 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&501 Offset == BS.Base) {502 GlobalInlineDescriptor &GD = *reinterpret_cast<GlobalInlineDescriptor *>(503 asBlockPointer().Pointee->rawData());504 GD.InitState = GlobalInitState::Initialized;505 return;506 }507 508 const Descriptor *Desc = getFieldDesc();509 assert(Desc);510 if (Desc->isPrimitiveArray()) {511 if (Desc->getNumElems() != 0)512 initializeElement(getIndex());513 return;514 }515 516 // Field has its bit in an inline descriptor.517 assert(BS.Base != 0 && "Only composite fields can be initialised");518 getInlineDesc()->IsInitialized = true;519}520 521void Pointer::initializeElement(unsigned Index) const {522 // Primitive global arrays don't have an initmap.523 if (isStatic() && BS.Base == 0)524 return;525 526 assert(Index < getFieldDesc()->getNumElems());527 528 InitMapPtr &IM = getInitMap();529 if (!IM) {530 const Descriptor *Desc = getFieldDesc();531 IM = std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems()));532 }533 534 assert(IM);535 536 // All initialized.537 if (IM->first)538 return;539 540 if (IM->second->initializeElement(Index)) {541 IM->first = true;542 IM->second.reset();543 }544}545 546void Pointer::initializeAllElements() const {547 assert(getFieldDesc()->isPrimitiveArray());548 assert(isArrayRoot());549 550 InitMapPtr &IM = getInitMap();551 if (!IM) {552 IM = std::make_pair(true, nullptr);553 } else {554 IM->first = true;555 IM->second.reset();556 }557}558 559bool Pointer::allElementsInitialized() const {560 assert(getFieldDesc()->isPrimitiveArray());561 assert(isArrayRoot());562 563 if (isStatic() && BS.Base == 0)564 return true;565 566 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor) &&567 Offset == BS.Base) {568 const GlobalInlineDescriptor &GD =569 *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData());570 return GD.InitState == GlobalInitState::Initialized;571 }572 573 InitMapPtr &IM = getInitMap();574 return IM && IM->first;575}576 577void Pointer::activate() const {578 // Field has its bit in an inline descriptor.579 assert(BS.Base != 0 && "Only composite fields can be activated");580 581 if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor))582 return;583 if (!getInlineDesc()->InUnion)584 return;585 586 std::function<void(Pointer &)> activate;587 activate = [&activate](Pointer &P) -> void {588 P.getInlineDesc()->IsActive = true;589 if (const Record *R = P.getRecord(); R && !R->isUnion()) {590 for (const Record::Field &F : R->fields()) {591 Pointer FieldPtr = P.atField(F.Offset);592 if (!FieldPtr.getInlineDesc()->IsActive)593 activate(FieldPtr);594 }595 // FIXME: Bases?596 }597 };598 599 std::function<void(Pointer &)> deactivate;600 deactivate = [&deactivate](Pointer &P) -> void {601 P.getInlineDesc()->IsActive = false;602 603 if (const Record *R = P.getRecord()) {604 for (const Record::Field &F : R->fields()) {605 Pointer FieldPtr = P.atField(F.Offset);606 if (FieldPtr.getInlineDesc()->IsActive)607 deactivate(FieldPtr);608 }609 // FIXME: Bases?610 }611 };612 613 Pointer B = *this;614 while (!B.isRoot() && B.inUnion()) {615 activate(B);616 617 // When walking up the pointer chain, deactivate618 // all union child pointers that aren't on our path.619 Pointer Cur = B;620 B = B.getBase();621 if (const Record *BR = B.getRecord(); BR && BR->isUnion()) {622 for (const Record::Field &F : BR->fields()) {623 Pointer FieldPtr = B.atField(F.Offset);624 if (FieldPtr != Cur)625 deactivate(FieldPtr);626 }627 }628 }629}630 631void Pointer::deactivate() const {632 // TODO: this only appears in constructors, so nothing to deactivate.633}634 635bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {636 // Two null pointers always have the same base.637 if (A.isZero() && B.isZero())638 return true;639 640 if (A.isIntegralPointer() && B.isIntegralPointer())641 return true;642 if (A.isFunctionPointer() && B.isFunctionPointer())643 return true;644 if (A.isTypeidPointer() && B.isTypeidPointer())645 return true;646 647 if (A.StorageKind != B.StorageKind)648 return false;649 650 return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee;651}652 653bool Pointer::pointToSameBlock(const Pointer &A, const Pointer &B) {654 if (!A.isBlockPointer() || !B.isBlockPointer())655 return false;656 return A.block() == B.block();657}658 659bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {660 return hasSameBase(A, B) && A.BS.Base == B.BS.Base &&661 A.getFieldDesc()->IsArray;662}663 664bool Pointer::pointsToLiteral() const {665 if (isZero() || !isBlockPointer())666 return false;667 668 if (block()->isDynamic())669 return false;670 671 const Expr *E = block()->getDescriptor()->asExpr();672 return E && !isa<MaterializeTemporaryExpr, StringLiteral>(E);673}674 675bool Pointer::pointsToStringLiteral() const {676 if (isZero() || !isBlockPointer())677 return false;678 679 if (block()->isDynamic())680 return false;681 682 const Expr *E = block()->getDescriptor()->asExpr();683 return isa_and_nonnull<StringLiteral>(E);684}685 686std::optional<std::pair<Pointer, Pointer>>687Pointer::computeSplitPoint(const Pointer &A, const Pointer &B) {688 if (!A.isBlockPointer() || !B.isBlockPointer())689 return std::nullopt;690 691 if (A.asBlockPointer().Pointee != B.asBlockPointer().Pointee)692 return std::nullopt;693 if (A.isRoot() && B.isRoot())694 return std::nullopt;695 696 if (A == B)697 return std::make_pair(A, B);698 699 auto getBase = [](const Pointer &P) -> Pointer {700 if (P.isArrayElement())701 return P.expand().getArray();702 return P.getBase();703 };704 705 Pointer IterA = A;706 Pointer IterB = B;707 Pointer CurA = IterA;708 Pointer CurB = IterB;709 for (;;) {710 if (IterA.asBlockPointer().Base > IterB.asBlockPointer().Base) {711 CurA = IterA;712 IterA = getBase(IterA);713 } else {714 CurB = IterB;715 IterB = getBase(IterB);716 }717 718 if (IterA == IterB)719 return std::make_pair(CurA, CurB);720 721 if (IterA.isRoot() && IterB.isRoot())722 return std::nullopt;723 }724 725 llvm_unreachable("The loop above should've returned.");726}727 728std::optional<APValue> Pointer::toRValue(const Context &Ctx,729 QualType ResultType) const {730 const ASTContext &ASTCtx = Ctx.getASTContext();731 assert(!ResultType.isNull());732 // Method to recursively traverse composites.733 std::function<bool(QualType, const Pointer &, APValue &)> Composite;734 Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr,735 APValue &R) {736 if (const auto *AT = Ty->getAs<AtomicType>())737 Ty = AT->getValueType();738 739 // Invalid pointers.740 if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||741 Ptr.isPastEnd())742 return false;743 744 // Primitive values.745 if (OptPrimType T = Ctx.classify(Ty)) {746 TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue(ASTCtx));747 return true;748 }749 750 if (const auto *RT = Ty->getAsCanonical<RecordType>()) {751 const auto *Record = Ptr.getRecord();752 assert(Record && "Missing record descriptor");753 754 bool Ok = true;755 if (RT->getDecl()->isUnion()) {756 const FieldDecl *ActiveField = nullptr;757 APValue Value;758 for (const auto &F : Record->fields()) {759 const Pointer &FP = Ptr.atField(F.Offset);760 QualType FieldTy = F.Decl->getType();761 if (FP.isActive()) {762 if (OptPrimType T = Ctx.classify(FieldTy)) {763 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));764 } else {765 Ok &= Composite(FieldTy, FP, Value);766 }767 ActiveField = FP.getFieldDesc()->asFieldDecl();768 break;769 }770 }771 R = APValue(ActiveField, Value);772 } else {773 unsigned NF = Record->getNumFields();774 unsigned NB = Record->getNumBases();775 unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();776 777 R = APValue(APValue::UninitStruct(), NB, NF);778 779 for (unsigned I = 0; I < NF; ++I) {780 const Record::Field *FD = Record->getField(I);781 QualType FieldTy = FD->Decl->getType();782 const Pointer &FP = Ptr.atField(FD->Offset);783 APValue &Value = R.getStructField(I);784 785 if (OptPrimType T = Ctx.classify(FieldTy)) {786 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx));787 } else {788 Ok &= Composite(FieldTy, FP, Value);789 }790 }791 792 for (unsigned I = 0; I < NB; ++I) {793 const Record::Base *BD = Record->getBase(I);794 QualType BaseTy = Ctx.getASTContext().getCanonicalTagType(BD->Decl);795 const Pointer &BP = Ptr.atField(BD->Offset);796 Ok &= Composite(BaseTy, BP, R.getStructBase(I));797 }798 799 for (unsigned I = 0; I < NV; ++I) {800 const Record::Base *VD = Record->getVirtualBase(I);801 QualType VirtBaseTy =802 Ctx.getASTContext().getCanonicalTagType(VD->Decl);803 const Pointer &VP = Ptr.atField(VD->Offset);804 Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));805 }806 }807 return Ok;808 }809 810 if (Ty->isIncompleteArrayType()) {811 R = APValue(APValue::UninitArray(), 0, 0);812 return true;813 }814 815 if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {816 const size_t NumElems = Ptr.getNumElems();817 QualType ElemTy = AT->getElementType();818 R = APValue(APValue::UninitArray{}, NumElems, NumElems);819 820 bool Ok = true;821 OptPrimType ElemT = Ctx.classify(ElemTy);822 for (unsigned I = 0; I != NumElems; ++I) {823 APValue &Slot = R.getArrayInitializedElt(I);824 if (ElemT) {825 TYPE_SWITCH(*ElemT, Slot = Ptr.elem<T>(I).toAPValue(ASTCtx));826 } else {827 Ok &= Composite(ElemTy, Ptr.atIndex(I).narrow(), Slot);828 }829 }830 return Ok;831 }832 833 // Complex types.834 if (const auto *CT = Ty->getAs<ComplexType>()) {835 // Can happen via C casts.836 if (!Ptr.getFieldDesc()->isPrimitiveArray())837 return false;838 839 QualType ElemTy = CT->getElementType();840 if (ElemTy->isIntegerType()) {841 OptPrimType ElemT = Ctx.classify(ElemTy);842 assert(ElemT);843 INT_TYPE_SWITCH(*ElemT, {844 auto V1 = Ptr.elem<T>(0);845 auto V2 = Ptr.elem<T>(1);846 R = APValue(V1.toAPSInt(), V2.toAPSInt());847 return true;848 });849 } else if (ElemTy->isFloatingType()) {850 R = APValue(Ptr.elem<Floating>(0).getAPFloat(),851 Ptr.elem<Floating>(1).getAPFloat());852 return true;853 }854 return false;855 }856 857 // Vector types.858 if (const auto *VT = Ty->getAs<VectorType>()) {859 assert(Ptr.getFieldDesc()->isPrimitiveArray());860 QualType ElemTy = VT->getElementType();861 PrimType ElemT = *Ctx.classify(ElemTy);862 863 SmallVector<APValue> Values;864 Values.reserve(VT->getNumElements());865 for (unsigned I = 0; I != VT->getNumElements(); ++I) {866 TYPE_SWITCH(ElemT,867 { Values.push_back(Ptr.elem<T>(I).toAPValue(ASTCtx)); });868 }869 870 assert(Values.size() == VT->getNumElements());871 R = APValue(Values.data(), Values.size());872 return true;873 }874 875 llvm_unreachable("invalid value to return");876 };877 878 // Invalid to read from.879 if (isDummy() || !isLive() || isPastEnd())880 return std::nullopt;881 882 // We can return these as rvalues, but we can't deref() them.883 if (isZero() || isIntegralPointer())884 return toAPValue(ASTCtx);885 886 // Just load primitive types.887 if (OptPrimType T = Ctx.classify(ResultType)) {888 TYPE_SWITCH(*T, return this->deref<T>().toAPValue(ASTCtx));889 }890 891 // Return the composite type.892 APValue Result;893 if (!Composite(ResultType, *this, Result))894 return std::nullopt;895 return Result;896}897 898std::optional<IntPointer> IntPointer::atOffset(const ASTContext &ASTCtx,899 unsigned Offset) const {900 if (!this->Desc)901 return *this;902 const Record *R = this->Desc->ElemRecord;903 if (!R)904 return *this;905 906 const Record::Field *F = nullptr;907 for (auto &It : R->fields()) {908 if (It.Offset == Offset) {909 F = &It;910 break;911 }912 }913 if (!F)914 return *this;915 916 const FieldDecl *FD = F->Decl;917 if (FD->getParent()->isInvalidDecl())918 return std::nullopt;919 920 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent());921 unsigned FieldIndex = FD->getFieldIndex();922 uint64_t FieldOffset =923 ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex))924 .getQuantity();925 return IntPointer{F->Desc, this->Value + FieldOffset};926}927 928IntPointer IntPointer::baseCast(const ASTContext &ASTCtx,929 unsigned BaseOffset) const {930 if (!Desc) {931 assert(Value == 0);932 return *this;933 }934 const Record *R = Desc->ElemRecord;935 const Descriptor *BaseDesc = nullptr;936 937 // This iterates over bases and checks for the proper offset. That's938 // potentially slow but this case really shouldn't happen a lot.939 for (const Record::Base &B : R->bases()) {940 if (B.Offset == BaseOffset) {941 BaseDesc = B.Desc;942 break;943 }944 }945 assert(BaseDesc);946 947 // Adjust the offset value based on the information from the record layout.948 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(R->getDecl());949 CharUnits BaseLayoutOffset =950 Layout.getBaseClassOffset(cast<CXXRecordDecl>(BaseDesc->asDecl()));951 952 return {BaseDesc, Value + BaseLayoutOffset.getQuantity()};953}954