aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Interpreter/Value.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Interpreter/Value.cpp')
-rw-r--r--clang/lib/Interpreter/Value.cpp266
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";
+}