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