clang 19.0.0git
Pointer.cpp
Go to the documentation of this file.
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
20using namespace clang;
21using namespace clang::interp;
22
24 : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),
25 Pointee->getDescriptor()->getMetadataSize()) {}
26
27Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset)
28 : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
29
31 : Offset(P.Offset), PointeeStorage(P.PointeeStorage),
32 StorageKind(P.StorageKind) {
33
34 if (isBlockPointer() && PointeeStorage.BS.Pointee)
35 PointeeStorage.BS.Pointee->addPointer(this);
36}
37
38Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset)
39 : Offset(Offset), StorageKind(Storage::Block) {
40 assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
41
42 PointeeStorage.BS = {Pointee, Base};
43
44 if (Pointee)
45 Pointee->addPointer(this);
46}
47
49 : Offset(P.Offset), PointeeStorage(P.PointeeStorage),
50 StorageKind(P.StorageKind) {
51
52 if (StorageKind == Storage::Block && PointeeStorage.BS.Pointee)
53 PointeeStorage.BS.Pointee->replacePointer(&P, this);
54}
55
58 return;
59
60 if (PointeeStorage.BS.Pointee) {
61 PointeeStorage.BS.Pointee->removePointer(this);
62 PointeeStorage.BS.Pointee->cleanup();
63 }
64}
65
67 // If the current storage type is Block, we need to remove
68 // this pointer from the block.
69 bool WasBlockPointer = isBlockPointer();
70 if (StorageKind == Storage::Block) {
71 Block *Old = PointeeStorage.BS.Pointee;
72 if (WasBlockPointer && Old) {
73 PointeeStorage.BS.Pointee->removePointer(this);
74 Old->cleanup();
75 }
76 }
77
78 StorageKind = P.StorageKind;
79 Offset = P.Offset;
80
81 if (P.isBlockPointer()) {
82 PointeeStorage.BS = P.PointeeStorage.BS;
83 PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;
84
85 if (PointeeStorage.BS.Pointee)
86 PointeeStorage.BS.Pointee->addPointer(this);
87 } else if (P.isIntegralPointer()) {
88 PointeeStorage.Int = P.PointeeStorage.Int;
89 } else {
90 assert(false && "Unhandled storage kind");
91 }
92}
93
95 // If the current storage type is Block, we need to remove
96 // this pointer from the block.
97 bool WasBlockPointer = isBlockPointer();
98 if (StorageKind == Storage::Block) {
99 Block *Old = PointeeStorage.BS.Pointee;
100 if (WasBlockPointer && Old) {
101 PointeeStorage.BS.Pointee->removePointer(this);
102 Old->cleanup();
103 }
104 }
105
106 StorageKind = P.StorageKind;
107 Offset = P.Offset;
108
109 if (P.isBlockPointer()) {
110 PointeeStorage.BS = P.PointeeStorage.BS;
111 PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee;
112
113 if (PointeeStorage.BS.Pointee)
114 PointeeStorage.BS.Pointee->addPointer(this);
115 } else if (P.isIntegralPointer()) {
116 PointeeStorage.Int = P.PointeeStorage.Int;
117 } else {
118 assert(false && "Unhandled storage kind");
119 }
120}
121
124
125 if (isZero())
126 return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,
127 /*IsOnePastEnd=*/false, /*IsNullPtr=*/true);
128 if (isIntegralPointer())
129 return APValue(static_cast<const Expr *>(nullptr),
131 Path,
132 /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
133
134 // Build the lvalue base from the block.
135 const Descriptor *Desc = getDeclDesc();
137 if (const auto *VD = Desc->asValueDecl())
138 Base = VD;
139 else if (const auto *E = Desc->asExpr())
140 Base = E;
141 else
142 llvm_unreachable("Invalid allocation type");
143
144 if (isDummy() || isUnknownSizeArray() || Desc->asExpr())
145 return APValue(Base, CharUnits::Zero(), Path,
146 /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
147
148 // TODO: compute the offset into the object.
149 CharUnits Offset = CharUnits::Zero();
150
151 // Build the path into the object.
152 Pointer Ptr = *this;
153 while (Ptr.isField() || Ptr.isArrayElement()) {
154 if (Ptr.isArrayRoot()) {
155 Path.push_back(APValue::LValuePathEntry::ArrayIndex(0));
156 Ptr = Ptr.getBase();
157 } else if (Ptr.isArrayElement()) {
158 if (Ptr.isOnePastEnd())
160 else
161 Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
162 Ptr = Ptr.getArray();
163 } else {
164 // TODO: figure out if base is virtual
165 bool IsVirtual = false;
166
167 // Create a path entry for the field.
168 const Descriptor *Desc = Ptr.getFieldDesc();
169 if (const auto *BaseOrMember = Desc->asDecl()) {
170 Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
171 Ptr = Ptr.getBase();
172 continue;
173 }
174 llvm_unreachable("Invalid field type");
175 }
176 }
177
178 // We assemble the LValuePath starting from the innermost pointer to the
179 // outermost one. SO in a.b.c, the first element in Path will refer to
180 // the field 'c', while later code expects it to refer to 'a'.
181 // Just invert the order of the elements.
182 std::reverse(Path.begin(), Path.end());
183
184 return APValue(Base, Offset, Path, /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
185}
186
187void Pointer::print(llvm::raw_ostream &OS) const {
188 OS << PointeeStorage.BS.Pointee << " (";
189 if (isBlockPointer()) {
190 OS << "Block) {";
191
192 if (isRoot())
193 OS << "rootptr(" << PointeeStorage.BS.Base << "), ";
194 else
195 OS << PointeeStorage.BS.Base << ", ";
196
197 if (isElementPastEnd())
198 OS << "pastend, ";
199 else
200 OS << Offset << ", ";
201
202 if (PointeeStorage.BS.Pointee)
203 OS << PointeeStorage.BS.Pointee->getSize();
204 else
205 OS << "nullptr";
206 } else {
207 OS << "Int) {";
208 OS << PointeeStorage.Int.Value << ", " << PointeeStorage.Int.Desc;
209 }
210 OS << "}";
211}
212
213std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
214 if (isZero())
215 return "nullptr";
216
217 if (isIntegralPointer())
218 return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str();
219
220 return toAPValue().getAsString(Ctx, getType());
221}
222
224 if (isIntegralPointer())
225 return true;
226
227 assert(PointeeStorage.BS.Pointee &&
228 "Cannot check if null pointer was initialized");
229 const Descriptor *Desc = getFieldDesc();
230 assert(Desc);
231 if (Desc->isPrimitiveArray()) {
232 if (isStatic() && PointeeStorage.BS.Base == 0)
233 return true;
234
235 InitMapPtr &IM = getInitMap();
236
237 if (!IM)
238 return false;
239
240 if (IM->first)
241 return true;
242
243 return IM->second->isElementInitialized(getIndex());
244 }
245
246 // Field has its bit in an inline descriptor.
247 return PointeeStorage.BS.Base == 0 || getInlineDesc()->IsInitialized;
248}
249
251 if (isIntegralPointer())
252 return;
253
254 assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer");
255 const Descriptor *Desc = getFieldDesc();
256
257 assert(Desc);
258 if (Desc->isPrimitiveArray()) {
259 // Primitive global arrays don't have an initmap.
260 if (isStatic() && PointeeStorage.BS.Base == 0)
261 return;
262
263 // Nothing to do for these.
264 if (Desc->getNumElems() == 0)
265 return;
266
267 InitMapPtr &IM = getInitMap();
268 if (!IM)
269 IM =
270 std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems()));
271
272 assert(IM);
273
274 // All initialized.
275 if (IM->first)
276 return;
277
278 if (IM->second->initializeElement(getIndex())) {
279 IM->first = true;
280 IM->second.reset();
281 }
282 return;
283 }
284
285 // Field has its bit in an inline descriptor.
286 assert(PointeeStorage.BS.Base != 0 &&
287 "Only composite fields can be initialised");
288 getInlineDesc()->IsInitialized = true;
289}
290
291void Pointer::activate() const {
292 // Field has its bit in an inline descriptor.
293 assert(PointeeStorage.BS.Base != 0 &&
294 "Only composite fields can be initialised");
295 getInlineDesc()->IsActive = true;
296}
297
299 // TODO: this only appears in constructors, so nothing to deactivate.
300}
301
302bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
303 // Two null pointers always have the same base.
304 if (A.isZero() && B.isZero())
305 return true;
306
308 return true;
309
311 return A.getSource() == B.getSource();
312
314}
315
316bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
317 return hasSameBase(A, B) &&
318 A.PointeeStorage.BS.Base == B.PointeeStorage.BS.Base &&
320}
321
322std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
323 // Method to recursively traverse composites.
324 std::function<bool(QualType, const Pointer &, APValue &)> Composite;
325 Composite = [&Composite, &Ctx](QualType Ty, const Pointer &Ptr, APValue &R) {
326 if (const auto *AT = Ty->getAs<AtomicType>())
327 Ty = AT->getValueType();
328
329 // Invalid pointers.
330 if (Ptr.isDummy() || !Ptr.isLive() ||
331 (!Ptr.isUnknownSizeArray() && Ptr.isOnePastEnd()))
332 return false;
333
334 // Primitive values.
335 if (std::optional<PrimType> T = Ctx.classify(Ty)) {
336 TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue());
337 return true;
338 }
339
340 if (const auto *RT = Ty->getAs<RecordType>()) {
341 const auto *Record = Ptr.getRecord();
342 assert(Record && "Missing record descriptor");
343
344 bool Ok = true;
345 if (RT->getDecl()->isUnion()) {
346 const FieldDecl *ActiveField = nullptr;
348 for (const auto &F : Record->fields()) {
349 const Pointer &FP = Ptr.atField(F.Offset);
350 QualType FieldTy = F.Decl->getType();
351 if (FP.isActive()) {
352 if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
353 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue());
354 } else {
355 Ok &= Composite(FieldTy, FP, Value);
356 }
357 ActiveField = FP.getFieldDesc()->asFieldDecl();
358 break;
359 }
360 }
361 R = APValue(ActiveField, Value);
362 } else {
363 unsigned NF = Record->getNumFields();
364 unsigned NB = Record->getNumBases();
365 unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
366
367 R = APValue(APValue::UninitStruct(), NB, NF);
368
369 for (unsigned I = 0; I < NF; ++I) {
370 const Record::Field *FD = Record->getField(I);
371 QualType FieldTy = FD->Decl->getType();
372 const Pointer &FP = Ptr.atField(FD->Offset);
373 APValue &Value = R.getStructField(I);
374
375 if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
376 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue());
377 } else {
378 Ok &= Composite(FieldTy, FP, Value);
379 }
380 }
381
382 for (unsigned I = 0; I < NB; ++I) {
383 const Record::Base *BD = Record->getBase(I);
384 QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl);
385 const Pointer &BP = Ptr.atField(BD->Offset);
386 Ok &= Composite(BaseTy, BP, R.getStructBase(I));
387 }
388
389 for (unsigned I = 0; I < NV; ++I) {
390 const Record::Base *VD = Record->getVirtualBase(I);
391 QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl);
392 const Pointer &VP = Ptr.atField(VD->Offset);
393 Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));
394 }
395 }
396 return Ok;
397 }
398
399 if (Ty->isIncompleteArrayType()) {
400 R = APValue(APValue::UninitArray(), 0, 0);
401 return true;
402 }
403
404 if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
405 const size_t NumElems = Ptr.getNumElems();
406 QualType ElemTy = AT->getElementType();
407 R = APValue(APValue::UninitArray{}, NumElems, NumElems);
408
409 bool Ok = true;
410 for (unsigned I = 0; I < NumElems; ++I) {
411 APValue &Slot = R.getArrayInitializedElt(I);
412 const Pointer &EP = Ptr.atIndex(I);
413 if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
414 TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue());
415 } else {
416 Ok &= Composite(ElemTy, EP.narrow(), Slot);
417 }
418 }
419 return Ok;
420 }
421
422 // Complex types.
423 if (const auto *CT = Ty->getAs<ComplexType>()) {
424 QualType ElemTy = CT->getElementType();
425
426 if (ElemTy->isIntegerType()) {
427 std::optional<PrimType> ElemT = Ctx.classify(ElemTy);
428 assert(ElemT);
429 INT_TYPE_SWITCH(*ElemT, {
430 auto V1 = Ptr.atIndex(0).deref<T>();
431 auto V2 = Ptr.atIndex(1).deref<T>();
432 R = APValue(V1.toAPSInt(), V2.toAPSInt());
433 return true;
434 });
435 } else if (ElemTy->isFloatingType()) {
436 R = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(),
437 Ptr.atIndex(1).deref<Floating>().getAPFloat());
438 return true;
439 }
440 return false;
441 }
442
443 // Vector types.
444 if (const auto *VT = Ty->getAs<VectorType>()) {
445 assert(Ptr.getFieldDesc()->isPrimitiveArray());
446 QualType ElemTy = VT->getElementType();
447 PrimType ElemT = *Ctx.classify(ElemTy);
448
450 Values.reserve(VT->getNumElements());
451 for (unsigned I = 0; I != VT->getNumElements(); ++I) {
452 TYPE_SWITCH(ElemT, {
453 Values.push_back(Ptr.atIndex(I).deref<T>().toAPValue());
454 });
455 }
456
457 assert(Values.size() == VT->getNumElements());
458 R = APValue(Values.data(), Values.size());
459 return true;
460 }
461
462 llvm_unreachable("invalid value to return");
463 };
464
465 if (isZero())
466 return APValue(static_cast<Expr *>(nullptr), CharUnits::Zero(), {}, false,
467 true);
468
469 if (isDummy() || !isLive())
470 return std::nullopt;
471
472 // Return the composite type.
474 if (!Composite(getType(), *this, Result))
475 return std::nullopt;
476 return Result;
477}
StringRef P
#define INT_TYPE_SWITCH(Expr, B)
Definition: PrimType.h:143
#define TYPE_SWITCH(Expr, B)
Definition: PrimType.h:122
A non-discriminated union of a base, field, or array index.
Definition: APValue.h:208
static LValuePathEntry ArrayIndex(uint64_t Index)
Definition: APValue.h:216
APValue - This class implements a discriminated union of [uninitialized] [APSInt] [APFloat],...
Definition: APValue.h:122
APValue & getArrayInitializedElt(unsigned I)
Definition: APValue.h:510
std::string getAsString(const ASTContext &Ctx, QualType Ty) const
Definition: APValue.cpp:946
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:185
QualType getRecordType(const RecordDecl *Decl) const
CharUnits - This is an opaque type for sizes expressed in character units.
Definition: CharUnits.h:38
static CharUnits fromQuantity(QuantityType Quantity)
fromQuantity - Construct a CharUnits quantity from a raw integer type.
Definition: CharUnits.h:63
static CharUnits Zero()
Zero - Construct a CharUnits quantity of zero.
Definition: CharUnits.h:53
Complex values, per C99 6.2.5p11.
Definition: Type.h:3102
This represents one expression.
Definition: Expr.h:110
Represents a member of a struct/union/class.
Definition: Decl.h:3059
A (possibly-)qualified type.
Definition: Type.h:940
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
Definition: Type.h:5565
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:7965
bool isFloatingType() const
Definition: Type.cpp:2248
Represents a GCC generic vector type.
Definition: Type.h:3985
A memory block, either on the stack or in the heap.
Definition: InterpBlock.h:49
Holds all information required to evaluate constexpr code in a module.
Definition: Context.h:40
ASTContext & getASTContext() const
Returns the AST context.
Definition: Context.h:61
std::optional< PrimType > classify(QualType T) const
Classifies a type.
Definition: Context.cpp:122
const APFloat & getAPFloat() const
Definition: Floating.h:40
A pointer to a memory block, live or dead.
Definition: Pointer.h:80
static bool hasSameBase(const Pointer &A, const Pointer &B)
Checks if two pointers are comparable.
Definition: Pointer.cpp:302
Pointer narrow() const
Restricts the scope of an array element pointer.
Definition: Pointer.h:170
void deactivate() const
Deactivates an entire strurcutre.
Definition: Pointer.cpp:298
bool isInitialized() const
Checks if an object was initialized.
Definition: Pointer.cpp:223
bool isStatic() const
Checks if the storage is static.
Definition: Pointer.h:444
Pointer atIndex(uint64_t Idx) const
Offsets a pointer inside an array.
Definition: Pointer.h:137
bool isDummy() const
Checks if the pointer points to a dummy value.
Definition: Pointer.h:490
void print(llvm::raw_ostream &OS) const
Prints the pointer.
Definition: Pointer.cpp:187
int64_t getIndex() const
Returns the index into an array.
Definition: Pointer.h:533
bool isActive() const
Checks if the object is active.
Definition: Pointer.h:482
Pointer atField(unsigned Off) const
Creates a pointer to a field.
Definition: Pointer.h:154
T & deref() const
Dereferences the pointer, if it's live.
Definition: Pointer.h:569
DeclTy getSource() const
Returns the expression or declaration the pointer has been created for.
Definition: Pointer.h:276
unsigned getNumElems() const
Returns the number of elements.
Definition: Pointer.h:524
Pointer getArray() const
Returns the parent array.
Definition: Pointer.h:294
bool isUnknownSizeArray() const
Checks if the structure is an array of unknown size.
Definition: Pointer.h:386
void activate() const
Activats a field.
Definition: Pointer.cpp:291
void operator=(const Pointer &P)
Definition: Pointer.cpp:66
bool isIntegralPointer() const
Definition: Pointer.h:422
QualType getType() const
Returns the type of the innermost field.
Definition: Pointer.h:315
bool isArrayElement() const
Checks if the pointer points to an array.
Definition: Pointer.h:392
bool isArrayRoot() const
Whether this array refers to an array, but not to the first element.
Definition: Pointer.h:369
bool isLive() const
Checks if the pointer is live.
Definition: Pointer.h:249
Pointer getBase() const
Returns a pointer to the object of which this pointer is a field.
Definition: Pointer.h:285
std::string toDiagnosticString(const ASTContext &Ctx) const
Converts the pointer to a string usable in diagnostics.
Definition: Pointer.cpp:213
bool isZero() const
Checks if the pointer is null.
Definition: Pointer.h:242
const IntPointer & asIntPointer() const
Definition: Pointer.h:417
bool isRoot() const
Pointer points directly to a block.
Definition: Pointer.h:398
const Descriptor * getDeclDesc() const
Accessor for information about the declaration site.
Definition: Pointer.h:265
bool isOnePastEnd() const
Checks if the index is one past end.
Definition: Pointer.h:551
static bool hasSameArray(const Pointer &A, const Pointer &B)
Checks if two pointers can be subtracted.
Definition: Pointer.cpp:316
bool isElementPastEnd() const
Checks if the pointer is an out-of-bounds element pointer.
Definition: Pointer.h:563
std::optional< APValue > toRValue(const Context &Ctx) const
Converts the pointer to an APValue that is an rvalue.
Definition: Pointer.cpp:322
bool isBlockPointer() const
Definition: Pointer.h:421
BlockPointer BS
Definition: Pointer.h:660
APValue toAPValue() const
Converts the pointer to an APValue.
Definition: Pointer.cpp:122
const Descriptor * getFieldDesc() const
Accessors for information about the innermost field.
Definition: Pointer.h:305
const BlockPointer & asBlockPointer() const
Definition: Pointer.h:413
void initialize() const
Initializes a field.
Definition: Pointer.cpp:250
bool isField() const
Checks if the item is a field in an object.
Definition: Pointer.h:255
Structure/Class descriptor.
Definition: Record.h:25
unsigned getNumBases() const
Definition: Record.h:89
const Field * getField(const FieldDecl *FD) const
Returns a field.
Definition: Record.cpp:39
const Base * getVirtualBase(const RecordDecl *RD) const
Returns a virtual base descriptor.
Definition: Record.cpp:59
unsigned getNumFields() const
Definition: Record.h:81
unsigned getNumVirtualBases() const
Definition: Record.h:100
llvm::iterator_range< const_field_iter > fields() const
Definition: Record.h:77
const Base * getBase(const RecordDecl *FD) const
Returns a base descriptor.
Definition: Record.cpp:45
std::optional< std::pair< bool, std::shared_ptr< InitMap > > > InitMapPtr
Definition: Descriptor.h:28
PrimType
Enumeration of the primitive types of the VM.
Definition: PrimType.h:33
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
const FunctionProtoType * T
#define bool
Definition: stdbool.h:24
unsigned Base
Start of the current subfield.
Definition: Pointer.h:41
Block * Pointee
The block the pointer is pointing to.
Definition: Pointer.h:39
Describes a memory block created by an allocation site.
Definition: Descriptor.h:91
unsigned getNumElems() const
Returns the number of elements stored in the block.
Definition: Descriptor.h:211
const ValueDecl * asValueDecl() const
Definition: Descriptor.h:176
const Decl * asDecl() const
Definition: Descriptor.h:172
const bool IsArray
Flag indicating if the block is an array.
Definition: Descriptor.h:129
bool isPrimitiveArray() const
Checks if the descriptor is of an array of primitives.
Definition: Descriptor.h:216
const FieldDecl * asFieldDecl() const
Definition: Descriptor.h:184
const Expr * asExpr() const
Definition: Descriptor.h:173
unsigned IsActive
Flag indicating if the field is the active member of a union.
Definition: Descriptor.h:75
unsigned IsInitialized
For primitive fields, it indicates if the field was initialized.
Definition: Descriptor.h:69