clang 23.0.0git
Value.cpp
Go to the documentation of this file.
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
15#include "InterpreterUtils.h"
17#include "clang/AST/Type.h"
19#include "llvm/ADT/StringExtras.h"
20#include <cassert>
21#include <utility>
22
23using namespace clang;
24
25namespace {
26
27// This is internal buffer maintained by Value, used to hold temporaries.
28class ValueStorage {
29public:
30 using DtorFunc = void (*)(void *);
31
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
43 unsigned char *getPayload() { return Storage; }
44 const unsigned char *getPayload() const { return Storage; }
45
46 static unsigned getPayloadOffset() {
47 static ValueStorage Dummy(nullptr, 0, 0);
48 return Dummy.getPayload() - reinterpret_cast<unsigned char *>(&Dummy);
49 }
50
51 static ValueStorage *getFromPayload(void *Payload) {
52 ValueStorage *R = reinterpret_cast<ValueStorage *>(
53 (unsigned char *)Payload - getPayloadOffset());
54 return R;
55 }
56
57 void Retain() { ++RefCnt; }
58
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.
76 bool IsAlive() const {
77 return std::memcmp(getPayload(), Canary, sizeof(Canary)) != 0;
78 }
79
80private:
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
98namespace clang {
99
100static 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 *ED = QT->getAsEnumDecl())
105 QT = ED->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;
119#undef X
120 }
121}
122
123Value::Value(const Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) {
124 const ASTContext &C = getASTContext();
126 if (ValueKind == K_PtrOrObj) {
128 if ((Canon->isPointerType() || Canon->isObjectType() ||
129 Canon->isReferenceType()) &&
130 (Canon->isRecordType() || Canon->isConstantArrayType() ||
131 Canon->isMemberPointerType())) {
132 IsManuallyAlloc = true;
133 // Compile dtor function.
135 void *DtorF = nullptr;
136 size_t ElementsSize = 1;
137 QualType DtorTy = getType();
138
139 if (const auto *ArrTy =
140 llvm::dyn_cast<ConstantArrayType>(DtorTy.getTypePtr())) {
141 DtorTy = ArrTy->getElementType();
142 llvm::APInt ArrSize(sizeof(size_t) * 8, 1);
143 do {
144 ArrSize *= ArrTy->getSize();
145 ArrTy = llvm::dyn_cast<ConstantArrayType>(
146 ArrTy->getElementType().getTypePtr());
147 } while (ArrTy);
148 ElementsSize = static_cast<size_t>(ArrSize.getZExtValue());
149 }
150 if (auto *CXXRD = DtorTy->getAsCXXRecordDecl()) {
152 Interp.CompileDtorCall(CXXRD))
153 DtorF = reinterpret_cast<void *>(Addr->getValue());
154 else
155 llvm::logAllUnhandledErrors(Addr.takeError(), llvm::errs());
156 }
157
158 size_t AllocSize =
160 unsigned char *Payload =
161 ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize);
162 setPtr((void *)Payload);
163 }
164 }
165}
166
167Value::Value(const Value &RHS)
168 : Interp(RHS.Interp), OpaqueType(RHS.OpaqueType), Data(RHS.Data),
170 if (IsManuallyAlloc)
171 ValueStorage::getFromPayload(getPtr())->Retain();
172}
173
174Value::Value(Value &&RHS) noexcept {
175 Interp = std::exchange(RHS.Interp, nullptr);
176 OpaqueType = std::exchange(RHS.OpaqueType, nullptr);
177 Data = RHS.Data;
178 ValueKind = std::exchange(RHS.ValueKind, K_Unspecified);
179 IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false);
180 // The move transfers ownership of the storage refcount from RHS to *this;
181 // RHS.IsManuallyAlloc is now false so its dtor won't Release, leaving
182 // *this as the sole owner of the existing reference. No Retain/Release is
183 // needed -- the physical RefCnt is unchanged across the move.
184}
185
186Value &Value::operator=(const Value &RHS) {
187 if (IsManuallyAlloc)
188 ValueStorage::getFromPayload(getPtr())->Release();
189
190 Interp = RHS.Interp;
192 Data = RHS.Data;
193 ValueKind = RHS.ValueKind;
195
196 if (IsManuallyAlloc)
197 ValueStorage::getFromPayload(getPtr())->Retain();
198
199 return *this;
200}
201
202Value &Value::operator=(Value &&RHS) noexcept {
203 if (this != &RHS) {
204 if (IsManuallyAlloc)
205 ValueStorage::getFromPayload(getPtr())->Release();
206
207 Interp = std::exchange(RHS.Interp, nullptr);
208 OpaqueType = std::exchange(RHS.OpaqueType, nullptr);
209 ValueKind = std::exchange(RHS.ValueKind, K_Unspecified);
210 IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false);
211
212 Data = RHS.Data;
213 }
214 return *this;
215}
216
217void Value::clear() {
218 if (IsManuallyAlloc)
219 ValueStorage::getFromPayload(getPtr())->Release();
221 OpaqueType = nullptr;
222 Interp = nullptr;
223 IsManuallyAlloc = false;
224}
225
226Value::~Value() { clear(); }
227
228void *Value::getPtr() const {
229 assert(ValueKind == K_PtrOrObj);
230 return Data.m_Ptr;
231}
232
233void Value::setRawBits(void *Ptr, unsigned NBytes /*= sizeof(Storage)*/) {
234 assert(NBytes <= sizeof(Storage) && "Greater than the total size");
235 memcpy(/*dest=*/Data.m_RawBits, /*src=*/Ptr, /*nbytes=*/NBytes);
236}
237
238QualType Value::getType() const {
240}
241
242const Interpreter &Value::getInterpreter() const {
243 assert(Interp != nullptr &&
244 "Can't get interpreter from a default constructed value");
245 return *Interp;
246}
247
248const ASTContext &Value::getASTContext() const {
249 return getInterpreter().getASTContext();
250}
251
252void Value::dump() const { print(llvm::outs()); }
253
254void Value::printType(llvm::raw_ostream &Out) const {
255 Out << Interp->ValueTypeToString(*this);
256}
257
258void Value::printData(llvm::raw_ostream &Out) const {
259 Out << Interp->ValueDataToString(*this);
260}
261// FIXME: We do not support the multiple inheritance case where one of the base
262// classes has a pretty-printer and the other does not.
263void Value::print(llvm::raw_ostream &Out) const {
264 assert(OpaqueType != nullptr && "Can't print default Value");
265
266 // Don't even try to print a void or an invalid type, it doesn't make sense.
267 if (getType()->isVoidType() || !isValid())
268 return;
269
270 // We need to get all the results together then print it, since `printType` is
271 // much faster than `printData`.
272 std::string Str;
273 llvm::raw_string_ostream SS(Str);
274
275 SS << "(";
276 printType(SS);
277 SS << ") ";
278 printData(SS);
279 SS << "\n";
280 Out << Str;
281}
282
283} // namespace clang
Defines the clang::ASTContext interface.
#define REPL_BUILTIN_TYPES
Definition Value.h:77
C Language Family Type Representation.
__DEVICE__ void * memcpy(void *__a, const void *__b, size_t __c)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:223
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
CanQualType VoidTy
static bool hasSameType(QualType T1, QualType T2)
Determine whether the given types T1 and T2 are equivalent.
This class is used for builtin types like 'int'.
Definition TypeBase.h:3228
Kind getKind() const
Definition TypeBase.h:3276
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition CharUnits.h:185
Provides top-level interfaces for incremental compilation and execution.
Definition Interpreter.h:98
const ASTContext & getASTContext() const
A (possibly-)qualified type.
Definition TypeBase.h:937
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition TypeBase.h:8447
static QualType getFromOpaquePtr(const void *Ptr)
Definition TypeBase.h:986
QualType getCanonicalType() const
Definition TypeBase.h:8499
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
bool isConstantArrayType() const
Definition TypeBase.h:8787
bool isPointerType() const
Definition TypeBase.h:8684
const T * castAs() const
Member-template castAs<specific type>.
Definition TypeBase.h:9344
bool isReferenceType() const
Definition TypeBase.h:8708
bool isMemberPointerType() const
Definition TypeBase.h:8765
bool isObjectType() const
Determine whether this type is an object type.
Definition TypeBase.h:2570
EnumDecl * getAsEnumDecl() const
Retrieves the EnumDecl this type refers to.
Definition Type.h:53
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9277
bool isRecordType() const
Definition TypeBase.h:8811
const Interpreter & getInterpreter() const
Definition Value.cpp:242
QualType getType() const
Definition Value.cpp:238
void setKind(Kind K)
Definition Value.h:138
void print(llvm::raw_ostream &Out) const
Definition Value.cpp:263
void printType(llvm::raw_ostream &Out) const
Definition Value.cpp:254
void * OpaqueType
Definition Value.h:201
Kind ValueKind
Definition Value.h:203
void clear()
Definition Value.cpp:217
void printData(llvm::raw_ostream &Out) const
Definition Value.cpp:258
Value()=default
void * getPtr() const
Definition Value.cpp:228
bool IsManuallyAlloc
Definition Value.h:204
bool isValid() const
Definition Value.h:133
void setPtr(void *Ptr)
Definition Value.h:142
const ASTContext & getASTContext() const
Definition Value.cpp:248
@ K_Unspecified
Definition Value.h:112
Storage Data
Definition Value.h:202
const Interpreter * Interp
Definition Value.h:200
The JSON file list parser is used to communicate input to InstallAPI.
static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT)
Definition Value.cpp:100