diff options
Diffstat (limited to 'clang/lib/Interpreter/Value.cpp')
-rw-r--r-- | clang/lib/Interpreter/Value.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp new file mode 100644 index 0000000..fe37eeb --- /dev/null +++ b/clang/lib/Interpreter/Value.cpp @@ -0,0 +1,266 @@ +//===--- Interpreter.h - Incremental Compiation and Execution---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the class that used to represent a value in incremental +// C++. +// +//===----------------------------------------------------------------------===// + +#include "clang/Interpreter/Value.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" +#include "clang/Interpreter/Interpreter.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_os_ostream.h" +#include <cassert> +#include <cstdint> +#include <utility> + +using namespace clang; + +namespace { + +// This is internal buffer maintained by Value, used to hold temporaries. +class ValueStorage { +public: + using DtorFunc = void (*)(void *); + + static unsigned char *CreatePayload(void *DtorF, size_t AllocSize, + size_t ElementsSize) { + if (AllocSize < sizeof(Canary)) + AllocSize = sizeof(Canary); + unsigned char *Buf = + new unsigned char[ValueStorage::getPayloadOffset() + AllocSize]; + ValueStorage *VS = new (Buf) ValueStorage(DtorF, AllocSize, ElementsSize); + std::memcpy(VS->getPayload(), Canary, sizeof(Canary)); + return VS->getPayload(); + } + + unsigned char *getPayload() { return Storage; } + const unsigned char *getPayload() const { return Storage; } + + static unsigned getPayloadOffset() { + static ValueStorage Dummy(nullptr, 0, 0); + return Dummy.getPayload() - reinterpret_cast<unsigned char *>(&Dummy); + } + + static ValueStorage *getFromPayload(void *Payload) { + ValueStorage *R = reinterpret_cast<ValueStorage *>( + (unsigned char *)Payload - getPayloadOffset()); + return R; + } + + void Retain() { ++RefCnt; } + + void Release() { + assert(RefCnt > 0 && "Can't release if reference count is already zero"); + if (--RefCnt == 0) { + // We hace a non-trivial dtor. + if (Dtor && IsAlive()) { + assert(Elements && "We at least should have 1 element in Value"); + size_t Stride = AllocSize / Elements; + for (size_t Idx = 0; Idx < Elements; ++Idx) + (*Dtor)(getPayload() + Idx * Stride); + } + delete[] reinterpret_cast<unsigned char *>(this); + } + } + + // Check whether the storage is valid by validating the canary bits. + // If someone accidentally write some invalid bits in the storage, the canary + // will be changed first, and `IsAlive` will return false then. + bool IsAlive() const { + return std::memcmp(getPayload(), Canary, sizeof(Canary)) != 0; + } + +private: + ValueStorage(void *DtorF, size_t AllocSize, size_t ElementsNum) + : RefCnt(1), Dtor(reinterpret_cast<DtorFunc>(DtorF)), + AllocSize(AllocSize), Elements(ElementsNum) {} + + mutable unsigned RefCnt; + DtorFunc Dtor = nullptr; + size_t AllocSize = 0; + size_t Elements = 0; + unsigned char Storage[1]; + + // These are some canary bits that are used for protecting the storage been + // damaged. + static constexpr unsigned char Canary[8] = {0x4c, 0x37, 0xad, 0x8f, + 0x2d, 0x23, 0x95, 0x91}; +}; +} // namespace + +static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) { + if (Ctx.hasSameType(QT, Ctx.VoidTy)) + return Value::K_Void; + + if (const auto *ET = QT->getAs<EnumType>()) + QT = ET->getDecl()->getIntegerType(); + + const auto *BT = QT->getAs<BuiltinType>(); + if (!BT || BT->isNullPtrType()) + return Value::K_PtrOrObj; + + switch (QT->getAs<BuiltinType>()->getKind()) { + default: + assert(false && "Type not supported"); + return Value::K_Unspecified; +#define X(type, name) \ + case BuiltinType::name: \ + return Value::K_##name; + REPL_BUILTIN_TYPES +#undef X + } +} + +Value::Value(Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) { + setKind(ConvertQualTypeToKind(getASTContext(), getType())); + if (ValueKind == K_PtrOrObj) { + QualType Canon = getType().getCanonicalType(); + if ((Canon->isPointerType() || Canon->isObjectType() || + Canon->isReferenceType()) && + (Canon->isRecordType() || Canon->isConstantArrayType() || + Canon->isMemberPointerType())) { + IsManuallyAlloc = true; + // Compile dtor function. + Interpreter &Interp = getInterpreter(); + void *DtorF = nullptr; + size_t ElementsSize = 1; + QualType DtorTy = getType(); + + if (const auto *ArrTy = + llvm::dyn_cast<ConstantArrayType>(DtorTy.getTypePtr())) { + DtorTy = ArrTy->getElementType(); + llvm::APInt ArrSize(sizeof(size_t) * 8, 1); + do { + ArrSize *= ArrTy->getSize(); + ArrTy = llvm::dyn_cast<ConstantArrayType>( + ArrTy->getElementType().getTypePtr()); + } while (ArrTy); + ElementsSize = static_cast<size_t>(ArrSize.getZExtValue()); + } + if (const auto *RT = DtorTy->getAs<RecordType>()) { + if (CXXRecordDecl *CXXRD = + llvm::dyn_cast<CXXRecordDecl>(RT->getDecl())) { + if (llvm::Expected<llvm::orc::ExecutorAddr> Addr = + Interp.CompileDtorCall(CXXRD)) + DtorF = reinterpret_cast<void *>(Addr->getValue()); + else + llvm::logAllUnhandledErrors(Addr.takeError(), llvm::errs()); + } + } + + size_t AllocSize = + getASTContext().getTypeSizeInChars(getType()).getQuantity(); + unsigned char *Payload = + ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize); + setPtr((void *)Payload); + } + } +} + +Value::Value(const Value &RHS) + : Interp(RHS.Interp), OpaqueType(RHS.OpaqueType), Data(RHS.Data), + ValueKind(RHS.ValueKind), IsManuallyAlloc(RHS.IsManuallyAlloc) { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Retain(); +} + +Value::Value(Value &&RHS) noexcept { + Interp = std::exchange(RHS.Interp, nullptr); + OpaqueType = std::exchange(RHS.OpaqueType, nullptr); + Data = RHS.Data; + ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); + IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false); + + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); +} + +Value &Value::operator=(const Value &RHS) { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + + Interp = RHS.Interp; + OpaqueType = RHS.OpaqueType; + Data = RHS.Data; + ValueKind = RHS.ValueKind; + IsManuallyAlloc = RHS.IsManuallyAlloc; + + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Retain(); + + return *this; +} + +Value &Value::operator=(Value &&RHS) noexcept { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + + Interp = std::exchange(RHS.Interp, nullptr); + OpaqueType = std::exchange(RHS.OpaqueType, nullptr); + ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); + IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false); + + Data = RHS.Data; + + return *this; +} + +void Value::clear() { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + ValueKind = K_Unspecified; + OpaqueType = nullptr; + Interp = nullptr; + IsManuallyAlloc = false; +} + +Value::~Value() { clear(); } + +void *Value::getPtr() const { + assert(ValueKind == K_PtrOrObj); + return Data.m_Ptr; +} + +QualType Value::getType() const { + return QualType::getFromOpaquePtr(OpaqueType); +} + +Interpreter &Value::getInterpreter() { + assert(Interp != nullptr && + "Can't get interpreter from a default constructed value"); + return *Interp; +} + +const Interpreter &Value::getInterpreter() const { + assert(Interp != nullptr && + "Can't get interpreter from a default constructed value"); + return *Interp; +} + +ASTContext &Value::getASTContext() { return getInterpreter().getASTContext(); } + +const ASTContext &Value::getASTContext() const { + return getInterpreter().getASTContext(); +} + +void Value::dump() const { print(llvm::outs()); } + +void Value::printType(llvm::raw_ostream &Out) const { + Out << "Not implement yet.\n"; +} +void Value::printData(llvm::raw_ostream &Out) const { + Out << "Not implement yet.\n"; +} +void Value::print(llvm::raw_ostream &Out) const { + assert(OpaqueType != nullptr && "Can't print default Value"); + Out << "Not implement yet.\n"; +} |