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