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 "PrimType.h"
17#include "Record.h"
18
19using namespace clang;
20using namespace clang::interp;
21
22Pointer::Pointer(Block *Pointee)
23 : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),
24 Pointee->getDescriptor()->getMetadataSize()) {}
25
26Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)
27 : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
28
29Pointer::Pointer(const Pointer &P)
30 : Offset(P.Offset), PointeeStorage(P.PointeeStorage),
31 StorageKind(P.StorageKind) {
32
33 if (isBlockPointer() && PointeeStorage.BS.Pointee)
34 PointeeStorage.BS.Pointee->addPointer(P: this);
35}
36
37Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)
38 : Offset(Offset), StorageKind(Storage::Block) {
39 assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
40
41 PointeeStorage.BS = {.Pointee: Pointee, .Base: Base};
42
43 if (Pointee)
44 Pointee->addPointer(P: this);
45}
46
47Pointer::Pointer(Pointer &&P)
48 : Offset(P.Offset), PointeeStorage(P.PointeeStorage),
49 StorageKind(P.StorageKind) {
50
51 if (StorageKind == Storage::Block && PointeeStorage.BS.Pointee)
52 PointeeStorage.BS.Pointee->replacePointer(Old: &P, New: this);
53}
54
55Pointer::~Pointer() {
56 if (isIntegralPointer())
57 return;
58
59 if (PointeeStorage.BS.Pointee) {
60 PointeeStorage.BS.Pointee->removePointer(P: this);
61 PointeeStorage.BS.Pointee->cleanup();
62 }
63}
64
65void Pointer::operator=(const Pointer &P) {
66
67 if (!this->isIntegralPointer() || !P.isBlockPointer())
68 assert(P.StorageKind == StorageKind);
69
70 bool WasBlockPointer = isBlockPointer();
71 StorageKind = P.StorageKind;
72 if (StorageKind == Storage::Block) {
73 Block *Old = PointeeStorage.BS.Pointee;
74 if (WasBlockPointer && PointeeStorage.BS.Pointee)
75 PointeeStorage.BS.Pointee->removePointer(P: this);
76
77 Offset = P.Offset;
78 PointeeStorage.BS = P.PointeeStorage.BS;
79
80 if (PointeeStorage.BS.Pointee)
81 PointeeStorage.BS.Pointee->addPointer(P: this);
82
83 if (WasBlockPointer && Old)
84 Old->cleanup();
85
86 } else if (StorageKind == Storage::Int) {
87 PointeeStorage.Int = P.PointeeStorage.Int;
88 } else {
89 assert(false && "Unhandled storage kind");
90 }
91}
92
93void Pointer::operator=(Pointer &&P) {
94 if (!this->isIntegralPointer() || !P.isBlockPointer())
95 assert(P.StorageKind == StorageKind);
96
97 bool WasBlockPointer = isBlockPointer();
98 StorageKind = P.StorageKind;
99 if (StorageKind == Storage::Block) {
100 Block *Old = PointeeStorage.BS.Pointee;
101 if (WasBlockPointer && PointeeStorage.BS.Pointee)
102 PointeeStorage.BS.Pointee->removePointer(P: this);
103
104 Offset = P.Offset;
105 PointeeStorage.BS = P.PointeeStorage.BS;
106
107 if (PointeeStorage.BS.Pointee)
108 PointeeStorage.BS.Pointee->addPointer(P: this);
109
110 if (WasBlockPointer && Old)
111 Old->cleanup();
112
113 } else if (StorageKind == Storage::Int) {
114 PointeeStorage.Int = P.PointeeStorage.Int;
115 } else {
116 assert(false && "Unhandled storage kind");
117 }
118}
119
120APValue Pointer::toAPValue() const {
121 llvm::SmallVector<APValue::LValuePathEntry, 5> Path;
122
123 if (isZero())
124 return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,
125 /*IsOnePastEnd=*/false, /*IsNullPtr=*/true);
126 if (isIntegralPointer())
127 return APValue(static_cast<const Expr *>(nullptr),
128 CharUnits::fromQuantity(Quantity: asIntPointer().Value + this->Offset),
129 Path,
130 /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
131
132 // Build the lvalue base from the block.
133 const Descriptor *Desc = getDeclDesc();
134 APValue::LValueBase Base;
135 if (const auto *VD = Desc->asValueDecl())
136 Base = VD;
137 else if (const auto *E = Desc->asExpr())
138 Base = E;
139 else
140 llvm_unreachable("Invalid allocation type");
141
142 if (isDummy() || isUnknownSizeArray() || Desc->asExpr())
143 return APValue(Base, CharUnits::Zero(), Path,
144 /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
145
146 // TODO: compute the offset into the object.
147 CharUnits Offset = CharUnits::Zero();
148 bool IsOnePastEnd = isOnePastEnd();
149
150 // Build the path into the object.
151 Pointer Ptr = *this;
152 while (Ptr.isField() || Ptr.isArrayElement()) {
153 if (Ptr.isArrayElement()) {
154 Path.push_back(Elt: APValue::LValuePathEntry::ArrayIndex(Index: Ptr.getIndex()));
155 Ptr = Ptr.getArray();
156 } else {
157 // TODO: figure out if base is virtual
158 bool IsVirtual = false;
159
160 // Create a path entry for the field.
161 const Descriptor *Desc = Ptr.getFieldDesc();
162 if (const auto *BaseOrMember = Desc->asDecl()) {
163 Path.push_back(Elt: APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
164 Ptr = Ptr.getBase();
165 continue;
166 }
167 llvm_unreachable("Invalid field type");
168 }
169 }
170
171 // We assemble the LValuePath starting from the innermost pointer to the
172 // outermost one. SO in a.b.c, the first element in Path will refer to
173 // the field 'c', while later code expects it to refer to 'a'.
174 // Just invert the order of the elements.
175 std::reverse(first: Path.begin(), last: Path.end());
176
177 return APValue(Base, Offset, Path, IsOnePastEnd, /*IsNullPtr=*/false);
178}
179
180void Pointer::print(llvm::raw_ostream &OS) const {
181 OS << PointeeStorage.BS.Pointee << " (";
182 if (isBlockPointer()) {
183 OS << "Block) {";
184
185 if (PointeeStorage.BS.Base == RootPtrMark)
186 OS << "rootptr, ";
187 else
188 OS << PointeeStorage.BS.Base << ", ";
189
190 if (Offset == PastEndMark)
191 OS << "pastend, ";
192 else
193 OS << Offset << ", ";
194
195 if (isBlockPointer() && PointeeStorage.BS.Pointee)
196 OS << PointeeStorage.BS.Pointee->getSize();
197 else
198 OS << "nullptr";
199 } else {
200 OS << "Int) {";
201 OS << PointeeStorage.Int.Value << ", " << PointeeStorage.Int.Desc;
202 }
203 OS << "}";
204}
205
206std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
207 if (isZero())
208 return "nullptr";
209
210 if (isIntegralPointer())
211 return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();
212
213 return toAPValue().getAsString(Ctx, Ty: getType());
214}
215
216bool Pointer::isInitialized() const {
217 if (isIntegralPointer())
218 return true;
219
220 assert(PointeeStorage.BS.Pointee &&
221 "Cannot check if null pointer was initialized");
222 const Descriptor *Desc = getFieldDesc();
223 assert(Desc);
224 if (Desc->isPrimitiveArray()) {
225 if (isStatic() && PointeeStorage.BS.Base == 0)
226 return true;
227
228 InitMapPtr &IM = getInitMap();
229
230 if (!IM)
231 return false;
232
233 if (IM->first)
234 return true;
235
236 return IM->second->isElementInitialized(I: getIndex());
237 }
238
239 // Field has its bit in an inline descriptor.
240 return PointeeStorage.BS.Base == 0 || getInlineDesc()->IsInitialized;
241}
242
243void Pointer::initialize() const {
244 if (isIntegralPointer())
245 return;
246
247 assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer");
248 const Descriptor *Desc = getFieldDesc();
249
250 assert(Desc);
251 if (Desc->isPrimitiveArray()) {
252 // Primitive global arrays don't have an initmap.
253 if (isStatic() && PointeeStorage.BS.Base == 0)
254 return;
255
256 // Nothing to do for these.
257 if (Desc->getNumElems() == 0)
258 return;
259
260 InitMapPtr &IM = getInitMap();
261 if (!IM)
262 IM =
263 std::make_pair(x: false, y: std::make_shared<InitMap>(args: Desc->getNumElems()));
264
265 assert(IM);
266
267 // All initialized.
268 if (IM->first)
269 return;
270
271 if (IM->second->initializeElement(I: getIndex())) {
272 IM->first = true;
273 IM->second.reset();
274 }
275 return;
276 }
277
278 // Field has its bit in an inline descriptor.
279 assert(PointeeStorage.BS.Base != 0 &&
280 "Only composite fields can be initialised");
281 getInlineDesc()->IsInitialized = true;
282}
283
284void Pointer::activate() const {
285 // Field has its bit in an inline descriptor.
286 assert(PointeeStorage.BS.Base != 0 &&
287 "Only composite fields can be initialised");
288 getInlineDesc()->IsActive = true;
289}
290
291void Pointer::deactivate() const {
292 // TODO: this only appears in constructors, so nothing to deactivate.
293}
294
295bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
296 // Two null pointers always have the same base.
297 if (A.isZero() && B.isZero())
298 return true;
299
300 if (A.isIntegralPointer() && B.isIntegralPointer())
301 return true;
302
303 if (A.isIntegralPointer() || B.isIntegralPointer())
304 return A.getSource() == B.getSource();
305
306 return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee;
307}
308
309bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
310 return hasSameBase(A, B) &&
311 A.PointeeStorage.BS.Base == B.PointeeStorage.BS.Base &&
312 A.getFieldDesc()->IsArray;
313}
314
315std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
316 // Method to recursively traverse composites.
317 std::function<bool(QualType, const Pointer &, APValue &)> Composite;
318 Composite = [&Composite, &Ctx](QualType Ty, const Pointer &Ptr, APValue &R) {
319 if (const auto *AT = Ty->getAs<AtomicType>())
320 Ty = AT->getValueType();
321
322 // Invalid pointers.
323 if (Ptr.isDummy() || !Ptr.isLive() ||
324 (!Ptr.isUnknownSizeArray() && Ptr.isOnePastEnd()))
325 return false;
326
327 // Primitive values.
328 if (std::optional<PrimType> T = Ctx.classify(T: Ty)) {
329 TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue());
330 return true;
331 }
332
333 if (const auto *RT = Ty->getAs<RecordType>()) {
334 const auto *Record = Ptr.getRecord();
335 assert(Record && "Missing record descriptor");
336
337 bool Ok = true;
338 if (RT->getDecl()->isUnion()) {
339 const FieldDecl *ActiveField = nullptr;
340 APValue Value;
341 for (const auto &F : Record->fields()) {
342 const Pointer &FP = Ptr.atField(Off: F.Offset);
343 QualType FieldTy = F.Decl->getType();
344 if (FP.isActive()) {
345 if (std::optional<PrimType> T = Ctx.classify(T: FieldTy)) {
346 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue());
347 } else {
348 Ok &= Composite(FieldTy, FP, Value);
349 }
350 break;
351 }
352 }
353 R = APValue(ActiveField, Value);
354 } else {
355 unsigned NF = Record->getNumFields();
356 unsigned NB = Record->getNumBases();
357 unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
358
359 R = APValue(APValue::UninitStruct(), NB, NF);
360
361 for (unsigned I = 0; I < NF; ++I) {
362 const Record::Field *FD = Record->getField(I);
363 QualType FieldTy = FD->Decl->getType();
364 const Pointer &FP = Ptr.atField(Off: FD->Offset);
365 APValue &Value = R.getStructField(i: I);
366
367 if (std::optional<PrimType> T = Ctx.classify(T: FieldTy)) {
368 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue());
369 } else {
370 Ok &= Composite(FieldTy, FP, Value);
371 }
372 }
373
374 for (unsigned I = 0; I < NB; ++I) {
375 const Record::Base *BD = Record->getBase(I);
376 QualType BaseTy = Ctx.getASTContext().getRecordType(Decl: BD->Decl);
377 const Pointer &BP = Ptr.atField(Off: BD->Offset);
378 Ok &= Composite(BaseTy, BP, R.getStructBase(i: I));
379 }
380
381 for (unsigned I = 0; I < NV; ++I) {
382 const Record::Base *VD = Record->getVirtualBase(I);
383 QualType VirtBaseTy = Ctx.getASTContext().getRecordType(Decl: VD->Decl);
384 const Pointer &VP = Ptr.atField(Off: VD->Offset);
385 Ok &= Composite(VirtBaseTy, VP, R.getStructBase(i: NB + I));
386 }
387 }
388 return Ok;
389 }
390
391 if (Ty->isIncompleteArrayType()) {
392 R = APValue(APValue::UninitArray(), 0, 0);
393 return true;
394 }
395
396 if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
397 const size_t NumElems = Ptr.getNumElems();
398 QualType ElemTy = AT->getElementType();
399 R = APValue(APValue::UninitArray{}, NumElems, NumElems);
400
401 bool Ok = true;
402 for (unsigned I = 0; I < NumElems; ++I) {
403 APValue &Slot = R.getArrayInitializedElt(I);
404 const Pointer &EP = Ptr.atIndex(Idx: I);
405 if (std::optional<PrimType> T = Ctx.classify(T: ElemTy)) {
406 TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue());
407 } else {
408 Ok &= Composite(ElemTy, EP.narrow(), Slot);
409 }
410 }
411 return Ok;
412 }
413
414 // Complex types.
415 if (const auto *CT = Ty->getAs<ComplexType>()) {
416 QualType ElemTy = CT->getElementType();
417
418 if (ElemTy->isIntegerType()) {
419 std::optional<PrimType> ElemT = Ctx.classify(T: ElemTy);
420 assert(ElemT);
421 INT_TYPE_SWITCH(*ElemT, {
422 auto V1 = Ptr.atIndex(0).deref<T>();
423 auto V2 = Ptr.atIndex(1).deref<T>();
424 R = APValue(V1.toAPSInt(), V2.toAPSInt());
425 return true;
426 });
427 } else if (ElemTy->isFloatingType()) {
428 R = APValue(Ptr.atIndex(Idx: 0).deref<Floating>().getAPFloat(),
429 Ptr.atIndex(Idx: 1).deref<Floating>().getAPFloat());
430 return true;
431 }
432 return false;
433 }
434
435 // Vector types.
436 if (const auto *VT = Ty->getAs<VectorType>()) {
437 assert(Ptr.getFieldDesc()->isPrimitiveArray());
438 QualType ElemTy = VT->getElementType();
439 PrimType ElemT = *Ctx.classify(T: ElemTy);
440
441 SmallVector<APValue> Values;
442 Values.reserve(N: VT->getNumElements());
443 for (unsigned I = 0; I != VT->getNumElements(); ++I) {
444 TYPE_SWITCH(ElemT, {
445 Values.push_back(Ptr.atIndex(I).deref<T>().toAPValue());
446 });
447 }
448
449 assert(Values.size() == VT->getNumElements());
450 R = APValue(Values.data(), Values.size());
451 return true;
452 }
453
454 llvm_unreachable("invalid value to return");
455 };
456
457 if (isZero())
458 return APValue(static_cast<Expr *>(nullptr), CharUnits::Zero(), {}, false,
459 true);
460
461 if (isDummy() || !isLive())
462 return std::nullopt;
463
464 // Return the composite type.
465 APValue Result;
466 if (!Composite(getType(), *this, Result))
467 return std::nullopt;
468 return Result;
469}
470

source code of clang/lib/AST/Interp/Pointer.cpp