https://github.com/SahilPatidar updated https://github.com/llvm/llvm-project/pull/156649
>From 4a56b9825b7dda9b729bd2a7790526a3a7d286c7 Mon Sep 17 00:00:00 2001 From: SahilPatidar <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
