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