xref: /freebsd/contrib/llvm-project/clang/lib/Interpreter/Value.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
1 //===------------ Value.cpp - Definition of interpreter value -------------===//
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 // This file defines the class that used to represent a value in incremental
10 // C++.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Interpreter/Value.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/Type.h"
17 #include "clang/Interpreter/Interpreter.h"
18 #include "llvm/ADT/StringExtras.h"
19 #include <cassert>
20 #include <utility>
21 
22 namespace {
23 
24 // This is internal buffer maintained by Value, used to hold temporaries.
25 class ValueStorage {
26 public:
27   using DtorFunc = void (*)(void *);
28 
29   static unsigned char *CreatePayload(void *DtorF, size_t AllocSize,
30                                       size_t ElementsSize) {
31     if (AllocSize < sizeof(Canary))
32       AllocSize = sizeof(Canary);
33     unsigned char *Buf =
34         new unsigned char[ValueStorage::getPayloadOffset() + AllocSize];
35     ValueStorage *VS = new (Buf) ValueStorage(DtorF, AllocSize, ElementsSize);
36     std::memcpy(VS->getPayload(), Canary, sizeof(Canary));
37     return VS->getPayload();
38   }
39 
40   unsigned char *getPayload() { return Storage; }
41   const unsigned char *getPayload() const { return Storage; }
42 
43   static unsigned getPayloadOffset() {
44     static ValueStorage Dummy(nullptr, 0, 0);
45     return Dummy.getPayload() - reinterpret_cast<unsigned char *>(&Dummy);
46   }
47 
48   static ValueStorage *getFromPayload(void *Payload) {
49     ValueStorage *R = reinterpret_cast<ValueStorage *>(
50         (unsigned char *)Payload - getPayloadOffset());
51     return R;
52   }
53 
54   void Retain() { ++RefCnt; }
55 
56   void Release() {
57     assert(RefCnt > 0 && "Can't release if reference count is already zero");
58     if (--RefCnt == 0) {
59       // We have a non-trivial dtor.
60       if (Dtor && IsAlive()) {
61         assert(Elements && "We at least should have 1 element in Value");
62         size_t Stride = AllocSize / Elements;
63         for (size_t Idx = 0; Idx < Elements; ++Idx)
64           (*Dtor)(getPayload() + Idx * Stride);
65       }
66       delete[] reinterpret_cast<unsigned char *>(this);
67     }
68   }
69 
70   // Check whether the storage is valid by validating the canary bits.
71   // If someone accidentally write some invalid bits in the storage, the canary
72   // will be changed first, and `IsAlive` will return false then.
73   bool IsAlive() const {
74     return std::memcmp(getPayload(), Canary, sizeof(Canary)) != 0;
75   }
76 
77 private:
78   ValueStorage(void *DtorF, size_t AllocSize, size_t ElementsNum)
79       : RefCnt(1), Dtor(reinterpret_cast<DtorFunc>(DtorF)),
80         AllocSize(AllocSize), Elements(ElementsNum) {}
81 
82   mutable unsigned RefCnt;
83   DtorFunc Dtor = nullptr;
84   size_t AllocSize = 0;
85   size_t Elements = 0;
86   unsigned char Storage[1];
87 
88   // These are some canary bits that are used for protecting the storage been
89   // damaged.
90   static constexpr unsigned char Canary[8] = {0x4c, 0x37, 0xad, 0x8f,
91                                               0x2d, 0x23, 0x95, 0x91};
92 };
93 } // namespace
94 
95 namespace clang {
96 
97 static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) {
98   if (Ctx.hasSameType(QT, Ctx.VoidTy))
99     return Value::K_Void;
100 
101   if (const auto *ET = QT->getAs<EnumType>())
102     QT = ET->getDecl()->getIntegerType();
103 
104   const auto *BT = QT->getAs<BuiltinType>();
105   if (!BT || BT->isNullPtrType())
106     return Value::K_PtrOrObj;
107 
108   switch (QT->castAs<BuiltinType>()->getKind()) {
109   default:
110     assert(false && "Type not supported");
111     return Value::K_Unspecified;
112 #define X(type, name)                                                          \
113   case BuiltinType::name:                                                      \
114     return Value::K_##name;
115     REPL_BUILTIN_TYPES
116 #undef X
117   }
118 }
119 
120 Value::Value(Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) {
121   setKind(ConvertQualTypeToKind(getASTContext(), getType()));
122   if (ValueKind == K_PtrOrObj) {
123     QualType Canon = getType().getCanonicalType();
124     if ((Canon->isPointerType() || Canon->isObjectType() ||
125          Canon->isReferenceType()) &&
126         (Canon->isRecordType() || Canon->isConstantArrayType() ||
127          Canon->isMemberPointerType())) {
128       IsManuallyAlloc = true;
129       // Compile dtor function.
130       Interpreter &Interp = getInterpreter();
131       void *DtorF = nullptr;
132       size_t ElementsSize = 1;
133       QualType DtorTy = getType();
134 
135       if (const auto *ArrTy =
136               llvm::dyn_cast<ConstantArrayType>(DtorTy.getTypePtr())) {
137         DtorTy = ArrTy->getElementType();
138         llvm::APInt ArrSize(sizeof(size_t) * 8, 1);
139         do {
140           ArrSize *= ArrTy->getSize();
141           ArrTy = llvm::dyn_cast<ConstantArrayType>(
142               ArrTy->getElementType().getTypePtr());
143         } while (ArrTy);
144         ElementsSize = static_cast<size_t>(ArrSize.getZExtValue());
145       }
146       if (const auto *RT = DtorTy->getAs<RecordType>()) {
147         if (CXXRecordDecl *CXXRD =
148                 llvm::dyn_cast<CXXRecordDecl>(RT->getDecl())) {
149           if (llvm::Expected<llvm::orc::ExecutorAddr> Addr =
150                   Interp.CompileDtorCall(CXXRD))
151             DtorF = reinterpret_cast<void *>(Addr->getValue());
152           else
153             llvm::logAllUnhandledErrors(Addr.takeError(), llvm::errs());
154         }
155       }
156 
157       size_t AllocSize =
158           getASTContext().getTypeSizeInChars(getType()).getQuantity();
159       unsigned char *Payload =
160           ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize);
161       setPtr((void *)Payload);
162     }
163   }
164 }
165 
166 Value::Value(const Value &RHS)
167     : Interp(RHS.Interp), OpaqueType(RHS.OpaqueType), Data(RHS.Data),
168       ValueKind(RHS.ValueKind), IsManuallyAlloc(RHS.IsManuallyAlloc) {
169   if (IsManuallyAlloc)
170     ValueStorage::getFromPayload(getPtr())->Retain();
171 }
172 
173 Value::Value(Value &&RHS) noexcept {
174   Interp = std::exchange(RHS.Interp, nullptr);
175   OpaqueType = std::exchange(RHS.OpaqueType, nullptr);
176   Data = RHS.Data;
177   ValueKind = std::exchange(RHS.ValueKind, K_Unspecified);
178   IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false);
179 
180   if (IsManuallyAlloc)
181     ValueStorage::getFromPayload(getPtr())->Release();
182 }
183 
184 Value &Value::operator=(const Value &RHS) {
185   if (IsManuallyAlloc)
186     ValueStorage::getFromPayload(getPtr())->Release();
187 
188   Interp = RHS.Interp;
189   OpaqueType = RHS.OpaqueType;
190   Data = RHS.Data;
191   ValueKind = RHS.ValueKind;
192   IsManuallyAlloc = RHS.IsManuallyAlloc;
193 
194   if (IsManuallyAlloc)
195     ValueStorage::getFromPayload(getPtr())->Retain();
196 
197   return *this;
198 }
199 
200 Value &Value::operator=(Value &&RHS) noexcept {
201   if (this != &RHS) {
202     if (IsManuallyAlloc)
203       ValueStorage::getFromPayload(getPtr())->Release();
204 
205     Interp = std::exchange(RHS.Interp, nullptr);
206     OpaqueType = std::exchange(RHS.OpaqueType, nullptr);
207     ValueKind = std::exchange(RHS.ValueKind, K_Unspecified);
208     IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false);
209 
210     Data = RHS.Data;
211   }
212   return *this;
213 }
214 
215 void Value::clear() {
216   if (IsManuallyAlloc)
217     ValueStorage::getFromPayload(getPtr())->Release();
218   ValueKind = K_Unspecified;
219   OpaqueType = nullptr;
220   Interp = nullptr;
221   IsManuallyAlloc = false;
222 }
223 
224 Value::~Value() { clear(); }
225 
226 void *Value::getPtr() const {
227   assert(ValueKind == K_PtrOrObj);
228   return Data.m_Ptr;
229 }
230 
231 QualType Value::getType() const {
232   return QualType::getFromOpaquePtr(OpaqueType);
233 }
234 
235 Interpreter &Value::getInterpreter() {
236   assert(Interp != nullptr &&
237          "Can't get interpreter from a default constructed value");
238   return *Interp;
239 }
240 
241 const Interpreter &Value::getInterpreter() const {
242   assert(Interp != nullptr &&
243          "Can't get interpreter from a default constructed value");
244   return *Interp;
245 }
246 
247 ASTContext &Value::getASTContext() { return getInterpreter().getASTContext(); }
248 
249 const ASTContext &Value::getASTContext() const {
250   return getInterpreter().getASTContext();
251 }
252 
253 void Value::dump() const { print(llvm::outs()); }
254 
255 void Value::printType(llvm::raw_ostream &Out) const {
256   Out << "Not implement yet.\n";
257 }
258 void Value::printData(llvm::raw_ostream &Out) const {
259   Out << "Not implement yet.\n";
260 }
261 void Value::print(llvm::raw_ostream &Out) const {
262   assert(OpaqueType != nullptr && "Can't print default Value");
263   Out << "Not implement yet.\n";
264 }
265 
266 } // namespace clang
267