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