xref: /freebsd/contrib/llvm-project/clang/lib/Interpreter/Value.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 "llvm/Support/ErrorHandling.h"
20 #include "llvm/Support/raw_os_ostream.h"
21 #include <cassert>
22 #include <cstdint>
23 #include <utility>
24 
25 namespace {
26 
27 // This is internal buffer maintained by Value, used to hold temporaries.
28 class ValueStorage {
29 public:
30   using DtorFunc = void (*)(void *);
31 
CreatePayload(void * DtorF,size_t AllocSize,size_t ElementsSize)32   static unsigned char *CreatePayload(void *DtorF, size_t AllocSize,
33                                       size_t ElementsSize) {
34     if (AllocSize < sizeof(Canary))
35       AllocSize = sizeof(Canary);
36     unsigned char *Buf =
37         new unsigned char[ValueStorage::getPayloadOffset() + AllocSize];
38     ValueStorage *VS = new (Buf) ValueStorage(DtorF, AllocSize, ElementsSize);
39     std::memcpy(VS->getPayload(), Canary, sizeof(Canary));
40     return VS->getPayload();
41   }
42 
getPayload()43   unsigned char *getPayload() { return Storage; }
getPayload() const44   const unsigned char *getPayload() const { return Storage; }
45 
getPayloadOffset()46   static unsigned getPayloadOffset() {
47     static ValueStorage Dummy(nullptr, 0, 0);
48     return Dummy.getPayload() - reinterpret_cast<unsigned char *>(&Dummy);
49   }
50 
getFromPayload(void * Payload)51   static ValueStorage *getFromPayload(void *Payload) {
52     ValueStorage *R = reinterpret_cast<ValueStorage *>(
53         (unsigned char *)Payload - getPayloadOffset());
54     return R;
55   }
56 
Retain()57   void Retain() { ++RefCnt; }
58 
Release()59   void Release() {
60     assert(RefCnt > 0 && "Can't release if reference count is already zero");
61     if (--RefCnt == 0) {
62       // We have a non-trivial dtor.
63       if (Dtor && IsAlive()) {
64         assert(Elements && "We at least should have 1 element in Value");
65         size_t Stride = AllocSize / Elements;
66         for (size_t Idx = 0; Idx < Elements; ++Idx)
67           (*Dtor)(getPayload() + Idx * Stride);
68       }
69       delete[] reinterpret_cast<unsigned char *>(this);
70     }
71   }
72 
73   // Check whether the storage is valid by validating the canary bits.
74   // If someone accidentally write some invalid bits in the storage, the canary
75   // will be changed first, and `IsAlive` will return false then.
IsAlive() const76   bool IsAlive() const {
77     return std::memcmp(getPayload(), Canary, sizeof(Canary)) != 0;
78   }
79 
80 private:
ValueStorage(void * DtorF,size_t AllocSize,size_t ElementsNum)81   ValueStorage(void *DtorF, size_t AllocSize, size_t ElementsNum)
82       : RefCnt(1), Dtor(reinterpret_cast<DtorFunc>(DtorF)),
83         AllocSize(AllocSize), Elements(ElementsNum) {}
84 
85   mutable unsigned RefCnt;
86   DtorFunc Dtor = nullptr;
87   size_t AllocSize = 0;
88   size_t Elements = 0;
89   unsigned char Storage[1];
90 
91   // These are some canary bits that are used for protecting the storage been
92   // damaged.
93   static constexpr unsigned char Canary[8] = {0x4c, 0x37, 0xad, 0x8f,
94                                               0x2d, 0x23, 0x95, 0x91};
95 };
96 } // namespace
97 
98 namespace clang {
99 
ConvertQualTypeToKind(const ASTContext & Ctx,QualType QT)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 
Value(Interpreter * In,void * Ty)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 
Value(const Value & RHS)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 
Value(Value && RHS)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 
operator =(const Value & RHS)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 
operator =(Value && RHS)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 
clear()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 
~Value()227 Value::~Value() { clear(); }
228 
getPtr() const229 void *Value::getPtr() const {
230   assert(ValueKind == K_PtrOrObj);
231   return Data.m_Ptr;
232 }
233 
getType() const234 QualType Value::getType() const {
235   return QualType::getFromOpaquePtr(OpaqueType);
236 }
237 
getInterpreter()238 Interpreter &Value::getInterpreter() {
239   assert(Interp != nullptr &&
240          "Can't get interpreter from a default constructed value");
241   return *Interp;
242 }
243 
getInterpreter() const244 const Interpreter &Value::getInterpreter() const {
245   assert(Interp != nullptr &&
246          "Can't get interpreter from a default constructed value");
247   return *Interp;
248 }
249 
getASTContext()250 ASTContext &Value::getASTContext() { return getInterpreter().getASTContext(); }
251 
getASTContext() const252 const ASTContext &Value::getASTContext() const {
253   return getInterpreter().getASTContext();
254 }
255 
dump() const256 void Value::dump() const { print(llvm::outs()); }
257 
printType(llvm::raw_ostream & Out) const258 void Value::printType(llvm::raw_ostream &Out) const {
259   Out << "Not implement yet.\n";
260 }
printData(llvm::raw_ostream & Out) const261 void Value::printData(llvm::raw_ostream &Out) const {
262   Out << "Not implement yet.\n";
263 }
print(llvm::raw_ostream & Out) const264 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 
269 } // namespace clang
270