[clang] [compiler-rt] [clang-repl] Reimplement value printing using MemoryAccess to support in-process and out-of-process (PR #156649)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 12 08:15:33 PDT 2025
https://github.com/SahilPatidar updated https://github.com/llvm/llvm-project/pull/156649
>From 4a56b9825b7dda9b729bd2a7790526a3a7d286c7 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Tue, 2 Sep 2025 12:08:29 +0530
Subject: [PATCH 1/5] [clang-repl] Reimplement value printing using
MemoryAccess to support in-process and out-of-process
---
clang/include/clang/Interpreter/Interpreter.h | 10 +-
clang/include/clang/Interpreter/Value.h | 50 ++-
clang/lib/Interpreter/Interpreter.cpp | 16 +-
.../Interpreter/InterpreterValuePrinter.cpp | 397 ++++++----------
clang/lib/Interpreter/Value.cpp | 422 +++++++++++++++++-
compiler-rt/lib/orc/CMakeLists.txt | 1 +
compiler-rt/lib/orc/send_value.cpp | 27 ++
7 files changed, 654 insertions(+), 269 deletions(-)
create mode 100644 compiler-rt/lib/orc/send_value.cpp
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 8c124aadf1005..6dd480118caa1 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -104,10 +104,7 @@ class Interpreter {
unsigned InitPTUSize = 0;
- // This member holds the last result of the value printing. It's a class
- // member because we might want to access it after more inputs. If no value
- // printing happens, it's in an invalid state.
- Value LastValue;
+ std::unique_ptr<ValueResultManager> ValMgr;
/// Compiler instance performing the incremental compilation.
std::unique_ptr<CompilerInstance> CI;
@@ -148,7 +145,7 @@ class Interpreter {
llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Code);
llvm::Error Execute(PartialTranslationUnit &T);
- llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr);
+ llvm::Error ParseAndExecute(llvm::StringRef Code);
/// Undo N previous incremental inputs.
llvm::Error Undo(unsigned N = 1);
@@ -187,9 +184,8 @@ class Interpreter {
/// @{
std::string ValueDataToString(const Value &V) const;
- std::string ValueTypeToString(const Value &V) const;
- llvm::Expected<Expr *> convertExprToValue(Expr *E);
+ llvm::Expected<Expr *> convertExprToValue(Expr *E, bool IsOOP = true);
// When we deallocate clang::Value we need to run the destructor of the type.
// This function forces emission of the needed dtor.
diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h
index b91301e6096eb..b2e72261874f1 100644
--- a/clang/include/clang/Interpreter/Value.h
+++ b/clang/include/clang/Interpreter/Value.h
@@ -32,8 +32,11 @@
#ifndef LLVM_CLANG_INTERPRETER_VALUE_H
#define LLVM_CLANG_INTERPRETER_VALUE_H
-
+#include "llvm/ADT/FunctionExtras.h"
#include "llvm/Config/llvm-config.h" // for LLVM_BUILD_LLVM_DYLIB, LLVM_BUILD_SHARED_LIBS
+#include "llvm/ExecutionEngine/Orc/LLJIT.h"
+#include "llvm/ExecutionEngine/Orc/MemoryAccess.h"
+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
#include "llvm/Support/Compiler.h"
#include <cassert>
#include <cstdint>
@@ -107,7 +110,7 @@ class REPL_EXTERNAL_VISIBILITY Value {
REPL_BUILTIN_TYPES
#undef X
- K_Void,
+ K_Void,
K_PtrOrObj,
K_Unspecified
};
@@ -206,5 +209,48 @@ template <> inline void *Value::as() const {
return Data.m_Ptr;
return (void *)as<uintptr_t>();
}
+
+class ValueBuffer {
+public:
+ QualType Ty;
+ virtual ~ValueBuffer() = default;
+ virtual std::string toString() const = 0;
+ virtual bool isValid() const = 0;
+};
+
+class ValueResultManager {
+public:
+ using ValueId = uint64_t;
+ using SendResultFn = llvm::unique_function<void(llvm::Error)>;
+
+ explicit ValueResultManager(ASTContext &Ctx, llvm::orc::MemoryAccess &MemAcc);
+
+ static std::unique_ptr<ValueResultManager>
+ Create(llvm::orc::LLJIT &EE, ASTContext &Ctx, bool IsOutOfProcess = true);
+
+ ValueId registerPendingResult(QualType QT) {
+ ValueId NewID = NextID.fetch_add(1, std::memory_order_relaxed);
+ IdToType.insert({NewID, QT});
+ return NewID;
+ }
+
+ void resetAndDump();
+
+ void deliverResult(SendResultFn SendResult, ValueId ID,
+ llvm::orc::ExecutorAddr VAddr);
+
+private:
+ std::atomic<ValueId> NextID{1};
+ void Initialize(llvm::orc::LLJIT &EE);
+
+ std::string ValueTypeToString(QualType QT) const;
+
+ mutable std::mutex Mutex;
+ ASTContext &Ctx;
+ llvm::orc::MemoryAccess &MemAcc;
+ std::unique_ptr<ValueBuffer> ValBuf = nullptr;
+ llvm::DenseMap<ValueId, clang::QualType> IdToType;
+};
+
} // namespace clang
#endif
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 47995216fac46..7915d005dc8f3 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -301,6 +301,9 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance,
return;
}
}
+
+ ValMgr = ValueResultManager::Create(IncrExecutor->GetExecutionEngine(),
+ getASTContext());
}
Interpreter::~Interpreter() {
@@ -323,6 +326,7 @@ Interpreter::~Interpreter() {
// code them.
const char *const Runtimes = R"(
#define __CLANG_REPL__ 1
+
#ifdef __cplusplus
#define EXTERN_C extern "C"
struct __clang_Interpreter_NewTag{} __ci_newtag;
@@ -343,6 +347,8 @@ const char *const Runtimes = R"(
memcpy(Placement, Src, Size);
}
#endif // __cplusplus
+ EXTERN_C void __clang_Interpreter_SendResultValue(void *Ctx, unsigned long long, void*);
+ EXTERN_C void __orc_rt_SendResultValue(unsigned long long, void*);
EXTERN_C void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*);
EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...);
)";
@@ -571,7 +577,7 @@ llvm::Error Interpreter::Execute(PartialTranslationUnit &T) {
return llvm::Error::success();
}
-llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) {
+llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code) {
auto PTU = Parse(Code);
if (!PTU)
@@ -580,12 +586,8 @@ llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) {
if (llvm::Error Err = Execute(*PTU))
return Err;
- if (LastValue.isValid()) {
- if (!V) {
- LastValue.dump();
- LastValue.clear();
- } else
- *V = std::move(LastValue);
+ if (ValMgr) {
+ ValMgr->resetAndDump();
}
return llvm::Error::success();
}
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 54abfa6dbb9d8..83064e022323f 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -337,11 +337,10 @@ std::string Interpreter::ValueDataToString(const Value &V) const {
return "@" + VoidPtrToString(V.getPtr());
}
-std::string Interpreter::ValueTypeToString(const Value &V) const {
- ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext());
- QualType QT = V.getType();
+std::string ValueResultManager::ValueTypeToString(QualType QT) const {
+ ASTContext &AstCtx = const_cast<ASTContext &>(Ctx);
- std::string QTStr = QualTypeToString(Ctx, QT);
+ std::string QTStr = QualTypeToString(AstCtx, QT);
if (QT->isReferenceType())
QTStr += " &";
@@ -349,6 +348,32 @@ std::string Interpreter::ValueTypeToString(const Value &V) const {
return QTStr;
}
+void ValueResultManager::resetAndDump() {
+ if (!ValBuf)
+ return;
+
+ QualType Ty = ValBuf->Ty;
+
+ std::unique_ptr<ValueBuffer> Val = nullptr;
+ ValBuf.swap(Val);
+
+ // Don't even try to print a void or an invalid type, it doesn't make sense.
+ if (Ty->isVoidType() || !Val->isValid())
+ return;
+
+ // We need to get all the results together then print it, since `printType` is
+ // much faster than `printData`.
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+
+ SS << "(";
+ SS << ValueTypeToString(Ty);
+ SS << ") ";
+ SS << Val->toString();
+ SS << "\n";
+ llvm::outs() << Str;
+}
+
llvm::Expected<llvm::orc::ExecutorAddr>
Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) const {
assert(CXXRD && "Cannot compile a destructor for a nullptr");
@@ -371,99 +396,126 @@ Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) const {
return AddrOrErr;
}
-enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };
+class ExprConverter {
+ ASTContext &Ctx;
-class InterfaceKindVisitor
- : public TypeVisitor<InterfaceKindVisitor, InterfaceKind> {
+public:
+ ExprConverter(ASTContext &Ctx) : Ctx(Ctx) {}
+
+ /// Create (&E) as a void*
+ ExprResult CreateAddressOfVoidPtrExpr(QualType Ty, Expr *ForCast) {
+ QualType VoidPtrTy = Ctx.getPointerType(Ctx.VoidTy);
+
+ // &E
+ Expr *AddrOf = UnaryOperator::Create(
+ Ctx, ForCast, UO_AddrOf, Ctx.getPointerType(Ty), VK_PRValue,
+ OK_Ordinary, SourceLocation(), false, FPOptionsOverride());
+
+ // static_cast<void*>(&E)
+ return CXXStaticCastExpr::Create(
+ Ctx, VoidPtrTy, VK_PRValue, CK_BitCast, AddrOf, nullptr,
+ Ctx.getTrivialTypeSourceInfo(VoidPtrTy), FPOptionsOverride(),
+ SourceLocation(), SourceLocation(), SourceRange());
+ }
- Sema &S;
- Expr *E;
- llvm::SmallVectorImpl<Expr *> &Args;
+ /// Create a temporary VarDecl with initializer.
+ VarDecl *createTempVarDecl(QualType Ty, llvm::StringRef BaseName,
+ Expr *Init) {
+ static unsigned Counter = 0;
+ IdentifierInfo &Id = Ctx.Idents.get((BaseName + Twine(++Counter)).str());
-public:
- InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl<Expr *> &Args)
- : S(S), E(E), Args(Args) {}
+ VarDecl *VD = VarDecl::Create(Ctx, Ctx.getTranslationUnitDecl(),
+ SourceLocation(), SourceLocation(), &Id, Ty,
+ Ctx.getTrivialTypeSourceInfo(Ty), SC_Auto);
- InterfaceKind computeInterfaceKind(QualType Ty) {
- return Visit(Ty.getTypePtr());
- }
+ VD->setInit(Init);
+ VD->setInitStyle(VarDecl::CInit);
+ VD->markUsed(Ctx);
- InterfaceKind VisitRecordType(const RecordType *Ty) {
- return InterfaceKind::WithAlloc;
+ return VD;
}
- InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) {
- return InterfaceKind::WithAlloc;
+ /// Wrap rvalues in a temporary so they become addressable.
+ Expr *CreateMaterializeTemporaryExpr(Expr *E) {
+ return new (Ctx) MaterializeTemporaryExpr(E->getType(), E,
+ /*BoundToLvalueReference=*/true);
}
- InterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) {
- return InterfaceKind::CopyArray;
- }
+ /// Generic helper: materialize if needed, then &expr as void*.
+ ExprResult makeAddressable(QualType QTy, Expr *E) {
+ if (E->isLValue() || E->isXValue())
+ return CreateAddressOfVoidPtrExpr(QTy, E);
- InterfaceKind VisitFunctionType(const FunctionType *Ty) {
- HandlePtrType(Ty);
- return InterfaceKind::NoAlloc;
- }
+ if (E->isPRValue())
+ return CreateAddressOfVoidPtrExpr(QTy, CreateMaterializeTemporaryExpr(E));
- InterfaceKind VisitPointerType(const PointerType *Ty) {
- HandlePtrType(Ty);
- return InterfaceKind::NoAlloc;
+ return ExprError();
}
- InterfaceKind VisitReferenceType(const ReferenceType *Ty) {
- ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);
- assert(!AddrOfE.isInvalid() && "Can not create unary expression");
- Args.push_back(AddrOfE.get());
- return InterfaceKind::NoAlloc;
+ ExprResult handleBuiltinTypeExpr(const BuiltinType *, QualType QTy, Expr *E) {
+ return makeAddressable(QTy, E);
}
+ ExprResult handlePointerTypeExpr(const PointerType *, QualType QTy, Expr *E) {
+ return makeAddressable(QTy, E);
+ }
+ ExprResult handleArrayTypeExpr(const ConstantArrayType *, QualType QTy,
+ Expr *E) {
+ return makeAddressable(QTy, E);
+ }
+};
- InterfaceKind VisitBuiltinType(const BuiltinType *Ty) {
- if (Ty->isNullPtrType())
- Args.push_back(E);
- else if (Ty->isFloatingType())
+class InterfaceKindVisitor : public TypeVisitor<InterfaceKindVisitor, bool> {
+ Sema &S;
+ Expr *E;
+ llvm::SmallVectorImpl<Expr *> &Args;
+ ExprConverter Converter;
+
+public:
+ InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl<Expr *> &Args)
+ : S(S), E(E), Args(Args), Converter(S.getASTContext()) {}
+
+ bool transformExpr(QualType Ty) { return Visit(Ty.getTypePtr()); }
+
+ bool VisitBuiltinType(const BuiltinType *Ty) {
+ if (Ty->isNullPtrType()) {
Args.push_back(E);
- else if (Ty->isIntegralOrEnumerationType())
- HandleIntegralOrEnumType(Ty);
- else if (Ty->isVoidType()) {
- // Do we need to still run `E`?
+ } else if (Ty->isFloatingType() || Ty->isIntegralOrEnumerationType()) {
+ Args.push_back(
+ Converter.handleBuiltinTypeExpr(Ty, QualType(Ty, 0), E).get());
+ } else if (Ty->isVoidType()) {
+ return false;
}
-
- return InterfaceKind::NoAlloc;
+ return true;
}
- InterfaceKind VisitEnumType(const EnumType *Ty) {
- HandleIntegralOrEnumType(Ty);
- return InterfaceKind::NoAlloc;
+ bool VisitPointerType(const PointerType *Ty) {
+ Args.push_back(
+ Converter.handlePointerTypeExpr(Ty, QualType(Ty, 0), E).get());
+ return true;
}
-private:
- // Force cast these types to the uint that fits the register size. That way we
- // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`.
- void HandleIntegralOrEnumType(const Type *Ty) {
- ASTContext &Ctx = S.getASTContext();
- uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);
- QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);
- TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);
- ExprResult CastedExpr =
- S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
- assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr");
- Args.push_back(CastedExpr.get());
+ bool VisitConstantArrayType(const ConstantArrayType *Ty) {
+ Args.push_back(Converter.handleArrayTypeExpr(Ty, QualType(Ty, 0), E).get());
+ return true;
}
- void HandlePtrType(const Type *Ty) {
- ASTContext &Ctx = S.getASTContext();
- TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);
- ExprResult CastedExpr =
- S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
- assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");
- Args.push_back(CastedExpr.get());
+ bool VisitReferenceType(const ReferenceType *Ty) {
+ ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);
+ assert(!AddrOfE.isInvalid() && "Cannot create unary expression");
+ Args.push_back(AddrOfE.get());
+ return true;
}
+
+ bool VisitRecordType(const RecordType *) { return true; }
+ bool VisitMemberPointerType(const MemberPointerType *) { return true; }
+ bool VisitFunctionType(const FunctionType *) { return true; }
+ bool VisitEnumType(const EnumType *) { return true; }
};
-static constexpr llvm::StringRef VPName[] = {
- "__clang_Interpreter_SetValueNoAlloc",
- "__clang_Interpreter_SetValueWithAlloc",
- "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
+enum RunTimeFnTag { OrcSendResult, ClangSendResult };
+
+static constexpr llvm::StringRef RunTimeFnTagName[] = {
+ "__orc_rt_SendResultValue", "__clang_Interpreter_SendResultValue"};
// This synthesizes a call expression to a speciall
// function that is responsible for generating the Value.
@@ -478,7 +530,7 @@ static constexpr llvm::StringRef VPName[] = {
// // 3. If x is a struct, but a rvalue.
// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,
// xQualType)) (x);
-llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
+llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E, bool isOOP) {
Sema &S = getCompilerInstance()->getSema();
ASTContext &Ctx = S.getASTContext();
@@ -500,34 +552,22 @@ llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();
return llvm::Error::success();
};
- if (llvm::Error Err =
- LookupInterface(ValuePrintingInfo[NoAlloc], VPName[NoAlloc]))
- return std::move(Err);
- if (llvm::Error Err =
- LookupInterface(ValuePrintingInfo[CopyArray], VPName[CopyArray]))
+ if (llvm::Error Err = LookupInterface(ValuePrintingInfo[OrcSendResult],
+ RunTimeFnTagName[OrcSendResult]))
return std::move(Err);
- if (llvm::Error Err =
- LookupInterface(ValuePrintingInfo[WithAlloc], VPName[WithAlloc]))
+ if (llvm::Error Err = LookupInterface(ValuePrintingInfo[ClangSendResult],
+ RunTimeFnTagName[ClangSendResult]))
return std::move(Err);
-
- if (Ctx.getLangOpts().CPlusPlus) {
- if (llvm::Error Err =
- LookupInterface(ValuePrintingInfo[NewTag], VPName[NewTag]))
- return std::move(Err);
- }
}
llvm::SmallVector<Expr *, 4> AdjustedArgs;
- // Create parameter `ThisInterp`.
- AdjustedArgs.push_back(CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this));
-
- // Create parameter `OutVal`.
- AdjustedArgs.push_back(
- CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue));
- // Build `__clang_Interpreter_SetValue*` call.
+ if (!isOOP)
+ // Create parameter `ValMgr`.
+ AdjustedArgs.push_back(
+ CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)ValMgr.get()));
// Get rid of ExprWithCleanups.
if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
@@ -542,87 +582,22 @@ llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
Ty = Ctx.getLValueReferenceType(Ty);
}
- Expr *TypeArg =
- CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr());
- // The QualType parameter `OpaqueType`, represented as `void*`.
- AdjustedArgs.push_back(TypeArg);
+ auto ID = ValMgr->registerPendingResult(Ty);
+
+ AdjustedArgs.push_back(IntegerLiteralExpr(Ctx, ID));
// We push the last parameter based on the type of the Expr. Note we need
// special care for rvalue struct.
InterfaceKindVisitor V(S, E, AdjustedArgs);
- Scope *Scope = nullptr;
ExprResult SetValueE;
- InterfaceKind Kind = V.computeInterfaceKind(DesugaredTy);
- switch (Kind) {
- case InterfaceKind::WithAlloc:
- LLVM_FALLTHROUGH;
- case InterfaceKind::CopyArray: {
- // __clang_Interpreter_SetValueWithAlloc.
- ExprResult AllocCall =
- S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc],
- E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
- if (AllocCall.isInvalid())
- return llvm::make_error<llvm::StringError>(
- "Cannot call to " + VPName[WithAlloc],
- llvm::inconvertibleErrorCode());
-
- TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
-
- // Force CodeGen to emit destructor.
- if (auto *RD = Ty->getAsCXXRecordDecl()) {
- auto *Dtor = S.LookupDestructor(RD);
- Dtor->addAttr(UsedAttr::CreateImplicit(Ctx));
- getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
- DeclGroupRef(Dtor));
- }
+ Scope *Scope = nullptr;
+ if (!V.transformExpr(DesugaredTy))
+ return E;
- // __clang_Interpreter_SetValueCopyArr.
- if (Kind == InterfaceKind::CopyArray) {
- const auto *CATy = cast<ConstantArrayType>(DesugaredTy.getTypePtr());
- size_t ArrSize = Ctx.getConstantArrayElementCount(CATy);
-
- if (!Ctx.getLangOpts().CPlusPlus)
- ArrSize *= Ctx.getTypeSizeInChars(CATy->getBaseElementTypeUnsafe())
- .getQuantity();
-
- Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize);
- Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
- SetValueE =
- S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray],
- SourceLocation(), Args, SourceLocation());
- if (SetValueE.isInvalid())
- return llvm::make_error<llvm::StringError>(
- "Cannot call to " + VPName[CopyArray],
- llvm::inconvertibleErrorCode());
- break;
- }
- Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};
- ExprResult CXXNewCall = S.BuildCXXNew(
- E->getSourceRange(),
- /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
- /*PlacementRParen=*/SourceLocation(),
- /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
- E->getSourceRange(), E);
-
- if (CXXNewCall.isInvalid())
- return llvm::make_error<llvm::StringError>(
- "Cannot build a call to placement new",
- llvm::inconvertibleErrorCode());
-
- SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(),
- /*DiscardedValue=*/false);
- break;
- }
- // __clang_Interpreter_SetValueNoAlloc.
- case InterfaceKind::NoAlloc: {
- SetValueE =
- S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::NoAlloc],
- E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
- break;
- }
- default:
- llvm_unreachable("Unhandled InterfaceKind");
- }
+ RunTimeFnTag Tag =
+ isOOP ? RunTimeFnTag::OrcSendResult : RunTimeFnTag::ClangSendResult;
+ SetValueE = S.ActOnCallExpr(Scope, ValuePrintingInfo[Tag], E->getBeginLoc(),
+ AdjustedArgs, E->getEndLoc());
// It could fail, like printing an array type in C. (not supported)
if (SetValueE.isInvalid())
@@ -630,97 +605,17 @@ llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
return SetValueE.get();
}
-
} // namespace clang
using namespace clang;
// Temporary rvalue struct that need special care.
extern "C" {
-REPL_EXTERNAL_VISIBILITY void *
-__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
- void *OpaqueType) {
- Value &VRef = *(Value *)OutVal;
- VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
- return VRef.getPtr();
-}
-
REPL_EXTERNAL_VISIBILITY void
-__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
- ...) {
- Value &VRef = *(Value *)OutVal;
- Interpreter *I = static_cast<Interpreter *>(This);
- VRef = Value(I, OpaqueType);
- if (VRef.isVoid())
- return;
-
- va_list args;
- va_start(args, /*last named param*/ OpaqueType);
-
- QualType QT = VRef.getType();
- if (VRef.getKind() == Value::K_PtrOrObj) {
- VRef.setPtr(va_arg(args, void *));
- } else {
- if (const auto *ED = QT->getAsEnumDecl())
- QT = ED->getIntegerType();
- switch (QT->castAs<BuiltinType>()->getKind()) {
- default:
- llvm_unreachable("unknown type kind!");
- break;
- // Types shorter than int are resolved as int, else va_arg has UB.
- case BuiltinType::Bool:
- VRef.setBool(va_arg(args, int));
- break;
- case BuiltinType::Char_S:
- VRef.setChar_S(va_arg(args, int));
- break;
- case BuiltinType::SChar:
- VRef.setSChar(va_arg(args, int));
- break;
- case BuiltinType::Char_U:
- VRef.setChar_U(va_arg(args, unsigned));
- break;
- case BuiltinType::UChar:
- VRef.setUChar(va_arg(args, unsigned));
- break;
- case BuiltinType::Short:
- VRef.setShort(va_arg(args, int));
- break;
- case BuiltinType::UShort:
- VRef.setUShort(va_arg(args, unsigned));
- break;
- case BuiltinType::Int:
- VRef.setInt(va_arg(args, int));
- break;
- case BuiltinType::UInt:
- VRef.setUInt(va_arg(args, unsigned));
- break;
- case BuiltinType::Long:
- VRef.setLong(va_arg(args, long));
- break;
- case BuiltinType::ULong:
- VRef.setULong(va_arg(args, unsigned long));
- break;
- case BuiltinType::LongLong:
- VRef.setLongLong(va_arg(args, long long));
- break;
- case BuiltinType::ULongLong:
- VRef.setULongLong(va_arg(args, unsigned long long));
- break;
- // Types shorter than double are resolved as double, else va_arg has UB.
- case BuiltinType::Float:
- VRef.setFloat(va_arg(args, double));
- break;
- case BuiltinType::Double:
- VRef.setDouble(va_arg(args, double));
- break;
- case BuiltinType::LongDouble:
- VRef.setLongDouble(va_arg(args, long double));
- break;
- // See REPL_BUILTIN_TYPES.
- }
- }
- va_end(args);
+__clang_Interpreter_SendResultValue(void *Ctx, uint64_t Id, void *Addr) {
+ static_cast<ValueResultManager *>(Ctx)->deliverResult(
+ [](llvm::Error Err) { llvm::cantFail(std::move(Err)); }, Id,
+ llvm::orc::ExecutorAddr::fromPtr(Addr));
}
}
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp
index d4c9d51ffcb61..8136dee622c96 100644
--- a/clang/lib/Interpreter/Value.cpp
+++ b/clang/lib/Interpreter/Value.cpp
@@ -11,12 +11,27 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Interpreter/Value.h"
+#include "clang/AST/Type.h"
+
#include "InterpreterUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Type.h"
#include "clang/Interpreter/Interpreter.h"
+#include "clang/Interpreter/Value.h"
#include "llvm/ADT/StringExtras.h"
+
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
+#include "llvm/ExecutionEngine/Orc/LLJIT.h"
+#include "llvm/ExecutionEngine/Orc/SelfExecutorProcessControl.h"
+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
+#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
+
+#include <atomic>
+#include <future>
+#include <mutex>
+#include <unordered_map>
+
#include <cassert>
#include <utility>
@@ -251,7 +266,7 @@ const ASTContext &Value::getASTContext() const {
void Value::dump() const { print(llvm::outs()); }
void Value::printType(llvm::raw_ostream &Out) const {
- Out << Interp->ValueTypeToString(*this);
+ // Out << Interp->ValueTypeToString(*this);
}
void Value::printData(llvm::raw_ostream &Out) const {
@@ -279,4 +294,407 @@ void Value::print(llvm::raw_ostream &Out) const {
Out << Str;
}
+class BuiltinValueBuffer : public ValueBuffer {
+public:
+ std::vector<char> raw;
+ BuiltinValueBuffer(QualType _Ty) { Ty = _Ty; }
+ template <typename T> T as() const {
+ T v{};
+ assert(raw.size() >= sizeof(T) && "Buffer too small for type!");
+ memcpy(&v, raw.data(), sizeof(T));
+ return v;
+ }
+ std::string toString() const override {
+ if (Ty->isCharType()) {
+ unsigned char c = as<unsigned char>();
+ switch (c) {
+ case '\n':
+ return "'\\n'";
+ case '\t':
+ return "'\\t'";
+ case '\r':
+ return "'\\r'";
+ case '\'':
+ return "'\\''";
+ case '\\':
+ return "'\\'";
+ default:
+ if (std::isprint(c))
+ return std::string("'") + static_cast<char>(c) + "'";
+ else {
+ return llvm::formatv("'\\x{0:02X}'", c).str();
+ }
+ }
+ }
+ if (auto *BT = Ty.getCanonicalType()->getAs<BuiltinType>()) {
+
+ auto formatFloating = [](auto Val, char Suffix = '\0') -> std::string {
+ std::string Out;
+ llvm::raw_string_ostream SS(Out);
+
+ if (std::isnan(Val) || std::isinf(Val)) {
+ SS << llvm::format("%g", Val);
+ return SS.str();
+ }
+ if (Val == static_cast<decltype(Val)>(static_cast<int64_t>(Val)))
+ SS << llvm::format("%.1f", Val);
+ else if (std::abs(Val) < 1e-4 || std::abs(Val) > 1e6 || Suffix == 'f')
+ SS << llvm::format("%#.6g", Val);
+ else if (Suffix == 'L')
+ SS << llvm::format("%#.12Lg", Val);
+ else
+ SS << llvm::format("%#.8g", Val);
+
+ if (Suffix != '\0')
+ SS << Suffix;
+ return SS.str();
+ };
+
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ switch (BT->getKind()) {
+ default:
+ return "{ error: unknown builtin type '" +
+ std::to_string(BT->getKind()) + " '}";
+ case clang::BuiltinType::Bool:
+ SS << ((as<bool>()) ? "true" : "false");
+ return Str;
+ case clang::BuiltinType::Short:
+ SS << as<short>();
+ return Str;
+ case clang::BuiltinType::UShort:
+ SS << as<unsigned short>();
+ return Str;
+ case clang::BuiltinType::Int:
+ SS << as<int>();
+ return Str;
+ case clang::BuiltinType::UInt:
+ SS << as<unsigned int>();
+ return Str;
+ case clang::BuiltinType::Long:
+ SS << as<long>();
+ return Str;
+ case clang::BuiltinType::ULong:
+ SS << as<unsigned long>();
+ return Str;
+ case clang::BuiltinType::LongLong:
+ SS << as<long long>();
+ return Str;
+ case clang::BuiltinType::ULongLong:
+ SS << as<unsigned long long>();
+ return Str;
+ case clang::BuiltinType::Float:
+ return formatFloating(as<float>(), /*suffix=*/'f');
+
+ case clang::BuiltinType::Double:
+ return formatFloating(as<double>());
+
+ case clang::BuiltinType::LongDouble:
+ return formatFloating(as<long double>(), /*suffix=*/'L');
+ }
+ }
+
+ return "";
+ }
+
+ bool isValid() const override { return !raw.empty(); }
+};
+
+class ArrayValueBuffer : public ValueBuffer {
+public:
+ std::vector<std::unique_ptr<ValueBuffer>> Elements;
+ ArrayValueBuffer(QualType EleTy) { Ty = EleTy; }
+ std::string toString() const override {
+ std::ostringstream OS;
+ OS << "{";
+ for (size_t i = 0; i < Elements.size(); ++i) {
+ OS << Elements[i]->toString();
+ if (i + 1 < Elements.size())
+ OS << ",";
+ }
+ OS << "}";
+ return OS.str();
+ }
+
+ bool isValid() const override { return !Elements.empty(); }
+};
+
+static std::string escapeString(const std::vector<char> &Raw) {
+ std::string Out;
+ for (char c : Raw) {
+ switch (c) {
+ case '\n':
+ Out += "\\n";
+ break;
+ case '\t':
+ Out += "\\t";
+ break;
+ case '\r':
+ Out += "\\r";
+ break;
+ case '\"':
+ Out += "\\\"";
+ break;
+ case '\\':
+ Out += "\\\\";
+ break;
+ default:
+ if (std::isprint(static_cast<unsigned char>(c)))
+ Out.push_back(c);
+ else {
+ char buf[5];
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(c));
+ Out += buf;
+ }
+ break;
+ }
+ }
+ return Out;
+}
+
+class PointerValueBuffer : public ValueBuffer {
+public:
+ uint64_t Address = 0;
+ std::unique_ptr<ValueBuffer> Pointee; // optional, used only for char*
+
+ PointerValueBuffer(QualType _Ty, uint64_t Addr = 0) : Address(Addr) {
+ Ty = _Ty;
+ }
+
+ std::string toString() const override {
+ auto PtrTy = dyn_cast<PointerType>(Ty.getTypePtr());
+ if (!PtrTy)
+ return "";
+
+ auto PointeeTy = PtrTy->getPointeeType();
+
+ // char* -> print string literal
+ if (PointeeTy->isCharType() && Pointee) {
+ if (auto *BE = static_cast<BuiltinValueBuffer *>(Pointee.get()))
+ return "\"" + escapeString(BE->raw) + "\"";
+ }
+
+ if (Address == 0)
+ return "nullptr";
+
+ std::ostringstream OS;
+ OS << "0x" << std::hex << Address;
+ return OS.str();
+ }
+
+ bool isValid() const override { return Address != 0; }
+};
+
+class ReaderDispatcher {
+private:
+ ASTContext &Ctx;
+ llvm::orc::MemoryAccess &MA;
+
+public:
+ ReaderDispatcher(ASTContext &Ctx, llvm::orc::MemoryAccess &MA)
+ : Ctx(Ctx), MA(MA) {}
+
+ llvm::Expected<std::unique_ptr<ValueBuffer>>
+ read(QualType QT, llvm::orc::ExecutorAddr Addr);
+
+ llvm::Expected<std::unique_ptr<ValueBuffer>>
+ readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr);
+
+ llvm::Expected<std::unique_ptr<ValueBuffer>>
+ readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr);
+
+ llvm::Expected<std::unique_ptr<ValueBuffer>>
+ readArray(QualType Ty, llvm::orc::ExecutorAddr Addr);
+
+ // TODO: record, function, etc.
+};
+
+class TypeReadVisitor
+ : public TypeVisitor<TypeReadVisitor,
+ llvm::Expected<std::unique_ptr<ValueBuffer>>> {
+ ReaderDispatcher &Dispatcher;
+ llvm::orc::ExecutorAddr Addr;
+
+public:
+ TypeReadVisitor(ReaderDispatcher &D, llvm::orc::ExecutorAddr A)
+ : Dispatcher(D), Addr(A) {}
+
+ llvm::Expected<std::unique_ptr<ValueBuffer>> VisitType(const Type *T) {
+ return llvm::make_error<llvm::StringError>(
+ "Unsupported type in ReaderDispatcher", llvm::inconvertibleErrorCode());
+ }
+
+ llvm::Expected<std::unique_ptr<ValueBuffer>>
+ VisitBuiltinType(const BuiltinType *BT) {
+ return Dispatcher.readBuiltin(QualType(BT, 0), Addr);
+ }
+
+ llvm::Expected<std::unique_ptr<ValueBuffer>>
+ VisitPointerType(const PointerType *PT) {
+ return Dispatcher.readPointer(QualType(PT, 0), Addr);
+ }
+
+ llvm::Expected<std::unique_ptr<ValueBuffer>>
+ VisitConstantArrayType(const ConstantArrayType *AT) {
+ return Dispatcher.readArray(QualType(AT, 0), Addr);
+ }
+
+ llvm::Expected<std::unique_ptr<ValueBuffer>>
+ VisitRecordType(const RecordType *RT) {
+ return llvm::make_error<llvm::StringError>(
+ "RecordType reading not yet implemented",
+ llvm::inconvertibleErrorCode());
+ }
+};
+
+llvm::Expected<std::unique_ptr<ValueBuffer>>
+ReaderDispatcher::read(QualType QT, llvm::orc::ExecutorAddr Addr) {
+ TypeReadVisitor V(*this, Addr);
+ return V.Visit(QT.getTypePtr());
+}
+
+llvm::Expected<std::unique_ptr<ValueBuffer>>
+ReaderDispatcher::readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr) {
+ auto Size = Ctx.getTypeSizeInChars(Ty).getQuantity();
+ auto ResOrErr = MA.readBuffers({llvm::orc::ExecutorAddrRange(Addr, Size)});
+ if (!ResOrErr)
+ return ResOrErr.takeError();
+
+ auto Buf = std::make_unique<BuiltinValueBuffer>(Ty);
+ const auto &Res = *ResOrErr;
+ std::vector<char> ElemBuf(Size);
+ std::memcpy(ElemBuf.data(), Res.back().data(), Size);
+ Buf->raw = std::move(ElemBuf);
+ return std::move(Buf);
+}
+
+llvm::Expected<std::unique_ptr<ValueBuffer>>
+ReaderDispatcher::readArray(QualType Ty, llvm::orc::ExecutorAddr Addr) {
+ const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(Ty);
+ if (!CAT)
+ return llvm::make_error<llvm::StringError>("Not a ConstantArrayType",
+ llvm::inconvertibleErrorCode());
+
+ QualType ElemTy = CAT->getElementType();
+ size_t ElemSize = Ctx.getTypeSizeInChars(ElemTy).getQuantity();
+
+ auto Buf = std::make_unique<ArrayValueBuffer>(Ty);
+
+ for (size_t i = 0; i < CAT->getZExtSize(); ++i) {
+ auto ElemAddr = Addr + i * ElemSize;
+ auto ElemBufOrErr = read(ElemTy, ElemAddr);
+ if (!ElemBufOrErr)
+ return ElemBufOrErr.takeError();
+ Buf->Elements.push_back(std::move(*ElemBufOrErr));
+ }
+
+ return std::move(Buf);
+}
+
+llvm::Expected<std::unique_ptr<ValueBuffer>>
+ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty,
+ llvm::orc::ExecutorAddr Addr) {
+ auto PtrTy = dyn_cast<PointerType>(Ty.getTypePtr());
+ if (!PtrTy)
+ return llvm::make_error<llvm::StringError>("Not a PointerType",
+ llvm::inconvertibleErrorCode());
+
+ auto AddrOrErr = MA.readUInt64s({Addr});
+ if (!AddrOrErr)
+ return AddrOrErr.takeError();
+
+ uint64_t PtrValAddr = AddrOrErr->back();
+ if (PtrValAddr == 0)
+ return std::make_unique<PointerValueBuffer>(Ty); // null pointer
+
+ llvm::orc::ExecutorAddr PointeeAddr(PtrValAddr);
+ auto PtrBuf = std::make_unique<PointerValueBuffer>(Ty, PtrValAddr);
+
+ QualType PointeeTy = PtrTy->getPointeeType();
+ if (PointeeTy->isCharType()) {
+ std::string S;
+ for (size_t i = 0; i < 1024; ++i) {
+ auto CRes = MA.readUInt8s({PointeeAddr + i});
+ if (!CRes)
+ return CRes.takeError();
+ char c = static_cast<char>(CRes->back());
+ if (c == '\0')
+ break;
+ S.push_back(c);
+ }
+ auto Buf = std::make_unique<BuiltinValueBuffer>(PointeeTy);
+ Buf->raw.assign(S.begin(), S.end());
+ PtrBuf->Pointee = std::move(Buf);
+ }
+ // else {
+ // auto BufOrErr = read(PointeeTy, PointeeAddr);
+ // if (!BufOrErr)
+ // return BufOrErr.takeError();
+ // PtrBuf->Pointee = std::move(*BufOrErr);
+ // }
+ return std::move(PtrBuf);
+}
+
+ValueResultManager::ValueResultManager(ASTContext &Ctx,
+ llvm::orc::MemoryAccess &MA)
+ : Ctx(Ctx), MemAcc(MA) {}
+
+std::unique_ptr<ValueResultManager>
+ValueResultManager::Create(llvm::orc::LLJIT &EE, ASTContext &Ctx, bool IsOop) {
+ auto &ES = EE.getExecutionSession();
+ auto &EPC = ES.getExecutorProcessControl();
+ auto VRMgr = std::make_unique<ValueResultManager>(Ctx, EPC.getMemoryAccess());
+ if (IsOop)
+ VRMgr->Initialize(EE);
+ return VRMgr;
+}
+
+void ValueResultManager::Initialize(llvm::orc::LLJIT &EE) {
+ auto &ES = EE.getExecutionSession();
+
+ llvm::orc::ExecutionSession::JITDispatchHandlerAssociationMap Handlers;
+ using OrcSendResultFn =
+ llvm::orc::shared::SPSError(uint64_t, llvm::orc::shared::SPSExecutorAddr);
+
+ const char *SendValFnTag = "___orc_rt_SendResultValue_tag";
+#ifndef __APPLE__
+ ++SendValFnTag;
+#endif
+ Handlers[ES.intern(SendValFnTag)] = ES.wrapAsyncWithSPS<OrcSendResultFn>(
+ this, &ValueResultManager::deliverResult);
+
+ llvm::cantFail(ES.registerJITDispatchHandlers(*EE.getPlatformJITDylib(),
+ std::move(Handlers)));
+}
+
+void ValueResultManager::deliverResult(SendResultFn SendResult, ValueId ID,
+ llvm::orc::ExecutorAddr Addr) {
+ QualType Ty;
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ auto It = IdToType.find(ID);
+ if (It == IdToType.end()) {
+ SendResult(llvm::make_error<llvm::StringError>(
+ "Unknown ValueId in deliverResult", llvm::inconvertibleErrorCode()));
+ }
+ Ty = It->second;
+ IdToType.erase(It);
+ }
+
+ ReaderDispatcher Runner(Ctx, MemAcc);
+ auto BufOrErr = Runner.read(Ty, Addr);
+
+ ValBuf.reset();
+ if (!BufOrErr) {
+ SendResult(BufOrErr.takeError());
+ return;
+ }
+
+ // Store the successfully read value buffer
+ ValBuf.swap(*BufOrErr);
+
+ SendResult(llvm::Error::success());
+ return;
+}
} // namespace clang
diff --git a/compiler-rt/lib/orc/CMakeLists.txt b/compiler-rt/lib/orc/CMakeLists.txt
index b8d1b03b788c9..1addf77794a94 100644
--- a/compiler-rt/lib/orc/CMakeLists.txt
+++ b/compiler-rt/lib/orc/CMakeLists.txt
@@ -8,6 +8,7 @@ set(ORC_COMMON_SOURCES
log_error_to_stderr.cpp
run_program_wrapper.cpp
resolve.cpp
+ send_value.cpp
)
# Common implementation headers will go here.
diff --git a/compiler-rt/lib/orc/send_value.cpp b/compiler-rt/lib/orc/send_value.cpp
new file mode 100644
index 0000000000000..31834439e6895
--- /dev/null
+++ b/compiler-rt/lib/orc/send_value.cpp
@@ -0,0 +1,27 @@
+//===- send_value.cpp ----------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "common.h"
+#include "jit_dispatch.h"
+#include "wrapper_function_utils.h"
+#include "debug.h"
+
+using namespace orc_rt;
+
+ORC_RT_JIT_DISPATCH_TAG(__orc_rt_SendResultValue_tag)
+
+ORC_RT_INTERFACE void __orc_rt_SendResultValue(uint64_t ResultId, void *V) {
+ Error OptErr = Error::success();
+ if (auto Err = WrapperFunction<SPSError(uint64_t, SPSExecutorAddr)>::call(
+ JITDispatch(&__orc_rt_SendResultValue_tag), OptErr, ResultId,
+ ExecutorAddr::fromPtr(V))) {
+ cantFail(std::move(OptErr));
+ cantFail(std::move(Err));
+ }
+ consumeError(std::move(OptErr));
+}
>From ab676fd6002cd6a95cb3bbb45aa459dc80609d87 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 3 Sep 2025 17:11:34 +0530
Subject: [PATCH 2/5] Fix issues and add ValueToString helper
---
clang/include/clang/Interpreter/Interpreter.h | 2 +-
clang/include/clang/Interpreter/Value.h | 64 +++-
.../Interpreter/InterpreterValuePrinter.cpp | 285 +++++++++++-------
clang/lib/Interpreter/Value.cpp | 218 ++------------
4 files changed, 251 insertions(+), 318 deletions(-)
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 6dd480118caa1..c1e7ca48c0961 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -185,7 +185,7 @@ class Interpreter {
std::string ValueDataToString(const Value &V) const;
- llvm::Expected<Expr *> convertExprToValue(Expr *E, bool IsOOP = true);
+ llvm::Expected<Expr *> convertExprToValue(Expr *E, bool IsOOP = false);
// When we deallocate clang::Value we need to run the destructor of the type.
// This function forces emission of the needed dtor.
diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h
index b2e72261874f1..dda8c6bbf09aa 100644
--- a/clang/include/clang/Interpreter/Value.h
+++ b/clang/include/clang/Interpreter/Value.h
@@ -212,10 +212,68 @@ template <> inline void *Value::as() const {
class ValueBuffer {
public:
+ enum Kind { K_Builtin, K_Array, K_Pointer, K_Unknown };
QualType Ty;
+ Kind K;
+ ValueBuffer(Kind K = K_Unknown) : K(K) {}
virtual ~ValueBuffer() = default;
- virtual std::string toString() const = 0;
- virtual bool isValid() const = 0;
+ bool isUnknown() const { return K == K_Unknown; }
+ bool isBuiltin() const { return K == K_Builtin; }
+ bool isArray() const { return K == K_Array; }
+ bool isPointer() const { return K == K_Pointer; }
+ static bool classof(const ValueBuffer *) { return true; }
+};
+
+class BuiltinValueBuffer;
+class ArrayValueBuffer;
+class PointerValueBuffer;
+
+class BuiltinValueBuffer : public ValueBuffer {
+public:
+ std::vector<uint8_t> raw;
+ BuiltinValueBuffer(QualType _Ty) : ValueBuffer(K_Builtin) { Ty = _Ty; }
+ template <typename T> T as() const {
+ T v{};
+ // assert(raw.size() >= sizeof(T) && "Buffer too small for type!");
+ memcpy(&v, raw.data(), sizeof(T));
+ return v;
+ }
+ static bool classof(const ValueBuffer *B) { return B->isBuiltin(); }
+};
+
+class ArrayValueBuffer : public ValueBuffer {
+public:
+ std::vector<std::unique_ptr<ValueBuffer>> Elements;
+ ArrayValueBuffer(QualType EleTy) : ValueBuffer(K_Array) { Ty = EleTy; }
+
+ static bool classof(const ValueBuffer *B) { return B->isArray(); }
+};
+
+class PointerValueBuffer : public ValueBuffer {
+public:
+ uint64_t Address = 0;
+ std::unique_ptr<ValueBuffer> Pointee; // optional, used only for char*
+
+ PointerValueBuffer(QualType _Ty, uint64_t Addr = 0)
+ : ValueBuffer(K_Pointer), Address(Addr) {
+ Ty = _Ty;
+ }
+
+ static bool classof(const ValueBuffer *B) { return B->isPointer(); }
+};
+
+class ValueToString {
+private:
+ ASTContext &Ctx;
+
+public:
+ ValueToString(ASTContext &Ctx) : Ctx(Ctx) {}
+ std::string toString(const ValueBuffer *);
+
+private:
+ std::string BuiltinToString(const BuiltinValueBuffer &B);
+ std::string PointerToString(const PointerValueBuffer &P);
+ std::string ArrayToString(const ArrayValueBuffer &A);
};
class ValueResultManager {
@@ -226,7 +284,7 @@ class ValueResultManager {
explicit ValueResultManager(ASTContext &Ctx, llvm::orc::MemoryAccess &MemAcc);
static std::unique_ptr<ValueResultManager>
- Create(llvm::orc::LLJIT &EE, ASTContext &Ctx, bool IsOutOfProcess = true);
+ Create(llvm::orc::LLJIT &EE, ASTContext &Ctx, bool IsOutOfProcess = false);
ValueId registerPendingResult(QualType QT) {
ValueId NewID = NextID.fetch_add(1, std::memory_order_relaxed);
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 83064e022323f..7faae1ecaa9d9 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -96,12 +96,15 @@ static std::string QualTypeToString(ASTContext &Ctx, QualType QT) {
return GetFullTypeName(Ctx, NonRefTy);
}
-static std::string EnumToString(const Value &V) {
+static std::string EnumToString(ASTContext &Ctx, QualType QT, uint64_t Data) {
std::string Str;
llvm::raw_string_ostream SS(Str);
- ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext());
- uint64_t Data = V.convertTo<uint64_t>();
+ QualType DesugaredTy = QT.getDesugaredType(Ctx);
+ const EnumType *EnumTy = DesugaredTy.getNonReferenceType()->getAs<EnumType>();
+ assert(EnumTy && "Fail to cast to enum type");
+
+ EnumDecl *ED = EnumTy->getDecl();
bool IsFirst = true;
llvm::APSInt AP = Ctx.MakeIntValue(Data, V.getType());
@@ -120,12 +123,13 @@ static std::string EnumToString(const Value &V) {
return Str;
}
-static std::string FunctionToString(const Value &V, const void *Ptr) {
+static std::string FunctionToString(ASTContext &Ctx, QualType QT,
+ const void *Ptr) {
std::string Str;
llvm::raw_string_ostream SS(Str);
SS << "Function @" << Ptr;
- const DeclContext *PTU = V.getASTContext().getTranslationUnitDecl();
+ const DeclContext *PTU = Ctx.getTranslationUnitDecl();
// Find the last top-level-stmt-decl. This is a forward iterator but the
// partial translation unit should not be large.
const TopLevelStmtDecl *TLSD = nullptr;
@@ -154,84 +158,85 @@ static std::string FunctionToString(const Value &V, const void *Ptr) {
return Str;
}
-static std::string VoidPtrToString(const void *Ptr) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << Ptr;
- return Str;
-}
-
-static std::string CharPtrToString(const char *Ptr) {
- if (!Ptr)
- return "0";
-
- std::string Result = "\"";
- Result += Ptr;
- Result += '"';
- return Result;
+static std::string escapeString(const std::vector<uint8_t> &Raw) {
+ std::string Out;
+ for (char c : Raw) {
+ switch (c) {
+ case '\n':
+ Out += "\\n";
+ break;
+ case '\t':
+ Out += "\\t";
+ break;
+ case '\r':
+ Out += "\\r";
+ break;
+ case '\"':
+ Out += "\\\"";
+ break;
+ case '\\':
+ Out += "\\\\";
+ break;
+ default:
+ if (std::isprint(static_cast<unsigned char>(c)))
+ Out.push_back(c);
+ else {
+ char buf[5];
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(c));
+ Out += buf;
+ }
+ break;
+ }
+ }
+ return Out;
}
namespace clang {
-struct ValueRef : public Value {
- ValueRef(const Interpreter *In, void *Ty) : Value(In, Ty) {
- // Tell the base class to not try to deallocate if it manages the value.
- IsManuallyAlloc = false;
- }
-};
-
-std::string Interpreter::ValueDataToString(const Value &V) const {
- Sema &S = getCompilerInstance()->getSema();
- ASTContext &Ctx = S.getASTContext();
-
- QualType QT = V.getType();
+std::string Interpreter::ValueDataToString(const Value &V) const { return ""; }
+
+std::string ValueToString::toString(const ValueBuffer *Buf) {
+ if (const BuiltinValueBuffer *B = llvm::dyn_cast<BuiltinValueBuffer>(Buf))
+ return BuiltinToString(*B);
+ else if (const ArrayValueBuffer *A = llvm::dyn_cast<ArrayValueBuffer>(Buf))
+ return ArrayToString(*A);
+ else if (const PointerValueBuffer *P =
+ llvm::dyn_cast<PointerValueBuffer>(Buf))
+ return PointerToString(*P);
+ return "";
+}
- if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(QT)) {
- QualType ElemTy = CAT->getElementType();
- size_t ElemCount = Ctx.getConstantArrayElementCount(CAT);
- const Type *BaseTy = CAT->getBaseElementTypeUnsafe();
- size_t ElemSize = Ctx.getTypeSizeInChars(BaseTy).getQuantity();
+std::string ValueToString::BuiltinToString(const BuiltinValueBuffer &B) {
+ if (B.raw.empty())
+ return ""; // No data in buffer
- // Treat null terminated char arrays as strings basically.
- if (ElemTy->isCharType()) {
- char last = *(char *)(((uintptr_t)V.getPtr()) + ElemCount * ElemSize - 1);
- if (last == '\0')
- return CharPtrToString((char *)V.getPtr());
- }
+ QualType QT = B.Ty;
+ QualType DesugaredTy = QT.getDesugaredType(Ctx);
+ QualType NonRefTy = DesugaredTy.getNonReferenceType();
- std::string Result = "{ ";
- for (unsigned Idx = 0, N = CAT->getZExtSize(); Idx < N; ++Idx) {
- ValueRef InnerV = ValueRef(this, ElemTy.getAsOpaquePtr());
- if (ElemTy->isBuiltinType()) {
- // Single dim arrays, advancing.
- uintptr_t Offset = (uintptr_t)V.getPtr() + Idx * ElemSize;
- InnerV.setRawBits((void *)Offset, ElemSize * 8);
- } else {
- // Multi dim arrays, position to the next dimension.
- size_t Stride = ElemCount / N;
- uintptr_t Offset = ((uintptr_t)V.getPtr()) + Idx * Stride * ElemSize;
- InnerV.setPtr((void *)Offset);
+ if (NonRefTy->isCharType()) {
+ unsigned char c = B.as<unsigned char>();
+ switch (c) {
+ case '\n':
+ return "'\\n'";
+ case '\t':
+ return "'\\t'";
+ case '\r':
+ return "'\\r'";
+ case '\'':
+ return "'\\''";
+ case '\\':
+ return "'\\'";
+ case '\0':
+ return "";
+ default:
+ if (std::isprint(c))
+ return std::string("'") + static_cast<char>(c) + "'";
+ else {
+ return llvm::formatv("'\\x{0:02X}'", c).str();
}
-
- Result += ValueDataToString(InnerV);
-
- // Skip the \0 if the char types
- if (Idx < N - 1)
- Result += ", ";
}
- Result += " }";
- return Result;
}
-
- QualType DesugaredTy = QT.getDesugaredType(Ctx);
- QualType NonRefTy = DesugaredTy.getNonReferenceType();
-
- // FIXME: Add support for user defined printers.
- // LookupResult R = LookupUserDefined(S, QT);
- // if (!R.empty())
- // return CallUserSpecifiedPrinter(R, V);
-
- // If it is a builtin type dispatch to the builtin overloads.
if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) {
auto formatFloating = [](auto Val, char Suffix = '\0') -> std::string {
@@ -263,78 +268,117 @@ std::string Interpreter::ValueDataToString(const Value &V) const {
return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) +
" '}";
case clang::BuiltinType::Bool:
- SS << ((V.getBool()) ? "true" : "false");
- return Str;
- case clang::BuiltinType::Char_S:
- SS << '\'' << V.getChar_S() << '\'';
- return Str;
- case clang::BuiltinType::SChar:
- SS << '\'' << V.getSChar() << '\'';
- return Str;
- case clang::BuiltinType::Char_U:
- SS << '\'' << V.getChar_U() << '\'';
- return Str;
- case clang::BuiltinType::UChar:
- SS << '\'' << V.getUChar() << '\'';
+ SS << ((B.as<bool>()) ? "true" : "false");
return Str;
case clang::BuiltinType::Short:
- SS << V.getShort();
+ SS << B.as<short>();
return Str;
case clang::BuiltinType::UShort:
- SS << V.getUShort();
+ SS << B.as<unsigned short>();
return Str;
case clang::BuiltinType::Int:
- SS << V.getInt();
+ SS << B.as<int>();
return Str;
case clang::BuiltinType::UInt:
- SS << V.getUInt();
+ SS << B.as<unsigned int>();
return Str;
case clang::BuiltinType::Long:
- SS << V.getLong();
+ SS << B.as<long>();
return Str;
case clang::BuiltinType::ULong:
- SS << V.getULong();
+ SS << B.as<unsigned long>();
return Str;
case clang::BuiltinType::LongLong:
- SS << V.getLongLong();
+ SS << B.as<long long>();
return Str;
case clang::BuiltinType::ULongLong:
- SS << V.getULongLong();
+ SS << B.as<unsigned long long>();
return Str;
case clang::BuiltinType::Float:
- return formatFloating(V.getFloat(), /*suffix=*/'f');
+ return formatFloating(B.as<float>(), /*suffix=*/'f');
case clang::BuiltinType::Double:
- return formatFloating(V.getDouble());
+ return formatFloating(B.as<double>());
case clang::BuiltinType::LongDouble:
- return formatFloating(V.getLongDouble(), /*suffix=*/'L');
+ return formatFloating(B.as<long double>(), /*suffix=*/'L');
}
}
+ if (NonRefTy->isEnumeralType())
+ return EnumToString(Ctx, QT, B.as<uint64_t>());
+
+ return "";
+}
+
+std::string ValueToString::PointerToString(const PointerValueBuffer &P) {
+
+ QualType QT = P.Ty;
+ QualType DesugaredTy = QT.getDesugaredType(Ctx);
+ QualType NonRefTy = DesugaredTy.getNonReferenceType();
+
+ auto PtrTy = dyn_cast<PointerType>(QT.getTypePtr());
+ if (!PtrTy)
+ return "";
+
+ auto PointeeTy = PtrTy->getPointeeType();
+
+ // char* -> print string literal
+ if (PointeeTy->isCharType() && P.Pointee) {
+ if (auto *BE = static_cast<BuiltinValueBuffer *>(P.Pointee.get()))
+ return "\"" + escapeString(BE->raw) + "\"";
+ }
+
+ if (P.Address == 0)
+ return "nullptr";
+
if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
NonRefTy->getPointeeType()->isFunctionProtoType())
- return FunctionToString(V, V.getPtr());
+ return FunctionToString(Ctx, QT, (void *)P.Address);
if (NonRefTy->isFunctionType())
- return FunctionToString(V, &V);
+ return FunctionToString(Ctx, QT, (void *)P.Address);
- if (NonRefTy->isEnumeralType())
- return EnumToString(V);
-
- if (NonRefTy->isNullPtrType())
- return "nullptr\n";
-
- // FIXME: Add support for custom printers in C.
- if (NonRefTy->isPointerType()) {
- if (NonRefTy->getPointeeType()->isCharType())
- return CharPtrToString((char *)V.getPtr());
+ std::ostringstream OS;
+ OS << "@0x" << std::hex << P.Address;
+ return OS.str();
+}
- return VoidPtrToString(V.getPtr());
+std::string ValueToString::ArrayToString(const ArrayValueBuffer &A) {
+ if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(A.Ty)) {
+ QualType ElemTy = CAT->getElementType();
+ std::ostringstream OS;
+ // Treat null terminated char arrays as strings basically.
+ if (ElemTy->isCharType() && !A.Elements.empty()) {
+ if (const auto *B =
+ llvm::dyn_cast<BuiltinValueBuffer>(A.Elements.back().get())) {
+ char last = (char)B->raw.back();
+ if (last != '\0')
+ goto not_a_string;
+ }
+ OS << "\"";
+ for (size_t i = 0; i < A.Elements.size(); ++i) {
+ if (const auto *B =
+ llvm::dyn_cast<BuiltinValueBuffer>(A.Elements[i].get())) {
+ OS << static_cast<char>(B->raw.back());
+ }
+ }
+ OS << "\"";
+ return OS.str();
+ }
+ }
+not_a_string:
+ std::ostringstream OS;
+
+ OS << "{ ";
+ for (size_t i = 0; i < A.Elements.size(); ++i) {
+ OS << this->toString(A.Elements[i].get());
+ if (i + 1 < A.Elements.size())
+ OS << ", ";
}
- // Fall back to printing just the address of the unknown object.
- return "@" + VoidPtrToString(V.getPtr());
+ OS << " }";
+ return OS.str();
}
std::string ValueResultManager::ValueTypeToString(QualType QT) const {
@@ -358,18 +402,18 @@ void ValueResultManager::resetAndDump() {
ValBuf.swap(Val);
// Don't even try to print a void or an invalid type, it doesn't make sense.
- if (Ty->isVoidType() || !Val->isValid())
+ if (Ty->isVoidType() || (!Val || (Val && Val->isUnknown())))
return;
// We need to get all the results together then print it, since `printType` is
// much faster than `printData`.
std::string Str;
llvm::raw_string_ostream SS(Str);
-
+ ValueToString ValToStr(Ctx);
SS << "(";
SS << ValueTypeToString(Ty);
SS << ") ";
- SS << Val->toString();
+ SS << ValToStr.toString(Val.get());
SS << "\n";
llvm::outs() << Str;
}
@@ -435,7 +479,7 @@ class ExprConverter {
return VD;
}
- /// Wrap rvalues in a temporary so they become addressable.
+ /// Wrap rvalues in a temporary (var) so they become addressable.
Expr *CreateMaterializeTemporaryExpr(Expr *E) {
return new (Ctx) MaterializeTemporaryExpr(E->getType(), E,
/*BoundToLvalueReference=*/true);
@@ -462,6 +506,9 @@ class ExprConverter {
Expr *E) {
return makeAddressable(QTy, E);
}
+ ExprResult handleEnumTypeExpr(const EnumType *, QualType QTy, Expr *E) {
+ return makeAddressable(QTy, E);
+ }
};
class InterfaceKindVisitor : public TypeVisitor<InterfaceKindVisitor, bool> {
@@ -506,10 +553,14 @@ class InterfaceKindVisitor : public TypeVisitor<InterfaceKindVisitor, bool> {
return true;
}
+ bool VisitEnumType(const EnumType *Ty) {
+ Args.push_back(Converter.handleEnumTypeExpr(Ty, QualType(Ty, 0), E).get());
+ return true;
+ }
+
bool VisitRecordType(const RecordType *) { return true; }
bool VisitMemberPointerType(const MemberPointerType *) { return true; }
bool VisitFunctionType(const FunctionType *) { return true; }
- bool VisitEnumType(const EnumType *) { return true; }
};
enum RunTimeFnTag { OrcSendResult, ClangSendResult };
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp
index 8136dee622c96..3ac741c83c808 100644
--- a/clang/lib/Interpreter/Value.cpp
+++ b/clang/lib/Interpreter/Value.cpp
@@ -294,197 +294,6 @@ void Value::print(llvm::raw_ostream &Out) const {
Out << Str;
}
-class BuiltinValueBuffer : public ValueBuffer {
-public:
- std::vector<char> raw;
- BuiltinValueBuffer(QualType _Ty) { Ty = _Ty; }
- template <typename T> T as() const {
- T v{};
- assert(raw.size() >= sizeof(T) && "Buffer too small for type!");
- memcpy(&v, raw.data(), sizeof(T));
- return v;
- }
- std::string toString() const override {
- if (Ty->isCharType()) {
- unsigned char c = as<unsigned char>();
- switch (c) {
- case '\n':
- return "'\\n'";
- case '\t':
- return "'\\t'";
- case '\r':
- return "'\\r'";
- case '\'':
- return "'\\''";
- case '\\':
- return "'\\'";
- default:
- if (std::isprint(c))
- return std::string("'") + static_cast<char>(c) + "'";
- else {
- return llvm::formatv("'\\x{0:02X}'", c).str();
- }
- }
- }
- if (auto *BT = Ty.getCanonicalType()->getAs<BuiltinType>()) {
-
- auto formatFloating = [](auto Val, char Suffix = '\0') -> std::string {
- std::string Out;
- llvm::raw_string_ostream SS(Out);
-
- if (std::isnan(Val) || std::isinf(Val)) {
- SS << llvm::format("%g", Val);
- return SS.str();
- }
- if (Val == static_cast<decltype(Val)>(static_cast<int64_t>(Val)))
- SS << llvm::format("%.1f", Val);
- else if (std::abs(Val) < 1e-4 || std::abs(Val) > 1e6 || Suffix == 'f')
- SS << llvm::format("%#.6g", Val);
- else if (Suffix == 'L')
- SS << llvm::format("%#.12Lg", Val);
- else
- SS << llvm::format("%#.8g", Val);
-
- if (Suffix != '\0')
- SS << Suffix;
- return SS.str();
- };
-
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- switch (BT->getKind()) {
- default:
- return "{ error: unknown builtin type '" +
- std::to_string(BT->getKind()) + " '}";
- case clang::BuiltinType::Bool:
- SS << ((as<bool>()) ? "true" : "false");
- return Str;
- case clang::BuiltinType::Short:
- SS << as<short>();
- return Str;
- case clang::BuiltinType::UShort:
- SS << as<unsigned short>();
- return Str;
- case clang::BuiltinType::Int:
- SS << as<int>();
- return Str;
- case clang::BuiltinType::UInt:
- SS << as<unsigned int>();
- return Str;
- case clang::BuiltinType::Long:
- SS << as<long>();
- return Str;
- case clang::BuiltinType::ULong:
- SS << as<unsigned long>();
- return Str;
- case clang::BuiltinType::LongLong:
- SS << as<long long>();
- return Str;
- case clang::BuiltinType::ULongLong:
- SS << as<unsigned long long>();
- return Str;
- case clang::BuiltinType::Float:
- return formatFloating(as<float>(), /*suffix=*/'f');
-
- case clang::BuiltinType::Double:
- return formatFloating(as<double>());
-
- case clang::BuiltinType::LongDouble:
- return formatFloating(as<long double>(), /*suffix=*/'L');
- }
- }
-
- return "";
- }
-
- bool isValid() const override { return !raw.empty(); }
-};
-
-class ArrayValueBuffer : public ValueBuffer {
-public:
- std::vector<std::unique_ptr<ValueBuffer>> Elements;
- ArrayValueBuffer(QualType EleTy) { Ty = EleTy; }
- std::string toString() const override {
- std::ostringstream OS;
- OS << "{";
- for (size_t i = 0; i < Elements.size(); ++i) {
- OS << Elements[i]->toString();
- if (i + 1 < Elements.size())
- OS << ",";
- }
- OS << "}";
- return OS.str();
- }
-
- bool isValid() const override { return !Elements.empty(); }
-};
-
-static std::string escapeString(const std::vector<char> &Raw) {
- std::string Out;
- for (char c : Raw) {
- switch (c) {
- case '\n':
- Out += "\\n";
- break;
- case '\t':
- Out += "\\t";
- break;
- case '\r':
- Out += "\\r";
- break;
- case '\"':
- Out += "\\\"";
- break;
- case '\\':
- Out += "\\\\";
- break;
- default:
- if (std::isprint(static_cast<unsigned char>(c)))
- Out.push_back(c);
- else {
- char buf[5];
- snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(c));
- Out += buf;
- }
- break;
- }
- }
- return Out;
-}
-
-class PointerValueBuffer : public ValueBuffer {
-public:
- uint64_t Address = 0;
- std::unique_ptr<ValueBuffer> Pointee; // optional, used only for char*
-
- PointerValueBuffer(QualType _Ty, uint64_t Addr = 0) : Address(Addr) {
- Ty = _Ty;
- }
-
- std::string toString() const override {
- auto PtrTy = dyn_cast<PointerType>(Ty.getTypePtr());
- if (!PtrTy)
- return "";
-
- auto PointeeTy = PtrTy->getPointeeType();
-
- // char* -> print string literal
- if (PointeeTy->isCharType() && Pointee) {
- if (auto *BE = static_cast<BuiltinValueBuffer *>(Pointee.get()))
- return "\"" + escapeString(BE->raw) + "\"";
- }
-
- if (Address == 0)
- return "nullptr";
-
- std::ostringstream OS;
- OS << "0x" << std::hex << Address;
- return OS.str();
- }
-
- bool isValid() const override { return Address != 0; }
-};
-
class ReaderDispatcher {
private:
ASTContext &Ctx;
@@ -539,6 +348,11 @@ class TypeReadVisitor
return Dispatcher.readArray(QualType(AT, 0), Addr);
}
+ llvm::Expected<std::unique_ptr<ValueBuffer>>
+ VisitEnumType(const EnumType *ET) {
+ return Dispatcher.readBuiltin(QualType(ET, 0), Addr);
+ }
+
llvm::Expected<std::unique_ptr<ValueBuffer>>
VisitRecordType(const RecordType *RT) {
return llvm::make_error<llvm::StringError>(
@@ -562,7 +376,7 @@ ReaderDispatcher::readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr) {
auto Buf = std::make_unique<BuiltinValueBuffer>(Ty);
const auto &Res = *ResOrErr;
- std::vector<char> ElemBuf(Size);
+ std::vector<uint8_t> ElemBuf(Size);
std::memcpy(ElemBuf.data(), Res.back().data(), Size);
Buf->raw = std::move(ElemBuf);
return std::move(Buf);
@@ -598,12 +412,20 @@ ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty,
if (!PtrTy)
return llvm::make_error<llvm::StringError>("Not a PointerType",
llvm::inconvertibleErrorCode());
+ unsigned PtrWidth = Ctx.getTypeSizeInChars(Ty).getQuantity();
+ uint64_t PtrValAddr = 0;
+ if (PtrWidth == 32) {
+ auto AddrOrErr = MA.readUInt32s({Addr});
+ if (!AddrOrErr)
+ return AddrOrErr.takeError();
+ PtrValAddr = AddrOrErr->back();
+ } else {
+ auto AddrOrErr = MA.readUInt64s({Addr});
+ if (!AddrOrErr)
+ return AddrOrErr.takeError();
+ PtrValAddr = AddrOrErr->back();
+ }
- auto AddrOrErr = MA.readUInt64s({Addr});
- if (!AddrOrErr)
- return AddrOrErr.takeError();
-
- uint64_t PtrValAddr = AddrOrErr->back();
if (PtrValAddr == 0)
return std::make_unique<PointerValueBuffer>(Ty); // null pointer
@@ -624,6 +446,8 @@ ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty,
}
auto Buf = std::make_unique<BuiltinValueBuffer>(PointeeTy);
Buf->raw.assign(S.begin(), S.end());
+ if (S.empty())
+ Buf->raw.push_back('\0'); // represent ""
PtrBuf->Pointee = std::move(Buf);
}
// else {
>From e8ce46b065b240dbc4bf1da22b1d5c107778b027 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sat, 6 Sep 2025 12:02:16 +0530
Subject: [PATCH 3/5] Rebased
---
clang/lib/Interpreter/InterpreterValuePrinter.cpp | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 7faae1ecaa9d9..492fbe36bc963 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -100,15 +100,10 @@ static std::string EnumToString(ASTContext &Ctx, QualType QT, uint64_t Data) {
std::string Str;
llvm::raw_string_ostream SS(Str);
- QualType DesugaredTy = QT.getDesugaredType(Ctx);
- const EnumType *EnumTy = DesugaredTy.getNonReferenceType()->getAs<EnumType>();
- assert(EnumTy && "Fail to cast to enum type");
-
- EnumDecl *ED = EnumTy->getDecl();
bool IsFirst = true;
- llvm::APSInt AP = Ctx.MakeIntValue(Data, V.getType());
+ llvm::APSInt AP = Ctx.MakeIntValue(Data, QT);
- auto *ED = V.getType()->castAsEnumDecl();
+ auto *ED = QT->castAsEnumDecl();
for (auto I = ED->enumerator_begin(), E = ED->enumerator_end(); I != E; ++I) {
if (I->getInitVal() == AP) {
if (!IsFirst)
>From a5d7d9e4d8d9cdabe878542fa83a53381d708d71 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Mon, 8 Sep 2025 16:54:10 +0530
Subject: [PATCH 4/5] Fix some issues
---
.../Interpreter/InterpreterValuePrinter.cpp | 225 ++++++++++--------
clang/lib/Interpreter/Value.cpp | 53 +++--
2 files changed, 158 insertions(+), 120 deletions(-)
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 492fbe36bc963..508ae0bf71d9f 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -136,7 +136,8 @@ static std::string FunctionToString(ASTContext &Ctx, QualType QT,
// *OpaqueType, void *Val);
const FunctionDecl *FD = nullptr;
if (auto *InterfaceCall = llvm::dyn_cast<CallExpr>(TLSD->getStmt())) {
- const auto *Arg = InterfaceCall->getArg(/*Val*/ 3);
+ const auto *Arg = InterfaceCall->getArg(InterfaceCall->getNumArgs() - 1);
+
// Get rid of cast nodes.
while (const CastExpr *CastE = llvm::dyn_cast<CastExpr>(Arg))
Arg = CastE->getSubExpr();
@@ -156,32 +157,32 @@ static std::string FunctionToString(ASTContext &Ctx, QualType QT,
static std::string escapeString(const std::vector<uint8_t> &Raw) {
std::string Out;
for (char c : Raw) {
- switch (c) {
- case '\n':
- Out += "\\n";
- break;
- case '\t':
- Out += "\\t";
- break;
- case '\r':
- Out += "\\r";
- break;
- case '\"':
- Out += "\\\"";
- break;
- case '\\':
- Out += "\\\\";
- break;
- default:
- if (std::isprint(static_cast<unsigned char>(c)))
- Out.push_back(c);
- else {
- char buf[5];
- snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(c));
- Out += buf;
- }
- break;
+ // switch (c) {
+ // case '\n':
+ // Out += "\\n";
+ // break;
+ // case '\t':
+ // Out += "\\t";
+ // break;
+ // case '\r':
+ // Out += "\\r";
+ // break;
+ // case '\"':
+ // Out += "\\\"";
+ // break;
+ // case '\\':
+ // Out += "\\\\";
+ // break;
+ // default:
+ if (std::isprint(static_cast<unsigned char>(c)))
+ Out.push_back(c);
+ else {
+ char buf[5];
+ snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(c));
+ Out += buf;
}
+ // break;
+ // }
}
return Out;
}
@@ -212,16 +213,16 @@ std::string ValueToString::BuiltinToString(const BuiltinValueBuffer &B) {
if (NonRefTy->isCharType()) {
unsigned char c = B.as<unsigned char>();
switch (c) {
- case '\n':
- return "'\\n'";
- case '\t':
- return "'\\t'";
- case '\r':
- return "'\\r'";
- case '\'':
- return "'\\''";
- case '\\':
- return "'\\'";
+ // case '\n':
+ // return "'\\n'";
+ // case '\t':
+ // return "'\\t'";
+ // case '\r':
+ // return "'\\r'";
+ // case '\'':
+ // return "'\\''";
+ // case '\\':
+ // return "'\\'";
case '\0':
return "";
default:
@@ -312,16 +313,19 @@ std::string ValueToString::PointerToString(const PointerValueBuffer &P) {
QualType DesugaredTy = QT.getDesugaredType(Ctx);
QualType NonRefTy = DesugaredTy.getNonReferenceType();
- auto PtrTy = dyn_cast<PointerType>(QT.getTypePtr());
- if (!PtrTy)
- return "";
+ if (auto PtrTy = dyn_cast<PointerType>(QT.getTypePtr())) {
+ if (!PtrTy)
+ return "";
+
+ auto PointeeTy = PtrTy->getPointeeType();
- auto PointeeTy = PtrTy->getPointeeType();
+ // char* -> print string literal
+ if (PointeeTy->isCharType() && P.Pointee) {
+ if (auto *BE = static_cast<BuiltinValueBuffer *>(P.Pointee.get()))
+ return "\"" + escapeString(BE->raw) + "\"";
+ }
- // char* -> print string literal
- if (PointeeTy->isCharType() && P.Pointee) {
- if (auto *BE = static_cast<BuiltinValueBuffer *>(P.Pointee.get()))
- return "\"" + escapeString(BE->raw) + "\"";
+ return std::to_string(P.Address);
}
if (P.Address == 0)
@@ -334,6 +338,9 @@ std::string ValueToString::PointerToString(const PointerValueBuffer &P) {
if (NonRefTy->isFunctionType())
return FunctionToString(Ctx, QT, (void *)P.Address);
+ if (NonRefTy->isNullPtrType())
+ return "nullptr\n";
+
std::ostringstream OS;
OS << "@0x" << std::hex << P.Address;
return OS.str();
@@ -342,24 +349,26 @@ std::string ValueToString::PointerToString(const PointerValueBuffer &P) {
std::string ValueToString::ArrayToString(const ArrayValueBuffer &A) {
if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(A.Ty)) {
QualType ElemTy = CAT->getElementType();
- std::ostringstream OS;
// Treat null terminated char arrays as strings basically.
if (ElemTy->isCharType() && !A.Elements.empty()) {
if (const auto *B =
llvm::dyn_cast<BuiltinValueBuffer>(A.Elements.back().get())) {
- char last = (char)B->raw.back();
+ char last = (char)B->raw.front();
if (last != '\0')
goto not_a_string;
}
- OS << "\"";
+ std::string Res;
+ Res += "\"";
for (size_t i = 0; i < A.Elements.size(); ++i) {
if (const auto *B =
llvm::dyn_cast<BuiltinValueBuffer>(A.Elements[i].get())) {
- OS << static_cast<char>(B->raw.back());
+ char c = static_cast<char>(B->raw.back());
+ if (c != '\0')
+ Res += c;
}
}
- OS << "\"";
- return OS.str();
+ Res += "\"";
+ return Res;
}
}
not_a_string:
@@ -436,73 +445,90 @@ Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) const {
}
class ExprConverter {
+ Sema &S;
ASTContext &Ctx;
public:
- ExprConverter(ASTContext &Ctx) : Ctx(Ctx) {}
+ ExprConverter(Sema &S, ASTContext &Ctx) : S(S), Ctx(Ctx) {}
/// Create (&E) as a void*
- ExprResult CreateAddressOfVoidPtrExpr(QualType Ty, Expr *ForCast) {
+ ExprResult CreateAddressOfVoidPtrExpr(QualType Ty, Expr *ForCast,
+ bool takeAddr = false) {
QualType VoidPtrTy = Ctx.getPointerType(Ctx.VoidTy);
// &E
- Expr *AddrOf = UnaryOperator::Create(
- Ctx, ForCast, UO_AddrOf, Ctx.getPointerType(Ty), VK_PRValue,
- OK_Ordinary, SourceLocation(), false, FPOptionsOverride());
+ Expr *AddrOf = ForCast;
+ if (takeAddr) {
+ AddrOf = UnaryOperator::Create(
+ Ctx, ForCast, UO_AddrOf, Ctx.getPointerType(Ty), VK_PRValue,
+ OK_Ordinary, SourceLocation(), false, FPOptionsOverride());
+ }
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);
+ ExprResult CastedExpr =
+ S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), AddrOf);
+ assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");
+ return CastedExpr.get();
// static_cast<void*>(&E)
- return CXXStaticCastExpr::Create(
- Ctx, VoidPtrTy, VK_PRValue, CK_BitCast, AddrOf, nullptr,
- Ctx.getTrivialTypeSourceInfo(VoidPtrTy), FPOptionsOverride(),
- SourceLocation(), SourceLocation(), SourceRange());
- }
-
- /// Create a temporary VarDecl with initializer.
- VarDecl *createTempVarDecl(QualType Ty, llvm::StringRef BaseName,
- Expr *Init) {
- static unsigned Counter = 0;
- IdentifierInfo &Id = Ctx.Idents.get((BaseName + Twine(++Counter)).str());
-
- VarDecl *VD = VarDecl::Create(Ctx, Ctx.getTranslationUnitDecl(),
- SourceLocation(), SourceLocation(), &Id, Ty,
- Ctx.getTrivialTypeSourceInfo(Ty), SC_Auto);
-
- VD->setInit(Init);
- VD->setInitStyle(VarDecl::CInit);
- VD->markUsed(Ctx);
-
- return VD;
+ // return CXXStaticCastExpr::Create(
+ // Ctx, VoidPtrTy, VK_PRValue, CK_BitCast, AddrOf, nullptr,
+ // Ctx.getTrivialTypeSourceInfo(VoidPtrTy), FPOptionsOverride(),
+ // SourceLocation(), SourceLocation(), SourceRange());
}
/// Wrap rvalues in a temporary (var) so they become addressable.
Expr *CreateMaterializeTemporaryExpr(Expr *E) {
- return new (Ctx) MaterializeTemporaryExpr(E->getType(), E,
- /*BoundToLvalueReference=*/true);
+ return S.CreateMaterializeTemporaryExpr(E->getType(), E,
+ /*BoundToLvalueReference=*/true);
}
- /// Generic helper: materialize if needed, then &expr as void*.
- ExprResult makeAddressable(QualType QTy, Expr *E) {
+ ExprResult makeScalarAddressable(QualType Ty, Expr *E) {
if (E->isLValue() || E->isXValue())
- return CreateAddressOfVoidPtrExpr(QTy, E);
-
- if (E->isPRValue())
- return CreateAddressOfVoidPtrExpr(QTy, CreateMaterializeTemporaryExpr(E));
-
- return ExprError();
+ return CreateAddressOfVoidPtrExpr(Ty, E, /*takeAddr=*/true);
+ return CreateAddressOfVoidPtrExpr(Ty, CreateMaterializeTemporaryExpr(E),
+ /*takeAddr=*/true);
}
ExprResult handleBuiltinTypeExpr(const BuiltinType *, QualType QTy, Expr *E) {
- return makeAddressable(QTy, E);
+ return makeScalarAddressable(QTy, E);
}
+
+ ExprResult handleEnumTypeExpr(const EnumType *, QualType QTy, Expr *E) {
+ return makeScalarAddressable(QTy, E);
+ }
+
ExprResult handlePointerTypeExpr(const PointerType *, QualType QTy, Expr *E) {
- return makeAddressable(QTy, E);
+ // Pointer expressions always evaluate to a pointer value.
+ // No need to take address or materialize.
+ return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/false);
}
+
ExprResult handleArrayTypeExpr(const ConstantArrayType *, QualType QTy,
Expr *E) {
- return makeAddressable(QTy, E);
+ if (isa<StringLiteral>(E)) {
+ if (Ctx.getLangOpts().CPlusPlus)
+ return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true);
+ return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/false);
+ }
+
+ if (E->isLValue() || E->isXValue())
+ return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true);
+ return CreateAddressOfVoidPtrExpr(QTy, E,
+ /*takeAddr=*/false);
}
- ExprResult handleEnumTypeExpr(const EnumType *, QualType QTy, Expr *E) {
- return makeAddressable(QTy, E);
+
+ ExprResult handleFunctionTypeExpr(const FunctionType *, QualType QTy,
+ Expr *E) {
+ if (Ctx.getLangOpts().CPlusPlus)
+ return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true);
+ return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/false);
+ }
+
+ ExprResult handleAnyObjectExpr(const Type *, QualType QTy, Expr *E) {
+ if (E->isLValue() || E->isXValue())
+ return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true);
+ return CreateAddressOfVoidPtrExpr(QTy, CreateMaterializeTemporaryExpr(E),
+ /*takeAddr=*/true);
}
};
@@ -514,10 +540,15 @@ class InterfaceKindVisitor : public TypeVisitor<InterfaceKindVisitor, bool> {
public:
InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl<Expr *> &Args)
- : S(S), E(E), Args(Args), Converter(S.getASTContext()) {}
+ : S(S), E(E), Args(Args), Converter(S, S.getASTContext()) {}
bool transformExpr(QualType Ty) { return Visit(Ty.getTypePtr()); }
+ bool VisitType(const Type *T) {
+ Args.push_back(Converter.handleAnyObjectExpr(T, QualType(T, 0), E).get());
+ return true;
+ }
+
bool VisitBuiltinType(const BuiltinType *Ty) {
if (Ty->isNullPtrType()) {
Args.push_back(E);
@@ -548,14 +579,16 @@ class InterfaceKindVisitor : public TypeVisitor<InterfaceKindVisitor, bool> {
return true;
}
+ bool VisitFunctionType(const FunctionType *Ty) {
+ Args.push_back(
+ Converter.handleFunctionTypeExpr(Ty, QualType(Ty, 0), E).get());
+ return true;
+ }
+
bool VisitEnumType(const EnumType *Ty) {
Args.push_back(Converter.handleEnumTypeExpr(Ty, QualType(Ty, 0), E).get());
return true;
}
-
- bool VisitRecordType(const RecordType *) { return true; }
- bool VisitMemberPointerType(const MemberPointerType *) { return true; }
- bool VisitFunctionType(const FunctionType *) { return true; }
};
enum RunTimeFnTag { OrcSendResult, ClangSendResult };
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp
index 3ac741c83c808..f3c89b620b505 100644
--- a/clang/lib/Interpreter/Value.cpp
+++ b/clang/lib/Interpreter/Value.cpp
@@ -315,6 +315,9 @@ class ReaderDispatcher {
llvm::Expected<std::unique_ptr<ValueBuffer>>
readArray(QualType Ty, llvm::orc::ExecutorAddr Addr);
+ llvm::Expected<std::unique_ptr<ValueBuffer>>
+ readOtherObject(QualType Ty, llvm::orc::ExecutorAddr Addr);
+
// TODO: record, function, etc.
};
@@ -329,8 +332,7 @@ class TypeReadVisitor
: Dispatcher(D), Addr(A) {}
llvm::Expected<std::unique_ptr<ValueBuffer>> VisitType(const Type *T) {
- return llvm::make_error<llvm::StringError>(
- "Unsupported type in ReaderDispatcher", llvm::inconvertibleErrorCode());
+ return Dispatcher.readOtherObject(QualType(T, 0), Addr);
}
llvm::Expected<std::unique_ptr<ValueBuffer>>
@@ -352,13 +354,6 @@ class TypeReadVisitor
VisitEnumType(const EnumType *ET) {
return Dispatcher.readBuiltin(QualType(ET, 0), Addr);
}
-
- llvm::Expected<std::unique_ptr<ValueBuffer>>
- VisitRecordType(const RecordType *RT) {
- return llvm::make_error<llvm::StringError>(
- "RecordType reading not yet implemented",
- llvm::inconvertibleErrorCode());
- }
};
llvm::Expected<std::unique_ptr<ValueBuffer>>
@@ -412,20 +407,20 @@ ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty,
if (!PtrTy)
return llvm::make_error<llvm::StringError>("Not a PointerType",
llvm::inconvertibleErrorCode());
- unsigned PtrWidth = Ctx.getTypeSizeInChars(Ty).getQuantity();
- uint64_t PtrValAddr = 0;
- if (PtrWidth == 32) {
- auto AddrOrErr = MA.readUInt32s({Addr});
- if (!AddrOrErr)
- return AddrOrErr.takeError();
- PtrValAddr = AddrOrErr->back();
- } else {
- auto AddrOrErr = MA.readUInt64s({Addr});
- if (!AddrOrErr)
- return AddrOrErr.takeError();
- PtrValAddr = AddrOrErr->back();
- }
-
+ // unsigned PtrWidth = Ctx.getTypeSizeInChars(Ty).getQuantity();
+ // uint64_t PtrValAddr = 0;
+ // if (PtrWidth == 32) {
+ // auto AddrOrErr = MA.readUInt32s({Addr});
+ // if (!AddrOrErr)
+ // return AddrOrErr.takeError();
+ // PtrValAddr = AddrOrErr->back();
+ // } else {
+ // auto AddrOrErr = MA.readUInt64s({Addr});
+ // if (!AddrOrErr)
+ // return AddrOrErr.takeError();
+ // PtrValAddr = AddrOrErr->back();
+ // }
+ uint64_t PtrValAddr = Addr.getValue();
if (PtrValAddr == 0)
return std::make_unique<PointerValueBuffer>(Ty); // null pointer
@@ -459,6 +454,16 @@ ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty,
return std::move(PtrBuf);
}
+llvm::Expected<std::unique_ptr<ValueBuffer>>
+ReaderDispatcher::readOtherObject(QualType Ty, llvm::orc::ExecutorAddr Addr) {
+ unsigned PtrWidth = Ctx.getTypeSizeInChars(Ty).getQuantity();
+ uint64_t PtrValAddr = Addr.getValue();
+ if (PtrValAddr == 0)
+ return std::make_unique<PointerValueBuffer>(Ty); // null pointer
+
+ return std::make_unique<PointerValueBuffer>(Ty, PtrValAddr);
+}
+
ValueResultManager::ValueResultManager(ASTContext &Ctx,
llvm::orc::MemoryAccess &MA)
: Ctx(Ctx), MemAcc(MA) {}
@@ -502,7 +507,7 @@ void ValueResultManager::deliverResult(SendResultFn SendResult, ValueId ID,
SendResult(llvm::make_error<llvm::StringError>(
"Unknown ValueId in deliverResult", llvm::inconvertibleErrorCode()));
}
- Ty = It->second;
+ Ty = It->second.getCanonicalType();
IdToType.erase(It);
}
>From 65638d3638633e9d5a7ba317f53483acb1f9ed9a Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Fri, 12 Sep 2025 20:38:40 +0530
Subject: [PATCH 5/5] Introduce new Value class design, inspired by APValue
---
clang/include/clang/Interpreter/Interpreter.h | 2 +-
clang/include/clang/Interpreter/Value.h | 405 +++++++++++------
clang/lib/Interpreter/Interpreter.cpp | 7 +-
.../Interpreter/InterpreterValuePrinter.cpp | 301 +++++++------
clang/lib/Interpreter/Value.cpp | 407 +++++++-----------
.../Interpreter/InterpreterExtensionsTest.cpp | 2 +-
.../unittests/Interpreter/InterpreterTest.cpp | 199 +++++----
7 files changed, 710 insertions(+), 613 deletions(-)
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index c1e7ca48c0961..3b2575964d40f 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -145,7 +145,7 @@ class Interpreter {
llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Code);
llvm::Error Execute(PartialTranslationUnit &T);
- llvm::Error ParseAndExecute(llvm::StringRef Code);
+ llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr);
/// Undo N previous incremental inputs.
llvm::Error Undo(unsigned N = 1);
diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h
index dda8c6bbf09aa..3da9f5769d98f 100644
--- a/clang/include/clang/Interpreter/Value.h
+++ b/clang/include/clang/Interpreter/Value.h
@@ -95,171 +95,302 @@ class QualType;
X(double, Double) \
X(long double, LongDouble)
-class REPL_EXTERNAL_VISIBILITY Value {
- union Storage {
-#define X(type, name) type m_##name;
- REPL_BUILTIN_TYPES
-#undef X
- void *m_Ptr;
- unsigned char m_RawBits[sizeof(long double) * 8]; // widest type
- };
-
+class REPL_EXTERNAL_VISIBILITY Value final {
public:
- enum Kind {
+ enum BuiltinKind {
#define X(type, name) K_##name,
REPL_BUILTIN_TYPES
#undef X
-
- K_Void,
- K_PtrOrObj,
- K_Unspecified
+ K_Unspecified
};
- Value() = default;
- Value(const Interpreter *In, void *Ty);
- Value(const Value &RHS);
- Value(Value &&RHS) noexcept;
- Value &operator=(const Value &RHS);
- Value &operator=(Value &&RHS) noexcept;
- ~Value();
-
- void printType(llvm::raw_ostream &Out) const;
- void printData(llvm::raw_ostream &Out) const;
- void print(llvm::raw_ostream &Out) const;
- void dump() const;
- void clear();
-
- const ASTContext &getASTContext() const;
- const Interpreter &getInterpreter() const;
- QualType getType() const;
-
- bool isValid() const { return ValueKind != K_Unspecified; }
- bool isVoid() const { return ValueKind == K_Void; }
- bool hasValue() const { return isValid() && !isVoid(); }
- bool isManuallyAlloc() const { return IsManuallyAlloc; }
- Kind getKind() const { return ValueKind; }
- void setKind(Kind K) { ValueKind = K; }
- void setOpaqueType(void *Ty) { OpaqueType = Ty; }
-
- void *getPtr() const;
- void setPtr(void *Ptr) { Data.m_Ptr = Ptr; }
- void setRawBits(void *Ptr, unsigned NBits = sizeof(Storage));
-
-#define X(type, name) \
- void set##name(type Val) { Data.m_##name = Val; } \
- type get##name() const { return Data.m_##name; }
- REPL_BUILTIN_TYPES
+private:
+ struct Builtins {
+ private:
+ BuiltinKind BK = K_Unspecified;
+ union {
+#define X(type, name) type m_##name;
+ REPL_BUILTIN_TYPES
#undef X
+ };
- /// \brief Get the value with cast.
- //
- /// Get the value cast to T. This is similar to reinterpret_cast<T>(value),
- /// casting the value of builtins (except void), enums and pointers.
- /// Values referencing an object are treated as pointers to the object.
- template <typename T> T convertTo() const {
- return convertFwd<T>::cast(*this);
- }
-
-protected:
- bool isPointerOrObjectType() const { return ValueKind == K_PtrOrObj; }
+ public:
+ Builtins() = default;
+ explicit Builtins(BuiltinKind BK) : BK(BK) {}
+ ~Builtins() {}
- /// \brief Get to the value with type checking casting the underlying
- /// stored value to T.
- template <typename T> T as() const {
- switch (ValueKind) {
- default:
- return T();
-#define X(type, name) \
- case Value::K_##name: \
- return (T)Data.m_##name;
- REPL_BUILTIN_TYPES
-#undef X
+ void setKind(BuiltinKind K) {
+ assert(BK == K_Unspecified);
+ BK = K;
}
+ BuiltinKind getKind() const { return BK; }
+#define X(type, name) \
+ void set##name(type Val) { \
+ assert(BK == K_Unspecified || BK == K_##name); \
+ m_##name = Val; \
+ } \
+ type get##name() const { \
+ assert(BK == K_##name); \
+ return m_##name; \
}
+ REPL_BUILTIN_TYPES
+#undef X
+ };
- // Allow convertTo to be partially specialized.
- template <typename T> struct convertFwd {
- static T cast(const Value &V) {
- if (V.isPointerOrObjectType())
- return (T)(uintptr_t)V.as<void *>();
- if (!V.isValid() || V.isVoid()) {
- return T();
- }
- return V.as<T>();
+ struct ArrValue {
+ std::vector<Value> Elements;
+ uint64_t ArrSize;
+ ArrValue(uint64_t Size) : ArrSize(Size) {
+ Elements.reserve(ArrSize);
+ for (uint64_t I = 0; I < ArrSize; ++I)
+ Elements.emplace_back();
}
};
- template <typename T> struct convertFwd<T *> {
- static T *cast(const Value &V) {
- if (V.isPointerOrObjectType())
- return (T *)(uintptr_t)V.as<void *>();
- return nullptr;
+ struct PtrValue {
+ uint64_t Addr = 0;
+ Value *Pointee; // optional for str
+ PtrValue(uint64_t Addr) : Addr(Addr), Pointee(new Value()) {}
+ ~PtrValue() {
+ if (Pointee != nullptr)
+ delete Pointee;
}
};
- const Interpreter *Interp = nullptr;
- void *OpaqueType = nullptr;
- Storage Data;
- Kind ValueKind = K_Unspecified;
- bool IsManuallyAlloc = false;
-};
-
-template <> inline void *Value::as() const {
- if (isPointerOrObjectType())
- return Data.m_Ptr;
- return (void *)as<uintptr_t>();
-}
+ struct StrValue {
+ std::string StringBuf;
+ StrValue(std::string str) : StringBuf(std::move(str)) {}
+ ~StrValue() = default;
+ };
-class ValueBuffer {
public:
- enum Kind { K_Builtin, K_Array, K_Pointer, K_Unknown };
- QualType Ty;
- Kind K;
- ValueBuffer(Kind K = K_Unknown) : K(K) {}
- virtual ~ValueBuffer() = default;
- bool isUnknown() const { return K == K_Unknown; }
- bool isBuiltin() const { return K == K_Builtin; }
- bool isArray() const { return K == K_Array; }
- bool isPointer() const { return K == K_Pointer; }
- static bool classof(const ValueBuffer *) { return true; }
-};
+ using DataType =
+ llvm::AlignedCharArrayUnion<ArrValue, PtrValue, Builtins, StrValue>;
+ enum ValKind { K_Builtin, K_Array, K_Pointer, K_Str, K_None };
-class BuiltinValueBuffer;
-class ArrayValueBuffer;
-class PointerValueBuffer;
+private:
+ QualType Ty;
+ ValKind VKind = K_None;
+ DataType Data;
-class BuiltinValueBuffer : public ValueBuffer {
public:
- std::vector<uint8_t> raw;
- BuiltinValueBuffer(QualType _Ty) : ValueBuffer(K_Builtin) { Ty = _Ty; }
- template <typename T> T as() const {
+ Value() = default;
+ explicit Value(QualType Ty, ValKind K) : Ty(Ty), VKind(K) {}
+ Value(const Value &RHS);
+ Value(Value &&RHS) : Ty(RHS.Ty), VKind(RHS.VKind), Data(RHS.Data) {
+ RHS.VKind = K_None;
+ }
+
+ Value &operator=(const Value &RHS);
+ Value &operator=(Value &&RHS);
+
+ explicit Value(QualType QT, std::vector<uint8_t> Raw);
+
+ struct UninitArr {};
+
+ explicit Value(UninitArr, QualType QT, uint64_t ArrSize)
+ : Ty(QT), VKind(K_None) {
+ MakeArray(ArrSize);
+ }
+
+ explicit Value(QualType QT, uint64_t Addr) : Ty(QT), VKind(K_None) {
+ MakePointer(Addr);
+ }
+
+ explicit Value(QualType QT, const char *buf) : Ty(QT), VKind(K_None) {
+ MakeStr(buf);
+ }
+
+ ~Value() {
+ if (VKind != K_None)
+ destroy();
+ }
+
+ template <typename T> static T as(std::vector<uint8_t> &raw) {
T v{};
// assert(raw.size() >= sizeof(T) && "Buffer too small for type!");
memcpy(&v, raw.data(), sizeof(T));
return v;
}
- static bool classof(const ValueBuffer *B) { return B->isBuiltin(); }
-};
-class ArrayValueBuffer : public ValueBuffer {
-public:
- std::vector<std::unique_ptr<ValueBuffer>> Elements;
- ArrayValueBuffer(QualType EleTy) : ValueBuffer(K_Array) { Ty = EleTy; }
+ // ---- Kind checks ----
+ bool isUnknown() const { return VKind == K_None; }
+ bool isBuiltin() const { return VKind == K_Builtin; }
+ bool isArray() const { return VKind == K_Array; }
+ bool isPointer() const { return VKind == K_Pointer; }
+ bool isStr() const { return VKind == K_Str; }
+ ValKind getKind() const { return VKind; }
+ QualType getType() const { return Ty; }
+ bool isAbsent() const { return VKind == K_None; }
+ bool hasValue() const { return VKind != K_None; }
+ BuiltinKind getBuiltinKind() const {
+ if (isBuiltin())
+ return asBuiltin().getKind();
+ return BuiltinKind::K_Unspecified;
+ }
- static bool classof(const ValueBuffer *B) { return B->isArray(); }
-};
+protected:
+ // ---- accessors ----
+ Builtins &asBuiltin() {
+ assert(isBuiltin() && "Not a builtin value");
+ return *((Builtins *)(char *)&Data);
+ }
+
+ const Builtins &asBuiltin() const {
+ return const_cast<Value *>(this)->asBuiltin();
+ }
+
+ ArrValue &asArray() {
+ assert(isArray() && "Not an array value");
+ return *((ArrValue *)(char *)&Data);
+ }
+
+ const ArrValue &asArray() const {
+ return const_cast<Value *>(this)->asArray();
+ }
+
+ PtrValue &asPointer() {
+ assert(isPointer() && "Not a pointer value");
+ return *((PtrValue *)(char *)&Data);
+ }
+
+ const PtrValue &asPointer() const {
+ return const_cast<Value *>(this)->asPointer();
+ }
+
+ StrValue &asStr() {
+ assert(isStr() && "Not a Str value");
+ return *((StrValue *)(char *)&Data);
+ }
+
+ const StrValue &asStr() const { return const_cast<Value *>(this)->asStr(); }
-class PointerValueBuffer : public ValueBuffer {
public:
- uint64_t Address = 0;
- std::unique_ptr<ValueBuffer> Pointee; // optional, used only for char*
+ bool hasBuiltinThis(BuiltinKind K) const {
+ if (isBuiltin())
+ return asBuiltin().getKind() == K;
+ return false;
+ }
+
+ void setStrVal(const char *buf) {
+ assert(isStr() && "Not a Str");
+ asStr().StringBuf = buf;
+ }
+
+ StringRef getStrVal() {
+ assert(isStr() && "Not a Str");
+ return StringRef(asStr().StringBuf);
+ }
+
+ const StringRef getStrVal() const {
+ assert(isStr() && "Not a Str");
+ return StringRef(asStr().StringBuf);
+ }
+
+ uint64_t getArraySize() const { return asArray().ArrSize; }
+
+ uint64_t getArrayInitializedElts() const { return asArray().ArrSize; }
+
+ Value &getArrayInitializedElt(unsigned I) {
+ assert(isArray() && "Invalid accessor");
+ assert(I < getArrayInitializedElts() && "Index out of range");
+ return ((ArrValue *)(char *)&Data)->Elements[I];
+ }
+
+ const Value &getArrayInitializedElt(unsigned I) const {
+ return const_cast<Value *>(this)->getArrayInitializedElt(I);
+ }
+
+ bool HasPointee() const {
+ assert(isPointer() && "Invalid accessor");
+ return !(asPointer().Pointee->isAbsent());
+ }
+
+ Value &getPointerPointee() {
+ assert(isPointer() && "Invalid accessor");
+ return *asPointer().Pointee;
+ }
- PointerValueBuffer(QualType _Ty, uint64_t Addr = 0)
- : ValueBuffer(K_Pointer), Address(Addr) {
- Ty = _Ty;
+ const Value &getPointerPointee() const {
+ return const_cast<Value *>(this)->getPointerPointee();
}
- static bool classof(const ValueBuffer *B) { return B->isPointer(); }
+ uint64_t getAddr() const { return asPointer().Addr; }
+
+#define X(type, name) \
+ void set##name(type Val) { asBuiltin().set##name(Val); } \
+ type get##name() const { return asBuiltin().get##name(); }
+ REPL_BUILTIN_TYPES
+#undef X
+
+ // ---- Printing ----
+ void printType(llvm::raw_ostream &Out, ASTContext &Ctx) const;
+ void printData(llvm::raw_ostream &Out, ASTContext &Ctx) const;
+ void print(llvm::raw_ostream &Out, ASTContext &Ctx) const;
+ void dump(ASTContext &Ctx) const;
+
+ // ---- clear ----
+ void clear() { destroy(); }
+
+private:
+ void MakeBuiltIns() {
+ assert(isAbsent() && "Bad state change");
+ new ((void *)(char *)&Data) Builtins(BuiltinKind::K_Unspecified);
+ VKind = K_Builtin;
+ }
+
+ void MakeArray(uint64_t Size) {
+ assert(isAbsent() && "Bad state change");
+ new ((void *)(char *)&Data) ArrValue(Size);
+ VKind = K_Array;
+ }
+
+ void MakePointer(uint64_t Addr = 0) {
+ assert(isAbsent() && "Bad state change");
+ new ((void *)(char *)&Data) PtrValue(Addr);
+ VKind = K_Pointer;
+ }
+
+ void MakeStr(std::string Str = "") {
+ assert(isAbsent() && "Bad state change");
+ new ((void *)(char *)&Data) StrValue(Str);
+ VKind = K_Str;
+ }
+
+ void setBuiltins(Builtins &LHS, const Builtins &RHS) {
+ switch (RHS.getKind()) {
+ default:
+ assert(false && "Type not supported");
+
+#define X(type, name) \
+ case BuiltinKind::K_##name: { \
+ LHS.setKind(BuiltinKind::K_##name); \
+ LHS.set##name(RHS.get##name()); \
+ } break;
+ REPL_BUILTIN_TYPES
+#undef X
+ }
+ }
+
+ void destroy() {
+ switch (VKind) {
+ case K_Builtin:
+ reinterpret_cast<Builtins *>(&Data)->~Builtins();
+ break;
+ case K_Array:
+ reinterpret_cast<ArrValue *>(&Data)->~ArrValue();
+ break;
+ case K_Pointer:
+ reinterpret_cast<PtrValue *>(&Data)->~PtrValue();
+ break;
+ case K_Str:
+ reinterpret_cast<StrValue *>(&Data)->~StrValue();
+ break;
+ default:
+ break;
+ }
+ VKind = K_None;
+ }
};
class ValueToString {
@@ -268,12 +399,13 @@ class ValueToString {
public:
ValueToString(ASTContext &Ctx) : Ctx(Ctx) {}
- std::string toString(const ValueBuffer *);
+ std::string toString(const Value *);
+ std::string toString(QualType);
private:
- std::string BuiltinToString(const BuiltinValueBuffer &B);
- std::string PointerToString(const PointerValueBuffer &P);
- std::string ArrayToString(const ArrayValueBuffer &A);
+ std::string BuiltinToString(const Value &B);
+ std::string PointerToString(const Value &P);
+ std::string ArrayToString(const Value &A);
};
class ValueResultManager {
@@ -296,17 +428,16 @@ class ValueResultManager {
void deliverResult(SendResultFn SendResult, ValueId ID,
llvm::orc::ExecutorAddr VAddr);
+ Value release() { return std::move(LastVal); }
private:
std::atomic<ValueId> NextID{1};
void Initialize(llvm::orc::LLJIT &EE);
- std::string ValueTypeToString(QualType QT) const;
-
mutable std::mutex Mutex;
ASTContext &Ctx;
llvm::orc::MemoryAccess &MemAcc;
- std::unique_ptr<ValueBuffer> ValBuf = nullptr;
+ Value LastVal;
llvm::DenseMap<ValueId, clang::QualType> IdToType;
};
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 7915d005dc8f3..8e4173923b956 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -577,7 +577,7 @@ llvm::Error Interpreter::Execute(PartialTranslationUnit &T) {
return llvm::Error::success();
}
-llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code) {
+llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) {
auto PTU = Parse(Code);
if (!PTU)
@@ -587,7 +587,10 @@ llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code) {
return Err;
if (ValMgr) {
- ValMgr->resetAndDump();
+ if (V) {
+ *V = ValMgr->release();
+ } else
+ ValMgr->resetAndDump();
}
return llvm::Error::success();
}
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 508ae0bf71d9f..b52929ab97660 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -154,85 +154,36 @@ static std::string FunctionToString(ASTContext &Ctx, QualType QT,
return Str;
}
-static std::string escapeString(const std::vector<uint8_t> &Raw) {
- std::string Out;
- for (char c : Raw) {
- // switch (c) {
- // case '\n':
- // Out += "\\n";
- // break;
- // case '\t':
- // Out += "\\t";
- // break;
- // case '\r':
- // Out += "\\r";
- // break;
- // case '\"':
- // Out += "\\\"";
- // break;
- // case '\\':
- // Out += "\\\\";
- // break;
- // default:
- if (std::isprint(static_cast<unsigned char>(c)))
- Out.push_back(c);
- else {
- char buf[5];
- snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(c));
- Out += buf;
- }
- // break;
- // }
- }
- return Out;
-}
-
namespace clang {
std::string Interpreter::ValueDataToString(const Value &V) const { return ""; }
-std::string ValueToString::toString(const ValueBuffer *Buf) {
- if (const BuiltinValueBuffer *B = llvm::dyn_cast<BuiltinValueBuffer>(Buf))
- return BuiltinToString(*B);
- else if (const ArrayValueBuffer *A = llvm::dyn_cast<ArrayValueBuffer>(Buf))
- return ArrayToString(*A);
- else if (const PointerValueBuffer *P =
- llvm::dyn_cast<PointerValueBuffer>(Buf))
- return PointerToString(*P);
+std::string ValueToString::toString(const Value *Buf) {
+
+ switch (Buf->getKind()) {
+ case Value::K_Builtin:
+ return BuiltinToString(*Buf);
+ case Value::K_Pointer:
+ return PointerToString(*Buf);
+ case Value::K_Str:
+ break;
+ case Value::K_Array:
+ return ArrayToString(*Buf);
+
+ default:
+ break;
+ }
return "";
}
-std::string ValueToString::BuiltinToString(const BuiltinValueBuffer &B) {
- if (B.raw.empty())
+std::string ValueToString::BuiltinToString(const Value &B) {
+ if (!B.hasValue())
return ""; // No data in buffer
- QualType QT = B.Ty;
+ QualType QT = B.getType();
QualType DesugaredTy = QT.getDesugaredType(Ctx);
QualType NonRefTy = DesugaredTy.getNonReferenceType();
- if (NonRefTy->isCharType()) {
- unsigned char c = B.as<unsigned char>();
- switch (c) {
- // case '\n':
- // return "'\\n'";
- // case '\t':
- // return "'\\t'";
- // case '\r':
- // return "'\\r'";
- // case '\'':
- // return "'\\''";
- // case '\\':
- // return "'\\'";
- case '\0':
- return "";
- default:
- if (std::isprint(c))
- return std::string("'") + static_cast<char>(c) + "'";
- else {
- return llvm::formatv("'\\x{0:02X}'", c).str();
- }
- }
- }
if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) {
auto formatFloating = [](auto Val, char Suffix = '\0') -> std::string {
@@ -264,52 +215,64 @@ std::string ValueToString::BuiltinToString(const BuiltinValueBuffer &B) {
return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) +
" '}";
case clang::BuiltinType::Bool:
- SS << ((B.as<bool>()) ? "true" : "false");
+ SS << ((B.getBool()) ? "true" : "false");
+ return Str;
+ case clang::BuiltinType::Char_S:
+ SS << '\'' << B.getChar_S() << '\'';
+ return Str;
+ case clang::BuiltinType::SChar:
+ SS << '\'' << B.getSChar() << '\'';
+ return Str;
+ case clang::BuiltinType::Char_U:
+ SS << '\'' << B.getChar_U() << '\'';
+ return Str;
+ case clang::BuiltinType::UChar:
+ SS << '\'' << B.getUChar() << '\'';
return Str;
case clang::BuiltinType::Short:
- SS << B.as<short>();
+ SS << B.getShort();
return Str;
case clang::BuiltinType::UShort:
- SS << B.as<unsigned short>();
+ SS << B.getUShort();
return Str;
case clang::BuiltinType::Int:
- SS << B.as<int>();
+ SS << B.getInt();
return Str;
case clang::BuiltinType::UInt:
- SS << B.as<unsigned int>();
+ SS << B.getUInt();
return Str;
case clang::BuiltinType::Long:
- SS << B.as<long>();
+ SS << B.getLong();
return Str;
case clang::BuiltinType::ULong:
- SS << B.as<unsigned long>();
+ SS << B.getULong();
return Str;
case clang::BuiltinType::LongLong:
- SS << B.as<long long>();
+ SS << B.getLongLong();
return Str;
case clang::BuiltinType::ULongLong:
- SS << B.as<unsigned long long>();
+ SS << B.getULongLong();
return Str;
case clang::BuiltinType::Float:
- return formatFloating(B.as<float>(), /*suffix=*/'f');
+ return formatFloating(B.getFloat(), /*suffix=*/'f');
case clang::BuiltinType::Double:
- return formatFloating(B.as<double>());
+ return formatFloating(B.getDouble());
case clang::BuiltinType::LongDouble:
- return formatFloating(B.as<long double>(), /*suffix=*/'L');
+ return formatFloating(B.getLongDouble(), /*suffix=*/'L');
}
}
if (NonRefTy->isEnumeralType())
- return EnumToString(Ctx, QT, B.as<uint64_t>());
+ return EnumToString(Ctx, QT, B.getUInt());
return "";
}
-std::string ValueToString::PointerToString(const PointerValueBuffer &P) {
+std::string ValueToString::PointerToString(const Value &P) {
- QualType QT = P.Ty;
+ QualType QT = P.getType();
QualType DesugaredTy = QT.getDesugaredType(Ctx);
QualType NonRefTy = DesugaredTy.getNonReferenceType();
@@ -320,49 +283,45 @@ std::string ValueToString::PointerToString(const PointerValueBuffer &P) {
auto PointeeTy = PtrTy->getPointeeType();
// char* -> print string literal
- if (PointeeTy->isCharType() && P.Pointee) {
- if (auto *BE = static_cast<BuiltinValueBuffer *>(P.Pointee.get()))
- return "\"" + escapeString(BE->raw) + "\"";
+ if (PointeeTy->isCharType() && P.HasPointee()) {
+ if (P.getPointerPointee().isStr())
+ return "\"" + P.getPointerPointee().getStrVal().str() + "\"";
}
-
- return std::to_string(P.Address);
}
- if (P.Address == 0)
- return "nullptr";
-
if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
NonRefTy->getPointeeType()->isFunctionProtoType())
- return FunctionToString(Ctx, QT, (void *)P.Address);
+ return FunctionToString(Ctx, QT, (void *)P.getAddr());
if (NonRefTy->isFunctionType())
- return FunctionToString(Ctx, QT, (void *)P.Address);
+ return FunctionToString(Ctx, QT, (void *)P.getAddr());
if (NonRefTy->isNullPtrType())
return "nullptr\n";
std::ostringstream OS;
- OS << "@0x" << std::hex << P.Address;
+ OS << "@0x" << std::hex << P.getAddr();
return OS.str();
}
-std::string ValueToString::ArrayToString(const ArrayValueBuffer &A) {
- if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(A.Ty)) {
+std::string ValueToString::ArrayToString(const Value &A) {
+ if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(A.getType())) {
QualType ElemTy = CAT->getElementType();
// Treat null terminated char arrays as strings basically.
- if (ElemTy->isCharType() && !A.Elements.empty()) {
- if (const auto *B =
- llvm::dyn_cast<BuiltinValueBuffer>(A.Elements.back().get())) {
- char last = (char)B->raw.front();
+ if (ElemTy->isCharType()) {
+ const Value &LastV =
+ A.getArrayInitializedElt(A.getArrayInitializedElts() - 1);
+ if (LastV.hasValue() && !LastV.isAbsent() && LastV.isBuiltin()) {
+ char last = LastV.getChar_S();
if (last != '\0')
goto not_a_string;
}
std::string Res;
Res += "\"";
- for (size_t i = 0; i < A.Elements.size(); ++i) {
- if (const auto *B =
- llvm::dyn_cast<BuiltinValueBuffer>(A.Elements[i].get())) {
- char c = static_cast<char>(B->raw.back());
+ for (size_t I = 0, N = A.getArraySize(); I < N; ++I) {
+ const Value &EleVal = A.getArrayInitializedElt(I);
+ if (EleVal.hasValue() && EleVal.isBuiltin()) {
+ char c = EleVal.getChar_S();
if (c != '\0')
Res += c;
}
@@ -375,9 +334,12 @@ std::string ValueToString::ArrayToString(const ArrayValueBuffer &A) {
std::ostringstream OS;
OS << "{ ";
- for (size_t i = 0; i < A.Elements.size(); ++i) {
- OS << this->toString(A.Elements[i].get());
- if (i + 1 < A.Elements.size())
+ for (size_t I = 0, N = A.getArraySize(); I < N; ++I) {
+ const Value &EleVal = A.getArrayInitializedElt(I);
+ if (EleVal.hasValue())
+ OS << this->toString(&EleVal);
+
+ if (I + 1 < N)
OS << ", ";
}
@@ -385,7 +347,7 @@ std::string ValueToString::ArrayToString(const ArrayValueBuffer &A) {
return OS.str();
}
-std::string ValueResultManager::ValueTypeToString(QualType QT) const {
+std::string ValueToString::toString(QualType QT) {
ASTContext &AstCtx = const_cast<ASTContext &>(Ctx);
std::string QTStr = QualTypeToString(AstCtx, QT);
@@ -397,16 +359,15 @@ std::string ValueResultManager::ValueTypeToString(QualType QT) const {
}
void ValueResultManager::resetAndDump() {
- if (!ValBuf)
+ if (!LastVal.hasValue() || LastVal.isAbsent())
return;
- QualType Ty = ValBuf->Ty;
+ QualType Ty = LastVal.getType();
- std::unique_ptr<ValueBuffer> Val = nullptr;
- ValBuf.swap(Val);
+ Value V = std::move(LastVal);
// Don't even try to print a void or an invalid type, it doesn't make sense.
- if (Ty->isVoidType() || (!Val || (Val && Val->isUnknown())))
+ if (Ty->isVoidType())
return;
// We need to get all the results together then print it, since `printType` is
@@ -415,11 +376,12 @@ void ValueResultManager::resetAndDump() {
llvm::raw_string_ostream SS(Str);
ValueToString ValToStr(Ctx);
SS << "(";
- SS << ValueTypeToString(Ty);
+ SS << ValToStr.toString(Ty);
SS << ") ";
- SS << ValToStr.toString(Val.get());
+ SS << ValToStr.toString(&V);
SS << "\n";
llvm::outs() << Str;
+ V.clear();
}
llvm::Expected<llvm::orc::ExecutorAddr>
@@ -451,29 +413,52 @@ class ExprConverter {
public:
ExprConverter(Sema &S, ASTContext &Ctx) : S(S), Ctx(Ctx) {}
- /// Create (&E) as a void*
+private:
+ bool isAddressOfExpr(Expr *E) {
+ if (!E)
+ return false;
+ if (auto *UO = dyn_cast<UnaryOperator>(E))
+ return UO->getOpcode() == UO_AddrOf;
+ if (auto *ICE = dyn_cast<ImplicitCastExpr>(E))
+ return isAddressOfExpr(ICE->getSubExpr());
+ return false;
+ }
+
+ /// Build a single &E using Sema.
+ ExprResult buildAddrOfWithSema(Expr *E,
+ SourceLocation Loc = SourceLocation()) {
+ // Sema will materialize temporaries as necessary.
+ ExprResult Res = S.CreateBuiltinUnaryOp(Loc, UO_AddrOf, E);
+ if (Res.isInvalid())
+ return ExprError();
+ return Res;
+ }
+
+public:
+ /// Create (&E) as a void* (uses Sema for & creation)
ExprResult CreateAddressOfVoidPtrExpr(QualType Ty, Expr *ForCast,
bool takeAddr = false) {
- QualType VoidPtrTy = Ctx.getPointerType(Ctx.VoidTy);
+ ExprResult AddrOfRes = ForCast;
- // &E
- Expr *AddrOf = ForCast;
if (takeAddr) {
- AddrOf = UnaryOperator::Create(
- Ctx, ForCast, UO_AddrOf, Ctx.getPointerType(Ty), VK_PRValue,
- OK_Ordinary, SourceLocation(), false, FPOptionsOverride());
+ // don't create & twice
+ if (!isAddressOfExpr(ForCast)) {
+ AddrOfRes = buildAddrOfWithSema(ForCast);
+ if (AddrOfRes.isInvalid())
+ return ExprError();
+ } else {
+ // already an &-expression; keep it as-is
+ AddrOfRes = ForCast;
+ }
}
TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);
- ExprResult CastedExpr =
- S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), AddrOf);
- assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");
- return CastedExpr.get();
- // static_cast<void*>(&E)
- // return CXXStaticCastExpr::Create(
- // Ctx, VoidPtrTy, VK_PRValue, CK_BitCast, AddrOf, nullptr,
- // Ctx.getTrivialTypeSourceInfo(VoidPtrTy), FPOptionsOverride(),
- // SourceLocation(), SourceLocation(), SourceRange());
+ ExprResult CastedExpr = S.BuildCStyleCastExpr(
+ SourceLocation(), TSI, SourceLocation(), AddrOfRes.get());
+ if (CastedExpr.isInvalid())
+ return ExprError();
+
+ return CastedExpr;
}
/// Wrap rvalues in a temporary (var) so they become addressable.
@@ -494,7 +479,13 @@ class ExprConverter {
}
ExprResult handleEnumTypeExpr(const EnumType *, QualType QTy, Expr *E) {
- return makeScalarAddressable(QTy, E);
+ uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);
+ QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);
+ ExprResult CastedExpr =
+ S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
+ assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr");
+ return makeScalarAddressable(QTy, CastedExpr.get());
}
ExprResult handlePointerTypeExpr(const PointerType *, QualType QTy, Expr *E) {
@@ -524,12 +515,44 @@ class ExprConverter {
return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/false);
}
- ExprResult handleAnyObjectExpr(const Type *, QualType QTy, Expr *E) {
+ ExprResult handleMemberPointerTypeExpr(const Type *, QualType QTy, Expr *E) {
if (E->isLValue() || E->isXValue())
return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true);
return CreateAddressOfVoidPtrExpr(QTy, CreateMaterializeTemporaryExpr(E),
/*takeAddr=*/true);
}
+
+ ExprResult handleRecordTypeExpr(const Type *, QualType QTy, Expr *E) {
+ if (E->isLValue() || E->isXValue())
+ return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true);
+
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(QTy, SourceLocation());
+ ExprResult CXXNewCall =
+ S.BuildCXXNew(E->getSourceRange(),
+ /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(),
+ MultiExprArg(),
+ /*PlacementRParen=*/SourceLocation(),
+ /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI,
+ std::nullopt, E->getSourceRange(), E);
+
+ if (CXXNewCall.isInvalid())
+ return ExprError();
+
+ auto CallRes = S.ActOnFinishFullExpr(CXXNewCall.get(),
+ /*DiscardedValue=*/false);
+ if (CallRes.isInvalid())
+ return ExprError();
+ return CreateAddressOfVoidPtrExpr(QTy, CallRes.get(), /*takeAddr=*/false);
+ }
+
+ ExprResult handleAnyObjectExpr(const Type *, QualType QTy, Expr *E) {
+ if (E->isLValue() || E->isXValue())
+ return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true);
+ auto Res = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);
+ assert(!Res.isInvalid());
+ return CreateAddressOfVoidPtrExpr(QTy, Res.get(),
+ /*takeAddr=*/false);
+ }
};
class InterfaceKindVisitor : public TypeVisitor<InterfaceKindVisitor, bool> {
@@ -567,6 +590,18 @@ class InterfaceKindVisitor : public TypeVisitor<InterfaceKindVisitor, bool> {
return true;
}
+ bool VisitMemberPointerType(const MemberPointerType *Ty) {
+ Args.push_back(
+ Converter.handleMemberPointerTypeExpr(Ty, QualType(Ty, 0), E).get());
+ return true;
+ }
+
+ bool VisitRecordType(const RecordType *Ty) {
+ // Args.push_back(
+ // Converter.handleRecordTypeExpr(Ty, QualType(Ty, 0), E).get());
+ return false;
+ }
+
bool VisitConstantArrayType(const ConstantArrayType *Ty) {
Args.push_back(Converter.handleArrayTypeExpr(Ty, QualType(Ty, 0), E).get());
return true;
@@ -591,7 +626,7 @@ class InterfaceKindVisitor : public TypeVisitor<InterfaceKindVisitor, bool> {
}
};
-enum RunTimeFnTag { OrcSendResult, ClangSendResult };
+enum RunTimeFnTag { OrcSendResult, ClangSendResult, ClangDestroyObj };
static constexpr llvm::StringRef RunTimeFnTagName[] = {
"__orc_rt_SendResultValue", "__clang_Interpreter_SendResultValue"};
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp
index f3c89b620b505..438700afa98bd 100644
--- a/clang/lib/Interpreter/Value.cpp
+++ b/clang/lib/Interpreter/Value.cpp
@@ -112,173 +112,114 @@ class ValueStorage {
namespace clang {
-static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) {
- if (Ctx.hasSameType(QT, Ctx.VoidTy))
- return Value::K_Void;
-
- if (const auto *ED = QT->getAsEnumDecl())
- QT = ED->getIntegerType();
-
- const auto *BT = QT->getAs<BuiltinType>();
- if (!BT || BT->isNullPtrType())
- return Value::K_PtrOrObj;
-
- switch (QT->castAs<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(const Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) {
- const ASTContext &C = getASTContext();
- setKind(ConvertQualTypeToKind(C, 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.
- const 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 (auto *CXXRD = DtorTy->getAsCXXRecordDecl()) {
- 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);
+// static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT)
+// {
+// if (Ctx.hasSameType(QT, Ctx.VoidTy))
+// return Value::K_Void;
+
+// if (const auto *ED = QT->getAsEnumDecl())
+// QT = ED->getIntegerType();
+
+// const auto *BT = QT->getAs<BuiltinType>();
+// if (!BT || BT->isNullPtrType())
+// return Value::K_PtrOrObj;
+
+// switch (QT->castAs<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(const Value &RHS) : Ty(RHS.getType()), VKind(K_None) {
+ switch (RHS.getKind()) {
+ case K_None:
+ VKind = RHS.VKind;
+ break;
+ case K_Builtin: {
+ MakeBuiltIns();
+ if (RHS.asBuiltin().getKind() != BuiltinKind::K_Unspecified) {
+ setBuiltins(asBuiltin(), RHS.asBuiltin());
}
+ break;
+ }
+ case K_Array: {
+ MakeArray(RHS.getArraySize());
+ for (uint64_t I = 0, N = RHS.getArraySize(); I < N; ++I)
+ getArrayInitializedElt(I) = RHS.getArrayInitializedElt(I);
+ break;
+ }
+ case K_Pointer: {
+ MakePointer(RHS.getAddr());
+ if (RHS.HasPointee())
+ getPointerPointee() = RHS.getPointerPointee();
+ break;
+ }
+ case K_Str:
+ MakeStr(RHS.getStrVal().str());
+ break;
}
-}
-
-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();
+ if (this != &RHS)
+ *this = Value(RHS);
return *this;
}
-Value &Value::operator=(Value &&RHS) noexcept {
+Value &Value::operator=(Value &&RHS) {
if (this != &RHS) {
- 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);
+ if (VKind != K_None)
+ destroy();
+ Ty = RHS.Ty;
+ VKind = RHS.VKind;
Data = RHS.Data;
+ RHS.VKind = K_None;
}
- 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;
-}
-
-void Value::setRawBits(void *Ptr, unsigned NBits /*= sizeof(Storage)*/) {
- assert(NBits <= sizeof(Storage) && "Greater than the total size");
- memcpy(/*dest=*/Data.m_RawBits, /*src=*/Ptr, /*nbytes=*/NBits / 8);
-}
-
-QualType Value::getType() const {
- return QualType::getFromOpaquePtr(OpaqueType);
+ return *this;
}
-const Interpreter &Value::getInterpreter() const {
- assert(Interp != nullptr &&
- "Can't get interpreter from a default constructed value");
- return *Interp;
-}
+Value::Value(QualType QT, std::vector<uint8_t> Raw) : Ty(QT), VKind(K_None) {
+ MakeBuiltIns();
+ Builtins &B = asBuiltin();
+ if (const auto *ED = QT->getAsEnumDecl())
+ QT = ED->getIntegerType();
+ switch (QT->castAs<BuiltinType>()->getKind()) {
+ default:
+ assert(false && "Type not supported");
-const ASTContext &Value::getASTContext() const {
- return getInterpreter().getASTContext();
+#define X(type, name) \
+ case BuiltinType::name: { \
+ B.setKind(BuiltinKind::K_##name); \
+ B.set##name(as<type>(Raw)); \
+ } break;
+ REPL_BUILTIN_TYPES
+#undef X
+ }
}
-void Value::dump() const { print(llvm::outs()); }
+void Value::dump(ASTContext &Ctx) const { print(llvm::outs(), Ctx); }
-void Value::printType(llvm::raw_ostream &Out) const {
- // Out << Interp->ValueTypeToString(*this);
+void Value::printType(llvm::raw_ostream &Out, ASTContext &Ctx) const {
+ Out << ValueToString(Ctx).toString(getType());
}
-void Value::printData(llvm::raw_ostream &Out) const {
- Out << Interp->ValueDataToString(*this);
+void Value::printData(llvm::raw_ostream &Out, ASTContext &Ctx) const {
+ Out << ValueToString(Ctx).toString(this);
}
// FIXME: We do not support the multiple inheritance case where one of the base
// classes has a pretty-printer and the other does not.
-void Value::print(llvm::raw_ostream &Out) const {
- assert(OpaqueType != nullptr && "Can't print default Value");
-
+void Value::print(llvm::raw_ostream &Out, ASTContext &Ctx) const {
// Don't even try to print a void or an invalid type, it doesn't make sense.
- if (getType()->isVoidType() || !isValid())
+ if (getType()->isVoidType() || isAbsent())
return;
// We need to get all the results together then print it, since `printType` is
@@ -287,145 +228,97 @@ void Value::print(llvm::raw_ostream &Out) const {
llvm::raw_string_ostream SS(Str);
SS << "(";
- printType(SS);
+ printType(SS, Ctx);
SS << ") ";
- printData(SS);
+ printData(SS, Ctx);
SS << "\n";
Out << Str;
}
-class ReaderDispatcher {
+class ValueReaderDispatcher {
private:
ASTContext &Ctx;
llvm::orc::MemoryAccess &MA;
public:
- ReaderDispatcher(ASTContext &Ctx, llvm::orc::MemoryAccess &MA)
+ ValueReaderDispatcher(ASTContext &Ctx, llvm::orc::MemoryAccess &MA)
: Ctx(Ctx), MA(MA) {}
- llvm::Expected<std::unique_ptr<ValueBuffer>>
- read(QualType QT, llvm::orc::ExecutorAddr Addr);
-
- llvm::Expected<std::unique_ptr<ValueBuffer>>
- readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr);
+ llvm::Expected<Value> read(QualType QT, llvm::orc::ExecutorAddr Addr);
- llvm::Expected<std::unique_ptr<ValueBuffer>>
- readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr);
+ llvm::Expected<Value> readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr);
- llvm::Expected<std::unique_ptr<ValueBuffer>>
- readArray(QualType Ty, llvm::orc::ExecutorAddr Addr);
+ llvm::Expected<Value> readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr);
- llvm::Expected<std::unique_ptr<ValueBuffer>>
- readOtherObject(QualType Ty, llvm::orc::ExecutorAddr Addr);
+ llvm::Expected<Value> readArray(QualType Ty, llvm::orc::ExecutorAddr Addr);
+ llvm::Expected<Value> readOtherObject(QualType Ty,
+ llvm::orc::ExecutorAddr Addr);
// TODO: record, function, etc.
};
-class TypeReadVisitor
- : public TypeVisitor<TypeReadVisitor,
- llvm::Expected<std::unique_ptr<ValueBuffer>>> {
- ReaderDispatcher &Dispatcher;
+class ValueReadVisitor
+ : public TypeVisitor<ValueReadVisitor, llvm::Expected<Value>> {
+ ValueReaderDispatcher &Dispatcher;
llvm::orc::ExecutorAddr Addr;
public:
- TypeReadVisitor(ReaderDispatcher &D, llvm::orc::ExecutorAddr A)
+ ValueReadVisitor(ValueReaderDispatcher &D, llvm::orc::ExecutorAddr A)
: Dispatcher(D), Addr(A) {}
- llvm::Expected<std::unique_ptr<ValueBuffer>> VisitType(const Type *T) {
+ llvm::Expected<Value> VisitType(const Type *T) {
return Dispatcher.readOtherObject(QualType(T, 0), Addr);
}
- llvm::Expected<std::unique_ptr<ValueBuffer>>
- VisitBuiltinType(const BuiltinType *BT) {
+ llvm::Expected<Value> VisitBuiltinType(const BuiltinType *BT) {
return Dispatcher.readBuiltin(QualType(BT, 0), Addr);
}
- llvm::Expected<std::unique_ptr<ValueBuffer>>
- VisitPointerType(const PointerType *PT) {
+ llvm::Expected<Value> VisitPointerType(const PointerType *PT) {
return Dispatcher.readPointer(QualType(PT, 0), Addr);
}
- llvm::Expected<std::unique_ptr<ValueBuffer>>
- VisitConstantArrayType(const ConstantArrayType *AT) {
+ llvm::Expected<Value> VisitConstantArrayType(const ConstantArrayType *AT) {
return Dispatcher.readArray(QualType(AT, 0), Addr);
}
- llvm::Expected<std::unique_ptr<ValueBuffer>>
- VisitEnumType(const EnumType *ET) {
+ llvm::Expected<Value> VisitEnumType(const EnumType *ET) {
return Dispatcher.readBuiltin(QualType(ET, 0), Addr);
}
};
-llvm::Expected<std::unique_ptr<ValueBuffer>>
-ReaderDispatcher::read(QualType QT, llvm::orc::ExecutorAddr Addr) {
- TypeReadVisitor V(*this, Addr);
+llvm::Expected<Value>
+ValueReaderDispatcher::read(QualType QT, llvm::orc::ExecutorAddr Addr) {
+ ValueReadVisitor V(*this, Addr);
return V.Visit(QT.getTypePtr());
}
-llvm::Expected<std::unique_ptr<ValueBuffer>>
-ReaderDispatcher::readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr) {
+llvm::Expected<Value>
+ValueReaderDispatcher::readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr) {
+ if (Ty->isVoidType())
+ return Value();
auto Size = Ctx.getTypeSizeInChars(Ty).getQuantity();
auto ResOrErr = MA.readBuffers({llvm::orc::ExecutorAddrRange(Addr, Size)});
if (!ResOrErr)
return ResOrErr.takeError();
- auto Buf = std::make_unique<BuiltinValueBuffer>(Ty);
const auto &Res = *ResOrErr;
- std::vector<uint8_t> ElemBuf(Size);
- std::memcpy(ElemBuf.data(), Res.back().data(), Size);
- Buf->raw = std::move(ElemBuf);
- return std::move(Buf);
-}
-
-llvm::Expected<std::unique_ptr<ValueBuffer>>
-ReaderDispatcher::readArray(QualType Ty, llvm::orc::ExecutorAddr Addr) {
- const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(Ty);
- if (!CAT)
- return llvm::make_error<llvm::StringError>("Not a ConstantArrayType",
- llvm::inconvertibleErrorCode());
-
- QualType ElemTy = CAT->getElementType();
- size_t ElemSize = Ctx.getTypeSizeInChars(ElemTy).getQuantity();
-
- auto Buf = std::make_unique<ArrayValueBuffer>(Ty);
-
- for (size_t i = 0; i < CAT->getZExtSize(); ++i) {
- auto ElemAddr = Addr + i * ElemSize;
- auto ElemBufOrErr = read(ElemTy, ElemAddr);
- if (!ElemBufOrErr)
- return ElemBufOrErr.takeError();
- Buf->Elements.push_back(std::move(*ElemBufOrErr));
- }
-
- return std::move(Buf);
+ return Value(Ty, Res.back());
}
-llvm::Expected<std::unique_ptr<ValueBuffer>>
-ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty,
- llvm::orc::ExecutorAddr Addr) {
+llvm::Expected<Value>
+ValueReaderDispatcher::readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr) {
auto PtrTy = dyn_cast<PointerType>(Ty.getTypePtr());
if (!PtrTy)
return llvm::make_error<llvm::StringError>("Not a PointerType",
llvm::inconvertibleErrorCode());
- // unsigned PtrWidth = Ctx.getTypeSizeInChars(Ty).getQuantity();
- // uint64_t PtrValAddr = 0;
- // if (PtrWidth == 32) {
- // auto AddrOrErr = MA.readUInt32s({Addr});
- // if (!AddrOrErr)
- // return AddrOrErr.takeError();
- // PtrValAddr = AddrOrErr->back();
- // } else {
- // auto AddrOrErr = MA.readUInt64s({Addr});
- // if (!AddrOrErr)
- // return AddrOrErr.takeError();
- // PtrValAddr = AddrOrErr->back();
- // }
+
uint64_t PtrValAddr = Addr.getValue();
if (PtrValAddr == 0)
- return std::make_unique<PointerValueBuffer>(Ty); // null pointer
+ return Value(Ty, PtrValAddr); // null pointer
llvm::orc::ExecutorAddr PointeeAddr(PtrValAddr);
- auto PtrBuf = std::make_unique<PointerValueBuffer>(Ty, PtrValAddr);
+ Value Val(Ty, PtrValAddr);
QualType PointeeTy = PtrTy->getPointeeType();
if (PointeeTy->isCharType()) {
@@ -439,29 +332,57 @@ ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty,
break;
S.push_back(c);
}
- auto Buf = std::make_unique<BuiltinValueBuffer>(PointeeTy);
- Buf->raw.assign(S.begin(), S.end());
- if (S.empty())
- Buf->raw.push_back('\0'); // represent ""
- PtrBuf->Pointee = std::move(Buf);
+ Value Str(PointeeTy, S.c_str());
+ Val.getPointerPointee() = std::move(Str);
}
- // else {
- // auto BufOrErr = read(PointeeTy, PointeeAddr);
- // if (!BufOrErr)
- // return BufOrErr.takeError();
- // PtrBuf->Pointee = std::move(*BufOrErr);
- // }
- return std::move(PtrBuf);
+
+ return std::move(Val);
}
-llvm::Expected<std::unique_ptr<ValueBuffer>>
-ReaderDispatcher::readOtherObject(QualType Ty, llvm::orc::ExecutorAddr Addr) {
- unsigned PtrWidth = Ctx.getTypeSizeInChars(Ty).getQuantity();
- uint64_t PtrValAddr = Addr.getValue();
- if (PtrValAddr == 0)
- return std::make_unique<PointerValueBuffer>(Ty); // null pointer
+llvm::Expected<Value>
+ValueReaderDispatcher::readArray(QualType Ty, llvm::orc::ExecutorAddr Addr) {
+ const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(Ty);
+ if (!CAT)
+ return llvm::make_error<llvm::StringError>("Not a ConstantArrayType",
+ llvm::inconvertibleErrorCode());
+
+ QualType ElemTy = CAT->getElementType();
+ size_t ElemSize = Ctx.getTypeSizeInChars(ElemTy).getQuantity();
+
+ Value Val(Value::UninitArr(), Ty, CAT->getZExtSize());
+ for (size_t i = 0; i < CAT->getZExtSize(); ++i) {
+ auto ElemAddr = Addr + i * ElemSize;
+ if (ElemTy->isPointerType()) {
+ auto BufOrErr = MA.readUInt64s({ElemAddr});
+ if (!BufOrErr)
+ return BufOrErr.takeError();
+ llvm::orc::ExecutorAddr Addr(BufOrErr->back());
+ ElemAddr = Addr;
+ }
- return std::make_unique<PointerValueBuffer>(Ty, PtrValAddr);
+ auto ElemBufOrErr = read(ElemTy, ElemAddr);
+ if (!ElemBufOrErr)
+ return ElemBufOrErr.takeError();
+ Val.getArrayInitializedElt(i) = std::move(*ElemBufOrErr);
+ }
+
+ return std::move(Val);
+}
+
+llvm::Expected<Value>
+ValueReaderDispatcher::readOtherObject(QualType Ty,
+ llvm::orc::ExecutorAddr Addr) {
+ llvm::outs() << Addr.getValue();
+ if (Ty->isRecordType()) {
+ llvm::outs() << "Here in recordtype\n";
+ auto BufOrErr = MA.readUInt64s({Addr});
+ if (!BufOrErr)
+ return BufOrErr.takeError();
+ Addr = llvm::orc::ExecutorAddr(BufOrErr->back());
+ }
+ uint64_t PtrValAddr = Addr.getValue();
+ llvm::outs() << PtrValAddr;
+ return Value(Ty, PtrValAddr);
}
ValueResultManager::ValueResultManager(ASTContext &Ctx,
@@ -511,18 +432,16 @@ void ValueResultManager::deliverResult(SendResultFn SendResult, ValueId ID,
IdToType.erase(It);
}
- ReaderDispatcher Runner(Ctx, MemAcc);
+ ValueReaderDispatcher Runner(Ctx, MemAcc);
auto BufOrErr = Runner.read(Ty, Addr);
- ValBuf.reset();
if (!BufOrErr) {
SendResult(BufOrErr.takeError());
return;
}
// Store the successfully read value buffer
- ValBuf.swap(*BufOrErr);
-
+ LastVal = std::move(*BufOrErr);
SendResult(llvm::Error::success());
return;
}
diff --git a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
index f50f6e320776d..1ae96e101b50d 100644
--- a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
@@ -100,7 +100,7 @@ TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) {
Value V1;
llvm::cantFail(I.ParseAndExecute("int x = 42;"));
llvm::cantFail(I.ParseAndExecute("x", &V1));
- EXPECT_FALSE(V1.isValid());
+ EXPECT_TRUE(V1.isAbsent());
EXPECT_FALSE(V1.hasValue());
}
diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp
index 9ff9092524d21..765b834f07070 100644
--- a/clang/unittests/Interpreter/InterpreterTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterTest.cpp
@@ -241,66 +241,66 @@ TEST_F(InterpreterTest, FindMangledNameSymbol) {
#endif // _WIN32
}
-static Value AllocateObject(TypeDecl *TD, Interpreter &Interp) {
- std::string Name = TD->getQualifiedNameAsString();
- Value Addr;
- // FIXME: Consider providing an option in clang::Value to take ownership of
- // the memory created from the interpreter.
- // cantFail(Interp.ParseAndExecute("new " + Name + "()", &Addr));
-
- // The lifetime of the temporary is extended by the clang::Value.
- cantFail(Interp.ParseAndExecute(Name + "()", &Addr));
- return Addr;
-}
-
-static NamedDecl *LookupSingleName(Interpreter &Interp, const char *Name) {
- Sema &SemaRef = Interp.getCompilerInstance()->getSema();
- ASTContext &C = SemaRef.getASTContext();
- DeclarationName DeclName = &C.Idents.get(Name);
- LookupResult R(SemaRef, DeclName, SourceLocation(), Sema::LookupOrdinaryName);
- SemaRef.LookupName(R, SemaRef.TUScope);
- assert(!R.empty());
- return R.getFoundDecl();
-}
-
-TEST_F(InterpreterTest, InstantiateTemplate) {
- // FIXME: We cannot yet handle delayed template parsing. If we run with
- // -fdelayed-template-parsing we try adding the newly created decl to the
- // active PTU which causes an assert.
- std::vector<const char *> Args = {"-fno-delayed-template-parsing"};
- std::unique_ptr<Interpreter> Interp = createInterpreter(Args);
-
- llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);"
- "class A {};"
- "struct B {"
- " template<typename T>"
- " static int callme(T) { return 42; }"
- "};"));
- auto &PTU = llvm::cantFail(Interp->Parse("auto _t = &B::callme<A*>;"));
- auto PTUDeclRange = PTU.TUPart->decls();
- EXPECT_EQ(1, std::distance(PTUDeclRange.begin(), PTUDeclRange.end()));
-
- // Lower the PTU
- if (llvm::Error Err = Interp->Execute(PTU)) {
- // We cannot execute on the platform.
- consumeError(std::move(Err));
- return;
- }
-
- TypeDecl *TD = cast<TypeDecl>(LookupSingleName(*Interp, "A"));
- Value NewA = AllocateObject(TD, *Interp);
-
- // Find back the template specialization
- VarDecl *VD = static_cast<VarDecl *>(*PTUDeclRange.begin());
- UnaryOperator *UO = llvm::cast<UnaryOperator>(VD->getInit());
- NamedDecl *TmpltSpec = llvm::cast<DeclRefExpr>(UO->getSubExpr())->getDecl();
-
- std::string MangledName = MangleName(TmpltSpec);
- typedef int (*TemplateSpecFn)(void *);
- auto fn =
- cantFail(Interp->getSymbolAddress(MangledName)).toPtr<TemplateSpecFn>();
- EXPECT_EQ(42, fn(NewA.getPtr()));
-}
+// static Value AllocateObject(TypeDecl *TD, Interpreter &Interp) {
+// std::string Name = TD->getQualifiedNameAsString();
+// Value Addr;
+// // FIXME: Consider providing an option in clang::Value to take ownership of
+// // the memory created from the interpreter.
+// // cantFail(Interp.ParseAndExecute("new " + Name + "()", &Addr));
+
+// // The lifetime of the temporary is extended by the clang::Value.
+// cantFail(Interp.ParseAndExecute(Name + "()", &Addr));
+// return Addr;
+// }
+
+// static NamedDecl *LookupSingleName(Interpreter &Interp, const char *Name) {
+// Sema &SemaRef = Interp.getCompilerInstance()->getSema();
+// ASTContext &C = SemaRef.getASTContext();
+// DeclarationName DeclName = &C.Idents.get(Name);
+// LookupResult R(SemaRef, DeclName, SourceLocation(), Sema::LookupOrdinaryName);
+// SemaRef.LookupName(R, SemaRef.TUScope);
+// assert(!R.empty());
+// return R.getFoundDecl();
+// }
+
+// TEST_F(InterpreterTest, InstantiateTemplate) {
+// // FIXME: We cannot yet handle delayed template parsing. If we run with
+// // -fdelayed-template-parsing we try adding the newly created decl to the
+// // active PTU which causes an assert.
+// std::vector<const char *> Args = {"-fno-delayed-template-parsing"};
+// std::unique_ptr<Interpreter> Interp = createInterpreter(Args);
+
+// llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);"
+// "class A {};"
+// "struct B {"
+// " template<typename T>"
+// " static int callme(T) { return 42; }"
+// "};"));
+// auto &PTU = llvm::cantFail(Interp->Parse("auto _t = &B::callme<A*>;"));
+// auto PTUDeclRange = PTU.TUPart->decls();
+// EXPECT_EQ(1, std::distance(PTUDeclRange.begin(), PTUDeclRange.end()));
+
+// // Lower the PTU
+// if (llvm::Error Err = Interp->Execute(PTU)) {
+// // We cannot execute on the platform.
+// consumeError(std::move(Err));
+// return;
+// }
+
+// TypeDecl *TD = cast<TypeDecl>(LookupSingleName(*Interp, "A"));
+// Value NewA = AllocateObject(TD, *Interp);
+
+// // Find back the template specialization
+// VarDecl *VD = static_cast<VarDecl *>(*PTUDeclRange.begin());
+// UnaryOperator *UO = llvm::cast<UnaryOperator>(VD->getInit());
+// NamedDecl *TmpltSpec = llvm::cast<DeclRefExpr>(UO->getSubExpr())->getDecl();
+
+// std::string MangledName = MangleName(TmpltSpec);
+// typedef int (*TemplateSpecFn)(void *);
+// auto fn =
+// cantFail(Interp->getSymbolAddress(MangledName)).toPtr<TemplateSpecFn>();
+// EXPECT_EQ(42, fn((void *)NewA.getAddr()));
+// }
TEST_F(InterpreterTest, Value) {
std::vector<const char *> Args = {"-fno-sized-deallocation"};
@@ -309,40 +309,53 @@ TEST_F(InterpreterTest, Value) {
Value V1;
llvm::cantFail(Interp->ParseAndExecute("int x = 42;"));
llvm::cantFail(Interp->ParseAndExecute("x", &V1));
- EXPECT_TRUE(V1.isValid());
+ EXPECT_FALSE(V1.isAbsent());
EXPECT_TRUE(V1.hasValue());
+ EXPECT_TRUE(V1.isBuiltin());
EXPECT_EQ(V1.getInt(), 42);
- EXPECT_EQ(V1.convertTo<int>(), 42);
EXPECT_TRUE(V1.getType()->isIntegerType());
- EXPECT_EQ(V1.getKind(), Value::K_Int);
- EXPECT_FALSE(V1.isManuallyAlloc());
+ EXPECT_EQ(V1.getBuiltinKind(), Value::K_Int);
+
+ Value V1c;
+ llvm::cantFail(Interp->ParseAndExecute("int arr[2] = {42, 24};"));
+ llvm::cantFail(Interp->ParseAndExecute("arr", &V1c));
+ EXPECT_FALSE(V1c.isAbsent());
+ EXPECT_TRUE(V1c.hasValue());
+ EXPECT_TRUE(V1c.isArray());
+ EXPECT_FALSE(V1c.getArrayInitializedElt(0).isAbsent());
+ EXPECT_TRUE(V1c.getArrayInitializedElt(0).hasValue());
+ EXPECT_TRUE(V1c.getArrayInitializedElt(0).isBuiltin());
+ EXPECT_EQ(V1c.getArrayInitializedElt(0).getInt(), 42);
+ EXPECT_FALSE(V1c.getArrayInitializedElt(1).isAbsent());
+ EXPECT_TRUE(V1c.getArrayInitializedElt(1).hasValue());
+ EXPECT_TRUE(V1c.getArrayInitializedElt(1).isBuiltin());
+ EXPECT_EQ(V1c.getArrayInitializedElt(1).getInt(), 24);
+ EXPECT_TRUE(V1c.getType()->isConstantArrayType());
Value V1b;
llvm::cantFail(Interp->ParseAndExecute("char c = 42;"));
llvm::cantFail(Interp->ParseAndExecute("c", &V1b));
- EXPECT_TRUE(V1b.getKind() == Value::K_Char_S ||
- V1b.getKind() == Value::K_Char_U);
+ EXPECT_TRUE(V1b.isBuiltin());
+ EXPECT_TRUE(V1b.getBuiltinKind() == Value::K_Char_S ||
+ V1b.getBuiltinKind() == Value::K_Char_U);
Value V2;
llvm::cantFail(Interp->ParseAndExecute("double y = 3.14;"));
llvm::cantFail(Interp->ParseAndExecute("y", &V2));
- EXPECT_TRUE(V2.isValid());
+ EXPECT_FALSE(V2.isAbsent());
EXPECT_TRUE(V2.hasValue());
EXPECT_EQ(V2.getDouble(), 3.14);
- EXPECT_EQ(V2.convertTo<double>(), 3.14);
EXPECT_TRUE(V2.getType()->isFloatingType());
- EXPECT_EQ(V2.getKind(), Value::K_Double);
- EXPECT_FALSE(V2.isManuallyAlloc());
+ EXPECT_EQ(V2.getBuiltinKind(), Value::K_Double);
- Value V3;
- llvm::cantFail(Interp->ParseAndExecute(
- "struct S { int* p; S() { p = new int(42); } ~S() { delete p; }};"));
- llvm::cantFail(Interp->ParseAndExecute("S{}", &V3));
- EXPECT_TRUE(V3.isValid());
- EXPECT_TRUE(V3.hasValue());
- EXPECT_TRUE(V3.getType()->isRecordType());
- EXPECT_EQ(V3.getKind(), Value::K_PtrOrObj);
- EXPECT_TRUE(V3.isManuallyAlloc());
+ // Value V3;
+ // llvm::cantFail(Interp->ParseAndExecute(
+ // "struct S { int* p; S() { p = new int(42); } ~S() { delete p; }};"));
+ // llvm::cantFail(Interp->ParseAndExecute("S{}", &V3));
+ // EXPECT_FALSE(V3.isAbsent());
+ // EXPECT_TRUE(V3.hasValue());
+ // EXPECT_TRUE(V3.getType()->isRecordType());
+ // EXPECT_TRUE(V3.isPointer());
Value V4;
llvm::cantFail(Interp->ParseAndExecute("int getGlobal();"));
@@ -365,28 +378,24 @@ TEST_F(InterpreterTest, Value) {
Value V6;
llvm::cantFail(Interp->ParseAndExecute("void foo() {}"));
llvm::cantFail(Interp->ParseAndExecute("foo()", &V6));
- EXPECT_TRUE(V6.isValid());
+ EXPECT_TRUE(V6.isAbsent());
EXPECT_FALSE(V6.hasValue());
- EXPECT_TRUE(V6.getType()->isVoidType());
- EXPECT_EQ(V6.getKind(), Value::K_Void);
- EXPECT_FALSE(V2.isManuallyAlloc());
+ // EXPECT_TRUE(V6.getType()->isVoidType());
Value V7;
llvm::cantFail(Interp->ParseAndExecute("foo", &V7));
- EXPECT_TRUE(V7.isValid());
+ EXPECT_FALSE(V7.isAbsent());
EXPECT_TRUE(V7.hasValue());
EXPECT_TRUE(V7.getType()->isFunctionProtoType());
- EXPECT_EQ(V7.getKind(), Value::K_PtrOrObj);
- EXPECT_FALSE(V7.isManuallyAlloc());
+ EXPECT_TRUE(V7.isPointer());
Value V8;
llvm::cantFail(Interp->ParseAndExecute("struct SS{ void f() {} };"));
llvm::cantFail(Interp->ParseAndExecute("&SS::f", &V8));
- EXPECT_TRUE(V8.isValid());
+ EXPECT_FALSE(V8.isAbsent());
EXPECT_TRUE(V8.hasValue());
EXPECT_TRUE(V8.getType()->isMemberFunctionPointerType());
- EXPECT_EQ(V8.getKind(), Value::K_PtrOrObj);
- EXPECT_TRUE(V8.isManuallyAlloc());
+ EXPECT_TRUE(V8.isPointer());
Value V9;
llvm::cantFail(Interp->ParseAndExecute("struct A { virtual int f(); };"));
@@ -394,30 +403,30 @@ TEST_F(InterpreterTest, Value) {
Interp->ParseAndExecute("struct B : A { int f() { return 42; }};"));
llvm::cantFail(Interp->ParseAndExecute("int (B::*ptr)() = &B::f;"));
llvm::cantFail(Interp->ParseAndExecute("ptr", &V9));
- EXPECT_TRUE(V9.isValid());
+ EXPECT_FALSE(V9.isAbsent());
EXPECT_TRUE(V9.hasValue());
EXPECT_TRUE(V9.getType()->isMemberFunctionPointerType());
- EXPECT_EQ(V9.getKind(), Value::K_PtrOrObj);
- EXPECT_TRUE(V9.isManuallyAlloc());
+ EXPECT_TRUE(V9.isPointer());
Value V10;
llvm::cantFail(Interp->ParseAndExecute(
"enum D : unsigned int {Zero = 0, One}; One", &V10));
+ EXPECT_FALSE(V10.getBuiltinKind() == Value::K_Unspecified);
std::string prettyType;
llvm::raw_string_ostream OSType(prettyType);
- V10.printType(OSType);
+ V10.printType(OSType, Interp->getASTContext());
EXPECT_STREQ(prettyType.c_str(), "D");
// FIXME: We should print only the value or the constant not the type.
std::string prettyData;
llvm::raw_string_ostream OSData(prettyData);
- V10.printData(OSData);
+ V10.printData(OSData, Interp->getASTContext());
EXPECT_STREQ(prettyData.c_str(), "(One) : unsigned int 1");
std::string prettyPrint;
llvm::raw_string_ostream OSPrint(prettyPrint);
- V10.print(OSPrint);
+ V10.print(OSPrint, Interp->getASTContext());
EXPECT_STREQ(prettyPrint.c_str(), "(D) (One) : unsigned int 1\n");
}
More information about the llvm-commits
mailing list