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