[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
Mon Sep 8 04:29:46 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/4] [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/4] 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/4] 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/4] 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);
}
More information about the llvm-commits
mailing list