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