xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
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) : Pointer(Pointee, 0, 0) {}
23 
24 Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
25     : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
26 
27 Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
28 
29 Pointer::Pointer(Pointer &&P)
30     : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) {
31   if (Pointee)
32     Pointee->replacePointer(&P, this);
33 }
34 
35 Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset)
36     : Pointee(Pointee), Base(Base), Offset(Offset) {
37   assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
38   if (Pointee)
39     Pointee->addPointer(this);
40 }
41 
42 Pointer::~Pointer() {
43   if (Pointee) {
44     Pointee->removePointer(this);
45     Pointee->cleanup();
46   }
47 }
48 
49 void Pointer::operator=(const Pointer &P) {
50   Block *Old = Pointee;
51 
52   if (Pointee)
53     Pointee->removePointer(this);
54 
55   Offset = P.Offset;
56   Base = P.Base;
57 
58   Pointee = P.Pointee;
59   if (Pointee)
60     Pointee->addPointer(this);
61 
62   if (Old)
63     Old->cleanup();
64 }
65 
66 void Pointer::operator=(Pointer &&P) {
67   Block *Old = Pointee;
68 
69   if (Pointee)
70     Pointee->removePointer(this);
71 
72   Offset = P.Offset;
73   Base = P.Base;
74 
75   Pointee = P.Pointee;
76   if (Pointee)
77     Pointee->replacePointer(&P, this);
78 
79   if (Old)
80     Old->cleanup();
81 }
82 
83 APValue Pointer::toAPValue() const {
84   APValue::LValueBase Base;
85   llvm::SmallVector<APValue::LValuePathEntry, 5> Path;
86   CharUnits Offset;
87   bool IsNullPtr;
88   bool IsOnePastEnd;
89 
90   if (isZero()) {
91     Base = static_cast<const Expr *>(nullptr);
92     IsNullPtr = true;
93     IsOnePastEnd = false;
94     Offset = CharUnits::Zero();
95   } else {
96     // Build the lvalue base from the block.
97     const Descriptor *Desc = getDeclDesc();
98     if (auto *VD = Desc->asValueDecl())
99       Base = VD;
100     else if (auto *E = Desc->asExpr())
101       Base = E;
102     else
103       llvm_unreachable("Invalid allocation type");
104 
105     // Not a null pointer.
106     IsNullPtr = false;
107 
108     if (isUnknownSizeArray()) {
109       IsOnePastEnd = false;
110       Offset = CharUnits::Zero();
111     } else if (Desc->asExpr()) {
112       // Pointer pointing to a an expression.
113       IsOnePastEnd = false;
114       Offset = CharUnits::Zero();
115     } else {
116       // TODO: compute the offset into the object.
117       Offset = CharUnits::Zero();
118 
119       // Build the path into the object.
120       Pointer Ptr = *this;
121       while (Ptr.isField() || Ptr.isArrayElement()) {
122         if (Ptr.isArrayElement()) {
123           Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
124           Ptr = Ptr.getArray();
125         } else {
126           // TODO: figure out if base is virtual
127           bool IsVirtual = false;
128 
129           // Create a path entry for the field.
130           const Descriptor *Desc = Ptr.getFieldDesc();
131           if (const auto *BaseOrMember = Desc->asDecl()) {
132             Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
133             Ptr = Ptr.getBase();
134             continue;
135           }
136           llvm_unreachable("Invalid field type");
137         }
138       }
139 
140       IsOnePastEnd = isOnePastEnd();
141     }
142   }
143 
144   // We assemble the LValuePath starting from the innermost pointer to the
145   // outermost one. SO in a.b.c, the first element in Path will refer to
146   // the field 'c', while later code expects it to refer to 'a'.
147   // Just invert the order of the elements.
148   std::reverse(Path.begin(), Path.end());
149 
150   return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr);
151 }
152 
153 std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
154   if (!Pointee)
155     return "nullptr";
156 
157   return toAPValue().getAsString(Ctx, getType());
158 }
159 
160 bool Pointer::isInitialized() const {
161   assert(Pointee && "Cannot check if null pointer was initialized");
162   const Descriptor *Desc = getFieldDesc();
163   assert(Desc);
164   if (Desc->isPrimitiveArray()) {
165     if (isStatic() && Base == 0)
166       return true;
167 
168     InitMapPtr &IM = getInitMap();
169 
170     if (!IM)
171       return false;
172 
173     if (IM->first)
174       return true;
175 
176     return IM->second->isElementInitialized(getIndex());
177   }
178 
179   // Field has its bit in an inline descriptor.
180   return Base == 0 || getInlineDesc()->IsInitialized;
181 }
182 
183 void Pointer::initialize() const {
184   assert(Pointee && "Cannot initialize null pointer");
185   const Descriptor *Desc = getFieldDesc();
186 
187   assert(Desc);
188   if (Desc->isPrimitiveArray()) {
189     // Primitive global arrays don't have an initmap.
190     if (isStatic() && Base == 0)
191       return;
192 
193     InitMapPtr &IM = getInitMap();
194     if (!IM)
195       IM =
196           std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems()));
197 
198     assert(IM);
199 
200     // All initialized.
201     if (IM->first)
202       return;
203 
204     if (IM->second->initializeElement(getIndex())) {
205       IM->first = true;
206       IM->second.reset();
207     }
208     return;
209   }
210 
211   // Field has its bit in an inline descriptor.
212   assert(Base != 0 && "Only composite fields can be initialised");
213   getInlineDesc()->IsInitialized = true;
214 }
215 
216 void Pointer::activate() const {
217   // Field has its bit in an inline descriptor.
218   assert(Base != 0 && "Only composite fields can be initialised");
219   getInlineDesc()->IsActive = true;
220 }
221 
222 void Pointer::deactivate() const {
223   // TODO: this only appears in constructors, so nothing to deactivate.
224 }
225 
226 bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
227   return A.Pointee == B.Pointee;
228 }
229 
230 bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
231   return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray;
232 }
233 
234 APValue Pointer::toRValue(const Context &Ctx) const {
235   // Primitives.
236   if (getFieldDesc()->isPrimitive()) {
237     PrimType PT = *Ctx.classify(getType());
238     TYPE_SWITCH(PT, return deref<T>().toAPValue());
239     llvm_unreachable("Unhandled PrimType?");
240   }
241 
242   APValue Result;
243   // Records.
244   if (getFieldDesc()->isRecord()) {
245     const Record *R = getRecord();
246     Result =
247         APValue(APValue::UninitStruct(), R->getNumBases(), R->getNumFields());
248 
249     for (unsigned I = 0; I != R->getNumFields(); ++I) {
250       const Pointer &FieldPtr = this->atField(R->getField(I)->Offset);
251       Result.getStructField(I) = FieldPtr.toRValue(Ctx);
252     }
253 
254     for (unsigned I = 0; I != R->getNumBases(); ++I) {
255       const Pointer &BasePtr = this->atField(R->getBase(I)->Offset);
256       Result.getStructBase(I) = BasePtr.toRValue(Ctx);
257     }
258   }
259 
260   // TODO: Arrays
261 
262   return Result;
263 }
264