xref: /openbsd-src/gnu/llvm/clang/lib/AST/Interp/Pointer.h (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- Pointer.h - Types for the constexpr VM -----------------*- C++ -*-===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // Defines the classes responsible for pointer tracking.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick 
13e5dd7070Spatrick #ifndef LLVM_CLANG_AST_INTERP_POINTER_H
14e5dd7070Spatrick #define LLVM_CLANG_AST_INTERP_POINTER_H
15e5dd7070Spatrick 
16e5dd7070Spatrick #include "Descriptor.h"
17ec727ea7Spatrick #include "InterpBlock.h"
18ec727ea7Spatrick #include "clang/AST/ComparisonCategories.h"
19e5dd7070Spatrick #include "clang/AST/Decl.h"
20e5dd7070Spatrick #include "clang/AST/DeclCXX.h"
21e5dd7070Spatrick #include "clang/AST/Expr.h"
22e5dd7070Spatrick #include "llvm/ADT/PointerUnion.h"
23e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
24e5dd7070Spatrick 
25e5dd7070Spatrick namespace clang {
26e5dd7070Spatrick namespace interp {
27e5dd7070Spatrick class Block;
28e5dd7070Spatrick class DeadBlock;
29e5dd7070Spatrick class Pointer;
30e5dd7070Spatrick enum PrimType : unsigned;
31e5dd7070Spatrick 
32e5dd7070Spatrick /// A pointer to a memory block, live or dead.
33e5dd7070Spatrick ///
34e5dd7070Spatrick /// This object can be allocated into interpreter stack frames. If pointing to
35e5dd7070Spatrick /// a live block, it is a link in the chain of pointers pointing to the block.
36*12c85518Srobert ///
37*12c85518Srobert /// In the simplest form, a Pointer has a Block* (the pointee) and both Base
38*12c85518Srobert /// and Offset are 0, which means it will point to raw data.
39*12c85518Srobert ///
40*12c85518Srobert /// The Base field is used to access metadata about the data. For primitive
41*12c85518Srobert /// arrays, the Base is followed by an InitMap. In a variety of cases, the
42*12c85518Srobert /// Base is preceded by an InlineDescriptor, which is used to track the
43*12c85518Srobert /// initialization state, among other things.
44*12c85518Srobert ///
45*12c85518Srobert /// The Offset field is used to access the actual data. In other words, the
46*12c85518Srobert /// data the pointer decribes can be found at
47*12c85518Srobert /// Pointee->rawData() + Pointer.Offset.
48*12c85518Srobert ///
49*12c85518Srobert ///
50*12c85518Srobert /// Pointee                      Offset
51*12c85518Srobert /// │                              │
52*12c85518Srobert /// │                              │
53*12c85518Srobert /// ▼                              ▼
54*12c85518Srobert /// ┌───────┬────────────┬─────────┬────────────────────────────┐
55*12c85518Srobert /// │ Block │ InlineDesc │ InitMap │ Actual Data                │
56*12c85518Srobert /// └───────┴────────────┴─────────┴────────────────────────────┘
57*12c85518Srobert ///                      ▲
58*12c85518Srobert ///                      │
59*12c85518Srobert ///                      │
60*12c85518Srobert ///                     Base
61e5dd7070Spatrick class Pointer {
62e5dd7070Spatrick private:
63*12c85518Srobert   static constexpr unsigned PastEndMark = ~0u;
64*12c85518Srobert   static constexpr unsigned RootPtrMark = ~0u;
65e5dd7070Spatrick 
66e5dd7070Spatrick public:
Pointer()67e5dd7070Spatrick   Pointer() {}
68e5dd7070Spatrick   Pointer(Block *B);
69*12c85518Srobert   Pointer(Block *B, unsigned BaseAndOffset);
70e5dd7070Spatrick   Pointer(const Pointer &P);
71e5dd7070Spatrick   Pointer(Pointer &&P);
72e5dd7070Spatrick   ~Pointer();
73e5dd7070Spatrick 
74e5dd7070Spatrick   void operator=(const Pointer &P);
75e5dd7070Spatrick   void operator=(Pointer &&P);
76e5dd7070Spatrick 
77e5dd7070Spatrick   /// Converts the pointer to an APValue.
78e5dd7070Spatrick   APValue toAPValue() const;
79e5dd7070Spatrick 
80e5dd7070Spatrick   /// Offsets a pointer inside an array.
atIndex(unsigned Idx)81e5dd7070Spatrick   Pointer atIndex(unsigned Idx) const {
82e5dd7070Spatrick     if (Base == RootPtrMark)
83e5dd7070Spatrick       return Pointer(Pointee, RootPtrMark, getDeclDesc()->getSize());
84e5dd7070Spatrick     unsigned Off = Idx * elemSize();
85e5dd7070Spatrick     if (getFieldDesc()->ElemDesc)
86e5dd7070Spatrick       Off += sizeof(InlineDescriptor);
87e5dd7070Spatrick     else
88e5dd7070Spatrick       Off += sizeof(InitMap *);
89e5dd7070Spatrick     return Pointer(Pointee, Base, Base + Off);
90e5dd7070Spatrick   }
91e5dd7070Spatrick 
92e5dd7070Spatrick   /// Creates a pointer to a field.
atField(unsigned Off)93e5dd7070Spatrick   Pointer atField(unsigned Off) const {
94e5dd7070Spatrick     unsigned Field = Offset + Off;
95e5dd7070Spatrick     return Pointer(Pointee, Field, Field);
96e5dd7070Spatrick   }
97e5dd7070Spatrick 
98e5dd7070Spatrick   /// Restricts the scope of an array element pointer.
narrow()99e5dd7070Spatrick   Pointer narrow() const {
100e5dd7070Spatrick     // Null pointers cannot be narrowed.
101e5dd7070Spatrick     if (isZero() || isUnknownSizeArray())
102e5dd7070Spatrick       return *this;
103e5dd7070Spatrick 
104e5dd7070Spatrick     // Pointer to an array of base types - enter block.
105e5dd7070Spatrick     if (Base == RootPtrMark)
106e5dd7070Spatrick       return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark);
107e5dd7070Spatrick 
108e5dd7070Spatrick     // Pointer is one past end - magic offset marks that.
109e5dd7070Spatrick     if (isOnePastEnd())
110e5dd7070Spatrick       return Pointer(Pointee, Base, PastEndMark);
111e5dd7070Spatrick 
112e5dd7070Spatrick     // Primitive arrays are a bit special since they do not have inline
113e5dd7070Spatrick     // descriptors. If Offset != Base, then the pointer already points to
114e5dd7070Spatrick     // an element and there is nothing to do. Otherwise, the pointer is
115e5dd7070Spatrick     // adjusted to the first element of the array.
116e5dd7070Spatrick     if (inPrimitiveArray()) {
117e5dd7070Spatrick       if (Offset != Base)
118e5dd7070Spatrick         return *this;
119e5dd7070Spatrick       return Pointer(Pointee, Base, Offset + sizeof(InitMap *));
120e5dd7070Spatrick     }
121e5dd7070Spatrick 
122e5dd7070Spatrick     // Pointer is to a field or array element - enter it.
123e5dd7070Spatrick     if (Offset != Base)
124e5dd7070Spatrick       return Pointer(Pointee, Offset, Offset);
125e5dd7070Spatrick 
126e5dd7070Spatrick     // Enter the first element of an array.
127e5dd7070Spatrick     if (!getFieldDesc()->isArray())
128e5dd7070Spatrick       return *this;
129e5dd7070Spatrick 
130e5dd7070Spatrick     const unsigned NewBase = Base + sizeof(InlineDescriptor);
131e5dd7070Spatrick     return Pointer(Pointee, NewBase, NewBase);
132e5dd7070Spatrick   }
133e5dd7070Spatrick 
134e5dd7070Spatrick   /// Expands a pointer to the containing array, undoing narrowing.
expand()135e5dd7070Spatrick   Pointer expand() const {
136e5dd7070Spatrick     if (isElementPastEnd()) {
137e5dd7070Spatrick       // Revert to an outer one-past-end pointer.
138e5dd7070Spatrick       unsigned Adjust;
139e5dd7070Spatrick       if (inPrimitiveArray())
140e5dd7070Spatrick         Adjust = sizeof(InitMap *);
141e5dd7070Spatrick       else
142e5dd7070Spatrick         Adjust = sizeof(InlineDescriptor);
143e5dd7070Spatrick       return Pointer(Pointee, Base, Base + getSize() + Adjust);
144e5dd7070Spatrick     }
145e5dd7070Spatrick 
146e5dd7070Spatrick     // Do not step out of array elements.
147e5dd7070Spatrick     if (Base != Offset)
148e5dd7070Spatrick       return *this;
149e5dd7070Spatrick 
150e5dd7070Spatrick     // If at base, point to an array of base types.
151e5dd7070Spatrick     if (Base == 0)
152e5dd7070Spatrick       return Pointer(Pointee, RootPtrMark, 0);
153e5dd7070Spatrick 
154e5dd7070Spatrick     // Step into the containing array, if inside one.
155e5dd7070Spatrick     unsigned Next = Base - getInlineDesc()->Offset;
156e5dd7070Spatrick     Descriptor *Desc = Next == 0 ? getDeclDesc() : getDescriptor(Next)->Desc;
157e5dd7070Spatrick     if (!Desc->IsArray)
158e5dd7070Spatrick       return *this;
159e5dd7070Spatrick     return Pointer(Pointee, Next, Offset);
160e5dd7070Spatrick   }
161e5dd7070Spatrick 
162e5dd7070Spatrick   /// Checks if the pointer is null.
isZero()163e5dd7070Spatrick   bool isZero() const { return Pointee == nullptr; }
164e5dd7070Spatrick   /// Checks if the pointer is live.
isLive()165e5dd7070Spatrick   bool isLive() const { return Pointee && !Pointee->IsDead; }
166e5dd7070Spatrick   /// Checks if the item is a field in an object.
isField()167e5dd7070Spatrick   bool isField() const { return Base != 0 && Base != RootPtrMark; }
168e5dd7070Spatrick 
169e5dd7070Spatrick   /// Accessor for information about the declaration site.
getDeclDesc()170e5dd7070Spatrick   Descriptor *getDeclDesc() const { return Pointee->Desc; }
getDeclLoc()171e5dd7070Spatrick   SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); }
172e5dd7070Spatrick 
173e5dd7070Spatrick   /// Returns a pointer to the object of which this pointer is a field.
getBase()174e5dd7070Spatrick   Pointer getBase() const {
175e5dd7070Spatrick     if (Base == RootPtrMark) {
176e5dd7070Spatrick       assert(Offset == PastEndMark && "cannot get base of a block");
177e5dd7070Spatrick       return Pointer(Pointee, Base, 0);
178e5dd7070Spatrick     }
179e5dd7070Spatrick     assert(Offset == Base && "not an inner field");
180e5dd7070Spatrick     unsigned NewBase = Base - getInlineDesc()->Offset;
181e5dd7070Spatrick     return Pointer(Pointee, NewBase, NewBase);
182e5dd7070Spatrick   }
183e5dd7070Spatrick   /// Returns the parent array.
getArray()184e5dd7070Spatrick   Pointer getArray() const {
185e5dd7070Spatrick     if (Base == RootPtrMark) {
186e5dd7070Spatrick       assert(Offset != 0 && Offset != PastEndMark && "not an array element");
187e5dd7070Spatrick       return Pointer(Pointee, Base, 0);
188e5dd7070Spatrick     }
189e5dd7070Spatrick     assert(Offset != Base && "not an array element");
190e5dd7070Spatrick     return Pointer(Pointee, Base, Base);
191e5dd7070Spatrick   }
192e5dd7070Spatrick 
193e5dd7070Spatrick   /// Accessors for information about the innermost field.
getFieldDesc()194e5dd7070Spatrick   Descriptor *getFieldDesc() const {
195e5dd7070Spatrick     if (Base == 0 || Base == RootPtrMark)
196e5dd7070Spatrick       return getDeclDesc();
197e5dd7070Spatrick     return getInlineDesc()->Desc;
198e5dd7070Spatrick   }
199e5dd7070Spatrick 
200e5dd7070Spatrick   /// Returns the type of the innermost field.
getType()201e5dd7070Spatrick   QualType getType() const { return getFieldDesc()->getType(); }
202e5dd7070Spatrick 
203e5dd7070Spatrick   /// Returns the element size of the innermost field.
elemSize()204e5dd7070Spatrick   size_t elemSize() const {
205e5dd7070Spatrick     if (Base == RootPtrMark)
206e5dd7070Spatrick       return getDeclDesc()->getSize();
207e5dd7070Spatrick     return getFieldDesc()->getElemSize();
208e5dd7070Spatrick   }
209e5dd7070Spatrick   /// Returns the total size of the innermost field.
getSize()210e5dd7070Spatrick   size_t getSize() const { return getFieldDesc()->getSize(); }
211e5dd7070Spatrick 
212e5dd7070Spatrick   /// Returns the offset into an array.
getOffset()213e5dd7070Spatrick   unsigned getOffset() const {
214e5dd7070Spatrick     assert(Offset != PastEndMark && "invalid offset");
215e5dd7070Spatrick     if (Base == RootPtrMark)
216e5dd7070Spatrick       return Offset;
217e5dd7070Spatrick 
218e5dd7070Spatrick     unsigned Adjust = 0;
219e5dd7070Spatrick     if (Offset != Base) {
220e5dd7070Spatrick       if (getFieldDesc()->ElemDesc)
221e5dd7070Spatrick         Adjust = sizeof(InlineDescriptor);
222e5dd7070Spatrick       else
223e5dd7070Spatrick         Adjust = sizeof(InitMap *);
224e5dd7070Spatrick     }
225e5dd7070Spatrick     return Offset - Base - Adjust;
226e5dd7070Spatrick   }
227e5dd7070Spatrick 
228e5dd7070Spatrick   /// Checks if the innermost field is an array.
inArray()229e5dd7070Spatrick   bool inArray() const { return getFieldDesc()->IsArray; }
230e5dd7070Spatrick   /// Checks if the structure is a primitive array.
inPrimitiveArray()231e5dd7070Spatrick   bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); }
232e5dd7070Spatrick   /// Checks if the structure is an array of unknown size.
isUnknownSizeArray()233e5dd7070Spatrick   bool isUnknownSizeArray() const {
234e5dd7070Spatrick     return getFieldDesc()->isUnknownSizeArray();
235e5dd7070Spatrick   }
236e5dd7070Spatrick   /// Checks if the pointer points to an array.
isArrayElement()237e5dd7070Spatrick   bool isArrayElement() const { return Base != Offset; }
238e5dd7070Spatrick   /// Pointer points directly to a block.
isRoot()239e5dd7070Spatrick   bool isRoot() const {
240e5dd7070Spatrick     return (Base == 0 || Base == RootPtrMark) && Offset == 0;
241e5dd7070Spatrick   }
242e5dd7070Spatrick 
243e5dd7070Spatrick   /// Returns the record descriptor of a class.
getRecord()244e5dd7070Spatrick   Record *getRecord() const { return getFieldDesc()->ElemRecord; }
245*12c85518Srobert   // Returns the element record type, if this is a non-primive array.
getElemRecord()246*12c85518Srobert   Record *getElemRecord() const { return getFieldDesc()->ElemDesc->ElemRecord; }
247e5dd7070Spatrick   /// Returns the field information.
getField()248e5dd7070Spatrick   const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); }
249e5dd7070Spatrick 
250e5dd7070Spatrick   /// Checks if the object is a union.
251e5dd7070Spatrick   bool isUnion() const;
252e5dd7070Spatrick 
253e5dd7070Spatrick   /// Checks if the storage is extern.
isExtern()254e5dd7070Spatrick   bool isExtern() const { return Pointee->isExtern(); }
255e5dd7070Spatrick   /// Checks if the storage is static.
isStatic()256e5dd7070Spatrick   bool isStatic() const { return Pointee->isStatic(); }
257e5dd7070Spatrick   /// Checks if the storage is temporary.
isTemporary()258e5dd7070Spatrick   bool isTemporary() const { return Pointee->isTemporary(); }
259e5dd7070Spatrick   /// Checks if the storage is a static temporary.
isStaticTemporary()260e5dd7070Spatrick   bool isStaticTemporary() const { return isStatic() && isTemporary(); }
261e5dd7070Spatrick 
262e5dd7070Spatrick   /// Checks if the field is mutable.
isMutable()263*12c85518Srobert   bool isMutable() const {
264*12c85518Srobert     return Base != 0 && getInlineDesc()->IsFieldMutable;
265*12c85518Srobert   }
266e5dd7070Spatrick   /// Checks if an object was initialized.
267e5dd7070Spatrick   bool isInitialized() const;
268e5dd7070Spatrick   /// Checks if the object is active.
isActive()269e5dd7070Spatrick   bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; }
270e5dd7070Spatrick   /// Checks if a structure is a base class.
isBaseClass()271e5dd7070Spatrick   bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
272e5dd7070Spatrick 
273e5dd7070Spatrick   /// Checks if an object or a subfield is mutable.
isConst()274e5dd7070Spatrick   bool isConst() const {
275e5dd7070Spatrick     return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
276e5dd7070Spatrick   }
277e5dd7070Spatrick 
278e5dd7070Spatrick   /// Returns the declaration ID.
getDeclID()279*12c85518Srobert   std::optional<unsigned> getDeclID() const { return Pointee->getDeclID(); }
280e5dd7070Spatrick 
281e5dd7070Spatrick   /// Returns the byte offset from the start.
getByteOffset()282e5dd7070Spatrick   unsigned getByteOffset() const {
283e5dd7070Spatrick     return Offset;
284e5dd7070Spatrick   }
285e5dd7070Spatrick 
286e5dd7070Spatrick   /// Returns the number of elements.
getNumElems()287e5dd7070Spatrick   unsigned getNumElems() const { return getSize() / elemSize(); }
288e5dd7070Spatrick 
289e5dd7070Spatrick   /// Returns the index into an array.
getIndex()290e5dd7070Spatrick   int64_t getIndex() const {
291e5dd7070Spatrick     if (isElementPastEnd())
292e5dd7070Spatrick       return 1;
293e5dd7070Spatrick     if (auto ElemSize = elemSize())
294e5dd7070Spatrick       return getOffset() / ElemSize;
295e5dd7070Spatrick     return 0;
296e5dd7070Spatrick   }
297e5dd7070Spatrick 
298e5dd7070Spatrick   /// Checks if the index is one past end.
isOnePastEnd()299e5dd7070Spatrick   bool isOnePastEnd() const {
300e5dd7070Spatrick     return isElementPastEnd() || getSize() == getOffset();
301e5dd7070Spatrick   }
302e5dd7070Spatrick 
303e5dd7070Spatrick   /// Checks if the pointer is an out-of-bounds element pointer.
isElementPastEnd()304e5dd7070Spatrick   bool isElementPastEnd() const { return Offset == PastEndMark; }
305e5dd7070Spatrick 
306e5dd7070Spatrick   /// Dereferences the pointer, if it's live.
deref()307e5dd7070Spatrick   template <typename T> T &deref() const {
308e5dd7070Spatrick     assert(isLive() && "Invalid pointer");
309*12c85518Srobert     return *reinterpret_cast<T *>(Pointee->rawData() + Offset);
310e5dd7070Spatrick   }
311e5dd7070Spatrick 
312e5dd7070Spatrick   /// Dereferences a primitive element.
elem(unsigned I)313e5dd7070Spatrick   template <typename T> T &elem(unsigned I) const {
314*12c85518Srobert     return reinterpret_cast<T *>(Pointee->rawData())[I];
315e5dd7070Spatrick   }
316e5dd7070Spatrick 
317e5dd7070Spatrick   /// Initializes a field.
318e5dd7070Spatrick   void initialize() const;
319e5dd7070Spatrick   /// Activats a field.
320e5dd7070Spatrick   void activate() const;
321e5dd7070Spatrick   /// Deactivates an entire strurcutre.
322e5dd7070Spatrick   void deactivate() const;
323e5dd7070Spatrick 
324e5dd7070Spatrick   /// Checks if two pointers are comparable.
325e5dd7070Spatrick   static bool hasSameBase(const Pointer &A, const Pointer &B);
326e5dd7070Spatrick   /// Checks if two pointers can be subtracted.
327e5dd7070Spatrick   static bool hasSameArray(const Pointer &A, const Pointer &B);
328e5dd7070Spatrick 
329e5dd7070Spatrick   /// Prints the pointer.
print(llvm::raw_ostream & OS)330e5dd7070Spatrick   void print(llvm::raw_ostream &OS) const {
331*12c85518Srobert     OS << Pointee << " {" << Base << ", " << Offset << ", ";
332e5dd7070Spatrick     if (Pointee)
333e5dd7070Spatrick       OS << Pointee->getSize();
334e5dd7070Spatrick     else
335e5dd7070Spatrick       OS << "nullptr";
336e5dd7070Spatrick     OS << "}";
337e5dd7070Spatrick   }
338e5dd7070Spatrick 
339e5dd7070Spatrick private:
340e5dd7070Spatrick   friend class Block;
341e5dd7070Spatrick   friend class DeadBlock;
342e5dd7070Spatrick 
343e5dd7070Spatrick   Pointer(Block *Pointee, unsigned Base, unsigned Offset);
344e5dd7070Spatrick 
345e5dd7070Spatrick   /// Returns the embedded descriptor preceding a field.
getInlineDesc()346e5dd7070Spatrick   InlineDescriptor *getInlineDesc() const { return getDescriptor(Base); }
347e5dd7070Spatrick 
348e5dd7070Spatrick   /// Returns a descriptor at a given offset.
getDescriptor(unsigned Offset)349e5dd7070Spatrick   InlineDescriptor *getDescriptor(unsigned Offset) const {
350e5dd7070Spatrick     assert(Offset != 0 && "Not a nested pointer");
351*12c85518Srobert     return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) -
352*12c85518Srobert            1;
353e5dd7070Spatrick   }
354e5dd7070Spatrick 
355e5dd7070Spatrick   /// Returns a reference to the pointer which stores the initialization map.
getInitMap()356e5dd7070Spatrick   InitMap *&getInitMap() const {
357*12c85518Srobert     return *reinterpret_cast<InitMap **>(Pointee->rawData() + Base);
358e5dd7070Spatrick   }
359e5dd7070Spatrick 
360e5dd7070Spatrick   /// The block the pointer is pointing to.
361e5dd7070Spatrick   Block *Pointee = nullptr;
362e5dd7070Spatrick   /// Start of the current subfield.
363e5dd7070Spatrick   unsigned Base = 0;
364e5dd7070Spatrick   /// Offset into the block.
365e5dd7070Spatrick   unsigned Offset = 0;
366e5dd7070Spatrick 
367e5dd7070Spatrick   /// Previous link in the pointer chain.
368e5dd7070Spatrick   Pointer *Prev = nullptr;
369e5dd7070Spatrick   /// Next link in the pointer chain.
370e5dd7070Spatrick   Pointer *Next = nullptr;
371e5dd7070Spatrick };
372e5dd7070Spatrick 
373e5dd7070Spatrick inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {
374e5dd7070Spatrick   P.print(OS);
375e5dd7070Spatrick   return OS;
376e5dd7070Spatrick }
377e5dd7070Spatrick 
378e5dd7070Spatrick } // namespace interp
379e5dd7070Spatrick } // namespace clang
380e5dd7070Spatrick 
381e5dd7070Spatrick #endif
382