[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
Wed Sep 3 04:47:18 PDT 2025
https://github.com/SahilPatidar created https://github.com/llvm/llvm-project/pull/156649
Reimplement value printing on top of ORC MemoryAccess. The previous
implementation only supported in-process evaluation; with this new
design it now works in both in-process and out-of-process modes.
The implementation introduces a ValueBuffer hierarchy (Builtin, Array,
Pointer) for capturing evaluated values, a ReaderDispatcher for reading
values from MemoryAccess, and a ValueToString printer for converting
buffers back to strings.
>From 73f4690f6761804655ca1ecac848456a8bc88b65 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/2] [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 83d2962cbf3ba..3ebe8aa924fcc 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -105,10 +105,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;
/// When CodeGen is created the first llvm::Module gets cached in many places
/// and we must keep it alive.
@@ -153,7 +150,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);
@@ -198,9 +195,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 5e5ae81b9ba44..52b9cdb9c4022 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -414,6 +414,9 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance,
return;
}
}
+
+ ValMgr = ValueResultManager::Create(IncrExecutor->GetExecutionEngine(),
+ getASTContext());
}
Interpreter::~Interpreter() {
@@ -436,6 +439,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;
@@ -456,6 +460,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, ...);
)";
@@ -708,7 +714,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)
@@ -717,12 +723,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 0ea6274b79cba..092aabedac870 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -341,11 +341,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 += " &";
@@ -353,6 +352,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");
@@ -375,99 +400,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.
@@ -482,7 +534,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();
@@ -504,34 +556,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))
@@ -546,87 +586,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())
@@ -634,97 +609,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 *ET = QT->getAs<EnumType>())
- QT = ET->getDecl()->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 be2ab5587a980..bb160fc6ca7d2 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>
@@ -254,7 +269,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 {
@@ -282,4 +297,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 33a530cf230fb7a20b72a327a6abdcfa0cf4bdb2 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/2] Fix issues and add ValueToString helper
---
clang/include/clang/Interpreter/Interpreter.h | 2 +-
clang/include/clang/Interpreter/Value.h | 64 +++-
.../Interpreter/InterpreterValuePrinter.cpp | 282 ++++++++++--------
clang/lib/Interpreter/Value.cpp | 218 ++------------
4 files changed, 247 insertions(+), 319 deletions(-)
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 3ebe8aa924fcc..018c3885e6a13 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -196,7 +196,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 092aabedac870..fbf2cb0df56ae 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -96,17 +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());
- QualType DesugaredTy = V.getType().getDesugaredType(Ctx);
+ QualType DesugaredTy = QT.getDesugaredType(Ctx);
const EnumType *EnumTy = DesugaredTy.getNonReferenceType()->getAs<EnumType>();
assert(EnumTy && "Fail to cast to enum type");
EnumDecl *ED = EnumTy->getDecl();
- uint64_t Data = V.convertTo<uint64_t>();
bool IsFirst = true;
llvm::APSInt AP = Ctx.MakeIntValue(Data, DesugaredTy);
@@ -124,12 +122,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;
@@ -158,84 +157,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 {
@@ -267,78 +267,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 {
@@ -362,18 +401,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;
}
@@ -439,7 +478,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);
@@ -466,6 +505,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> {
@@ -510,10 +552,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 bb160fc6ca7d2..3f47292c27879 100644
--- a/clang/lib/Interpreter/Value.cpp
+++ b/clang/lib/Interpreter/Value.cpp
@@ -297,197 +297,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;
@@ -542,6 +351,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>(
@@ -565,7 +379,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);
@@ -601,12 +415,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
@@ -627,6 +449,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 {
More information about the llvm-commits
mailing list