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 | |
19 | using namespace clang; |
20 | using namespace clang::interp; |
21 | |
22 | Pointer::Pointer(Block *Pointee) |
23 | : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(), |
24 | Pointee->getDescriptor()->getMetadataSize()) {} |
25 | |
26 | Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset) |
27 | : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {} |
28 | |
29 | Pointer::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 | |
37 | Pointer::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 | |
47 | Pointer::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 | |
55 | Pointer::~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 | |
65 | void 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 | |
93 | void 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 | |
120 | APValue 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 | |
180 | void 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 | |
206 | std::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 | |
216 | bool 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 | |
243 | void 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 | |
284 | void 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 | |
291 | void Pointer::deactivate() const { |
292 | // TODO: this only appears in constructors, so nothing to deactivate. |
293 | } |
294 | |
295 | bool 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 | |
309 | bool 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 | |
315 | std::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 | |