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 | |
22 | using namespace clang; |
23 | using namespace clang::interp; |
24 | |
25 | Pointer::Pointer(Block *Pointee) |
26 | : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(), |
27 | Pointee->getDescriptor()->getMetadataSize()) {} |
28 | |
29 | Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset) |
30 | : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {} |
31 | |
32 | Pointer::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 | |
40 | Pointer::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 | |
50 | Pointer::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 | |
58 | Pointer::~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 | |
69 | void 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 | |
106 | void 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 | |
143 | APValue 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 | |
302 | void 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. |
343 | size_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 | |
374 | std::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 | |
387 | bool 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 | |
423 | void 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 | |
471 | void 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 | |
514 | void Pointer::deactivate() const { |
515 | // TODO: this only appears in constructors, so nothing to deactivate. |
516 | } |
517 | |
518 | bool 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 | |
539 | bool 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 | |
545 | bool 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 | |
551 | bool 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 | |
562 | bool 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 | |
573 | std::optional<std::pair<Pointer, Pointer>> |
574 | Pointer::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 | |
615 | std::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 | |
782 | IntPointer 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 | |
809 | IntPointer 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 | |