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 "Function.h"
11#include "InterpBlock.h"
12#include "PrimType.h"
13
14using namespace clang;
15using namespace clang::interp;
16
17Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
18
19Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
20    : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
21
22Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
23
24Pointer::Pointer(Pointer &&P)
25    : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) {
26  if (Pointee)
27    Pointee->movePointer(&P, this);
28}
29
30Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset)
31    : Pointee(Pointee), Base(Base), Offset(Offset) {
32  assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
33  if (Pointee)
34    Pointee->addPointer(this);
35}
36
37Pointer::~Pointer() {
38  if (Pointee) {
39    Pointee->removePointer(this);
40    Pointee->cleanup();
41  }
42}
43
44void Pointer::operator=(const Pointer &P) {
45  Block *Old = Pointee;
46
47  if (Pointee)
48    Pointee->removePointer(this);
49
50  Offset = P.Offset;
51  Base = P.Base;
52
53  Pointee = P.Pointee;
54  if (Pointee)
55    Pointee->addPointer(this);
56
57  if (Old)
58    Old->cleanup();
59}
60
61void Pointer::operator=(Pointer &&P) {
62  Block *Old = Pointee;
63
64  if (Pointee)
65    Pointee->removePointer(this);
66
67  Offset = P.Offset;
68  Base = P.Base;
69
70  Pointee = P.Pointee;
71  if (Pointee)
72    Pointee->movePointer(&P, this);
73
74  if (Old)
75    Old->cleanup();
76}
77
78APValue Pointer::toAPValue() const {
79  APValue::LValueBase Base;
80  llvm::SmallVector<APValue::LValuePathEntry, 5> Path;
81  CharUnits Offset;
82  bool IsNullPtr;
83  bool IsOnePastEnd;
84
85  if (isZero()) {
86    Base = static_cast<const Expr *>(nullptr);
87    IsNullPtr = true;
88    IsOnePastEnd = false;
89    Offset = CharUnits::Zero();
90  } else {
91    // Build the lvalue base from the block.
92    Descriptor *Desc = getDeclDesc();
93    if (auto *VD = Desc->asValueDecl())
94      Base = VD;
95    else if (auto *E = Desc->asExpr())
96      Base = E;
97    else
98      llvm_unreachable("Invalid allocation type");
99
100    // Not a null pointer.
101    IsNullPtr = false;
102
103    if (isUnknownSizeArray()) {
104      IsOnePastEnd = false;
105      Offset = CharUnits::Zero();
106    } else {
107      // TODO: compute the offset into the object.
108      Offset = CharUnits::Zero();
109
110      // Build the path into the object.
111      Pointer Ptr = *this;
112      while (Ptr.isField() || Ptr.isArrayElement()) {
113        if (Ptr.isArrayElement()) {
114          Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
115          Ptr = Ptr.getArray();
116        } else {
117          // TODO: figure out if base is virtual
118          bool IsVirtual = false;
119
120          // Create a path entry for the field.
121          Descriptor *Desc = Ptr.getFieldDesc();
122          if (auto *BaseOrMember = Desc->asDecl()) {
123            Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
124            Ptr = Ptr.getBase();
125            continue;
126          }
127          llvm_unreachable("Invalid field type");
128        }
129      }
130
131      IsOnePastEnd = isOnePastEnd();
132    }
133  }
134
135  // We assemble the LValuePath starting from the innermost pointer to the
136  // outermost one. SO in a.b.c, the first element in Path will refer to
137  // the field 'c', while later code expects it to refer to 'a'.
138  // Just invert the order of the elements.
139  std::reverse(Path.begin(), Path.end());
140
141  return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr);
142}
143
144bool Pointer::isInitialized() const {
145  assert(Pointee && "Cannot check if null pointer was initialized");
146  Descriptor *Desc = getFieldDesc();
147  assert(Desc);
148  if (Desc->isPrimitiveArray()) {
149    if (isStatic() && Base == 0)
150      return true;
151    // Primitive array field are stored in a bitset.
152    InitMap *Map = getInitMap();
153    if (!Map)
154      return false;
155    if (Map == (InitMap *)-1)
156      return true;
157    return Map->isInitialized(getIndex());
158  } else {
159    // Field has its bit in an inline descriptor.
160    return Base == 0 || getInlineDesc()->IsInitialized;
161  }
162}
163
164void Pointer::initialize() const {
165  assert(Pointee && "Cannot initialize null pointer");
166  Descriptor *Desc = getFieldDesc();
167
168  assert(Desc);
169  if (Desc->isArray()) {
170    if (Desc->isPrimitiveArray()) {
171      // Primitive global arrays don't have an initmap.
172      if (isStatic() && Base == 0)
173        return;
174
175      // Primitive array initializer.
176      InitMap *&Map = getInitMap();
177      if (Map == (InitMap *)-1)
178        return;
179      if (Map == nullptr)
180        Map = InitMap::allocate(Desc->getNumElems());
181      if (Map->initialize(getIndex())) {
182        free(Map);
183        Map = (InitMap *)-1;
184      }
185    }
186  } else {
187    // Field has its bit in an inline descriptor.
188    assert(Base != 0 && "Only composite fields can be initialised");
189    getInlineDesc()->IsInitialized = true;
190  }
191}
192
193void Pointer::activate() const {
194  // Field has its bit in an inline descriptor.
195  assert(Base != 0 && "Only composite fields can be initialised");
196  getInlineDesc()->IsActive = true;
197}
198
199void Pointer::deactivate() const {
200  // TODO: this only appears in constructors, so nothing to deactivate.
201}
202
203bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
204  return A.Pointee == B.Pointee;
205}
206
207bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
208  return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray;
209}
210