From 456b66b4143ae8d6a43ff77f77ed6cb03bd17454 Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Tue, 2 Sep 2025 12:08:29 +0530 Subject: [PATCH 01/11] [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 078d70b3b1749..9bfe8ac9cc8e1 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -109,10 +109,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 ValMgr; /// Compiler instance performing the incremental compilation. std::unique_ptr CI; @@ -185,7 +182,7 @@ class Interpreter { llvm::Expected 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); @@ -226,9 +223,8 @@ class Interpreter { /// @{ std::string ValueDataToString(const Value &V) const; - std::string ValueTypeToString(const Value &V) const; - llvm::Expected convertExprToValue(Expr *E); + llvm::Expected 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 #include @@ -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(); } + +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; + + explicit ValueResultManager(ASTContext &Ctx, llvm::orc::MemoryAccess &MemAcc); + + static std::unique_ptr + 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 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 ValBuf = nullptr; + llvm::DenseMap IdToType; +}; + } // namespace clang #endif diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 07c170a63ce82..178efa3e75398 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -302,6 +302,9 @@ Interpreter::Interpreter(std::unique_ptr Instance, return; } } + + ValMgr = ValueResultManager::Create(IncrExecutor->GetExecutionEngine(), + getASTContext()); } Interpreter::~Interpreter() { @@ -324,6 +327,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; @@ -344,6 +348,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, ...); )"; @@ -699,7 +705,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) @@ -708,12 +714,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(V.getASTContext()); - QualType QT = V.getType(); +std::string ValueResultManager::ValueTypeToString(QualType QT) const { + ASTContext &AstCtx = const_cast(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 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 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 { +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(&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 &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 &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 { + Sema &S; + Expr *E; + llvm::SmallVectorImpl &Args; + ExprConverter Converter; + +public: + InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl &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 Interpreter::convertExprToValue(Expr *E) { +llvm::Expected Interpreter::convertExprToValue(Expr *E, bool isOOP) { Sema &S = getCompilerInstance()->getSema(); ASTContext &Ctx = S.getASTContext(); @@ -500,34 +552,22 @@ llvm::Expected 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 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(E)) @@ -542,87 +582,22 @@ llvm::Expected 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( - "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(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( - "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( - "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 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(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(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()->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(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 +#include +#include +#include + #include #include @@ -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 raw; + BuiltinValueBuffer(QualType _Ty) { Ty = _Ty; } + template 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(); + 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(c) + "'"; + else { + return llvm::formatv("'\\x{0:02X}'", c).str(); + } + } + } + if (auto *BT = Ty.getCanonicalType()->getAs()) { + + 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(static_cast(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()) ? "true" : "false"); + return Str; + case clang::BuiltinType::Short: + SS << as(); + return Str; + case clang::BuiltinType::UShort: + SS << as(); + return Str; + case clang::BuiltinType::Int: + SS << as(); + return Str; + case clang::BuiltinType::UInt: + SS << as(); + return Str; + case clang::BuiltinType::Long: + SS << as(); + return Str; + case clang::BuiltinType::ULong: + SS << as(); + return Str; + case clang::BuiltinType::LongLong: + SS << as(); + return Str; + case clang::BuiltinType::ULongLong: + SS << as(); + return Str; + case clang::BuiltinType::Float: + return formatFloating(as(), /*suffix=*/'f'); + + case clang::BuiltinType::Double: + return formatFloating(as()); + + case clang::BuiltinType::LongDouble: + return formatFloating(as(), /*suffix=*/'L'); + } + } + + return ""; + } + + bool isValid() const override { return !raw.empty(); } +}; + +class ArrayValueBuffer : public ValueBuffer { +public: + std::vector> 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 &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(c))) + Out.push_back(c); + else { + char buf[5]; + snprintf(buf, sizeof(buf), "\\x%02X", static_cast(c)); + Out += buf; + } + break; + } + } + return Out; +} + +class PointerValueBuffer : public ValueBuffer { +public: + uint64_t Address = 0; + std::unique_ptr 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(Ty.getTypePtr()); + if (!PtrTy) + return ""; + + auto PointeeTy = PtrTy->getPointeeType(); + + // char* -> print string literal + if (PointeeTy->isCharType() && Pointee) { + if (auto *BE = static_cast(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> + read(QualType QT, llvm::orc::ExecutorAddr Addr); + + llvm::Expected> + readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr); + + llvm::Expected> + readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr); + + llvm::Expected> + readArray(QualType Ty, llvm::orc::ExecutorAddr Addr); + + // TODO: record, function, etc. +}; + +class TypeReadVisitor + : public TypeVisitor>> { + ReaderDispatcher &Dispatcher; + llvm::orc::ExecutorAddr Addr; + +public: + TypeReadVisitor(ReaderDispatcher &D, llvm::orc::ExecutorAddr A) + : Dispatcher(D), Addr(A) {} + + llvm::Expected> VisitType(const Type *T) { + return llvm::make_error( + "Unsupported type in ReaderDispatcher", llvm::inconvertibleErrorCode()); + } + + llvm::Expected> + VisitBuiltinType(const BuiltinType *BT) { + return Dispatcher.readBuiltin(QualType(BT, 0), Addr); + } + + llvm::Expected> + VisitPointerType(const PointerType *PT) { + return Dispatcher.readPointer(QualType(PT, 0), Addr); + } + + llvm::Expected> + VisitConstantArrayType(const ConstantArrayType *AT) { + return Dispatcher.readArray(QualType(AT, 0), Addr); + } + + llvm::Expected> + VisitRecordType(const RecordType *RT) { + return llvm::make_error( + "RecordType reading not yet implemented", + llvm::inconvertibleErrorCode()); + } +}; + +llvm::Expected> +ReaderDispatcher::read(QualType QT, llvm::orc::ExecutorAddr Addr) { + TypeReadVisitor V(*this, Addr); + return V.Visit(QT.getTypePtr()); +} + +llvm::Expected> +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(Ty); + const auto &Res = *ResOrErr; + std::vector ElemBuf(Size); + std::memcpy(ElemBuf.data(), Res.back().data(), Size); + Buf->raw = std::move(ElemBuf); + return std::move(Buf); +} + +llvm::Expected> +ReaderDispatcher::readArray(QualType Ty, llvm::orc::ExecutorAddr Addr) { + const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(Ty); + if (!CAT) + return llvm::make_error("Not a ConstantArrayType", + llvm::inconvertibleErrorCode()); + + QualType ElemTy = CAT->getElementType(); + size_t ElemSize = Ctx.getTypeSizeInChars(ElemTy).getQuantity(); + + auto Buf = std::make_unique(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> +ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty, + llvm::orc::ExecutorAddr Addr) { + auto PtrTy = dyn_cast(Ty.getTypePtr()); + if (!PtrTy) + return llvm::make_error("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(Ty); // null pointer + + llvm::orc::ExecutorAddr PointeeAddr(PtrValAddr); + auto PtrBuf = std::make_unique(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(CRes->back()); + if (c == '\0') + break; + S.push_back(c); + } + auto Buf = std::make_unique(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::Create(llvm::orc::LLJIT &EE, ASTContext &Ctx, bool IsOop) { + auto &ES = EE.getExecutionSession(); + auto &EPC = ES.getExecutorProcessControl(); + auto VRMgr = std::make_unique(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( + 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 Lock(Mutex); + auto It = IdToType.find(ID); + if (It == IdToType.end()) { + SendResult(llvm::make_error( + "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::call( + JITDispatch(&__orc_rt_SendResultValue_tag), OptErr, ResultId, + ExecutorAddr::fromPtr(V))) { + cantFail(std::move(OptErr)); + cantFail(std::move(Err)); + } + consumeError(std::move(OptErr)); +} From dcdf61cc5c67b60bc13dac6225e4bff5208e12b8 Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Wed, 3 Sep 2025 17:11:34 +0530 Subject: [PATCH 02/11] 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 9bfe8ac9cc8e1..d57803a7d809c 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -224,7 +224,7 @@ class Interpreter { std::string ValueDataToString(const Value &V) const; - llvm::Expected convertExprToValue(Expr *E, bool IsOOP = true); + llvm::Expected 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 raw; + BuiltinValueBuffer(QualType _Ty) : ValueBuffer(K_Builtin) { Ty = _Ty; } + template 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> 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 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 - 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(V.getASTContext()); - uint64_t Data = V.convertTo(); + QualType DesugaredTy = QT.getDesugaredType(Ctx); + const EnumType *EnumTy = DesugaredTy.getNonReferenceType()->getAs(); + 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 &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(c))) + Out.push_back(c); + else { + char buf[5]; + snprintf(buf, sizeof(buf), "\\x%02X", static_cast(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(Buf)) + return BuiltinToString(*B); + else if (const ArrayValueBuffer *A = llvm::dyn_cast(Buf)) + return ArrayToString(*A); + else if (const PointerValueBuffer *P = + llvm::dyn_cast(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(); + 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(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()) { 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()) ? "true" : "false"); return Str; case clang::BuiltinType::Short: - SS << V.getShort(); + SS << B.as(); return Str; case clang::BuiltinType::UShort: - SS << V.getUShort(); + SS << B.as(); return Str; case clang::BuiltinType::Int: - SS << V.getInt(); + SS << B.as(); return Str; case clang::BuiltinType::UInt: - SS << V.getUInt(); + SS << B.as(); return Str; case clang::BuiltinType::Long: - SS << V.getLong(); + SS << B.as(); return Str; case clang::BuiltinType::ULong: - SS << V.getULong(); + SS << B.as(); return Str; case clang::BuiltinType::LongLong: - SS << V.getLongLong(); + SS << B.as(); return Str; case clang::BuiltinType::ULongLong: - SS << V.getULongLong(); + SS << B.as(); return Str; case clang::BuiltinType::Float: - return formatFloating(V.getFloat(), /*suffix=*/'f'); + return formatFloating(B.as(), /*suffix=*/'f'); case clang::BuiltinType::Double: - return formatFloating(V.getDouble()); + return formatFloating(B.as()); case clang::BuiltinType::LongDouble: - return formatFloating(V.getLongDouble(), /*suffix=*/'L'); + return formatFloating(B.as(), /*suffix=*/'L'); } } + if (NonRefTy->isEnumeralType()) + return EnumToString(Ctx, QT, B.as()); + + 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(QT.getTypePtr()); + if (!PtrTy) + return ""; + + auto PointeeTy = PtrTy->getPointeeType(); + + // char* -> print string literal + if (PointeeTy->isCharType() && P.Pointee) { + if (auto *BE = static_cast(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(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(A.Elements[i].get())) { + OS << static_cast(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 { @@ -506,10 +553,14 @@ class InterfaceKindVisitor : public TypeVisitor { 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 raw; - BuiltinValueBuffer(QualType _Ty) { Ty = _Ty; } - template 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(); - 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(c) + "'"; - else { - return llvm::formatv("'\\x{0:02X}'", c).str(); - } - } - } - if (auto *BT = Ty.getCanonicalType()->getAs()) { - - 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(static_cast(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()) ? "true" : "false"); - return Str; - case clang::BuiltinType::Short: - SS << as(); - return Str; - case clang::BuiltinType::UShort: - SS << as(); - return Str; - case clang::BuiltinType::Int: - SS << as(); - return Str; - case clang::BuiltinType::UInt: - SS << as(); - return Str; - case clang::BuiltinType::Long: - SS << as(); - return Str; - case clang::BuiltinType::ULong: - SS << as(); - return Str; - case clang::BuiltinType::LongLong: - SS << as(); - return Str; - case clang::BuiltinType::ULongLong: - SS << as(); - return Str; - case clang::BuiltinType::Float: - return formatFloating(as(), /*suffix=*/'f'); - - case clang::BuiltinType::Double: - return formatFloating(as()); - - case clang::BuiltinType::LongDouble: - return formatFloating(as(), /*suffix=*/'L'); - } - } - - return ""; - } - - bool isValid() const override { return !raw.empty(); } -}; - -class ArrayValueBuffer : public ValueBuffer { -public: - std::vector> 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 &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(c))) - Out.push_back(c); - else { - char buf[5]; - snprintf(buf, sizeof(buf), "\\x%02X", static_cast(c)); - Out += buf; - } - break; - } - } - return Out; -} - -class PointerValueBuffer : public ValueBuffer { -public: - uint64_t Address = 0; - std::unique_ptr 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(Ty.getTypePtr()); - if (!PtrTy) - return ""; - - auto PointeeTy = PtrTy->getPointeeType(); - - // char* -> print string literal - if (PointeeTy->isCharType() && Pointee) { - if (auto *BE = static_cast(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> + VisitEnumType(const EnumType *ET) { + return Dispatcher.readBuiltin(QualType(ET, 0), Addr); + } + llvm::Expected> VisitRecordType(const RecordType *RT) { return llvm::make_error( @@ -562,7 +376,7 @@ ReaderDispatcher::readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr) { auto Buf = std::make_unique(Ty); const auto &Res = *ResOrErr; - std::vector ElemBuf(Size); + std::vector 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("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(Ty); // null pointer @@ -624,6 +446,8 @@ ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty, } auto Buf = std::make_unique(PointeeTy); Buf->raw.assign(S.begin(), S.end()); + if (S.empty()) + Buf->raw.push_back('\0'); // represent "" PtrBuf->Pointee = std::move(Buf); } // else { From 7ae5095d061c6f92c3266483d973673b5077971f Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Sat, 6 Sep 2025 12:02:16 +0530 Subject: [PATCH 03/11] 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(); - 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 d540221aede0022f3eec64731818b944c187d9fd Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Mon, 8 Sep 2025 16:54:10 +0530 Subject: [PATCH 04/11] 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(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(Arg)) Arg = CastE->getSubExpr(); @@ -156,32 +157,32 @@ static std::string FunctionToString(ASTContext &Ctx, QualType QT, static std::string escapeString(const std::vector &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(c))) - Out.push_back(c); - else { - char buf[5]; - snprintf(buf, sizeof(buf), "\\x%02X", static_cast(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(c))) + Out.push_back(c); + else { + char buf[5]; + snprintf(buf, sizeof(buf), "\\x%02X", static_cast(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(); 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(QT.getTypePtr()); - if (!PtrTy) - return ""; + if (auto PtrTy = dyn_cast(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(P.Pointee.get())) + return "\"" + escapeString(BE->raw) + "\""; + } - // char* -> print string literal - if (PointeeTy->isCharType() && P.Pointee) { - if (auto *BE = static_cast(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(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(A.Elements[i].get())) { - OS << static_cast(B->raw.back()); + char c = static_cast(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(&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(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 { public: InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl &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 { 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> readArray(QualType Ty, llvm::orc::ExecutorAddr Addr); + llvm::Expected> + readOtherObject(QualType Ty, llvm::orc::ExecutorAddr Addr); + // TODO: record, function, etc. }; @@ -329,8 +332,7 @@ class TypeReadVisitor : Dispatcher(D), Addr(A) {} llvm::Expected> VisitType(const Type *T) { - return llvm::make_error( - "Unsupported type in ReaderDispatcher", llvm::inconvertibleErrorCode()); + return Dispatcher.readOtherObject(QualType(T, 0), Addr); } llvm::Expected> @@ -352,13 +354,6 @@ class TypeReadVisitor VisitEnumType(const EnumType *ET) { return Dispatcher.readBuiltin(QualType(ET, 0), Addr); } - - llvm::Expected> - VisitRecordType(const RecordType *RT) { - return llvm::make_error( - "RecordType reading not yet implemented", - llvm::inconvertibleErrorCode()); - } }; llvm::Expected> @@ -412,20 +407,20 @@ ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty, if (!PtrTy) return llvm::make_error("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(Ty); // null pointer @@ -459,6 +454,16 @@ ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty, return std::move(PtrBuf); } +llvm::Expected> +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(Ty); // null pointer + + return std::make_unique(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( "Unknown ValueId in deliverResult", llvm::inconvertibleErrorCode())); } - Ty = It->second; + Ty = It->second.getCanonicalType(); IdToType.erase(It); } From 243331fe645b8f3eb2fd13e328530a83bbf404fd Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Fri, 12 Sep 2025 20:38:40 +0530 Subject: [PATCH 05/11] Introduce new Value class design, inspired by APValue --- clang/include/clang/Interpreter/Interpreter.h | 2 +- clang/include/clang/Interpreter/Value.h | 405 +++++++++++------ clang/lib/Interpreter/Interpreter.cpp | 7 +- .../Interpreter/InterpreterValuePrinter.cpp | 301 +++++++------ clang/lib/Interpreter/Value.cpp | 407 +++++++----------- .../Interpreter/InterpreterExtensionsTest.cpp | 2 +- .../unittests/Interpreter/InterpreterTest.cpp | 199 +++++---- 7 files changed, 710 insertions(+), 613 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index d57803a7d809c..5555ad2a82245 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -182,7 +182,7 @@ class Interpreter { llvm::Expected Parse(llvm::StringRef Code); llvm::Error Execute(PartialTranslationUnit &T); - llvm::Error ParseAndExecute(llvm::StringRef Code); + llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr); /// Undo N previous incremental inputs. llvm::Error Undo(unsigned N = 1); diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h index dda8c6bbf09aa..3da9f5769d98f 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -95,171 +95,302 @@ class QualType; X(double, Double) \ X(long double, LongDouble) -class REPL_EXTERNAL_VISIBILITY Value { - union Storage { -#define X(type, name) type m_##name; - REPL_BUILTIN_TYPES -#undef X - void *m_Ptr; - unsigned char m_RawBits[sizeof(long double) * 8]; // widest type - }; - +class REPL_EXTERNAL_VISIBILITY Value final { public: - enum Kind { + enum BuiltinKind { #define X(type, name) K_##name, REPL_BUILTIN_TYPES #undef X - - K_Void, - K_PtrOrObj, - K_Unspecified + K_Unspecified }; - Value() = default; - Value(const Interpreter *In, void *Ty); - Value(const Value &RHS); - Value(Value &&RHS) noexcept; - Value &operator=(const Value &RHS); - Value &operator=(Value &&RHS) noexcept; - ~Value(); - - void printType(llvm::raw_ostream &Out) const; - void printData(llvm::raw_ostream &Out) const; - void print(llvm::raw_ostream &Out) const; - void dump() const; - void clear(); - - const ASTContext &getASTContext() const; - const Interpreter &getInterpreter() const; - QualType getType() const; - - bool isValid() const { return ValueKind != K_Unspecified; } - bool isVoid() const { return ValueKind == K_Void; } - bool hasValue() const { return isValid() && !isVoid(); } - bool isManuallyAlloc() const { return IsManuallyAlloc; } - Kind getKind() const { return ValueKind; } - void setKind(Kind K) { ValueKind = K; } - void setOpaqueType(void *Ty) { OpaqueType = Ty; } - - void *getPtr() const; - void setPtr(void *Ptr) { Data.m_Ptr = Ptr; } - void setRawBits(void *Ptr, unsigned NBits = sizeof(Storage)); - -#define X(type, name) \ - void set##name(type Val) { Data.m_##name = Val; } \ - type get##name() const { return Data.m_##name; } - REPL_BUILTIN_TYPES +private: + struct Builtins { + private: + BuiltinKind BK = K_Unspecified; + union { +#define X(type, name) type m_##name; + REPL_BUILTIN_TYPES #undef X + }; - /// \brief Get the value with cast. - // - /// Get the value cast to T. This is similar to reinterpret_cast(value), - /// casting the value of builtins (except void), enums and pointers. - /// Values referencing an object are treated as pointers to the object. - template T convertTo() const { - return convertFwd::cast(*this); - } - -protected: - bool isPointerOrObjectType() const { return ValueKind == K_PtrOrObj; } + public: + Builtins() = default; + explicit Builtins(BuiltinKind BK) : BK(BK) {} + ~Builtins() {} - /// \brief Get to the value with type checking casting the underlying - /// stored value to T. - template T as() const { - switch (ValueKind) { - default: - return T(); -#define X(type, name) \ - case Value::K_##name: \ - return (T)Data.m_##name; - REPL_BUILTIN_TYPES -#undef X + void setKind(BuiltinKind K) { + assert(BK == K_Unspecified); + BK = K; } + BuiltinKind getKind() const { return BK; } +#define X(type, name) \ + void set##name(type Val) { \ + assert(BK == K_Unspecified || BK == K_##name); \ + m_##name = Val; \ + } \ + type get##name() const { \ + assert(BK == K_##name); \ + return m_##name; \ } + REPL_BUILTIN_TYPES +#undef X + }; - // Allow convertTo to be partially specialized. - template struct convertFwd { - static T cast(const Value &V) { - if (V.isPointerOrObjectType()) - return (T)(uintptr_t)V.as(); - if (!V.isValid() || V.isVoid()) { - return T(); - } - return V.as(); + struct ArrValue { + std::vector Elements; + uint64_t ArrSize; + ArrValue(uint64_t Size) : ArrSize(Size) { + Elements.reserve(ArrSize); + for (uint64_t I = 0; I < ArrSize; ++I) + Elements.emplace_back(); } }; - template struct convertFwd { - static T *cast(const Value &V) { - if (V.isPointerOrObjectType()) - return (T *)(uintptr_t)V.as(); - return nullptr; + struct PtrValue { + uint64_t Addr = 0; + Value *Pointee; // optional for str + PtrValue(uint64_t Addr) : Addr(Addr), Pointee(new Value()) {} + ~PtrValue() { + if (Pointee != nullptr) + delete Pointee; } }; - const Interpreter *Interp = nullptr; - void *OpaqueType = nullptr; - Storage Data; - Kind ValueKind = K_Unspecified; - bool IsManuallyAlloc = false; -}; - -template <> inline void *Value::as() const { - if (isPointerOrObjectType()) - return Data.m_Ptr; - return (void *)as(); -} + struct StrValue { + std::string StringBuf; + StrValue(std::string str) : StringBuf(std::move(str)) {} + ~StrValue() = default; + }; -class ValueBuffer { public: - enum Kind { K_Builtin, K_Array, K_Pointer, K_Unknown }; - QualType Ty; - Kind K; - ValueBuffer(Kind K = K_Unknown) : K(K) {} - virtual ~ValueBuffer() = default; - bool isUnknown() const { return K == K_Unknown; } - bool isBuiltin() const { return K == K_Builtin; } - bool isArray() const { return K == K_Array; } - bool isPointer() const { return K == K_Pointer; } - static bool classof(const ValueBuffer *) { return true; } -}; + using DataType = + llvm::AlignedCharArrayUnion; + enum ValKind { K_Builtin, K_Array, K_Pointer, K_Str, K_None }; -class BuiltinValueBuffer; -class ArrayValueBuffer; -class PointerValueBuffer; +private: + QualType Ty; + ValKind VKind = K_None; + DataType Data; -class BuiltinValueBuffer : public ValueBuffer { public: - std::vector raw; - BuiltinValueBuffer(QualType _Ty) : ValueBuffer(K_Builtin) { Ty = _Ty; } - template T as() const { + Value() = default; + explicit Value(QualType Ty, ValKind K) : Ty(Ty), VKind(K) {} + Value(const Value &RHS); + Value(Value &&RHS) : Ty(RHS.Ty), VKind(RHS.VKind), Data(RHS.Data) { + RHS.VKind = K_None; + } + + Value &operator=(const Value &RHS); + Value &operator=(Value &&RHS); + + explicit Value(QualType QT, std::vector Raw); + + struct UninitArr {}; + + explicit Value(UninitArr, QualType QT, uint64_t ArrSize) + : Ty(QT), VKind(K_None) { + MakeArray(ArrSize); + } + + explicit Value(QualType QT, uint64_t Addr) : Ty(QT), VKind(K_None) { + MakePointer(Addr); + } + + explicit Value(QualType QT, const char *buf) : Ty(QT), VKind(K_None) { + MakeStr(buf); + } + + ~Value() { + if (VKind != K_None) + destroy(); + } + + template static T as(std::vector &raw) { T v{}; // assert(raw.size() >= sizeof(T) && "Buffer too small for type!"); memcpy(&v, raw.data(), sizeof(T)); return v; } - static bool classof(const ValueBuffer *B) { return B->isBuiltin(); } -}; -class ArrayValueBuffer : public ValueBuffer { -public: - std::vector> Elements; - ArrayValueBuffer(QualType EleTy) : ValueBuffer(K_Array) { Ty = EleTy; } + // ---- Kind checks ---- + bool isUnknown() const { return VKind == K_None; } + bool isBuiltin() const { return VKind == K_Builtin; } + bool isArray() const { return VKind == K_Array; } + bool isPointer() const { return VKind == K_Pointer; } + bool isStr() const { return VKind == K_Str; } + ValKind getKind() const { return VKind; } + QualType getType() const { return Ty; } + bool isAbsent() const { return VKind == K_None; } + bool hasValue() const { return VKind != K_None; } + BuiltinKind getBuiltinKind() const { + if (isBuiltin()) + return asBuiltin().getKind(); + return BuiltinKind::K_Unspecified; + } - static bool classof(const ValueBuffer *B) { return B->isArray(); } -}; +protected: + // ---- accessors ---- + Builtins &asBuiltin() { + assert(isBuiltin() && "Not a builtin value"); + return *((Builtins *)(char *)&Data); + } + + const Builtins &asBuiltin() const { + return const_cast(this)->asBuiltin(); + } + + ArrValue &asArray() { + assert(isArray() && "Not an array value"); + return *((ArrValue *)(char *)&Data); + } + + const ArrValue &asArray() const { + return const_cast(this)->asArray(); + } + + PtrValue &asPointer() { + assert(isPointer() && "Not a pointer value"); + return *((PtrValue *)(char *)&Data); + } + + const PtrValue &asPointer() const { + return const_cast(this)->asPointer(); + } + + StrValue &asStr() { + assert(isStr() && "Not a Str value"); + return *((StrValue *)(char *)&Data); + } + + const StrValue &asStr() const { return const_cast(this)->asStr(); } -class PointerValueBuffer : public ValueBuffer { public: - uint64_t Address = 0; - std::unique_ptr Pointee; // optional, used only for char* + bool hasBuiltinThis(BuiltinKind K) const { + if (isBuiltin()) + return asBuiltin().getKind() == K; + return false; + } + + void setStrVal(const char *buf) { + assert(isStr() && "Not a Str"); + asStr().StringBuf = buf; + } + + StringRef getStrVal() { + assert(isStr() && "Not a Str"); + return StringRef(asStr().StringBuf); + } + + const StringRef getStrVal() const { + assert(isStr() && "Not a Str"); + return StringRef(asStr().StringBuf); + } + + uint64_t getArraySize() const { return asArray().ArrSize; } + + uint64_t getArrayInitializedElts() const { return asArray().ArrSize; } + + Value &getArrayInitializedElt(unsigned I) { + assert(isArray() && "Invalid accessor"); + assert(I < getArrayInitializedElts() && "Index out of range"); + return ((ArrValue *)(char *)&Data)->Elements[I]; + } + + const Value &getArrayInitializedElt(unsigned I) const { + return const_cast(this)->getArrayInitializedElt(I); + } + + bool HasPointee() const { + assert(isPointer() && "Invalid accessor"); + return !(asPointer().Pointee->isAbsent()); + } + + Value &getPointerPointee() { + assert(isPointer() && "Invalid accessor"); + return *asPointer().Pointee; + } - PointerValueBuffer(QualType _Ty, uint64_t Addr = 0) - : ValueBuffer(K_Pointer), Address(Addr) { - Ty = _Ty; + const Value &getPointerPointee() const { + return const_cast(this)->getPointerPointee(); } - static bool classof(const ValueBuffer *B) { return B->isPointer(); } + uint64_t getAddr() const { return asPointer().Addr; } + +#define X(type, name) \ + void set##name(type Val) { asBuiltin().set##name(Val); } \ + type get##name() const { return asBuiltin().get##name(); } + REPL_BUILTIN_TYPES +#undef X + + // ---- Printing ---- + void printType(llvm::raw_ostream &Out, ASTContext &Ctx) const; + void printData(llvm::raw_ostream &Out, ASTContext &Ctx) const; + void print(llvm::raw_ostream &Out, ASTContext &Ctx) const; + void dump(ASTContext &Ctx) const; + + // ---- clear ---- + void clear() { destroy(); } + +private: + void MakeBuiltIns() { + assert(isAbsent() && "Bad state change"); + new ((void *)(char *)&Data) Builtins(BuiltinKind::K_Unspecified); + VKind = K_Builtin; + } + + void MakeArray(uint64_t Size) { + assert(isAbsent() && "Bad state change"); + new ((void *)(char *)&Data) ArrValue(Size); + VKind = K_Array; + } + + void MakePointer(uint64_t Addr = 0) { + assert(isAbsent() && "Bad state change"); + new ((void *)(char *)&Data) PtrValue(Addr); + VKind = K_Pointer; + } + + void MakeStr(std::string Str = "") { + assert(isAbsent() && "Bad state change"); + new ((void *)(char *)&Data) StrValue(Str); + VKind = K_Str; + } + + void setBuiltins(Builtins &LHS, const Builtins &RHS) { + switch (RHS.getKind()) { + default: + assert(false && "Type not supported"); + +#define X(type, name) \ + case BuiltinKind::K_##name: { \ + LHS.setKind(BuiltinKind::K_##name); \ + LHS.set##name(RHS.get##name()); \ + } break; + REPL_BUILTIN_TYPES +#undef X + } + } + + void destroy() { + switch (VKind) { + case K_Builtin: + reinterpret_cast(&Data)->~Builtins(); + break; + case K_Array: + reinterpret_cast(&Data)->~ArrValue(); + break; + case K_Pointer: + reinterpret_cast(&Data)->~PtrValue(); + break; + case K_Str: + reinterpret_cast(&Data)->~StrValue(); + break; + default: + break; + } + VKind = K_None; + } }; class ValueToString { @@ -268,12 +399,13 @@ class ValueToString { public: ValueToString(ASTContext &Ctx) : Ctx(Ctx) {} - std::string toString(const ValueBuffer *); + std::string toString(const Value *); + std::string toString(QualType); private: - std::string BuiltinToString(const BuiltinValueBuffer &B); - std::string PointerToString(const PointerValueBuffer &P); - std::string ArrayToString(const ArrayValueBuffer &A); + std::string BuiltinToString(const Value &B); + std::string PointerToString(const Value &P); + std::string ArrayToString(const Value &A); }; class ValueResultManager { @@ -296,17 +428,16 @@ class ValueResultManager { void deliverResult(SendResultFn SendResult, ValueId ID, llvm::orc::ExecutorAddr VAddr); + Value release() { return std::move(LastVal); } private: std::atomic 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 ValBuf = nullptr; + Value LastVal; llvm::DenseMap IdToType; }; diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 178efa3e75398..7048976cf8f8b 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -705,7 +705,7 @@ llvm::Error Interpreter::Execute(PartialTranslationUnit &T) { return llvm::Error::success(); } -llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code) { +llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) { auto PTU = Parse(Code); if (!PTU) @@ -715,7 +715,10 @@ llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code) { return Err; if (ValMgr) { - ValMgr->resetAndDump(); + if (V) { + *V = ValMgr->release(); + } else + ValMgr->resetAndDump(); } return llvm::Error::success(); } diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp index 508ae0bf71d9f..b52929ab97660 100644 --- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -154,85 +154,36 @@ static std::string FunctionToString(ASTContext &Ctx, QualType QT, return Str; } -static std::string escapeString(const std::vector &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(c))) - Out.push_back(c); - else { - char buf[5]; - snprintf(buf, sizeof(buf), "\\x%02X", static_cast(c)); - Out += buf; - } - // break; - // } - } - return Out; -} - namespace clang { std::string Interpreter::ValueDataToString(const Value &V) const { return ""; } -std::string ValueToString::toString(const ValueBuffer *Buf) { - if (const BuiltinValueBuffer *B = llvm::dyn_cast(Buf)) - return BuiltinToString(*B); - else if (const ArrayValueBuffer *A = llvm::dyn_cast(Buf)) - return ArrayToString(*A); - else if (const PointerValueBuffer *P = - llvm::dyn_cast(Buf)) - return PointerToString(*P); +std::string ValueToString::toString(const Value *Buf) { + + switch (Buf->getKind()) { + case Value::K_Builtin: + return BuiltinToString(*Buf); + case Value::K_Pointer: + return PointerToString(*Buf); + case Value::K_Str: + break; + case Value::K_Array: + return ArrayToString(*Buf); + + default: + break; + } return ""; } -std::string ValueToString::BuiltinToString(const BuiltinValueBuffer &B) { - if (B.raw.empty()) +std::string ValueToString::BuiltinToString(const Value &B) { + if (!B.hasValue()) return ""; // No data in buffer - QualType QT = B.Ty; + QualType QT = B.getType(); QualType DesugaredTy = QT.getDesugaredType(Ctx); QualType NonRefTy = DesugaredTy.getNonReferenceType(); - if (NonRefTy->isCharType()) { - unsigned char c = B.as(); - 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(c) + "'"; - else { - return llvm::formatv("'\\x{0:02X}'", c).str(); - } - } - } if (auto *BT = DesugaredTy.getCanonicalType()->getAs()) { auto formatFloating = [](auto Val, char Suffix = '\0') -> std::string { @@ -264,52 +215,64 @@ std::string ValueToString::BuiltinToString(const BuiltinValueBuffer &B) { return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) + " '}"; case clang::BuiltinType::Bool: - SS << ((B.as()) ? "true" : "false"); + SS << ((B.getBool()) ? "true" : "false"); + return Str; + case clang::BuiltinType::Char_S: + SS << '\'' << B.getChar_S() << '\''; + return Str; + case clang::BuiltinType::SChar: + SS << '\'' << B.getSChar() << '\''; + return Str; + case clang::BuiltinType::Char_U: + SS << '\'' << B.getChar_U() << '\''; + return Str; + case clang::BuiltinType::UChar: + SS << '\'' << B.getUChar() << '\''; return Str; case clang::BuiltinType::Short: - SS << B.as(); + SS << B.getShort(); return Str; case clang::BuiltinType::UShort: - SS << B.as(); + SS << B.getUShort(); return Str; case clang::BuiltinType::Int: - SS << B.as(); + SS << B.getInt(); return Str; case clang::BuiltinType::UInt: - SS << B.as(); + SS << B.getUInt(); return Str; case clang::BuiltinType::Long: - SS << B.as(); + SS << B.getLong(); return Str; case clang::BuiltinType::ULong: - SS << B.as(); + SS << B.getULong(); return Str; case clang::BuiltinType::LongLong: - SS << B.as(); + SS << B.getLongLong(); return Str; case clang::BuiltinType::ULongLong: - SS << B.as(); + SS << B.getULongLong(); return Str; case clang::BuiltinType::Float: - return formatFloating(B.as(), /*suffix=*/'f'); + return formatFloating(B.getFloat(), /*suffix=*/'f'); case clang::BuiltinType::Double: - return formatFloating(B.as()); + return formatFloating(B.getDouble()); case clang::BuiltinType::LongDouble: - return formatFloating(B.as(), /*suffix=*/'L'); + return formatFloating(B.getLongDouble(), /*suffix=*/'L'); } } if (NonRefTy->isEnumeralType()) - return EnumToString(Ctx, QT, B.as()); + return EnumToString(Ctx, QT, B.getUInt()); return ""; } -std::string ValueToString::PointerToString(const PointerValueBuffer &P) { +std::string ValueToString::PointerToString(const Value &P) { - QualType QT = P.Ty; + QualType QT = P.getType(); QualType DesugaredTy = QT.getDesugaredType(Ctx); QualType NonRefTy = DesugaredTy.getNonReferenceType(); @@ -320,49 +283,45 @@ std::string ValueToString::PointerToString(const PointerValueBuffer &P) { auto PointeeTy = PtrTy->getPointeeType(); // char* -> print string literal - if (PointeeTy->isCharType() && P.Pointee) { - if (auto *BE = static_cast(P.Pointee.get())) - return "\"" + escapeString(BE->raw) + "\""; + if (PointeeTy->isCharType() && P.HasPointee()) { + if (P.getPointerPointee().isStr()) + return "\"" + P.getPointerPointee().getStrVal().str() + "\""; } - - return std::to_string(P.Address); } - if (P.Address == 0) - return "nullptr"; - if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) && NonRefTy->getPointeeType()->isFunctionProtoType()) - return FunctionToString(Ctx, QT, (void *)P.Address); + return FunctionToString(Ctx, QT, (void *)P.getAddr()); if (NonRefTy->isFunctionType()) - return FunctionToString(Ctx, QT, (void *)P.Address); + return FunctionToString(Ctx, QT, (void *)P.getAddr()); if (NonRefTy->isNullPtrType()) return "nullptr\n"; std::ostringstream OS; - OS << "@0x" << std::hex << P.Address; + OS << "@0x" << std::hex << P.getAddr(); return OS.str(); } -std::string ValueToString::ArrayToString(const ArrayValueBuffer &A) { - if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(A.Ty)) { +std::string ValueToString::ArrayToString(const Value &A) { + if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(A.getType())) { QualType ElemTy = CAT->getElementType(); // Treat null terminated char arrays as strings basically. - if (ElemTy->isCharType() && !A.Elements.empty()) { - if (const auto *B = - llvm::dyn_cast(A.Elements.back().get())) { - char last = (char)B->raw.front(); + if (ElemTy->isCharType()) { + const Value &LastV = + A.getArrayInitializedElt(A.getArrayInitializedElts() - 1); + if (LastV.hasValue() && !LastV.isAbsent() && LastV.isBuiltin()) { + char last = LastV.getChar_S(); if (last != '\0') goto not_a_string; } std::string Res; Res += "\""; - for (size_t i = 0; i < A.Elements.size(); ++i) { - if (const auto *B = - llvm::dyn_cast(A.Elements[i].get())) { - char c = static_cast(B->raw.back()); + for (size_t I = 0, N = A.getArraySize(); I < N; ++I) { + const Value &EleVal = A.getArrayInitializedElt(I); + if (EleVal.hasValue() && EleVal.isBuiltin()) { + char c = EleVal.getChar_S(); if (c != '\0') Res += c; } @@ -375,9 +334,12 @@ std::string ValueToString::ArrayToString(const ArrayValueBuffer &A) { std::ostringstream OS; OS << "{ "; - for (size_t i = 0; i < A.Elements.size(); ++i) { - OS << this->toString(A.Elements[i].get()); - if (i + 1 < A.Elements.size()) + for (size_t I = 0, N = A.getArraySize(); I < N; ++I) { + const Value &EleVal = A.getArrayInitializedElt(I); + if (EleVal.hasValue()) + OS << this->toString(&EleVal); + + if (I + 1 < N) OS << ", "; } @@ -385,7 +347,7 @@ std::string ValueToString::ArrayToString(const ArrayValueBuffer &A) { return OS.str(); } -std::string ValueResultManager::ValueTypeToString(QualType QT) const { +std::string ValueToString::toString(QualType QT) { ASTContext &AstCtx = const_cast(Ctx); std::string QTStr = QualTypeToString(AstCtx, QT); @@ -397,16 +359,15 @@ std::string ValueResultManager::ValueTypeToString(QualType QT) const { } void ValueResultManager::resetAndDump() { - if (!ValBuf) + if (!LastVal.hasValue() || LastVal.isAbsent()) return; - QualType Ty = ValBuf->Ty; + QualType Ty = LastVal.getType(); - std::unique_ptr Val = nullptr; - ValBuf.swap(Val); + Value V = std::move(LastVal); // Don't even try to print a void or an invalid type, it doesn't make sense. - if (Ty->isVoidType() || (!Val || (Val && Val->isUnknown()))) + if (Ty->isVoidType()) return; // We need to get all the results together then print it, since `printType` is @@ -415,11 +376,12 @@ void ValueResultManager::resetAndDump() { llvm::raw_string_ostream SS(Str); ValueToString ValToStr(Ctx); SS << "("; - SS << ValueTypeToString(Ty); + SS << ValToStr.toString(Ty); SS << ") "; - SS << ValToStr.toString(Val.get()); + SS << ValToStr.toString(&V); SS << "\n"; llvm::outs() << Str; + V.clear(); } llvm::Expected @@ -451,29 +413,52 @@ class ExprConverter { public: ExprConverter(Sema &S, ASTContext &Ctx) : S(S), Ctx(Ctx) {} - /// Create (&E) as a void* +private: + bool isAddressOfExpr(Expr *E) { + if (!E) + return false; + if (auto *UO = dyn_cast(E)) + return UO->getOpcode() == UO_AddrOf; + if (auto *ICE = dyn_cast(E)) + return isAddressOfExpr(ICE->getSubExpr()); + return false; + } + + /// Build a single &E using Sema. + ExprResult buildAddrOfWithSema(Expr *E, + SourceLocation Loc = SourceLocation()) { + // Sema will materialize temporaries as necessary. + ExprResult Res = S.CreateBuiltinUnaryOp(Loc, UO_AddrOf, E); + if (Res.isInvalid()) + return ExprError(); + return Res; + } + +public: + /// Create (&E) as a void* (uses Sema for & creation) ExprResult CreateAddressOfVoidPtrExpr(QualType Ty, Expr *ForCast, bool takeAddr = false) { - QualType VoidPtrTy = Ctx.getPointerType(Ctx.VoidTy); + ExprResult AddrOfRes = ForCast; - // &E - Expr *AddrOf = ForCast; if (takeAddr) { - AddrOf = UnaryOperator::Create( - Ctx, ForCast, UO_AddrOf, Ctx.getPointerType(Ty), VK_PRValue, - OK_Ordinary, SourceLocation(), false, FPOptionsOverride()); + // don't create & twice + if (!isAddressOfExpr(ForCast)) { + AddrOfRes = buildAddrOfWithSema(ForCast); + if (AddrOfRes.isInvalid()) + return ExprError(); + } else { + // already an &-expression; keep it as-is + AddrOfRes = ForCast; + } } TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy); - ExprResult CastedExpr = - S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), AddrOf); - assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression"); - return CastedExpr.get(); - // static_cast(&E) - // return CXXStaticCastExpr::Create( - // Ctx, VoidPtrTy, VK_PRValue, CK_BitCast, AddrOf, nullptr, - // Ctx.getTrivialTypeSourceInfo(VoidPtrTy), FPOptionsOverride(), - // SourceLocation(), SourceLocation(), SourceRange()); + ExprResult CastedExpr = S.BuildCStyleCastExpr( + SourceLocation(), TSI, SourceLocation(), AddrOfRes.get()); + if (CastedExpr.isInvalid()) + return ExprError(); + + return CastedExpr; } /// Wrap rvalues in a temporary (var) so they become addressable. @@ -494,7 +479,13 @@ class ExprConverter { } ExprResult handleEnumTypeExpr(const EnumType *, QualType QTy, Expr *E) { - return makeScalarAddressable(QTy, E); + uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy); + QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits); + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy); + ExprResult CastedExpr = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); + assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr"); + return makeScalarAddressable(QTy, CastedExpr.get()); } ExprResult handlePointerTypeExpr(const PointerType *, QualType QTy, Expr *E) { @@ -524,12 +515,44 @@ class ExprConverter { return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/false); } - ExprResult handleAnyObjectExpr(const Type *, QualType QTy, Expr *E) { + ExprResult handleMemberPointerTypeExpr(const Type *, QualType QTy, Expr *E) { if (E->isLValue() || E->isXValue()) return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true); return CreateAddressOfVoidPtrExpr(QTy, CreateMaterializeTemporaryExpr(E), /*takeAddr=*/true); } + + ExprResult handleRecordTypeExpr(const Type *, QualType QTy, Expr *E) { + if (E->isLValue() || E->isXValue()) + return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true); + + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(QTy, SourceLocation()); + ExprResult CXXNewCall = + S.BuildCXXNew(E->getSourceRange(), + /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), + MultiExprArg(), + /*PlacementRParen=*/SourceLocation(), + /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, + std::nullopt, E->getSourceRange(), E); + + if (CXXNewCall.isInvalid()) + return ExprError(); + + auto CallRes = S.ActOnFinishFullExpr(CXXNewCall.get(), + /*DiscardedValue=*/false); + if (CallRes.isInvalid()) + return ExprError(); + return CreateAddressOfVoidPtrExpr(QTy, CallRes.get(), /*takeAddr=*/false); + } + + ExprResult handleAnyObjectExpr(const Type *, QualType QTy, Expr *E) { + if (E->isLValue() || E->isXValue()) + return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true); + auto Res = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E); + assert(!Res.isInvalid()); + return CreateAddressOfVoidPtrExpr(QTy, Res.get(), + /*takeAddr=*/false); + } }; class InterfaceKindVisitor : public TypeVisitor { @@ -567,6 +590,18 @@ class InterfaceKindVisitor : public TypeVisitor { return true; } + bool VisitMemberPointerType(const MemberPointerType *Ty) { + Args.push_back( + Converter.handleMemberPointerTypeExpr(Ty, QualType(Ty, 0), E).get()); + return true; + } + + bool VisitRecordType(const RecordType *Ty) { + // Args.push_back( + // Converter.handleRecordTypeExpr(Ty, QualType(Ty, 0), E).get()); + return false; + } + bool VisitConstantArrayType(const ConstantArrayType *Ty) { Args.push_back(Converter.handleArrayTypeExpr(Ty, QualType(Ty, 0), E).get()); return true; @@ -591,7 +626,7 @@ class InterfaceKindVisitor : public TypeVisitor { } }; -enum RunTimeFnTag { OrcSendResult, ClangSendResult }; +enum RunTimeFnTag { OrcSendResult, ClangSendResult, ClangDestroyObj }; static constexpr llvm::StringRef RunTimeFnTagName[] = { "__orc_rt_SendResultValue", "__clang_Interpreter_SendResultValue"}; diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp index f3c89b620b505..438700afa98bd 100644 --- a/clang/lib/Interpreter/Value.cpp +++ b/clang/lib/Interpreter/Value.cpp @@ -112,173 +112,114 @@ class ValueStorage { namespace clang { -static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) { - if (Ctx.hasSameType(QT, Ctx.VoidTy)) - return Value::K_Void; - - if (const auto *ED = QT->getAsEnumDecl()) - QT = ED->getIntegerType(); - - const auto *BT = QT->getAs(); - if (!BT || BT->isNullPtrType()) - return Value::K_PtrOrObj; - - switch (QT->castAs()->getKind()) { - default: - assert(false && "Type not supported"); - return Value::K_Unspecified; -#define X(type, name) \ - case BuiltinType::name: \ - return Value::K_##name; - REPL_BUILTIN_TYPES -#undef X - } -} - -Value::Value(const Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) { - const ASTContext &C = getASTContext(); - setKind(ConvertQualTypeToKind(C, getType())); - if (ValueKind == K_PtrOrObj) { - QualType Canon = getType().getCanonicalType(); - if ((Canon->isPointerType() || Canon->isObjectType() || - Canon->isReferenceType()) && - (Canon->isRecordType() || Canon->isConstantArrayType() || - Canon->isMemberPointerType())) { - IsManuallyAlloc = true; - // Compile dtor function. - const Interpreter &Interp = getInterpreter(); - void *DtorF = nullptr; - size_t ElementsSize = 1; - QualType DtorTy = getType(); - - if (const auto *ArrTy = - llvm::dyn_cast(DtorTy.getTypePtr())) { - DtorTy = ArrTy->getElementType(); - llvm::APInt ArrSize(sizeof(size_t) * 8, 1); - do { - ArrSize *= ArrTy->getSize(); - ArrTy = llvm::dyn_cast( - ArrTy->getElementType().getTypePtr()); - } while (ArrTy); - ElementsSize = static_cast(ArrSize.getZExtValue()); - } - if (auto *CXXRD = DtorTy->getAsCXXRecordDecl()) { - if (llvm::Expected Addr = - Interp.CompileDtorCall(CXXRD)) - DtorF = reinterpret_cast(Addr->getValue()); - else - llvm::logAllUnhandledErrors(Addr.takeError(), llvm::errs()); - } - - size_t AllocSize = - getASTContext().getTypeSizeInChars(getType()).getQuantity(); - unsigned char *Payload = - ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize); - setPtr((void *)Payload); +// static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) +// { +// if (Ctx.hasSameType(QT, Ctx.VoidTy)) +// return Value::K_Void; + +// if (const auto *ED = QT->getAsEnumDecl()) +// QT = ED->getIntegerType(); + +// const auto *BT = QT->getAs(); +// if (!BT || BT->isNullPtrType()) +// return Value::K_PtrOrObj; + +// switch (QT->castAs()->getKind()) { +// default: +// assert(false && "Type not supported"); +// return Value::K_Unspecified; +// #define X(type, name) \ +// case BuiltinType::name: \ +// return Value::K_##name; +// REPL_BUILTIN_TYPES +// #undef X +// } +// } + +Value::Value(const Value &RHS) : Ty(RHS.getType()), VKind(K_None) { + switch (RHS.getKind()) { + case K_None: + VKind = RHS.VKind; + break; + case K_Builtin: { + MakeBuiltIns(); + if (RHS.asBuiltin().getKind() != BuiltinKind::K_Unspecified) { + setBuiltins(asBuiltin(), RHS.asBuiltin()); } + break; + } + case K_Array: { + MakeArray(RHS.getArraySize()); + for (uint64_t I = 0, N = RHS.getArraySize(); I < N; ++I) + getArrayInitializedElt(I) = RHS.getArrayInitializedElt(I); + break; + } + case K_Pointer: { + MakePointer(RHS.getAddr()); + if (RHS.HasPointee()) + getPointerPointee() = RHS.getPointerPointee(); + break; + } + case K_Str: + MakeStr(RHS.getStrVal().str()); + break; } -} - -Value::Value(const Value &RHS) - : Interp(RHS.Interp), OpaqueType(RHS.OpaqueType), Data(RHS.Data), - ValueKind(RHS.ValueKind), IsManuallyAlloc(RHS.IsManuallyAlloc) { - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Retain(); -} - -Value::Value(Value &&RHS) noexcept { - Interp = std::exchange(RHS.Interp, nullptr); - OpaqueType = std::exchange(RHS.OpaqueType, nullptr); - Data = RHS.Data; - ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); - IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false); - - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Release(); } Value &Value::operator=(const Value &RHS) { - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Release(); - - Interp = RHS.Interp; - OpaqueType = RHS.OpaqueType; - Data = RHS.Data; - ValueKind = RHS.ValueKind; - IsManuallyAlloc = RHS.IsManuallyAlloc; - - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Retain(); + if (this != &RHS) + *this = Value(RHS); return *this; } -Value &Value::operator=(Value &&RHS) noexcept { +Value &Value::operator=(Value &&RHS) { if (this != &RHS) { - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Release(); - - Interp = std::exchange(RHS.Interp, nullptr); - OpaqueType = std::exchange(RHS.OpaqueType, nullptr); - ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); - IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false); + if (VKind != K_None) + destroy(); + Ty = RHS.Ty; + VKind = RHS.VKind; Data = RHS.Data; + RHS.VKind = K_None; } - return *this; -} -void Value::clear() { - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Release(); - ValueKind = K_Unspecified; - OpaqueType = nullptr; - Interp = nullptr; - IsManuallyAlloc = false; -} - -Value::~Value() { clear(); } - -void *Value::getPtr() const { - assert(ValueKind == K_PtrOrObj); - return Data.m_Ptr; -} - -void Value::setRawBits(void *Ptr, unsigned NBits /*= sizeof(Storage)*/) { - assert(NBits <= sizeof(Storage) && "Greater than the total size"); - memcpy(/*dest=*/Data.m_RawBits, /*src=*/Ptr, /*nbytes=*/NBits / 8); -} - -QualType Value::getType() const { - return QualType::getFromOpaquePtr(OpaqueType); + return *this; } -const Interpreter &Value::getInterpreter() const { - assert(Interp != nullptr && - "Can't get interpreter from a default constructed value"); - return *Interp; -} +Value::Value(QualType QT, std::vector Raw) : Ty(QT), VKind(K_None) { + MakeBuiltIns(); + Builtins &B = asBuiltin(); + if (const auto *ED = QT->getAsEnumDecl()) + QT = ED->getIntegerType(); + switch (QT->castAs()->getKind()) { + default: + assert(false && "Type not supported"); -const ASTContext &Value::getASTContext() const { - return getInterpreter().getASTContext(); +#define X(type, name) \ + case BuiltinType::name: { \ + B.setKind(BuiltinKind::K_##name); \ + B.set##name(as(Raw)); \ + } break; + REPL_BUILTIN_TYPES +#undef X + } } -void Value::dump() const { print(llvm::outs()); } +void Value::dump(ASTContext &Ctx) const { print(llvm::outs(), Ctx); } -void Value::printType(llvm::raw_ostream &Out) const { - // Out << Interp->ValueTypeToString(*this); +void Value::printType(llvm::raw_ostream &Out, ASTContext &Ctx) const { + Out << ValueToString(Ctx).toString(getType()); } -void Value::printData(llvm::raw_ostream &Out) const { - Out << Interp->ValueDataToString(*this); +void Value::printData(llvm::raw_ostream &Out, ASTContext &Ctx) const { + Out << ValueToString(Ctx).toString(this); } // FIXME: We do not support the multiple inheritance case where one of the base // classes has a pretty-printer and the other does not. -void Value::print(llvm::raw_ostream &Out) const { - assert(OpaqueType != nullptr && "Can't print default Value"); - +void Value::print(llvm::raw_ostream &Out, ASTContext &Ctx) const { // Don't even try to print a void or an invalid type, it doesn't make sense. - if (getType()->isVoidType() || !isValid()) + if (getType()->isVoidType() || isAbsent()) return; // We need to get all the results together then print it, since `printType` is @@ -287,145 +228,97 @@ void Value::print(llvm::raw_ostream &Out) const { llvm::raw_string_ostream SS(Str); SS << "("; - printType(SS); + printType(SS, Ctx); SS << ") "; - printData(SS); + printData(SS, Ctx); SS << "\n"; Out << Str; } -class ReaderDispatcher { +class ValueReaderDispatcher { private: ASTContext &Ctx; llvm::orc::MemoryAccess &MA; public: - ReaderDispatcher(ASTContext &Ctx, llvm::orc::MemoryAccess &MA) + ValueReaderDispatcher(ASTContext &Ctx, llvm::orc::MemoryAccess &MA) : Ctx(Ctx), MA(MA) {} - llvm::Expected> - read(QualType QT, llvm::orc::ExecutorAddr Addr); - - llvm::Expected> - readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr); + llvm::Expected read(QualType QT, llvm::orc::ExecutorAddr Addr); - llvm::Expected> - readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr); + llvm::Expected readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr); - llvm::Expected> - readArray(QualType Ty, llvm::orc::ExecutorAddr Addr); + llvm::Expected readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr); - llvm::Expected> - readOtherObject(QualType Ty, llvm::orc::ExecutorAddr Addr); + llvm::Expected readArray(QualType Ty, llvm::orc::ExecutorAddr Addr); + llvm::Expected readOtherObject(QualType Ty, + llvm::orc::ExecutorAddr Addr); // TODO: record, function, etc. }; -class TypeReadVisitor - : public TypeVisitor>> { - ReaderDispatcher &Dispatcher; +class ValueReadVisitor + : public TypeVisitor> { + ValueReaderDispatcher &Dispatcher; llvm::orc::ExecutorAddr Addr; public: - TypeReadVisitor(ReaderDispatcher &D, llvm::orc::ExecutorAddr A) + ValueReadVisitor(ValueReaderDispatcher &D, llvm::orc::ExecutorAddr A) : Dispatcher(D), Addr(A) {} - llvm::Expected> VisitType(const Type *T) { + llvm::Expected VisitType(const Type *T) { return Dispatcher.readOtherObject(QualType(T, 0), Addr); } - llvm::Expected> - VisitBuiltinType(const BuiltinType *BT) { + llvm::Expected VisitBuiltinType(const BuiltinType *BT) { return Dispatcher.readBuiltin(QualType(BT, 0), Addr); } - llvm::Expected> - VisitPointerType(const PointerType *PT) { + llvm::Expected VisitPointerType(const PointerType *PT) { return Dispatcher.readPointer(QualType(PT, 0), Addr); } - llvm::Expected> - VisitConstantArrayType(const ConstantArrayType *AT) { + llvm::Expected VisitConstantArrayType(const ConstantArrayType *AT) { return Dispatcher.readArray(QualType(AT, 0), Addr); } - llvm::Expected> - VisitEnumType(const EnumType *ET) { + llvm::Expected VisitEnumType(const EnumType *ET) { return Dispatcher.readBuiltin(QualType(ET, 0), Addr); } }; -llvm::Expected> -ReaderDispatcher::read(QualType QT, llvm::orc::ExecutorAddr Addr) { - TypeReadVisitor V(*this, Addr); +llvm::Expected +ValueReaderDispatcher::read(QualType QT, llvm::orc::ExecutorAddr Addr) { + ValueReadVisitor V(*this, Addr); return V.Visit(QT.getTypePtr()); } -llvm::Expected> -ReaderDispatcher::readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr) { +llvm::Expected +ValueReaderDispatcher::readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr) { + if (Ty->isVoidType()) + return Value(); auto Size = Ctx.getTypeSizeInChars(Ty).getQuantity(); auto ResOrErr = MA.readBuffers({llvm::orc::ExecutorAddrRange(Addr, Size)}); if (!ResOrErr) return ResOrErr.takeError(); - auto Buf = std::make_unique(Ty); const auto &Res = *ResOrErr; - std::vector ElemBuf(Size); - std::memcpy(ElemBuf.data(), Res.back().data(), Size); - Buf->raw = std::move(ElemBuf); - return std::move(Buf); -} - -llvm::Expected> -ReaderDispatcher::readArray(QualType Ty, llvm::orc::ExecutorAddr Addr) { - const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(Ty); - if (!CAT) - return llvm::make_error("Not a ConstantArrayType", - llvm::inconvertibleErrorCode()); - - QualType ElemTy = CAT->getElementType(); - size_t ElemSize = Ctx.getTypeSizeInChars(ElemTy).getQuantity(); - - auto Buf = std::make_unique(Ty); - - for (size_t i = 0; i < CAT->getZExtSize(); ++i) { - auto ElemAddr = Addr + i * ElemSize; - auto ElemBufOrErr = read(ElemTy, ElemAddr); - if (!ElemBufOrErr) - return ElemBufOrErr.takeError(); - Buf->Elements.push_back(std::move(*ElemBufOrErr)); - } - - return std::move(Buf); + return Value(Ty, Res.back()); } -llvm::Expected> -ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty, - llvm::orc::ExecutorAddr Addr) { +llvm::Expected +ValueReaderDispatcher::readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr) { auto PtrTy = dyn_cast(Ty.getTypePtr()); if (!PtrTy) return llvm::make_error("Not a PointerType", llvm::inconvertibleErrorCode()); - // unsigned PtrWidth = Ctx.getTypeSizeInChars(Ty).getQuantity(); - // uint64_t PtrValAddr = 0; - // if (PtrWidth == 32) { - // auto AddrOrErr = MA.readUInt32s({Addr}); - // if (!AddrOrErr) - // return AddrOrErr.takeError(); - // PtrValAddr = AddrOrErr->back(); - // } else { - // auto AddrOrErr = MA.readUInt64s({Addr}); - // if (!AddrOrErr) - // return AddrOrErr.takeError(); - // PtrValAddr = AddrOrErr->back(); - // } + uint64_t PtrValAddr = Addr.getValue(); if (PtrValAddr == 0) - return std::make_unique(Ty); // null pointer + return Value(Ty, PtrValAddr); // null pointer llvm::orc::ExecutorAddr PointeeAddr(PtrValAddr); - auto PtrBuf = std::make_unique(Ty, PtrValAddr); + Value Val(Ty, PtrValAddr); QualType PointeeTy = PtrTy->getPointeeType(); if (PointeeTy->isCharType()) { @@ -439,29 +332,57 @@ ReaderDispatcher::ReaderDispatcher::readPointer(QualType Ty, break; S.push_back(c); } - auto Buf = std::make_unique(PointeeTy); - Buf->raw.assign(S.begin(), S.end()); - if (S.empty()) - Buf->raw.push_back('\0'); // represent "" - PtrBuf->Pointee = std::move(Buf); + Value Str(PointeeTy, S.c_str()); + Val.getPointerPointee() = std::move(Str); } - // else { - // auto BufOrErr = read(PointeeTy, PointeeAddr); - // if (!BufOrErr) - // return BufOrErr.takeError(); - // PtrBuf->Pointee = std::move(*BufOrErr); - // } - return std::move(PtrBuf); + + return std::move(Val); } -llvm::Expected> -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(Ty); // null pointer +llvm::Expected +ValueReaderDispatcher::readArray(QualType Ty, llvm::orc::ExecutorAddr Addr) { + const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(Ty); + if (!CAT) + return llvm::make_error("Not a ConstantArrayType", + llvm::inconvertibleErrorCode()); + + QualType ElemTy = CAT->getElementType(); + size_t ElemSize = Ctx.getTypeSizeInChars(ElemTy).getQuantity(); + + Value Val(Value::UninitArr(), Ty, CAT->getZExtSize()); + for (size_t i = 0; i < CAT->getZExtSize(); ++i) { + auto ElemAddr = Addr + i * ElemSize; + if (ElemTy->isPointerType()) { + auto BufOrErr = MA.readUInt64s({ElemAddr}); + if (!BufOrErr) + return BufOrErr.takeError(); + llvm::orc::ExecutorAddr Addr(BufOrErr->back()); + ElemAddr = Addr; + } - return std::make_unique(Ty, PtrValAddr); + auto ElemBufOrErr = read(ElemTy, ElemAddr); + if (!ElemBufOrErr) + return ElemBufOrErr.takeError(); + Val.getArrayInitializedElt(i) = std::move(*ElemBufOrErr); + } + + return std::move(Val); +} + +llvm::Expected +ValueReaderDispatcher::readOtherObject(QualType Ty, + llvm::orc::ExecutorAddr Addr) { + llvm::outs() << Addr.getValue(); + if (Ty->isRecordType()) { + llvm::outs() << "Here in recordtype\n"; + auto BufOrErr = MA.readUInt64s({Addr}); + if (!BufOrErr) + return BufOrErr.takeError(); + Addr = llvm::orc::ExecutorAddr(BufOrErr->back()); + } + uint64_t PtrValAddr = Addr.getValue(); + llvm::outs() << PtrValAddr; + return Value(Ty, PtrValAddr); } ValueResultManager::ValueResultManager(ASTContext &Ctx, @@ -511,18 +432,16 @@ void ValueResultManager::deliverResult(SendResultFn SendResult, ValueId ID, IdToType.erase(It); } - ReaderDispatcher Runner(Ctx, MemAcc); + ValueReaderDispatcher Runner(Ctx, MemAcc); auto BufOrErr = Runner.read(Ty, Addr); - ValBuf.reset(); if (!BufOrErr) { SendResult(BufOrErr.takeError()); return; } // Store the successfully read value buffer - ValBuf.swap(*BufOrErr); - + LastVal = std::move(*BufOrErr); SendResult(llvm::Error::success()); return; } diff --git a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp index f50f6e320776d..1ae96e101b50d 100644 --- a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp +++ b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp @@ -100,7 +100,7 @@ TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) { Value V1; llvm::cantFail(I.ParseAndExecute("int x = 42;")); llvm::cantFail(I.ParseAndExecute("x", &V1)); - EXPECT_FALSE(V1.isValid()); + EXPECT_TRUE(V1.isAbsent()); EXPECT_FALSE(V1.hasValue()); } diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp index 9ff9092524d21..765b834f07070 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -241,66 +241,66 @@ TEST_F(InterpreterTest, FindMangledNameSymbol) { #endif // _WIN32 } -static Value AllocateObject(TypeDecl *TD, Interpreter &Interp) { - std::string Name = TD->getQualifiedNameAsString(); - Value Addr; - // FIXME: Consider providing an option in clang::Value to take ownership of - // the memory created from the interpreter. - // cantFail(Interp.ParseAndExecute("new " + Name + "()", &Addr)); - - // The lifetime of the temporary is extended by the clang::Value. - cantFail(Interp.ParseAndExecute(Name + "()", &Addr)); - return Addr; -} - -static NamedDecl *LookupSingleName(Interpreter &Interp, const char *Name) { - Sema &SemaRef = Interp.getCompilerInstance()->getSema(); - ASTContext &C = SemaRef.getASTContext(); - DeclarationName DeclName = &C.Idents.get(Name); - LookupResult R(SemaRef, DeclName, SourceLocation(), Sema::LookupOrdinaryName); - SemaRef.LookupName(R, SemaRef.TUScope); - assert(!R.empty()); - return R.getFoundDecl(); -} - -TEST_F(InterpreterTest, InstantiateTemplate) { - // FIXME: We cannot yet handle delayed template parsing. If we run with - // -fdelayed-template-parsing we try adding the newly created decl to the - // active PTU which causes an assert. - std::vector Args = {"-fno-delayed-template-parsing"}; - std::unique_ptr Interp = createInterpreter(Args); - - llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);" - "class A {};" - "struct B {" - " template" - " static int callme(T) { return 42; }" - "};")); - auto &PTU = llvm::cantFail(Interp->Parse("auto _t = &B::callme;")); - auto PTUDeclRange = PTU.TUPart->decls(); - EXPECT_EQ(1, std::distance(PTUDeclRange.begin(), PTUDeclRange.end())); - - // Lower the PTU - if (llvm::Error Err = Interp->Execute(PTU)) { - // We cannot execute on the platform. - consumeError(std::move(Err)); - return; - } - - TypeDecl *TD = cast(LookupSingleName(*Interp, "A")); - Value NewA = AllocateObject(TD, *Interp); - - // Find back the template specialization - VarDecl *VD = static_cast(*PTUDeclRange.begin()); - UnaryOperator *UO = llvm::cast(VD->getInit()); - NamedDecl *TmpltSpec = llvm::cast(UO->getSubExpr())->getDecl(); - - std::string MangledName = MangleName(TmpltSpec); - typedef int (*TemplateSpecFn)(void *); - auto fn = - cantFail(Interp->getSymbolAddress(MangledName)).toPtr(); - EXPECT_EQ(42, fn(NewA.getPtr())); -} +// static Value AllocateObject(TypeDecl *TD, Interpreter &Interp) { +// std::string Name = TD->getQualifiedNameAsString(); +// Value Addr; +// // FIXME: Consider providing an option in clang::Value to take ownership of +// // the memory created from the interpreter. +// // cantFail(Interp.ParseAndExecute("new " + Name + "()", &Addr)); + +// // The lifetime of the temporary is extended by the clang::Value. +// cantFail(Interp.ParseAndExecute(Name + "()", &Addr)); +// return Addr; +// } + +// static NamedDecl *LookupSingleName(Interpreter &Interp, const char *Name) { +// Sema &SemaRef = Interp.getCompilerInstance()->getSema(); +// ASTContext &C = SemaRef.getASTContext(); +// DeclarationName DeclName = &C.Idents.get(Name); +// LookupResult R(SemaRef, DeclName, SourceLocation(), Sema::LookupOrdinaryName); +// SemaRef.LookupName(R, SemaRef.TUScope); +// assert(!R.empty()); +// return R.getFoundDecl(); +// } + +// TEST_F(InterpreterTest, InstantiateTemplate) { +// // FIXME: We cannot yet handle delayed template parsing. If we run with +// // -fdelayed-template-parsing we try adding the newly created decl to the +// // active PTU which causes an assert. +// std::vector Args = {"-fno-delayed-template-parsing"}; +// std::unique_ptr Interp = createInterpreter(Args); + +// llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);" +// "class A {};" +// "struct B {" +// " template" +// " static int callme(T) { return 42; }" +// "};")); +// auto &PTU = llvm::cantFail(Interp->Parse("auto _t = &B::callme;")); +// auto PTUDeclRange = PTU.TUPart->decls(); +// EXPECT_EQ(1, std::distance(PTUDeclRange.begin(), PTUDeclRange.end())); + +// // Lower the PTU +// if (llvm::Error Err = Interp->Execute(PTU)) { +// // We cannot execute on the platform. +// consumeError(std::move(Err)); +// return; +// } + +// TypeDecl *TD = cast(LookupSingleName(*Interp, "A")); +// Value NewA = AllocateObject(TD, *Interp); + +// // Find back the template specialization +// VarDecl *VD = static_cast(*PTUDeclRange.begin()); +// UnaryOperator *UO = llvm::cast(VD->getInit()); +// NamedDecl *TmpltSpec = llvm::cast(UO->getSubExpr())->getDecl(); + +// std::string MangledName = MangleName(TmpltSpec); +// typedef int (*TemplateSpecFn)(void *); +// auto fn = +// cantFail(Interp->getSymbolAddress(MangledName)).toPtr(); +// EXPECT_EQ(42, fn((void *)NewA.getAddr())); +// } TEST_F(InterpreterTest, Value) { std::vector Args = {"-fno-sized-deallocation"}; @@ -309,40 +309,53 @@ TEST_F(InterpreterTest, Value) { Value V1; llvm::cantFail(Interp->ParseAndExecute("int x = 42;")); llvm::cantFail(Interp->ParseAndExecute("x", &V1)); - EXPECT_TRUE(V1.isValid()); + EXPECT_FALSE(V1.isAbsent()); EXPECT_TRUE(V1.hasValue()); + EXPECT_TRUE(V1.isBuiltin()); EXPECT_EQ(V1.getInt(), 42); - EXPECT_EQ(V1.convertTo(), 42); EXPECT_TRUE(V1.getType()->isIntegerType()); - EXPECT_EQ(V1.getKind(), Value::K_Int); - EXPECT_FALSE(V1.isManuallyAlloc()); + EXPECT_EQ(V1.getBuiltinKind(), Value::K_Int); + + Value V1c; + llvm::cantFail(Interp->ParseAndExecute("int arr[2] = {42, 24};")); + llvm::cantFail(Interp->ParseAndExecute("arr", &V1c)); + EXPECT_FALSE(V1c.isAbsent()); + EXPECT_TRUE(V1c.hasValue()); + EXPECT_TRUE(V1c.isArray()); + EXPECT_FALSE(V1c.getArrayInitializedElt(0).isAbsent()); + EXPECT_TRUE(V1c.getArrayInitializedElt(0).hasValue()); + EXPECT_TRUE(V1c.getArrayInitializedElt(0).isBuiltin()); + EXPECT_EQ(V1c.getArrayInitializedElt(0).getInt(), 42); + EXPECT_FALSE(V1c.getArrayInitializedElt(1).isAbsent()); + EXPECT_TRUE(V1c.getArrayInitializedElt(1).hasValue()); + EXPECT_TRUE(V1c.getArrayInitializedElt(1).isBuiltin()); + EXPECT_EQ(V1c.getArrayInitializedElt(1).getInt(), 24); + EXPECT_TRUE(V1c.getType()->isConstantArrayType()); Value V1b; llvm::cantFail(Interp->ParseAndExecute("char c = 42;")); llvm::cantFail(Interp->ParseAndExecute("c", &V1b)); - EXPECT_TRUE(V1b.getKind() == Value::K_Char_S || - V1b.getKind() == Value::K_Char_U); + EXPECT_TRUE(V1b.isBuiltin()); + EXPECT_TRUE(V1b.getBuiltinKind() == Value::K_Char_S || + V1b.getBuiltinKind() == Value::K_Char_U); Value V2; llvm::cantFail(Interp->ParseAndExecute("double y = 3.14;")); llvm::cantFail(Interp->ParseAndExecute("y", &V2)); - EXPECT_TRUE(V2.isValid()); + EXPECT_FALSE(V2.isAbsent()); EXPECT_TRUE(V2.hasValue()); EXPECT_EQ(V2.getDouble(), 3.14); - EXPECT_EQ(V2.convertTo(), 3.14); EXPECT_TRUE(V2.getType()->isFloatingType()); - EXPECT_EQ(V2.getKind(), Value::K_Double); - EXPECT_FALSE(V2.isManuallyAlloc()); + EXPECT_EQ(V2.getBuiltinKind(), Value::K_Double); - Value V3; - llvm::cantFail(Interp->ParseAndExecute( - "struct S { int* p; S() { p = new int(42); } ~S() { delete p; }};")); - llvm::cantFail(Interp->ParseAndExecute("S{}", &V3)); - EXPECT_TRUE(V3.isValid()); - EXPECT_TRUE(V3.hasValue()); - EXPECT_TRUE(V3.getType()->isRecordType()); - EXPECT_EQ(V3.getKind(), Value::K_PtrOrObj); - EXPECT_TRUE(V3.isManuallyAlloc()); + // Value V3; + // llvm::cantFail(Interp->ParseAndExecute( + // "struct S { int* p; S() { p = new int(42); } ~S() { delete p; }};")); + // llvm::cantFail(Interp->ParseAndExecute("S{}", &V3)); + // EXPECT_FALSE(V3.isAbsent()); + // EXPECT_TRUE(V3.hasValue()); + // EXPECT_TRUE(V3.getType()->isRecordType()); + // EXPECT_TRUE(V3.isPointer()); Value V4; llvm::cantFail(Interp->ParseAndExecute("int getGlobal();")); @@ -365,28 +378,24 @@ TEST_F(InterpreterTest, Value) { Value V6; llvm::cantFail(Interp->ParseAndExecute("void foo() {}")); llvm::cantFail(Interp->ParseAndExecute("foo()", &V6)); - EXPECT_TRUE(V6.isValid()); + EXPECT_TRUE(V6.isAbsent()); EXPECT_FALSE(V6.hasValue()); - EXPECT_TRUE(V6.getType()->isVoidType()); - EXPECT_EQ(V6.getKind(), Value::K_Void); - EXPECT_FALSE(V2.isManuallyAlloc()); + // EXPECT_TRUE(V6.getType()->isVoidType()); Value V7; llvm::cantFail(Interp->ParseAndExecute("foo", &V7)); - EXPECT_TRUE(V7.isValid()); + EXPECT_FALSE(V7.isAbsent()); EXPECT_TRUE(V7.hasValue()); EXPECT_TRUE(V7.getType()->isFunctionProtoType()); - EXPECT_EQ(V7.getKind(), Value::K_PtrOrObj); - EXPECT_FALSE(V7.isManuallyAlloc()); + EXPECT_TRUE(V7.isPointer()); Value V8; llvm::cantFail(Interp->ParseAndExecute("struct SS{ void f() {} };")); llvm::cantFail(Interp->ParseAndExecute("&SS::f", &V8)); - EXPECT_TRUE(V8.isValid()); + EXPECT_FALSE(V8.isAbsent()); EXPECT_TRUE(V8.hasValue()); EXPECT_TRUE(V8.getType()->isMemberFunctionPointerType()); - EXPECT_EQ(V8.getKind(), Value::K_PtrOrObj); - EXPECT_TRUE(V8.isManuallyAlloc()); + EXPECT_TRUE(V8.isPointer()); Value V9; llvm::cantFail(Interp->ParseAndExecute("struct A { virtual int f(); };")); @@ -394,30 +403,30 @@ TEST_F(InterpreterTest, Value) { Interp->ParseAndExecute("struct B : A { int f() { return 42; }};")); llvm::cantFail(Interp->ParseAndExecute("int (B::*ptr)() = &B::f;")); llvm::cantFail(Interp->ParseAndExecute("ptr", &V9)); - EXPECT_TRUE(V9.isValid()); + EXPECT_FALSE(V9.isAbsent()); EXPECT_TRUE(V9.hasValue()); EXPECT_TRUE(V9.getType()->isMemberFunctionPointerType()); - EXPECT_EQ(V9.getKind(), Value::K_PtrOrObj); - EXPECT_TRUE(V9.isManuallyAlloc()); + EXPECT_TRUE(V9.isPointer()); Value V10; llvm::cantFail(Interp->ParseAndExecute( "enum D : unsigned int {Zero = 0, One}; One", &V10)); + EXPECT_FALSE(V10.getBuiltinKind() == Value::K_Unspecified); std::string prettyType; llvm::raw_string_ostream OSType(prettyType); - V10.printType(OSType); + V10.printType(OSType, Interp->getASTContext()); EXPECT_STREQ(prettyType.c_str(), "D"); // FIXME: We should print only the value or the constant not the type. std::string prettyData; llvm::raw_string_ostream OSData(prettyData); - V10.printData(OSData); + V10.printData(OSData, Interp->getASTContext()); EXPECT_STREQ(prettyData.c_str(), "(One) : unsigned int 1"); std::string prettyPrint; llvm::raw_string_ostream OSPrint(prettyPrint); - V10.print(OSPrint); + V10.print(OSPrint, Interp->getASTContext()); EXPECT_STREQ(prettyPrint.c_str(), "(D) (One) : unsigned int 1\n"); } From 58d4109e73e5d29349dbf082d27b00dd9d3aceab Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Sat, 13 Sep 2025 20:50:02 +0530 Subject: [PATCH 06/11] Introduce ValueCleanup for managed destruction of JIT values --- clang/include/clang/Interpreter/Interpreter.h | 6 - clang/include/clang/Interpreter/Value.h | 89 ++++++++++- clang/lib/Interpreter/Interpreter.cpp | 3 + .../Interpreter/InterpreterValuePrinter.cpp | 82 +++++++++- clang/lib/Interpreter/Value.cpp | 150 +++++++++++++++--- .../unittests/Interpreter/InterpreterTest.cpp | 136 ++++++++-------- compiler-rt/lib/orc/send_value.cpp | 14 +- 7 files changed, 369 insertions(+), 111 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 5555ad2a82245..2d35a5e910928 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -218,12 +218,6 @@ class Interpreter { std::unique_ptr JITBuilder; - /// @} - /// @name Value and pretty printing support - /// @{ - - std::string ValueDataToString(const Value &V) const; - llvm::Expected convertExprToValue(Expr *E, bool IsOOP = false); // When we deallocate clang::Value we need to run the destructor of the type. diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h index 3da9f5769d98f..1532e40651aa5 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -95,6 +95,50 @@ class QualType; X(double, Double) \ X(long double, LongDouble) +class Value; + +/// \struct ValueCleanup +/// \brief Encapsulates destructor invocation for REPL values. +/// +/// `ValueCleanup` provides the logic to run object destructors in the JIT +/// process. It captures the runtime addresses of the destructor wrapper +/// functions and the object destructor itself. +/// +/// Typical usage: +/// - Constructed when a JIT'd type requires cleanup. +/// - Attached to a `Value` via `setValueCleanup`. +/// - Invoked through `operator()(Value&)` to run the destructor on demand. +struct ValueCleanup { + using DtorLookupFn = + std::function(QualType Ty)>; + llvm::orc::ExecutionSession *ES; + llvm::orc::ExecutorAddr DtorWrapperFn; + llvm::orc::ExecutorAddr DtorFn; + DtorLookupFn ObjDtor; + ValueCleanup() = default; + ValueCleanup(llvm::orc::ExecutionSession *ES, + llvm::orc::ExecutorAddr WrapperFn, + llvm::orc::ExecutorAddr DtorFn, DtorLookupFn Dtor) + : ES(ES), DtorWrapperFn(WrapperFn), DtorFn(DtorFn), + ObjDtor(std::move(Dtor)) {} + ~ValueCleanup() = default; + void operator()(Value &V); +}; + +/// \class Value +/// \brief Represents a dynamically typed value in the REPL. +/// +/// `Value` provides a type-erased container for runtime values that can be +/// produced or consumed by the REPL. It supports multiple storage kinds: +/// +/// - Builtin scalars (int, float, etc.) +/// - Arrays of `Value` +/// - Pointers (with optional pointee tracking) +/// - Strings +/// - An empty state (`K_None`) +/// +/// `Value` also integrates with `ValueCleanup`, which holds runtime +/// destructor logic for objects that require cleanup. class REPL_EXTERNAL_VISIBILITY Value final { public: enum BuiltinKind { @@ -105,6 +149,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { }; private: + /// Storage for builtin scalar values. struct Builtins { private: BuiltinKind BK = K_Unspecified; @@ -137,6 +182,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { #undef X }; + /// Represents an array of `Value` elements. struct ArrValue { std::vector Elements; uint64_t ArrSize; @@ -147,6 +193,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { } }; + /// Represents a pointer. Holds the address and optionally a pointee `Value`. struct PtrValue { uint64_t Addr = 0; Value *Pointee; // optional for str @@ -157,6 +204,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { } }; + /// Represents a string value (wrapper over std::string). struct StrValue { std::string StringBuf; StrValue(std::string str) : StringBuf(std::move(str)) {} @@ -173,11 +221,16 @@ class REPL_EXTERNAL_VISIBILITY Value final { ValKind VKind = K_None; DataType Data; + /// Optional cleanup action (e.g. call dtor in JIT runtime). + std::optional Cleanup = std::nullopt; + public: Value() = default; explicit Value(QualType Ty, ValKind K) : Ty(Ty), VKind(K) {} Value(const Value &RHS); - Value(Value &&RHS) : Ty(RHS.Ty), VKind(RHS.VKind), Data(RHS.Data) { + Value(Value &&RHS) + : Ty(RHS.Ty), VKind(RHS.VKind), Data(RHS.Data), + Cleanup(std::move(RHS.Cleanup)) { RHS.VKind = K_None; } @@ -206,6 +259,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { destroy(); } + // ---- Raw buffer conversion ---- template static T as(std::vector &raw) { T v{}; // assert(raw.size() >= sizeof(T) && "Buffer too small for type!"); @@ -266,12 +320,14 @@ class REPL_EXTERNAL_VISIBILITY Value final { const StrValue &asStr() const { return const_cast(this)->asStr(); } public: + // ---- Query helpers ---- bool hasBuiltinThis(BuiltinKind K) const { if (isBuiltin()) return asBuiltin().getKind() == K; return false; } + // ---- String accessors ---- void setStrVal(const char *buf) { assert(isStr() && "Not a Str"); asStr().StringBuf = buf; @@ -287,9 +343,10 @@ class REPL_EXTERNAL_VISIBILITY Value final { return StringRef(asStr().StringBuf); } + // ---- Array accessors ---- uint64_t getArraySize() const { return asArray().ArrSize; } - uint64_t getArrayInitializedElts() const { return asArray().ArrSize; } + uint64_t getArrayInitializedElts() const { return asArray().Elements.size(); } Value &getArrayInitializedElt(unsigned I) { assert(isArray() && "Invalid accessor"); @@ -301,6 +358,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { return const_cast(this)->getArrayInitializedElt(I); } + // ---- Pointer accessors ---- bool HasPointee() const { assert(isPointer() && "Invalid accessor"); return !(asPointer().Pointee->isAbsent()); @@ -317,6 +375,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { uint64_t getAddr() const { return asPointer().Addr; } + // ---- Builtin setters/getters ---- #define X(type, name) \ void set##name(type Val) { asBuiltin().set##name(Val); } \ type get##name() const { return asBuiltin().get##name(); } @@ -329,10 +388,19 @@ class REPL_EXTERNAL_VISIBILITY Value final { void print(llvm::raw_ostream &Out, ASTContext &Ctx) const; void dump(ASTContext &Ctx) const; - // ---- clear ---- - void clear() { destroy(); } + // ---- Cleanup & destruction ---- + void setValueCleanup(ValueCleanup VC) { + assert(!Cleanup.has_value()); + Cleanup.emplace(std::move(VC)); + } + void clear() { + if (Cleanup.has_value()) + (*Cleanup)(*this); + destroy(); + } private: + // ---- Constructors for each kind ---- void MakeBuiltIns() { assert(isAbsent() && "Bad state change"); new ((void *)(char *)&Data) Builtins(BuiltinKind::K_Unspecified); @@ -408,6 +476,13 @@ class ValueToString { std::string ArrayToString(const Value &A); }; +/// \class ValueResultManager +/// \brief Manages values returned from JIT code. +/// +/// Each result is registered with a unique ID and its `QualType`. +/// The JIT code later calls back into the runtime with that ID, and +/// `deliverResult` uses it to look up the type, read the value from memory, +/// and attach any destructor cleanup before making it available to the host. class ValueResultManager { public: using ValueId = uint64_t; @@ -418,9 +493,12 @@ class ValueResultManager { static std::unique_ptr Create(llvm::orc::LLJIT &EE, ASTContext &Ctx, bool IsOutOfProcess = false); - ValueId registerPendingResult(QualType QT) { + ValueId registerPendingResult(QualType QT, + std::optional VC = std::nullopt) { ValueId NewID = NextID.fetch_add(1, std::memory_order_relaxed); IdToType.insert({NewID, QT}); + if (VC) + IdToValCleanup.insert({NewID, std::move(*VC)}); return NewID; } @@ -439,6 +517,7 @@ class ValueResultManager { llvm::orc::MemoryAccess &MemAcc; Value LastVal; llvm::DenseMap IdToType; + llvm::DenseMap IdToValCleanup; }; } // namespace clang diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 7048976cf8f8b..58350ffdacfb8 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -341,6 +341,9 @@ const char *const Runtimes = R"( void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) { __clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size); } + EXTERN_C void __clang_Interpreter_destroyObj(unsigned char *ptr) { + delete[] ptr; + } #else #define EXTERN_C extern EXTERN_C void *memcpy(void *restrict dst, const void *restrict src, __SIZE_TYPE__ n); diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp index b52929ab97660..a5b194094cc70 100644 --- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -156,8 +156,6 @@ static std::string FunctionToString(ASTContext &Ctx, QualType QT, namespace clang { -std::string Interpreter::ValueDataToString(const Value &V) const { return ""; } - std::string ValueToString::toString(const Value *Buf) { switch (Buf->getKind()) { @@ -597,9 +595,9 @@ class InterfaceKindVisitor : public TypeVisitor { } bool VisitRecordType(const RecordType *Ty) { - // Args.push_back( - // Converter.handleRecordTypeExpr(Ty, QualType(Ty, 0), E).get()); - return false; + Args.push_back( + Converter.handleRecordTypeExpr(Ty, QualType(Ty, 0), E).get()); + return true; } bool VisitConstantArrayType(const ConstantArrayType *Ty) { @@ -626,10 +624,18 @@ class InterfaceKindVisitor : public TypeVisitor { } }; -enum RunTimeFnTag { OrcSendResult, ClangSendResult, ClangDestroyObj }; +enum RunTimeFnTag { + OrcSendResult, + ClangSendResult, + ClangDestroyObj, + OrcRunDtorWrapper, + ClangRunDtorWrapper +}; static constexpr llvm::StringRef RunTimeFnTagName[] = { - "__orc_rt_SendResultValue", "__clang_Interpreter_SendResultValue"}; + "__orc_rt_SendResultValue", "__clang_Interpreter_SendResultValue", + "__clang_Interpreter_destroyObj", "__orc_rt_runDtor", + "__clang_Interpreter_runDtor"}; // This synthesizes a call expression to a speciall // function that is responsible for generating the Value. @@ -674,6 +680,10 @@ llvm::Expected Interpreter::convertExprToValue(Expr *E, bool isOOP) { if (llvm::Error Err = LookupInterface(ValuePrintingInfo[ClangSendResult], RunTimeFnTagName[ClangSendResult])) return std::move(Err); + + if (llvm::Error Err = LookupInterface(ValuePrintingInfo[ClangDestroyObj], + RunTimeFnTagName[ClangDestroyObj])) + return std::move(Err); } llvm::SmallVector AdjustedArgs; @@ -696,7 +706,49 @@ llvm::Expected Interpreter::convertExprToValue(Expr *E, bool isOOP) { Ty = Ctx.getLValueReferenceType(Ty); } - auto ID = ValMgr->registerPendingResult(Ty); + std::optional CleanUp = std::nullopt; + if (DesugaredTy->isRecordType() && E->isPRValue()) + if (auto *CXXRD = DesugaredTy->getAsCXXRecordDecl()) { + auto *Dtor = S.LookupDestructor(CXXRD); + Dtor->addAttr(UsedAttr::CreateImplicit(Ctx)); + getCompilerInstance()->getASTConsumer().HandleTopLevelDecl( + DeclGroupRef(Dtor)); + + auto ObjDtor = + [this](QualType Ty) -> llvm::Expected { + if (auto *CXXRD = Ty->getAsCXXRecordDecl()) + return this->CompileDtorCall(CXXRD); + return llvm::make_error( + "destructor not found!", llvm::inconvertibleErrorCode()); + }; + + std::string DestroyObjFnName = RunTimeFnTagName[ClangDestroyObj].str(); + std::string RunDtorWrapperFnName = + RunTimeFnTagName[isOOP ? OrcRunDtorWrapper : ClangRunDtorWrapper] + .str(); + + // #if defined(__APPLE__) + // // On macOS, runtime symbols may require a leading underscore + // DestroyObjFnName.insert(0, "_"); + // RunDtorWrapperFnName.insert(0, "_"); + // #endif + + auto RunDtorWrapperAddr = getSymbolAddress(RunDtorWrapperFnName); + if (!RunDtorWrapperAddr) + return RunDtorWrapperAddr.takeError(); + + auto DestroyObjAddr = getSymbolAddress(DestroyObjFnName); + if (!DestroyObjAddr) + return DestroyObjAddr.takeError(); + + if (auto E = getExecutionEngine()) { + CleanUp = std::make_optional( + &E->getExecutionSession(), *RunDtorWrapperAddr, *DestroyObjAddr, + std::move(ObjDtor)); + } + } + + auto ID = ValMgr->registerPendingResult(Ty, std::move(CleanUp)); AdjustedArgs.push_back(IntegerLiteralExpr(Ctx, ID)); @@ -731,6 +783,20 @@ __clang_Interpreter_SendResultValue(void *Ctx, uint64_t Id, void *Addr) { [](llvm::Error Err) { llvm::cantFail(std::move(Err)); }, Id, llvm::orc::ExecutorAddr::fromPtr(Addr)); } + +REPL_EXTERNAL_VISIBILITY llvm::orc::shared::CWrapperFunctionResult +__clang_Interpreter_runDtor(char *ArgData, size_t ArgSize) { + return llvm::orc::shared::WrapperFunction:: + handle(ArgData, ArgSize, + [](llvm::orc::ExecutorAddr DtorFn, + llvm::orc::ExecutorAddr This) -> llvm::Error { + DtorFn.toPtr()( + This.toPtr()); + return llvm::Error::success(); + }) + .release(); +} } // A trampoline to work around the fact that operator placement new cannot diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp index 438700afa98bd..eb9ac95987cee 100644 --- a/clang/lib/Interpreter/Value.cpp +++ b/clang/lib/Interpreter/Value.cpp @@ -37,6 +37,8 @@ using namespace clang; +#define DEBUG_TYPE "value" + namespace { // This is internal buffer maintained by Value, used to hold temporaries. @@ -136,7 +138,8 @@ namespace clang { // } // } -Value::Value(const Value &RHS) : Ty(RHS.getType()), VKind(K_None) { +Value::Value(const Value &RHS) + : Ty(RHS.getType()), VKind(K_None), Cleanup(RHS.Cleanup) { switch (RHS.getKind()) { case K_None: VKind = RHS.VKind; @@ -150,7 +153,7 @@ Value::Value(const Value &RHS) : Ty(RHS.getType()), VKind(K_None) { } case K_Array: { MakeArray(RHS.getArraySize()); - for (uint64_t I = 0, N = RHS.getArraySize(); I < N; ++I) + for (uint64_t I = 0, N = RHS.getArrayInitializedElts(); I < N; ++I) getArrayInitializedElt(I) = RHS.getArrayInitializedElt(I); break; } @@ -181,6 +184,7 @@ Value &Value::operator=(Value &&RHS) { Ty = RHS.Ty; VKind = RHS.VKind; Data = RHS.Data; + Cleanup = std::move(RHS.Cleanup); RHS.VKind = K_None; } @@ -235,6 +239,41 @@ void Value::print(llvm::raw_ostream &Out, ASTContext &Ctx) const { Out << Str; } +void ValueCleanup::operator()(Value &V) { + using namespace llvm; + LLVM_DEBUG(dbgs() << "ValueCleanup: destroying value at Addr=" << V.getAddr() + << ", Type=" << V.getType().getAsString() << "\n"); + if (ObjDtor) { + if (auto ObjDtorAddrOrErr = ObjDtor(V.getType())) { + Error E = Error::success(); + orc::ExecutorAddr ObjDtorFn = *ObjDtorAddrOrErr; + orc::ExecutorAddr Addr(V.getAddr()); + LLVM_DEBUG(dbgs() << "ValueCleanup: calling object-specific destructor, " + "Addr=" + << Addr.getValue() << "\n"); + cantFail(ES->callSPSWrapper( + DtorWrapperFn, E, ObjDtorFn, Addr)); + cantFail(std::move(E)); + } else { + LLVM_DEBUG(dbgs() << "ValueCleanup: failed to get ObjDtor address\n"); + + cantFail(ObjDtorAddrOrErr.takeError()); + } + } + + Error E = Error::success(); + orc::ExecutorAddr Addr(V.getAddr()); + LLVM_DEBUG(dbgs() << "ValueCleanup: calling raw destructor, Addr=" + << Addr.getValue() << "\n"); + cantFail(ES->callSPSWrapper( + DtorWrapperFn, E, DtorFn, Addr)); + cantFail(std::move(E)); + LLVM_DEBUG(dbgs() << "ValueCleanup: finished destruction for Addr=" + << V.getAddr() << "\n"); +} + class ValueReaderDispatcher { private: ASTContext &Ctx; @@ -295,33 +334,60 @@ ValueReaderDispatcher::read(QualType QT, llvm::orc::ExecutorAddr Addr) { llvm::Expected ValueReaderDispatcher::readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr) { - if (Ty->isVoidType()) - return Value(); + LLVM_DEBUG(llvm::dbgs() << "readBuiltin: start, Addr=" << Addr.getValue() + << ", Type=" << Ty.getAsString() << "\n"); + if (Ty->isVoidType()) { + LLVM_DEBUG(llvm::dbgs() + << "readBuiltin: void type, returning empty Value\n"); + return Value(Ty, Value::K_None); + } + auto Size = Ctx.getTypeSizeInChars(Ty).getQuantity(); auto ResOrErr = MA.readBuffers({llvm::orc::ExecutorAddrRange(Addr, Size)}); - if (!ResOrErr) + if (!ResOrErr) { + LLVM_DEBUG(llvm::dbgs() << "readBuiltin: failed to read memory\n"); + return ResOrErr.takeError(); + } const auto &Res = *ResOrErr; + LLVM_DEBUG(llvm::dbgs() << "readBuiltin: read succeeded, last byte addr=" + << Res.back().data() << "\n"); return Value(Ty, Res.back()); } llvm::Expected ValueReaderDispatcher::readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr) { + + LLVM_DEBUG(llvm::dbgs() << "readPointer: start, Addr=" << Addr.getValue() + << "\n"); + auto PtrTy = dyn_cast(Ty.getTypePtr()); - if (!PtrTy) + if (!PtrTy) { + LLVM_DEBUG(llvm::dbgs() << "readPointer: Not a PointerType!\n"); + return llvm::make_error("Not a PointerType", llvm::inconvertibleErrorCode()); + } uint64_t PtrValAddr = Addr.getValue(); - if (PtrValAddr == 0) + LLVM_DEBUG(llvm::dbgs() << "readPointer: raw pointer value=0x" + << llvm::format_hex(PtrValAddr, 10) << "\n"); + + if (PtrValAddr == 0) { + LLVM_DEBUG(llvm::dbgs() << "readPointer: null pointer detected\n"); return Value(Ty, PtrValAddr); // null pointer + } llvm::orc::ExecutorAddr PointeeAddr(PtrValAddr); Value Val(Ty, PtrValAddr); QualType PointeeTy = PtrTy->getPointeeType(); + LLVM_DEBUG(llvm::dbgs() << "readPointer: pointee type=" + << PointeeTy.getAsString() << "\n"); if (PointeeTy->isCharType()) { + LLVM_DEBUG(llvm::dbgs() << "readPointer: reading C-string at pointee addr=" + << PointeeAddr.getValue() << "\n"); std::string S; for (size_t i = 0; i < 1024; ++i) { auto CRes = MA.readUInt8s({PointeeAddr + i}); @@ -332,56 +398,70 @@ ValueReaderDispatcher::readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr) { break; S.push_back(c); } + + LLVM_DEBUG(llvm::dbgs() << "readPointer: read string=\"" << S << "\"\n"); + Value Str(PointeeTy, S.c_str()); Val.getPointerPointee() = std::move(Str); } + LLVM_DEBUG(llvm::dbgs() << "readPointer: finished\n"); return std::move(Val); } llvm::Expected ValueReaderDispatcher::readArray(QualType Ty, llvm::orc::ExecutorAddr Addr) { + LLVM_DEBUG(llvm::dbgs() << "readArray: start, Addr=" << Addr.getValue() + << "\n"); const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(Ty); - if (!CAT) + if (!CAT) { + LLVM_DEBUG(llvm::dbgs() << "readArray: Not a ConstantArrayType!\n"); + return llvm::make_error("Not a ConstantArrayType", llvm::inconvertibleErrorCode()); - + } QualType ElemTy = CAT->getElementType(); size_t ElemSize = Ctx.getTypeSizeInChars(ElemTy).getQuantity(); + uint64_t NumElts = CAT->getZExtSize(); + + LLVM_DEBUG(llvm::dbgs() << "readArray: element type size=" << ElemSize + << ", element count=" << NumElts << "\n"); - Value Val(Value::UninitArr(), Ty, CAT->getZExtSize()); - for (size_t i = 0; i < CAT->getZExtSize(); ++i) { + Value Val(Value::UninitArr(), Ty, NumElts); + for (size_t i = 0; i < NumElts; ++i) { auto ElemAddr = Addr + i * ElemSize; + LLVM_DEBUG(llvm::dbgs() << "readArray: reading element[" << i + << "] at Addr=" << ElemAddr.getValue() << "\n"); + if (ElemTy->isPointerType()) { + LLVM_DEBUG(llvm::dbgs() + << "readArray: element[" << i << "] is pointer type\n"); auto BufOrErr = MA.readUInt64s({ElemAddr}); if (!BufOrErr) return BufOrErr.takeError(); - llvm::orc::ExecutorAddr Addr(BufOrErr->back()); - ElemAddr = Addr; + llvm::orc::ExecutorAddr PointeeAddr(BufOrErr->back()); + LLVM_DEBUG(llvm::dbgs() + << "readArray: pointer element[" << i + << "] points to Addr=" << PointeeAddr.getValue() << "\n"); + ElemAddr = PointeeAddr; } auto ElemBufOrErr = read(ElemTy, ElemAddr); if (!ElemBufOrErr) return ElemBufOrErr.takeError(); Val.getArrayInitializedElt(i) = std::move(*ElemBufOrErr); + LLVM_DEBUG(llvm::dbgs() + << "readArray: element[" << i << "] successfully read\n"); } + LLVM_DEBUG(llvm::dbgs() << "readArray: finished reading array\n"); return std::move(Val); } llvm::Expected ValueReaderDispatcher::readOtherObject(QualType Ty, llvm::orc::ExecutorAddr Addr) { - llvm::outs() << Addr.getValue(); - if (Ty->isRecordType()) { - llvm::outs() << "Here in recordtype\n"; - auto BufOrErr = MA.readUInt64s({Addr}); - if (!BufOrErr) - return BufOrErr.takeError(); - Addr = llvm::orc::ExecutorAddr(BufOrErr->back()); - } uint64_t PtrValAddr = Addr.getValue(); - llvm::outs() << PtrValAddr; return Value(Ty, PtrValAddr); } @@ -419,29 +499,53 @@ void ValueResultManager::Initialize(llvm::orc::LLJIT &EE) { void ValueResultManager::deliverResult(SendResultFn SendResult, ValueId ID, llvm::orc::ExecutorAddr Addr) { + LLVM_DEBUG(llvm::dbgs() << "deliverResult called with ID=" << ID + << ", Addr=" << Addr.getValue() << "\n"); QualType Ty; - + std::optional VCOpt = std::nullopt; { std::lock_guard Lock(Mutex); auto It = IdToType.find(ID); if (It == IdToType.end()) { + LLVM_DEBUG(llvm::dbgs() + << "Unknown ValueId=" << ID << " in deliverResult\n"); SendResult(llvm::make_error( "Unknown ValueId in deliverResult", llvm::inconvertibleErrorCode())); } Ty = It->second.getCanonicalType(); + LLVM_DEBUG(llvm::dbgs() << "Resolved Type for ID=" << ID << "\n"); + IdToType.erase(It); + + auto valIt = IdToValCleanup.find(ID); + if (valIt != IdToValCleanup.end()) { + LLVM_DEBUG(llvm::dbgs() << "Found ValueCleanup for ID=" << ID << "\n"); + + VCOpt.emplace(std::move(valIt->second)); + IdToValCleanup.erase(valIt); + } else { + LLVM_DEBUG(llvm::dbgs() << "No ValueCleanup for ID=" << ID << "\n"); + } } ValueReaderDispatcher Runner(Ctx, MemAcc); auto BufOrErr = Runner.read(Ty, Addr); if (!BufOrErr) { + LLVM_DEBUG(llvm::dbgs() << "Failed to read value for ID=" << ID << "\n"); + SendResult(BufOrErr.takeError()); return; } // Store the successfully read value buffer LastVal = std::move(*BufOrErr); + LLVM_DEBUG(llvm::dbgs() << "Successfully read value for ID=" << ID << "\n"); + + if (VCOpt) { + LLVM_DEBUG(llvm::dbgs() << "Attaching ValueCleanup for ID=" << ID << "\n"); + LastVal.setValueCleanup(std::move(*VCOpt)); + } SendResult(llvm::Error::success()); return; } diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp index 765b834f07070..0742ed4c9377a 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -241,66 +241,66 @@ TEST_F(InterpreterTest, FindMangledNameSymbol) { #endif // _WIN32 } -// static Value AllocateObject(TypeDecl *TD, Interpreter &Interp) { -// std::string Name = TD->getQualifiedNameAsString(); -// Value Addr; -// // FIXME: Consider providing an option in clang::Value to take ownership of -// // the memory created from the interpreter. -// // cantFail(Interp.ParseAndExecute("new " + Name + "()", &Addr)); - -// // The lifetime of the temporary is extended by the clang::Value. -// cantFail(Interp.ParseAndExecute(Name + "()", &Addr)); -// return Addr; -// } - -// static NamedDecl *LookupSingleName(Interpreter &Interp, const char *Name) { -// Sema &SemaRef = Interp.getCompilerInstance()->getSema(); -// ASTContext &C = SemaRef.getASTContext(); -// DeclarationName DeclName = &C.Idents.get(Name); -// LookupResult R(SemaRef, DeclName, SourceLocation(), Sema::LookupOrdinaryName); -// SemaRef.LookupName(R, SemaRef.TUScope); -// assert(!R.empty()); -// return R.getFoundDecl(); -// } - -// TEST_F(InterpreterTest, InstantiateTemplate) { -// // FIXME: We cannot yet handle delayed template parsing. If we run with -// // -fdelayed-template-parsing we try adding the newly created decl to the -// // active PTU which causes an assert. -// std::vector Args = {"-fno-delayed-template-parsing"}; -// std::unique_ptr Interp = createInterpreter(Args); - -// llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);" -// "class A {};" -// "struct B {" -// " template" -// " static int callme(T) { return 42; }" -// "};")); -// auto &PTU = llvm::cantFail(Interp->Parse("auto _t = &B::callme;")); -// auto PTUDeclRange = PTU.TUPart->decls(); -// EXPECT_EQ(1, std::distance(PTUDeclRange.begin(), PTUDeclRange.end())); - -// // Lower the PTU -// if (llvm::Error Err = Interp->Execute(PTU)) { -// // We cannot execute on the platform. -// consumeError(std::move(Err)); -// return; -// } - -// TypeDecl *TD = cast(LookupSingleName(*Interp, "A")); -// Value NewA = AllocateObject(TD, *Interp); - -// // Find back the template specialization -// VarDecl *VD = static_cast(*PTUDeclRange.begin()); -// UnaryOperator *UO = llvm::cast(VD->getInit()); -// NamedDecl *TmpltSpec = llvm::cast(UO->getSubExpr())->getDecl(); - -// std::string MangledName = MangleName(TmpltSpec); -// typedef int (*TemplateSpecFn)(void *); -// auto fn = -// cantFail(Interp->getSymbolAddress(MangledName)).toPtr(); -// EXPECT_EQ(42, fn((void *)NewA.getAddr())); -// } +static Value AllocateObject(TypeDecl *TD, Interpreter &Interp) { + std::string Name = TD->getQualifiedNameAsString(); + Value Addr; + // FIXME: Consider providing an option in clang::Value to take ownership of + // the memory created from the interpreter. + // cantFail(Interp.ParseAndExecute("new " + Name + "()", &Addr)); + + // The lifetime of the temporary is extended by the clang::Value. + cantFail(Interp.ParseAndExecute(Name + "()", &Addr)); + return Addr; +} + +static NamedDecl *LookupSingleName(Interpreter &Interp, const char *Name) { + Sema &SemaRef = Interp.getCompilerInstance()->getSema(); + ASTContext &C = SemaRef.getASTContext(); + DeclarationName DeclName = &C.Idents.get(Name); + LookupResult R(SemaRef, DeclName, SourceLocation(), Sema::LookupOrdinaryName); + SemaRef.LookupName(R, SemaRef.TUScope); + assert(!R.empty()); + return R.getFoundDecl(); +} + +TEST_F(InterpreterTest, InstantiateTemplate) { + // FIXME: We cannot yet handle delayed template parsing. If we run with + // -fdelayed-template-parsing we try adding the newly created decl to the + // active PTU which causes an assert. + std::vector Args = {"-fno-delayed-template-parsing"}; + std::unique_ptr Interp = createInterpreter(Args); + + llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);" + "class A {};" + "struct B {" + " template" + " static int callme(T) { return 42; }" + "};")); + auto &PTU = llvm::cantFail(Interp->Parse("auto _t = &B::callme;")); + auto PTUDeclRange = PTU.TUPart->decls(); + EXPECT_EQ(1, std::distance(PTUDeclRange.begin(), PTUDeclRange.end())); + + // Lower the PTU + if (llvm::Error Err = Interp->Execute(PTU)) { + // We cannot execute on the platform. + consumeError(std::move(Err)); + return; + } + + TypeDecl *TD = cast(LookupSingleName(*Interp, "A")); + Value NewA = AllocateObject(TD, *Interp); + + // Find back the template specialization + VarDecl *VD = static_cast(*PTUDeclRange.begin()); + UnaryOperator *UO = llvm::cast(VD->getInit()); + NamedDecl *TmpltSpec = llvm::cast(UO->getSubExpr())->getDecl(); + + std::string MangledName = MangleName(TmpltSpec); + typedef int (*TemplateSpecFn)(void *); + auto fn = + cantFail(Interp->getSymbolAddress(MangledName)).toPtr(); + EXPECT_EQ(42, fn((void *)NewA.getAddr())); +} TEST_F(InterpreterTest, Value) { std::vector Args = {"-fno-sized-deallocation"}; @@ -348,14 +348,14 @@ TEST_F(InterpreterTest, Value) { EXPECT_TRUE(V2.getType()->isFloatingType()); EXPECT_EQ(V2.getBuiltinKind(), Value::K_Double); - // Value V3; - // llvm::cantFail(Interp->ParseAndExecute( - // "struct S { int* p; S() { p = new int(42); } ~S() { delete p; }};")); - // llvm::cantFail(Interp->ParseAndExecute("S{}", &V3)); - // EXPECT_FALSE(V3.isAbsent()); - // EXPECT_TRUE(V3.hasValue()); - // EXPECT_TRUE(V3.getType()->isRecordType()); - // EXPECT_TRUE(V3.isPointer()); + Value V3; + llvm::cantFail(Interp->ParseAndExecute( + "struct S { int* p; S() { p = new int(42); } ~S() { delete p; }};")); + llvm::cantFail(Interp->ParseAndExecute("S{}", &V3)); + EXPECT_FALSE(V3.isAbsent()); + EXPECT_TRUE(V3.hasValue()); + EXPECT_TRUE(V3.getType()->isRecordType()); + EXPECT_TRUE(V3.isPointer()); Value V4; llvm::cantFail(Interp->ParseAndExecute("int getGlobal();")); diff --git a/compiler-rt/lib/orc/send_value.cpp b/compiler-rt/lib/orc/send_value.cpp index 31834439e6895..06ec1eade2c2e 100644 --- a/compiler-rt/lib/orc/send_value.cpp +++ b/compiler-rt/lib/orc/send_value.cpp @@ -7,14 +7,26 @@ //===----------------------------------------------------------------------===// #include "common.h" +#include "debug.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 orc_rt_WrapperFunctionResult __orc_rt_runDtor(char *ArgData, + size_t ArgSize) { + return WrapperFunction::handle( + ArgData, ArgSize, + [](ExecutorAddr DtorFn, ExecutorAddr This) { + DtorFn.toPtr()( + This.toPtr()); + return Error::success(); + }) + .release(); +} + ORC_RT_INTERFACE void __orc_rt_SendResultValue(uint64_t ResultId, void *V) { Error OptErr = Error::success(); if (auto Err = WrapperFunction::call( From 4b9539e29f222ba45cbe4f9f5f7ee73a94cb5ae7 Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Sun, 14 Sep 2025 14:28:49 +0530 Subject: [PATCH 07/11] Refactor code and headers --- clang/include/clang/Interpreter/Value.h | 12 +++++++++--- clang/lib/Interpreter/InterpreterValuePrinter.cpp | 5 +---- clang/lib/Interpreter/Value.cpp | 2 -- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h index 1532e40651aa5..f2936fc6e60b7 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -32,10 +32,9 @@ #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 @@ -48,7 +47,11 @@ namespace llvm { class raw_ostream; - +namespace orc { +class ExecutionSession; +class LLJIT; +class MemoryAccess; +} // namespace orc } // namespace llvm namespace clang { @@ -393,6 +396,9 @@ class REPL_EXTERNAL_VISIBILITY Value final { assert(!Cleanup.has_value()); Cleanup.emplace(std::move(VC)); } + + bool hasAttachedCleanup() const { return Cleanup.has_value(); } + void clear() { if (Cleanup.has_value()) (*Cleanup)(*this); diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp index a5b194094cc70..6f46cced3c5f1 100644 --- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -22,6 +22,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" #include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" @@ -680,10 +681,6 @@ llvm::Expected Interpreter::convertExprToValue(Expr *E, bool isOOP) { if (llvm::Error Err = LookupInterface(ValuePrintingInfo[ClangSendResult], RunTimeFnTagName[ClangSendResult])) return std::move(Err); - - if (llvm::Error Err = LookupInterface(ValuePrintingInfo[ClangDestroyObj], - RunTimeFnTagName[ClangDestroyObj])) - return std::move(Err); } llvm::SmallVector AdjustedArgs; diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp index eb9ac95987cee..f26f648be5ee1 100644 --- a/clang/lib/Interpreter/Value.cpp +++ b/clang/lib/Interpreter/Value.cpp @@ -21,9 +21,7 @@ #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" From f1e545665d098145ca561731e8c7690bbe71f33f Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Mon, 15 Sep 2025 14:25:13 +0530 Subject: [PATCH 08/11] Fix some issues --- clang/include/clang/Interpreter/Value.h | 5 +- .../Interpreter/InterpreterValuePrinter.cpp | 30 +++++----- clang/lib/Interpreter/Value.cpp | 55 ++++++++++--------- 3 files changed, 48 insertions(+), 42 deletions(-) diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h index f2936fc6e60b7..c7b73d9434f6c 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -39,6 +39,7 @@ #include "llvm/Support/Compiler.h" #include #include +#include // NOTE: Since the REPL itself could also include this runtime, extreme caution // should be taken when MAKING CHANGES to this file, especially when INCLUDE NEW @@ -176,9 +177,10 @@ class REPL_EXTERNAL_VISIBILITY Value final { void set##name(type Val) { \ assert(BK == K_Unspecified || BK == K_##name); \ m_##name = Val; \ + BK = K_##name; \ } \ type get##name() const { \ - assert(BK == K_##name); \ + assert(BK != K_Unspecified); \ return m_##name; \ } REPL_BUILTIN_TYPES @@ -501,6 +503,7 @@ class ValueResultManager { ValueId registerPendingResult(QualType QT, std::optional VC = std::nullopt) { + std::lock_guard Lock(Mutex); ValueId NewID = NextID.fetch_add(1, std::memory_order_relaxed); IdToType.insert({NewID, QT}); if (VC) diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp index 6f46cced3c5f1..ad80d56e4c1d6 100644 --- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -275,19 +275,6 @@ std::string ValueToString::PointerToString(const Value &P) { QualType DesugaredTy = QT.getDesugaredType(Ctx); QualType NonRefTy = DesugaredTy.getNonReferenceType(); - if (auto PtrTy = dyn_cast(QT.getTypePtr())) { - if (!PtrTy) - return ""; - - auto PointeeTy = PtrTy->getPointeeType(); - - // char* -> print string literal - if (PointeeTy->isCharType() && P.HasPointee()) { - if (P.getPointerPointee().isStr()) - return "\"" + P.getPointerPointee().getStrVal().str() + "\""; - } - } - if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) && NonRefTy->getPointeeType()->isFunctionProtoType()) return FunctionToString(Ctx, QT, (void *)P.getAddr()); @@ -298,6 +285,21 @@ std::string ValueToString::PointerToString(const Value &P) { if (NonRefTy->isNullPtrType()) return "nullptr\n"; + if (NonRefTy->isPointerType()) { + auto PointeeTy = NonRefTy->getPointeeType(); + + // char* -> print string literal + if (PointeeTy->isCharType()) { + if (P.HasPointee() && P.getPointerPointee().isStr()) + return "\"" + P.getPointerPointee().getStrVal().str() + "\""; + return std::to_string(P.getAddr()); + } + + std::ostringstream OS; + OS << "0x" << std::hex << P.getAddr(); + return OS.str(); + } + std::ostringstream OS; OS << "@0x" << std::hex << P.getAddr(); return OS.str(); @@ -509,8 +511,6 @@ class ExprConverter { ExprResult handleFunctionTypeExpr(const FunctionType *, QualType QTy, Expr *E) { - if (Ctx.getLangOpts().CPlusPlus) - return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/true); return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/false); } diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp index f26f648be5ee1..46d799542b171 100644 --- a/clang/lib/Interpreter/Value.cpp +++ b/clang/lib/Interpreter/Value.cpp @@ -242,7 +242,8 @@ void ValueCleanup::operator()(Value &V) { LLVM_DEBUG(dbgs() << "ValueCleanup: destroying value at Addr=" << V.getAddr() << ", Type=" << V.getType().getAsString() << "\n"); if (ObjDtor) { - if (auto ObjDtorAddrOrErr = ObjDtor(V.getType())) { + auto ObjDtorAddrOrErr = ObjDtor(V.getType()); + if (ObjDtorAddrOrErr && !ObjDtorAddrOrErr->isNull()) { Error E = Error::success(); orc::ExecutorAddr ObjDtorFn = *ObjDtorAddrOrErr; orc::ExecutorAddr Addr(V.getAddr()); @@ -255,8 +256,7 @@ void ValueCleanup::operator()(Value &V) { cantFail(std::move(E)); } else { LLVM_DEBUG(dbgs() << "ValueCleanup: failed to get ObjDtor address\n"); - - cantFail(ObjDtorAddrOrErr.takeError()); + consumeError(ObjDtorAddrOrErr.takeError()); } } @@ -298,40 +298,43 @@ class ValueReadVisitor : public TypeVisitor> { ValueReaderDispatcher &Dispatcher; llvm::orc::ExecutorAddr Addr; + QualType QT; public: - ValueReadVisitor(ValueReaderDispatcher &D, llvm::orc::ExecutorAddr A) - : Dispatcher(D), Addr(A) {} + ValueReadVisitor(ValueReaderDispatcher &D, llvm::orc::ExecutorAddr A, + QualType QT) + : Dispatcher(D), Addr(A), QT(QT) {} llvm::Expected VisitType(const Type *T) { - return Dispatcher.readOtherObject(QualType(T, 0), Addr); + return Dispatcher.readOtherObject(QT, Addr); } llvm::Expected VisitBuiltinType(const BuiltinType *BT) { - return Dispatcher.readBuiltin(QualType(BT, 0), Addr); + return Dispatcher.readBuiltin(QT, Addr); } llvm::Expected VisitPointerType(const PointerType *PT) { - return Dispatcher.readPointer(QualType(PT, 0), Addr); + return Dispatcher.readPointer(QT, Addr); } llvm::Expected VisitConstantArrayType(const ConstantArrayType *AT) { - return Dispatcher.readArray(QualType(AT, 0), Addr); + return Dispatcher.readArray(QT, Addr); } llvm::Expected VisitEnumType(const EnumType *ET) { - return Dispatcher.readBuiltin(QualType(ET, 0), Addr); + return Dispatcher.readBuiltin(QT, Addr); } }; llvm::Expected ValueReaderDispatcher::read(QualType QT, llvm::orc::ExecutorAddr Addr) { - ValueReadVisitor V(*this, Addr); - return V.Visit(QT.getTypePtr()); + ValueReadVisitor V(*this, Addr, QT); + return V.Visit(QT.getCanonicalType().getTypePtr()); } llvm::Expected -ValueReaderDispatcher::readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr) { +ValueReaderDispatcher::readBuiltin(QualType QT, llvm::orc::ExecutorAddr Addr) { + QualType Ty = QT.getCanonicalType(); LLVM_DEBUG(llvm::dbgs() << "readBuiltin: start, Addr=" << Addr.getValue() << ", Type=" << Ty.getAsString() << "\n"); if (Ty->isVoidType()) { @@ -351,17 +354,16 @@ ValueReaderDispatcher::readBuiltin(QualType Ty, llvm::orc::ExecutorAddr Addr) { const auto &Res = *ResOrErr; LLVM_DEBUG(llvm::dbgs() << "readBuiltin: read succeeded, last byte addr=" << Res.back().data() << "\n"); - return Value(Ty, Res.back()); + return Value(QT, Res.back()); } llvm::Expected -ValueReaderDispatcher::readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr) { - +ValueReaderDispatcher::readPointer(QualType QT, llvm::orc::ExecutorAddr Addr) { + QualType Ty = QT.getCanonicalType(); LLVM_DEBUG(llvm::dbgs() << "readPointer: start, Addr=" << Addr.getValue() << "\n"); - auto PtrTy = dyn_cast(Ty.getTypePtr()); - if (!PtrTy) { + if (!Ty->isPointerType()) { LLVM_DEBUG(llvm::dbgs() << "readPointer: Not a PointerType!\n"); return llvm::make_error("Not a PointerType", @@ -374,13 +376,13 @@ ValueReaderDispatcher::readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr) { if (PtrValAddr == 0) { LLVM_DEBUG(llvm::dbgs() << "readPointer: null pointer detected\n"); - return Value(Ty, PtrValAddr); // null pointer + return Value(QT, PtrValAddr); // null pointer } llvm::orc::ExecutorAddr PointeeAddr(PtrValAddr); - Value Val(Ty, PtrValAddr); + Value Val(QT, PtrValAddr); - QualType PointeeTy = PtrTy->getPointeeType(); + QualType PointeeTy = Ty->getPointeeType(); LLVM_DEBUG(llvm::dbgs() << "readPointer: pointee type=" << PointeeTy.getAsString() << "\n"); if (PointeeTy->isCharType()) { @@ -408,9 +410,10 @@ ValueReaderDispatcher::readPointer(QualType Ty, llvm::orc::ExecutorAddr Addr) { } llvm::Expected -ValueReaderDispatcher::readArray(QualType Ty, llvm::orc::ExecutorAddr Addr) { +ValueReaderDispatcher::readArray(QualType QT, llvm::orc::ExecutorAddr Addr) { LLVM_DEBUG(llvm::dbgs() << "readArray: start, Addr=" << Addr.getValue() << "\n"); + QualType Ty = QT.getCanonicalType(); const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(Ty); if (!CAT) { LLVM_DEBUG(llvm::dbgs() << "readArray: Not a ConstantArrayType!\n"); @@ -425,7 +428,7 @@ ValueReaderDispatcher::readArray(QualType Ty, llvm::orc::ExecutorAddr Addr) { LLVM_DEBUG(llvm::dbgs() << "readArray: element type size=" << ElemSize << ", element count=" << NumElts << "\n"); - Value Val(Value::UninitArr(), Ty, NumElts); + Value Val(Value::UninitArr(), QT, NumElts); for (size_t i = 0; i < NumElts; ++i) { auto ElemAddr = Addr + i * ElemSize; LLVM_DEBUG(llvm::dbgs() << "readArray: reading element[" << i @@ -510,7 +513,7 @@ void ValueResultManager::deliverResult(SendResultFn SendResult, ValueId ID, SendResult(llvm::make_error( "Unknown ValueId in deliverResult", llvm::inconvertibleErrorCode())); } - Ty = It->second.getCanonicalType(); + Ty = It->second; LLVM_DEBUG(llvm::dbgs() << "Resolved Type for ID=" << ID << "\n"); IdToType.erase(It); @@ -526,8 +529,8 @@ void ValueResultManager::deliverResult(SendResultFn SendResult, ValueId ID, } } - ValueReaderDispatcher Runner(Ctx, MemAcc); - auto BufOrErr = Runner.read(Ty, Addr); + ValueReaderDispatcher Dispatcher(Ctx, MemAcc); + auto BufOrErr = Dispatcher.read(Ty, Addr); if (!BufOrErr) { LLVM_DEBUG(llvm::dbgs() << "Failed to read value for ID=" << ID << "\n"); From 190a591640f374f1378e85711e6bbd0b87311e85 Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Mon, 15 Sep 2025 14:46:03 +0530 Subject: [PATCH 09/11] Fix issues --- clang/include/clang/Interpreter/Value.h | 4 ++-- clang/lib/Interpreter/Value.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h index c7b73d9434f6c..2a652e5220a04 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -201,7 +201,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { /// Represents a pointer. Holds the address and optionally a pointee `Value`. struct PtrValue { uint64_t Addr = 0; - Value *Pointee; // optional for str + Value *Pointee = nullptr; // optional for str PtrValue(uint64_t Addr) : Addr(Addr), Pointee(new Value()) {} ~PtrValue() { if (Pointee != nullptr) @@ -231,7 +231,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { public: Value() = default; - explicit Value(QualType Ty, ValKind K) : Ty(Ty), VKind(K) {} + explicit Value(QualType Ty) : Ty(Ty), VKind(K_None) {} Value(const Value &RHS); Value(Value &&RHS) : Ty(RHS.Ty), VKind(RHS.VKind), Data(RHS.Data), diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp index 46d799542b171..9d794f4ff8beb 100644 --- a/clang/lib/Interpreter/Value.cpp +++ b/clang/lib/Interpreter/Value.cpp @@ -340,7 +340,7 @@ ValueReaderDispatcher::readBuiltin(QualType QT, llvm::orc::ExecutorAddr Addr) { if (Ty->isVoidType()) { LLVM_DEBUG(llvm::dbgs() << "readBuiltin: void type, returning empty Value\n"); - return Value(Ty, Value::K_None); + return Value(Ty); } auto Size = Ctx.getTypeSizeInChars(Ty).getQuantity(); From e56b4f33ad132ae9c55152ab138d1e2420ed3ff7 Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Mon, 15 Sep 2025 16:16:04 +0530 Subject: [PATCH 10/11] Rebase and add assertion in ValueCleanup() --- clang/include/clang/Interpreter/Value.h | 12 ++++++------ clang/lib/Interpreter/Interpreter.cpp | 9 ++++----- clang/lib/Interpreter/Value.cpp | 9 +++++++++ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h index 2a652e5220a04..07a7469722a93 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -325,7 +325,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { const StrValue &asStr() const { return const_cast(this)->asStr(); } public: - // ---- Query helpers ---- + // ---- BuiltinKind Query helpers ---- bool hasBuiltinThis(BuiltinKind K) const { if (isBuiltin()) return asBuiltin().getKind() == K; @@ -451,16 +451,16 @@ class REPL_EXTERNAL_VISIBILITY Value final { void destroy() { switch (VKind) { case K_Builtin: - reinterpret_cast(&Data)->~Builtins(); + ((Builtins *)(char *)&Data)->~Builtins(); break; case K_Array: - reinterpret_cast(&Data)->~ArrValue(); + ((ArrValue *)(char *)&Data)->~ArrValue(); break; case K_Pointer: - reinterpret_cast(&Data)->~PtrValue(); + ((PtrValue *)(char *)&Data)->~PtrValue(); break; case K_Str: - reinterpret_cast(&Data)->~StrValue(); + ((StrValue *)(char *)&Data)->~StrValue(); break; default: break; @@ -525,7 +525,7 @@ class ValueResultManager { ASTContext &Ctx; llvm::orc::MemoryAccess &MemAcc; Value LastVal; - llvm::DenseMap IdToType; + llvm::DenseMap IdToType; llvm::DenseMap IdToValCleanup; }; diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 58350ffdacfb8..2e833287a9c42 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -302,9 +302,6 @@ Interpreter::Interpreter(std::unique_ptr Instance, return; } } - - ValMgr = ValueResultManager::Create(IncrExecutor->GetExecutionEngine(), - getASTContext()); } Interpreter::~Interpreter() { @@ -676,9 +673,11 @@ llvm::Error Interpreter::CreateExecutor(JITConfig Config) { Executor = std::make_unique(*TSCtx, *JITBuilder, Config, Err); #endif - if (!Err) + if (!Err) { IncrExecutor = std::move(Executor); - + ValMgr = ValueResultManager::Create(IncrExecutor->GetExecutionEngine(), + getASTContext()); + } return Err; } diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp index 9d794f4ff8beb..c4b2927e58c95 100644 --- a/clang/lib/Interpreter/Value.cpp +++ b/clang/lib/Interpreter/Value.cpp @@ -239,8 +239,14 @@ void Value::print(llvm::raw_ostream &Out, ASTContext &Ctx) const { void ValueCleanup::operator()(Value &V) { using namespace llvm; + + if (!V.isPointer() || V.getAddr() != 0) + return; + LLVM_DEBUG(dbgs() << "ValueCleanup: destroying value at Addr=" << V.getAddr() << ", Type=" << V.getType().getAsString() << "\n"); + assert(!DtorWrapperFn.isNull() && + "Expected valid destructor wrapper function address, but found null"); if (ObjDtor) { auto ObjDtorAddrOrErr = ObjDtor(V.getType()); if (ObjDtorAddrOrErr && !ObjDtorAddrOrErr->isNull()) { @@ -260,6 +266,9 @@ void ValueCleanup::operator()(Value &V) { } } + assert(!DtorFn.isNull() && + "Expected valid destructor function address, but found null"); + Error E = Error::success(); orc::ExecutorAddr Addr(V.getAddr()); LLVM_DEBUG(dbgs() << "ValueCleanup: calling raw destructor, Addr=" From 6d7082e8249305e83704058385e308aa84f674be Mon Sep 17 00:00:00 2001 From: SahilPatidar Date: Mon, 15 Sep 2025 20:46:29 +0530 Subject: [PATCH 11/11] Make Value's destructor safe by updating ArrValue and StrValue --- clang/include/clang/Interpreter/Value.h | 61 +++++++++++++------ .../Interpreter/InterpreterValuePrinter.cpp | 12 +++- clang/lib/Interpreter/Value.cpp | 2 +- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h index 07a7469722a93..3f77ab70cc71e 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -185,17 +185,19 @@ class REPL_EXTERNAL_VISIBILITY Value final { } REPL_BUILTIN_TYPES #undef X + + Builtins(const Builtins &) = delete; + Builtins &operator=(const Builtins &) = delete; }; /// Represents an array of `Value` elements. struct ArrValue { - std::vector Elements; + Value *Elements; uint64_t ArrSize; - ArrValue(uint64_t Size) : ArrSize(Size) { - Elements.reserve(ArrSize); - for (uint64_t I = 0; I < ArrSize; ++I) - Elements.emplace_back(); - } + ArrValue(uint64_t Size) : Elements(new Value[Size]), ArrSize(Size) {} + ~ArrValue() { delete[] Elements; } + ArrValue(const ArrValue &) = delete; + ArrValue &operator=(const ArrValue &) = delete; }; /// Represents a pointer. Holds the address and optionally a pointee `Value`. @@ -207,13 +209,37 @@ class REPL_EXTERNAL_VISIBILITY Value final { if (Pointee != nullptr) delete Pointee; } + + PtrValue(const PtrValue &) = delete; + PtrValue &operator=(const PtrValue &) = delete; }; /// Represents a string value (wrapper over std::string). struct StrValue { - std::string StringBuf; - StrValue(std::string str) : StringBuf(std::move(str)) {} - ~StrValue() = default; + char *Buf; + size_t Length; + + StrValue(const char *Str) { + Length = strlen(Str); + Buf = new char[Length + 1]; + memcpy(Buf, Str, Length); + Buf[Length] = '\0'; + } + + ~StrValue() { delete[] Buf; } + + StrValue(const StrValue &) = delete; + StrValue &operator=(const StrValue &) = delete; + + void set(const char *Str) { + delete[] Buf; + Length = strlen(Str); + Buf = new char[Length + 1]; + memcpy(Buf, Str, Length); + Buf[Length] = '\0'; + } + + const char *get() const { return Buf; } }; public: @@ -230,7 +256,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { std::optional Cleanup = std::nullopt; public: - Value() = default; + Value() : VKind(K_None) {} explicit Value(QualType Ty) : Ty(Ty), VKind(K_None) {} Value(const Value &RHS); Value(Value &&RHS) @@ -335,23 +361,18 @@ class REPL_EXTERNAL_VISIBILITY Value final { // ---- String accessors ---- void setStrVal(const char *buf) { assert(isStr() && "Not a Str"); - asStr().StringBuf = buf; - } - - StringRef getStrVal() { - assert(isStr() && "Not a Str"); - return StringRef(asStr().StringBuf); + asStr().set(buf); } - const StringRef getStrVal() const { + const char *getStrVal() const { assert(isStr() && "Not a Str"); - return StringRef(asStr().StringBuf); + return asStr().get(); } // ---- Array accessors ---- uint64_t getArraySize() const { return asArray().ArrSize; } - uint64_t getArrayInitializedElts() const { return asArray().Elements.size(); } + uint64_t getArrayInitializedElts() const { return asArray().ArrSize; } Value &getArrayInitializedElt(unsigned I) { assert(isArray() && "Invalid accessor"); @@ -427,7 +448,7 @@ class REPL_EXTERNAL_VISIBILITY Value final { VKind = K_Pointer; } - void MakeStr(std::string Str = "") { + void MakeStr(const char *Str) { assert(isAbsent() && "Bad state change"); new ((void *)(char *)&Data) StrValue(Str); VKind = K_Str; diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp index ad80d56e4c1d6..df2f151399c76 100644 --- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -155,6 +155,16 @@ static std::string FunctionToString(ASTContext &Ctx, QualType QT, return Str; } +static std::string CharPtrToString(const char *Ptr) { + if (!Ptr) + return "0"; + + std::string Result = "\""; + Result += Ptr; + Result += '"'; + return Result; +} + namespace clang { std::string ValueToString::toString(const Value *Buf) { @@ -291,7 +301,7 @@ std::string ValueToString::PointerToString(const Value &P) { // char* -> print string literal if (PointeeTy->isCharType()) { if (P.HasPointee() && P.getPointerPointee().isStr()) - return "\"" + P.getPointerPointee().getStrVal().str() + "\""; + return CharPtrToString(P.getPointerPointee().getStrVal()); return std::to_string(P.getAddr()); } diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp index c4b2927e58c95..b8e5be6ba9366 100644 --- a/clang/lib/Interpreter/Value.cpp +++ b/clang/lib/Interpreter/Value.cpp @@ -162,7 +162,7 @@ Value::Value(const Value &RHS) break; } case K_Str: - MakeStr(RHS.getStrVal().str()); + MakeStr(RHS.getStrVal()); break; } }