1 //===--- Pointer.h - Types for the constexpr VM -----------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // Defines the classes responsible for pointer tracking.
11 //===----------------------------------------------------------------------===//
13 #ifndef LLVM_CLANG_AST_INTERP_POINTER_H
14 #define LLVM_CLANG_AST_INTERP_POINTER_H
16 #include "Descriptor.h"
17 #include "InterpBlock.h"
18 #include "clang/AST/ComparisonCategories.h"
19 #include "clang/AST/Decl.h"
20 #include "clang/AST/DeclCXX.h"
21 #include "clang/AST/Expr.h"
22 #include "llvm/ADT/PointerUnion.h"
23 #include "llvm/Support/raw_ostream.h"
31 enum PrimType
: unsigned;
34 inline llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const Pointer
&P
);
36 /// A pointer to a memory block, live or dead.
38 /// This object can be allocated into interpreter stack frames. If pointing to
39 /// a live block, it is a link in the chain of pointers pointing to the block.
41 /// In the simplest form, a Pointer has a Block* (the pointee) and both Base
42 /// and Offset are 0, which means it will point to raw data.
44 /// The Base field is used to access metadata about the data. For primitive
45 /// arrays, the Base is followed by an InitMap. In a variety of cases, the
46 /// Base is preceded by an InlineDescriptor, which is used to track the
47 /// initialization state, among other things.
49 /// The Offset field is used to access the actual data. In other words, the
50 /// data the pointer decribes can be found at
51 /// Pointee->rawData() + Pointer.Offset.
58 /// ┌───────┬────────────┬─────────┬────────────────────────────┐
59 /// │ Block │ InlineDesc │ InitMap │ Actual Data │
60 /// └───────┴────────────┴─────────┴────────────────────────────┘
67 static constexpr unsigned PastEndMark
= ~0u;
68 static constexpr unsigned RootPtrMark
= ~0u;
73 Pointer(Block
*B
, unsigned BaseAndOffset
);
74 Pointer(const Pointer
&P
);
78 void operator=(const Pointer
&P
);
79 void operator=(Pointer
&&P
);
81 /// Equality operators are just for tests.
82 bool operator==(const Pointer
&P
) const {
83 return Pointee
== P
.Pointee
&& Base
== P
.Base
&& Offset
== P
.Offset
;
86 bool operator!=(const Pointer
&P
) const {
87 return Pointee
!= P
.Pointee
|| Base
!= P
.Base
|| Offset
!= P
.Offset
;
90 /// Converts the pointer to an APValue.
91 APValue
toAPValue() const;
93 /// Converts the pointer to a string usable in diagnostics.
94 std::string
toDiagnosticString(const ASTContext
&Ctx
) const;
96 unsigned getIntegerRepresentation() const {
97 return reinterpret_cast<uintptr_t>(Pointee
) + Offset
;
100 /// Converts the pointer to an APValue that is an rvalue.
101 APValue
toRValue(const Context
&Ctx
) const;
103 /// Offsets a pointer inside an array.
104 [[nodiscard
]] Pointer
atIndex(unsigned Idx
) const {
105 if (Base
== RootPtrMark
)
106 return Pointer(Pointee
, RootPtrMark
, getDeclDesc()->getSize());
107 unsigned Off
= Idx
* elemSize();
108 if (getFieldDesc()->ElemDesc
)
109 Off
+= sizeof(InlineDescriptor
);
111 Off
+= sizeof(InitMapPtr
);
112 return Pointer(Pointee
, Base
, Base
+ Off
);
115 /// Creates a pointer to a field.
116 [[nodiscard
]] Pointer
atField(unsigned Off
) const {
117 unsigned Field
= Offset
+ Off
;
118 return Pointer(Pointee
, Field
, Field
);
121 /// Subtract the given offset from the current Base and Offset
123 [[nodiscard
]] Pointer
atFieldSub(unsigned Off
) const {
124 assert(Offset
>= Off
);
125 unsigned O
= Offset
- Off
;
126 return Pointer(Pointee
, O
, O
);
129 /// Restricts the scope of an array element pointer.
130 [[nodiscard
]] Pointer
narrow() const {
131 // Null pointers cannot be narrowed.
132 if (isZero() || isUnknownSizeArray())
135 // Pointer to an array of base types - enter block.
136 if (Base
== RootPtrMark
)
137 return Pointer(Pointee
, 0, Offset
== 0 ? Offset
: PastEndMark
);
139 // Pointer is one past end - magic offset marks that.
141 return Pointer(Pointee
, Base
, PastEndMark
);
143 // Primitive arrays are a bit special since they do not have inline
144 // descriptors. If Offset != Base, then the pointer already points to
145 // an element and there is nothing to do. Otherwise, the pointer is
146 // adjusted to the first element of the array.
147 if (inPrimitiveArray()) {
150 return Pointer(Pointee
, Base
, Offset
+ sizeof(InitMapPtr
));
153 // Pointer is to a field or array element - enter it.
155 return Pointer(Pointee
, Offset
, Offset
);
157 // Enter the first element of an array.
158 if (!getFieldDesc()->isArray())
161 const unsigned NewBase
= Base
+ sizeof(InlineDescriptor
);
162 return Pointer(Pointee
, NewBase
, NewBase
);
165 /// Expands a pointer to the containing array, undoing narrowing.
166 [[nodiscard
]] Pointer
expand() const {
167 if (isElementPastEnd()) {
168 // Revert to an outer one-past-end pointer.
170 if (inPrimitiveArray())
171 Adjust
= sizeof(InitMapPtr
);
173 Adjust
= sizeof(InlineDescriptor
);
174 return Pointer(Pointee
, Base
, Base
+ getSize() + Adjust
);
177 // Do not step out of array elements.
181 // If at base, point to an array of base types.
183 return Pointer(Pointee
, RootPtrMark
, 0);
185 // Step into the containing array, if inside one.
186 unsigned Next
= Base
- getInlineDesc()->Offset
;
187 const Descriptor
*Desc
=
188 Next
== 0 ? getDeclDesc() : getDescriptor(Next
)->Desc
;
191 return Pointer(Pointee
, Next
, Offset
);
194 /// Checks if the pointer is null.
195 bool isZero() const { return Pointee
== nullptr; }
196 /// Checks if the pointer is live.
197 bool isLive() const { return Pointee
&& !Pointee
->IsDead
; }
198 /// Checks if the item is a field in an object.
199 bool isField() const { return Base
!= 0 && Base
!= RootPtrMark
; }
201 /// Accessor for information about the declaration site.
202 const Descriptor
*getDeclDesc() const {
204 return Pointee
->Desc
;
206 SourceLocation
getDeclLoc() const { return getDeclDesc()->getLocation(); }
208 /// Returns a pointer to the object of which this pointer is a field.
209 [[nodiscard
]] Pointer
getBase() const {
210 if (Base
== RootPtrMark
) {
211 assert(Offset
== PastEndMark
&& "cannot get base of a block");
212 return Pointer(Pointee
, Base
, 0);
214 assert(Offset
== Base
&& "not an inner field");
215 unsigned NewBase
= Base
- getInlineDesc()->Offset
;
216 return Pointer(Pointee
, NewBase
, NewBase
);
218 /// Returns the parent array.
219 [[nodiscard
]] Pointer
getArray() const {
220 if (Base
== RootPtrMark
) {
221 assert(Offset
!= 0 && Offset
!= PastEndMark
&& "not an array element");
222 return Pointer(Pointee
, Base
, 0);
224 assert(Offset
!= Base
&& "not an array element");
225 return Pointer(Pointee
, Base
, Base
);
228 /// Accessors for information about the innermost field.
229 const Descriptor
*getFieldDesc() const {
230 if (Base
== 0 || Base
== RootPtrMark
)
231 return getDeclDesc();
232 return getInlineDesc()->Desc
;
235 /// Returns the type of the innermost field.
236 QualType
getType() const {
237 if (inPrimitiveArray() && Offset
!= Base
)
238 return getFieldDesc()->getType()->getAsArrayTypeUnsafe()->getElementType();
239 return getFieldDesc()->getType();
242 [[nodiscard
]] Pointer
getDeclPtr() const { return Pointer(Pointee
); }
244 /// Returns the element size of the innermost field.
245 size_t elemSize() const {
246 if (Base
== RootPtrMark
)
247 return getDeclDesc()->getSize();
248 return getFieldDesc()->getElemSize();
250 /// Returns the total size of the innermost field.
251 size_t getSize() const { return getFieldDesc()->getSize(); }
253 /// Returns the offset into an array.
254 unsigned getOffset() const {
255 assert(Offset
!= PastEndMark
&& "invalid offset");
256 if (Base
== RootPtrMark
)
260 if (Offset
!= Base
) {
261 if (getFieldDesc()->ElemDesc
)
262 Adjust
= sizeof(InlineDescriptor
);
264 Adjust
= sizeof(InitMapPtr
);
266 return Offset
- Base
- Adjust
;
269 /// Whether this array refers to an array, but not
270 /// to the first element.
271 bool isArrayRoot() const { return inArray() && Offset
== Base
; }
273 /// Checks if the innermost field is an array.
274 bool inArray() const { return getFieldDesc()->IsArray
; }
275 /// Checks if the structure is a primitive array.
276 bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); }
277 /// Checks if the structure is an array of unknown size.
278 bool isUnknownSizeArray() const {
279 return getFieldDesc()->isUnknownSizeArray();
281 /// Checks if the pointer points to an array.
282 bool isArrayElement() const { return Base
!= Offset
; }
283 /// Pointer points directly to a block.
284 bool isRoot() const {
285 return (Base
== 0 || Base
== RootPtrMark
) && Offset
== 0;
288 /// Returns the record descriptor of a class.
289 const Record
*getRecord() const { return getFieldDesc()->ElemRecord
; }
290 /// Returns the element record type, if this is a non-primive array.
291 const Record
*getElemRecord() const {
292 const Descriptor
*ElemDesc
= getFieldDesc()->ElemDesc
;
293 return ElemDesc
? ElemDesc
->ElemRecord
: nullptr;
295 /// Returns the field information.
296 const FieldDecl
*getField() const { return getFieldDesc()->asFieldDecl(); }
298 /// Checks if the object is a union.
299 bool isUnion() const;
301 /// Checks if the storage is extern.
302 bool isExtern() const { return Pointee
&& Pointee
->isExtern(); }
303 /// Checks if the storage is static.
304 bool isStatic() const {
306 return Pointee
->isStatic();
308 /// Checks if the storage is temporary.
309 bool isTemporary() const {
311 return Pointee
->isTemporary();
313 /// Checks if the storage is a static temporary.
314 bool isStaticTemporary() const { return isStatic() && isTemporary(); }
316 /// Checks if the field is mutable.
317 bool isMutable() const {
318 return Base
!= 0 && getInlineDesc()->IsFieldMutable
;
320 /// Checks if an object was initialized.
321 bool isInitialized() const;
322 /// Checks if the object is active.
323 bool isActive() const { return Base
== 0 || getInlineDesc()->IsActive
; }
324 /// Checks if a structure is a base class.
325 bool isBaseClass() const { return isField() && getInlineDesc()->IsBase
; }
326 /// Checks if the pointer pointers to a dummy value.
327 bool isDummy() const { return getDeclDesc()->isDummy(); }
329 /// Checks if an object or a subfield is mutable.
330 bool isConst() const {
331 return Base
== 0 ? getDeclDesc()->IsConst
: getInlineDesc()->IsConst
;
334 /// Returns the declaration ID.
335 std::optional
<unsigned> getDeclID() const {
337 return Pointee
->getDeclID();
340 /// Returns the byte offset from the start.
341 unsigned getByteOffset() const {
345 /// Returns the number of elements.
346 unsigned getNumElems() const { return getSize() / elemSize(); }
348 const Block
*block() const { return Pointee
; }
350 /// Returns the index into an array.
351 int64_t getIndex() const {
352 if (isElementPastEnd())
355 // narrow()ed element in a composite array.
356 if (Base
> 0 && Base
== Offset
)
359 if (auto ElemSize
= elemSize())
360 return getOffset() / ElemSize
;
364 /// Checks if the index is one past end.
365 bool isOnePastEnd() const {
368 return isElementPastEnd() || getSize() == getOffset();
371 /// Checks if the pointer is an out-of-bounds element pointer.
372 bool isElementPastEnd() const { return Offset
== PastEndMark
; }
374 /// Dereferences the pointer, if it's live.
375 template <typename T
> T
&deref() const {
376 assert(isLive() && "Invalid pointer");
379 return *reinterpret_cast<T
*>(Pointee
->rawData() + Base
+
382 return *reinterpret_cast<T
*>(Pointee
->rawData() + Offset
);
385 /// Dereferences a primitive element.
386 template <typename T
> T
&elem(unsigned I
) const {
387 assert(I
< getNumElems());
389 return reinterpret_cast<T
*>(Pointee
->data() + sizeof(InitMapPtr
))[I
];
392 /// Initializes a field.
393 void initialize() const;
394 /// Activats a field.
395 void activate() const;
396 /// Deactivates an entire strurcutre.
397 void deactivate() const;
399 /// Compare two pointers.
400 ComparisonCategoryResult
compare(const Pointer
&Other
) const {
401 if (!hasSameBase(*this, Other
))
402 return ComparisonCategoryResult::Unordered
;
404 if (Offset
< Other
.Offset
)
405 return ComparisonCategoryResult::Less
;
406 else if (Offset
> Other
.Offset
)
407 return ComparisonCategoryResult::Greater
;
409 return ComparisonCategoryResult::Equal
;
412 /// Checks if two pointers are comparable.
413 static bool hasSameBase(const Pointer
&A
, const Pointer
&B
);
414 /// Checks if two pointers can be subtracted.
415 static bool hasSameArray(const Pointer
&A
, const Pointer
&B
);
417 /// Prints the pointer.
418 void print(llvm::raw_ostream
&OS
) const {
419 OS
<< Pointee
<< " {";
420 if (Base
== RootPtrMark
)
425 if (Offset
== PastEndMark
)
428 OS
<< Offset
<< ", ";
431 OS
<< Pointee
->getSize();
439 friend class DeadBlock
;
440 friend struct InitMap
;
442 Pointer(Block
*Pointee
, unsigned Base
, unsigned Offset
);
444 /// Returns the embedded descriptor preceding a field.
445 InlineDescriptor
*getInlineDesc() const { return getDescriptor(Base
); }
447 /// Returns a descriptor at a given offset.
448 InlineDescriptor
*getDescriptor(unsigned Offset
) const {
449 assert(Offset
!= 0 && "Not a nested pointer");
451 return reinterpret_cast<InlineDescriptor
*>(Pointee
->rawData() + Offset
) -
455 /// Returns a reference to the InitMapPtr which stores the initialization map.
456 InitMapPtr
&getInitMap() const {
458 return *reinterpret_cast<InitMapPtr
*>(Pointee
->rawData() + Base
);
461 /// The block the pointer is pointing to.
462 Block
*Pointee
= nullptr;
463 /// Start of the current subfield.
465 /// Offset into the block.
468 /// Previous link in the pointer chain.
469 Pointer
*Prev
= nullptr;
470 /// Next link in the pointer chain.
471 Pointer
*Next
= nullptr;
474 inline llvm::raw_ostream
&operator<<(llvm::raw_ostream
&OS
, const Pointer
&P
) {
479 } // namespace interp