diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 078d70b3b1749..2d35a5e910928 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; @@ -221,14 +218,7 @@ class Interpreter { std::unique_ptr JITBuilder; - /// @} - /// @name Value and pretty printing support - /// @{ - - 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 = 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 b91301e6096eb..3f77ab70cc71e 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -33,10 +33,13 @@ #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/Shared/ExecutorAddress.h" #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 @@ -45,7 +48,11 @@ namespace llvm { class raw_ostream; - +namespace orc { +class ExecutionSession; +class LLJIT; +class MemoryAccess; +} // namespace orc } // namespace llvm namespace clang { @@ -92,119 +99,456 @@ class QualType; X(double, Double) \ X(long double, LongDouble) -class REPL_EXTERNAL_VISIBILITY Value { - union Storage { -#define X(type, name) type m_##name; +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 { +#define X(type, name) K_##name, REPL_BUILTIN_TYPES #undef X - void *m_Ptr; - unsigned char m_RawBits[sizeof(long double) * 8]; // widest type + K_Unspecified }; -public: - enum Kind { -#define X(type, name) K_##name, +private: + /// Storage for builtin scalar values. + struct Builtins { + private: + BuiltinKind BK = K_Unspecified; + union { +#define X(type, name) type m_##name; + REPL_BUILTIN_TYPES +#undef X + }; + + public: + Builtins() = default; + explicit Builtins(BuiltinKind BK) : BK(BK) {} + ~Builtins() {} + + 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; \ + BK = K_##name; \ + } \ + type get##name() const { \ + assert(BK != K_Unspecified); \ + return m_##name; \ + } REPL_BUILTIN_TYPES #undef X - K_Void, - K_PtrOrObj, - K_Unspecified + Builtins(const Builtins &) = delete; + Builtins &operator=(const Builtins &) = delete; + }; + + /// Represents an array of `Value` elements. + struct ArrValue { + Value *Elements; + uint64_t ArrSize; + 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`. + struct PtrValue { + uint64_t Addr = 0; + Value *Pointee = nullptr; // optional for str + PtrValue(uint64_t Addr) : Addr(Addr), Pointee(new Value()) {} + ~PtrValue() { + if (Pointee != nullptr) + delete Pointee; + } + + PtrValue(const PtrValue &) = delete; + PtrValue &operator=(const PtrValue &) = delete; }; - Value() = default; - Value(const Interpreter *In, void *Ty); + /// Represents a string value (wrapper over std::string). + struct StrValue { + 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: + using DataType = + llvm::AlignedCharArrayUnion; + enum ValKind { K_Builtin, K_Array, K_Pointer, K_Str, K_None }; + +private: + QualType Ty; + ValKind VKind = K_None; + DataType Data; + + /// Optional cleanup action (e.g. call dtor in JIT runtime). + std::optional Cleanup = std::nullopt; + +public: + Value() : VKind(K_None) {} + explicit Value(QualType Ty) : Ty(Ty), VKind(K_None) {} Value(const Value &RHS); - Value(Value &&RHS) noexcept; + Value(Value &&RHS) + : Ty(RHS.Ty), VKind(RHS.VKind), Data(RHS.Data), + Cleanup(std::move(RHS.Cleanup)) { + RHS.VKind = K_None; + } + 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)); + 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(); + } + + // ---- Raw buffer conversion ---- + 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; + } + + // ---- 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; + } +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(); } + +public: + // ---- BuiltinKind 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().set(buf); + } + + const char *getStrVal() const { + assert(isStr() && "Not a Str"); + return asStr().get(); + } + + // ---- Array accessors ---- + 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); + } + + // ---- Pointer accessors ---- + bool HasPointee() const { + assert(isPointer() && "Invalid accessor"); + return !(asPointer().Pointee->isAbsent()); + } + + Value &getPointerPointee() { + assert(isPointer() && "Invalid accessor"); + return *asPointer().Pointee; + } + + const Value &getPointerPointee() const { + return const_cast(this)->getPointerPointee(); + } + + uint64_t getAddr() const { return asPointer().Addr; } + + // ---- Builtin setters/getters ---- #define X(type, name) \ - void set##name(type Val) { Data.m_##name = Val; } \ - type get##name() const { return Data.m_##name; } + void set##name(type Val) { asBuiltin().set##name(Val); } \ + type get##name() const { return asBuiltin().get##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); + // ---- 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; + + // ---- Cleanup & destruction ---- + void setValueCleanup(ValueCleanup VC) { + assert(!Cleanup.has_value()); + Cleanup.emplace(std::move(VC)); } -protected: - bool isPointerOrObjectType() const { return ValueKind == K_PtrOrObj; } + bool hasAttachedCleanup() const { return Cleanup.has_value(); } - /// \brief Get to the value with type checking casting the underlying - /// stored value to T. - template T as() const { - switch (ValueKind) { + 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); + 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(const char *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: - return T(); + assert(false && "Type not supported"); + #define X(type, name) \ - case Value::K_##name: \ - return (T)Data.m_##name; + case BuiltinKind::K_##name: { \ + LHS.setKind(BuiltinKind::K_##name); \ + LHS.set##name(RHS.get##name()); \ + } break; 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(); + void destroy() { + switch (VKind) { + case K_Builtin: + ((Builtins *)(char *)&Data)->~Builtins(); + break; + case K_Array: + ((ArrValue *)(char *)&Data)->~ArrValue(); + break; + case K_Pointer: + ((PtrValue *)(char *)&Data)->~PtrValue(); + break; + case K_Str: + ((StrValue *)(char *)&Data)->~StrValue(); + break; + default: + break; } - }; + VKind = K_None; + } +}; - template struct convertFwd { - static T *cast(const Value &V) { - if (V.isPointerOrObjectType()) - return (T *)(uintptr_t)V.as(); - return nullptr; - } - }; +class ValueToString { +private: + ASTContext &Ctx; + +public: + ValueToString(ASTContext &Ctx) : Ctx(Ctx) {} + std::string toString(const Value *); + std::string toString(QualType); + +private: + std::string BuiltinToString(const Value &B); + std::string PointerToString(const Value &P); + 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; + 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 = false); + + 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) + IdToValCleanup.insert({NewID, std::move(*VC)}); + return NewID; + } + + void resetAndDump(); + + 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); - const Interpreter *Interp = nullptr; - void *OpaqueType = nullptr; - Storage Data; - Kind ValueKind = K_Unspecified; - bool IsManuallyAlloc = false; + mutable std::mutex Mutex; + ASTContext &Ctx; + llvm::orc::MemoryAccess &MemAcc; + Value LastVal; + llvm::DenseMap IdToType; + llvm::DenseMap IdToValCleanup; }; -template <> inline void *Value::as() const { - if (isPointerOrObjectType()) - return Data.m_Ptr; - return (void *)as(); -} } // namespace clang #endif diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 07c170a63ce82..2e833287a9c42 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -324,6 +324,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; @@ -337,6 +338,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); @@ -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, ...); )"; @@ -667,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; } @@ -708,12 +716,11 @@ 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(); + if (ValMgr) { + if (V) { + *V = ValMgr->release(); } else - *V = std::move(LastValue); + ValMgr->resetAndDump(); } return llvm::Error::success(); } diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp index 54abfa6dbb9d8..df2f151399c76 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" @@ -96,16 +97,14 @@ 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(); 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) @@ -120,12 +119,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; @@ -137,7 +137,8 @@ static std::string FunctionToString(const Value &V, const void *Ptr) { // *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(); @@ -154,13 +155,6 @@ 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"; @@ -173,65 +167,32 @@ static std::string CharPtrToString(const char *Ptr) { 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(); - - 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::toString(const Value *Buf) { - // 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()); - } - - 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); - } - - Result += ValueDataToString(InnerV); + 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); - // Skip the \0 if the char types - if (Idx < N - 1) - Result += ", "; - } - Result += " }"; - return Result; + default: + break; } + return ""; +} + +std::string ValueToString::BuiltinToString(const Value &B) { + if (!B.hasValue()) + return ""; // No data in buffer + QualType QT = B.getType(); 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,85 +224,144 @@ 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"); + SS << ((B.getBool()) ? "true" : "false"); return Str; case clang::BuiltinType::Char_S: - SS << '\'' << V.getChar_S() << '\''; + SS << '\'' << B.getChar_S() << '\''; return Str; case clang::BuiltinType::SChar: - SS << '\'' << V.getSChar() << '\''; + SS << '\'' << B.getSChar() << '\''; return Str; case clang::BuiltinType::Char_U: - SS << '\'' << V.getChar_U() << '\''; + SS << '\'' << B.getChar_U() << '\''; return Str; case clang::BuiltinType::UChar: - SS << '\'' << V.getUChar() << '\''; + SS << '\'' << B.getUChar() << '\''; return Str; case clang::BuiltinType::Short: - SS << V.getShort(); + SS << B.getShort(); return Str; case clang::BuiltinType::UShort: - SS << V.getUShort(); + SS << B.getUShort(); return Str; case clang::BuiltinType::Int: - SS << V.getInt(); + SS << B.getInt(); return Str; case clang::BuiltinType::UInt: - SS << V.getUInt(); + SS << B.getUInt(); return Str; case clang::BuiltinType::Long: - SS << V.getLong(); + SS << B.getLong(); return Str; case clang::BuiltinType::ULong: - SS << V.getULong(); + SS << B.getULong(); return Str; case clang::BuiltinType::LongLong: - SS << V.getLongLong(); + SS << B.getLongLong(); return Str; case clang::BuiltinType::ULongLong: - SS << V.getULongLong(); + SS << B.getULongLong(); return Str; case clang::BuiltinType::Float: - return formatFloating(V.getFloat(), /*suffix=*/'f'); + return formatFloating(B.getFloat(), /*suffix=*/'f'); case clang::BuiltinType::Double: - return formatFloating(V.getDouble()); + return formatFloating(B.getDouble()); case clang::BuiltinType::LongDouble: - return formatFloating(V.getLongDouble(), /*suffix=*/'L'); + return formatFloating(B.getLongDouble(), /*suffix=*/'L'); } } + if (NonRefTy->isEnumeralType()) + return EnumToString(Ctx, QT, B.getUInt()); + + return ""; +} + +std::string ValueToString::PointerToString(const Value &P) { + + QualType QT = P.getType(); + QualType DesugaredTy = QT.getDesugaredType(Ctx); + QualType NonRefTy = DesugaredTy.getNonReferenceType(); + if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) && NonRefTy->getPointeeType()->isFunctionProtoType()) - return FunctionToString(V, V.getPtr()); + return FunctionToString(Ctx, QT, (void *)P.getAddr()); if (NonRefTy->isFunctionType()) - return FunctionToString(V, &V); - - if (NonRefTy->isEnumeralType()) - return EnumToString(V); + return FunctionToString(Ctx, QT, (void *)P.getAddr()); 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()); + auto PointeeTy = NonRefTy->getPointeeType(); - return VoidPtrToString(V.getPtr()); + // char* -> print string literal + if (PointeeTy->isCharType()) { + if (P.HasPointee() && P.getPointerPointee().isStr()) + return CharPtrToString(P.getPointerPointee().getStrVal()); + return std::to_string(P.getAddr()); + } + + std::ostringstream OS; + OS << "0x" << std::hex << P.getAddr(); + return OS.str(); } - // Fall back to printing just the address of the unknown object. - return "@" + VoidPtrToString(V.getPtr()); + std::ostringstream OS; + OS << "@0x" << std::hex << P.getAddr(); + return OS.str(); } -std::string Interpreter::ValueTypeToString(const Value &V) const { - ASTContext &Ctx = const_cast(V.getASTContext()); - QualType QT = V.getType(); +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()) { + 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, 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; + } + } + Res += "\""; + return Res; + } + } +not_a_string: + std::ostringstream OS; + + OS << "{ "; + for (size_t I = 0, N = A.getArraySize(); I < N; ++I) { + const Value &EleVal = A.getArrayInitializedElt(I); + if (EleVal.hasValue()) + OS << this->toString(&EleVal); - std::string QTStr = QualTypeToString(Ctx, QT); + if (I + 1 < N) + OS << ", "; + } + + OS << " }"; + return OS.str(); +} + +std::string ValueToString::toString(QualType QT) { + ASTContext &AstCtx = const_cast(Ctx); + + std::string QTStr = QualTypeToString(AstCtx, QT); if (QT->isReferenceType()) QTStr += " &"; @@ -349,6 +369,32 @@ std::string Interpreter::ValueTypeToString(const Value &V) const { return QTStr; } +void ValueResultManager::resetAndDump() { + if (!LastVal.hasValue() || LastVal.isAbsent()) + return; + + QualType Ty = LastVal.getType(); + + 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()) + 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 << ValToStr.toString(Ty); + SS << ") "; + SS << ValToStr.toString(&V); + SS << "\n"; + llvm::outs() << Str; + V.clear(); +} + llvm::Expected Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) const { assert(CXXRD && "Cannot compile a destructor for a nullptr"); @@ -371,99 +417,236 @@ Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) const { return AddrOrErr; } -enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag }; +class ExprConverter { + Sema &S; + ASTContext &Ctx; + +public: + ExprConverter(Sema &S, ASTContext &Ctx) : S(S), Ctx(Ctx) {} -class InterfaceKindVisitor - : public TypeVisitor { +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; + } - Sema &S; - Expr *E; - llvm::SmallVectorImpl &Args; + /// 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: - InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl &Args) - : S(S), E(E), Args(Args) {} + /// Create (&E) as a void* (uses Sema for & creation) + ExprResult CreateAddressOfVoidPtrExpr(QualType Ty, Expr *ForCast, + bool takeAddr = false) { + ExprResult AddrOfRes = ForCast; + + if (takeAddr) { + // 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(), AddrOfRes.get()); + if (CastedExpr.isInvalid()) + return ExprError(); - InterfaceKind computeInterfaceKind(QualType Ty) { - return Visit(Ty.getTypePtr()); + return CastedExpr; } - InterfaceKind VisitRecordType(const RecordType *Ty) { - return InterfaceKind::WithAlloc; + /// Wrap rvalues in a temporary (var) so they become addressable. + Expr *CreateMaterializeTemporaryExpr(Expr *E) { + return S.CreateMaterializeTemporaryExpr(E->getType(), E, + /*BoundToLvalueReference=*/true); } - InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) { - return InterfaceKind::WithAlloc; + ExprResult makeScalarAddressable(QualType Ty, Expr *E) { + if (E->isLValue() || E->isXValue()) + return CreateAddressOfVoidPtrExpr(Ty, E, /*takeAddr=*/true); + return CreateAddressOfVoidPtrExpr(Ty, CreateMaterializeTemporaryExpr(E), + /*takeAddr=*/true); } - InterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) { - return InterfaceKind::CopyArray; + ExprResult handleBuiltinTypeExpr(const BuiltinType *, QualType QTy, Expr *E) { + return makeScalarAddressable(QTy, E); } - InterfaceKind VisitFunctionType(const FunctionType *Ty) { - HandlePtrType(Ty); - return InterfaceKind::NoAlloc; + ExprResult handleEnumTypeExpr(const EnumType *, QualType QTy, Expr *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()); } - InterfaceKind VisitPointerType(const PointerType *Ty) { - HandlePtrType(Ty); - return InterfaceKind::NoAlloc; + ExprResult handlePointerTypeExpr(const PointerType *, QualType QTy, Expr *E) { + // Pointer expressions always evaluate to a pointer value. + // No need to take address or materialize. + return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/false); } - 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 handleArrayTypeExpr(const ConstantArrayType *, QualType QTy, + Expr *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); } - InterfaceKind VisitBuiltinType(const BuiltinType *Ty) { - if (Ty->isNullPtrType()) - Args.push_back(E); - else if (Ty->isFloatingType()) + ExprResult handleFunctionTypeExpr(const FunctionType *, QualType QTy, + Expr *E) { + return CreateAddressOfVoidPtrExpr(QTy, E, /*takeAddr=*/false); + } + + 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 { + 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, 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); - 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 true; + } - return InterfaceKind::NoAlloc; + bool VisitPointerType(const PointerType *Ty) { + Args.push_back( + Converter.handlePointerTypeExpr(Ty, QualType(Ty, 0), E).get()); + return true; } - InterfaceKind VisitEnumType(const EnumType *Ty) { - HandleIntegralOrEnumType(Ty); - return InterfaceKind::NoAlloc; + bool VisitMemberPointerType(const MemberPointerType *Ty) { + Args.push_back( + Converter.handleMemberPointerTypeExpr(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 VisitRecordType(const RecordType *Ty) { + Args.push_back( + Converter.handleRecordTypeExpr(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 VisitConstantArrayType(const ConstantArrayType *Ty) { + Args.push_back(Converter.handleArrayTypeExpr(Ty, QualType(Ty, 0), E).get()); + return true; + } + + 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 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; } }; -static constexpr llvm::StringRef VPName[] = { - "__clang_Interpreter_SetValueNoAlloc", - "__clang_Interpreter_SetValueWithAlloc", - "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"}; +enum RunTimeFnTag { + OrcSendResult, + ClangSendResult, + ClangDestroyObj, + OrcRunDtorWrapper, + ClangRunDtorWrapper +}; + +static constexpr llvm::StringRef RunTimeFnTagName[] = { + "__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. @@ -478,7 +661,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 +683,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 +713,64 @@ 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); - - // 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); + 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)); - } - // __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()) + auto ObjDtor = + [this](QualType Ty) -> llvm::Expected { + if (auto *CXXRD = Ty->getAsCXXRecordDecl()) + return this->CompileDtorCall(CXXRD); return llvm::make_error( - "Cannot call to " + VPName[CopyArray], - llvm::inconvertibleErrorCode()); - break; + "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)); + } } - 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()); + auto ID = ValMgr->registerPendingResult(Ty, std::move(CleanUp)); - 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"); - } + 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); + ExprResult SetValueE; + Scope *Scope = nullptr; + if (!V.transformExpr(DesugaredTy)) + return E; + + 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 +778,31 @@ 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); +__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)); +} - 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); +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(); } } diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp index d4c9d51ffcb61..b8e5be6ba9366 100644 --- a/clang/lib/Interpreter/Value.cpp +++ b/clang/lib/Interpreter/Value.cpp @@ -11,17 +11,32 @@ // //===----------------------------------------------------------------------===// -#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/LLJIT.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" +#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" + +#include +#include +#include +#include + #include #include using namespace clang; +#define DEBUG_TYPE "value" + namespace { // This is internal buffer maintained by Value, used to hold temporaries. @@ -97,186 +112,451 @@ class ValueStorage { namespace clang { -static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) { - if (Ctx.hasSameType(QT, Ctx.VoidTy)) - return Value::K_Void; +// 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 +// } +// } - if (const auto *ED = QT->getAsEnumDecl()) - QT = ED->getIntegerType(); +Value::Value(const Value &RHS) + : Ty(RHS.getType()), VKind(K_None), Cleanup(RHS.Cleanup) { + 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.getArrayInitializedElts(); 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()); + break; + } +} + +Value &Value::operator=(const Value &RHS) { + if (this != &RHS) + *this = Value(RHS); + + return *this; +} - const auto *BT = QT->getAs(); - if (!BT || BT->isNullPtrType()) - return Value::K_PtrOrObj; +Value &Value::operator=(Value &&RHS) { + if (this != &RHS) { + if (VKind != K_None) + destroy(); + Ty = RHS.Ty; + VKind = RHS.VKind; + Data = RHS.Data; + Cleanup = std::move(RHS.Cleanup); + RHS.VKind = K_None; + } + + return *this; +} + +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"); - return Value::K_Unspecified; + #define X(type, name) \ - case BuiltinType::name: \ - return Value::K_##name; + case BuiltinType::name: { \ + B.setKind(BuiltinKind::K_##name); \ + B.set##name(as(Raw)); \ + } break; 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()); - } +void Value::dump(ASTContext &Ctx) const { print(llvm::outs(), Ctx); } - size_t AllocSize = - getASTContext().getTypeSizeInChars(getType()).getQuantity(); - unsigned char *Payload = - ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize); - setPtr((void *)Payload); - } - } +void Value::printType(llvm::raw_ostream &Out, ASTContext &Ctx) const { + Out << ValueToString(Ctx).toString(getType()); } -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(); +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, ASTContext &Ctx) const { + // Don't even try to print a void or an invalid type, it doesn't make sense. + if (getType()->isVoidType() || isAbsent()) + return; -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); + // 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); - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Release(); + SS << "("; + printType(SS, Ctx); + SS << ") "; + printData(SS, Ctx); + SS << "\n"; + Out << Str; } -Value &Value::operator=(const Value &RHS) { - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Release(); +void ValueCleanup::operator()(Value &V) { + using namespace llvm; - Interp = RHS.Interp; - OpaqueType = RHS.OpaqueType; - Data = RHS.Data; - ValueKind = RHS.ValueKind; - IsManuallyAlloc = RHS.IsManuallyAlloc; + if (!V.isPointer() || V.getAddr() != 0) + return; - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Retain(); + 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()) { + 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"); + consumeError(ObjDtorAddrOrErr.takeError()); + } + } - return *this; + 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=" + << 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"); } -Value &Value::operator=(Value &&RHS) noexcept { - if (this != &RHS) { - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Release(); +class ValueReaderDispatcher { +private: + ASTContext &Ctx; + llvm::orc::MemoryAccess &MA; + +public: + ValueReaderDispatcher(ASTContext &Ctx, llvm::orc::MemoryAccess &MA) + : Ctx(Ctx), MA(MA) {} - 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); + llvm::Expected read(QualType QT, llvm::orc::ExecutorAddr Addr); - Data = RHS.Data; + 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); + + llvm::Expected readOtherObject(QualType Ty, + llvm::orc::ExecutorAddr Addr); + // TODO: record, function, etc. +}; + +class ValueReadVisitor + : public TypeVisitor> { + ValueReaderDispatcher &Dispatcher; + llvm::orc::ExecutorAddr Addr; + QualType QT; + +public: + ValueReadVisitor(ValueReaderDispatcher &D, llvm::orc::ExecutorAddr A, + QualType QT) + : Dispatcher(D), Addr(A), QT(QT) {} + + llvm::Expected VisitType(const Type *T) { + return Dispatcher.readOtherObject(QT, Addr); } - return *this; -} -void Value::clear() { - if (IsManuallyAlloc) - ValueStorage::getFromPayload(getPtr())->Release(); - ValueKind = K_Unspecified; - OpaqueType = nullptr; - Interp = nullptr; - IsManuallyAlloc = false; + llvm::Expected VisitBuiltinType(const BuiltinType *BT) { + return Dispatcher.readBuiltin(QT, Addr); + } + + llvm::Expected VisitPointerType(const PointerType *PT) { + return Dispatcher.readPointer(QT, Addr); + } + + llvm::Expected VisitConstantArrayType(const ConstantArrayType *AT) { + return Dispatcher.readArray(QT, Addr); + } + + llvm::Expected VisitEnumType(const EnumType *ET) { + return Dispatcher.readBuiltin(QT, Addr); + } +}; + +llvm::Expected +ValueReaderDispatcher::read(QualType QT, llvm::orc::ExecutorAddr Addr) { + ValueReadVisitor V(*this, Addr, QT); + return V.Visit(QT.getCanonicalType().getTypePtr()); } -Value::~Value() { clear(); } +llvm::Expected +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()) { + LLVM_DEBUG(llvm::dbgs() + << "readBuiltin: void type, returning empty Value\n"); + return Value(Ty); + } + + auto Size = Ctx.getTypeSizeInChars(Ty).getQuantity(); + auto ResOrErr = MA.readBuffers({llvm::orc::ExecutorAddrRange(Addr, Size)}); + if (!ResOrErr) { + LLVM_DEBUG(llvm::dbgs() << "readBuiltin: failed to read memory\n"); + + return ResOrErr.takeError(); + } -void *Value::getPtr() const { - assert(ValueKind == K_PtrOrObj); - return Data.m_Ptr; + const auto &Res = *ResOrErr; + LLVM_DEBUG(llvm::dbgs() << "readBuiltin: read succeeded, last byte addr=" + << Res.back().data() << "\n"); + return Value(QT, Res.back()); } -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); +llvm::Expected +ValueReaderDispatcher::readPointer(QualType QT, llvm::orc::ExecutorAddr Addr) { + QualType Ty = QT.getCanonicalType(); + LLVM_DEBUG(llvm::dbgs() << "readPointer: start, Addr=" << Addr.getValue() + << "\n"); + + if (!Ty->isPointerType()) { + LLVM_DEBUG(llvm::dbgs() << "readPointer: Not a PointerType!\n"); + + return llvm::make_error("Not a PointerType", + llvm::inconvertibleErrorCode()); + } + + uint64_t PtrValAddr = Addr.getValue(); + 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(QT, PtrValAddr); // null pointer + } + + llvm::orc::ExecutorAddr PointeeAddr(PtrValAddr); + Value Val(QT, PtrValAddr); + + QualType PointeeTy = Ty->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}); + if (!CRes) + return CRes.takeError(); + char c = static_cast(CRes->back()); + if (c == '\0') + 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); } -QualType Value::getType() const { - return QualType::getFromOpaquePtr(OpaqueType); +llvm::Expected +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"); + + 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(), QT, 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 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); } -const Interpreter &Value::getInterpreter() const { - assert(Interp != nullptr && - "Can't get interpreter from a default constructed value"); - return *Interp; +llvm::Expected +ValueReaderDispatcher::readOtherObject(QualType Ty, + llvm::orc::ExecutorAddr Addr) { + uint64_t PtrValAddr = Addr.getValue(); + return Value(Ty, PtrValAddr); } -const ASTContext &Value::getASTContext() const { - return getInterpreter().getASTContext(); +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 Value::dump() const { print(llvm::outs()); } +void ValueResultManager::Initialize(llvm::orc::LLJIT &EE) { + auto &ES = EE.getExecutionSession(); -void Value::printType(llvm::raw_ostream &Out) const { - Out << Interp->ValueTypeToString(*this); -} + 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); -void Value::printData(llvm::raw_ostream &Out) const { - Out << Interp->ValueDataToString(*this); + llvm::cantFail(ES.registerJITDispatchHandlers(*EE.getPlatformJITDylib(), + std::move(Handlers))); } -// 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"); - // Don't even try to print a void or an invalid type, it doesn't make sense. - if (getType()->isVoidType() || !isValid()) +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; + 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 Dispatcher(Ctx, MemAcc); + auto BufOrErr = Dispatcher.read(Ty, Addr); + + if (!BufOrErr) { + LLVM_DEBUG(llvm::dbgs() << "Failed to read value for ID=" << ID << "\n"); + + SendResult(BufOrErr.takeError()); 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); + // Store the successfully read value buffer + LastVal = std::move(*BufOrErr); + LLVM_DEBUG(llvm::dbgs() << "Successfully read value for ID=" << ID << "\n"); - SS << "("; - printType(SS); - SS << ") "; - printData(SS); - SS << "\n"; - Out << Str; + if (VCOpt) { + LLVM_DEBUG(llvm::dbgs() << "Attaching ValueCleanup for ID=" << ID << "\n"); + LastVal.setValueCleanup(std::move(*VCOpt)); + } + SendResult(llvm::Error::success()); + return; } - } // namespace clang 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..0742ed4c9377a 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -299,7 +299,7 @@ TEST_F(InterpreterTest, InstantiateTemplate) { typedef int (*TemplateSpecFn)(void *); auto fn = cantFail(Interp->getSymbolAddress(MangledName)).toPtr(); - EXPECT_EQ(42, fn(NewA.getPtr())); + EXPECT_EQ(42, fn((void *)NewA.getAddr())); } TEST_F(InterpreterTest, Value) { @@ -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_FALSE(V3.isAbsent()); EXPECT_TRUE(V3.hasValue()); EXPECT_TRUE(V3.getType()->isRecordType()); - EXPECT_EQ(V3.getKind(), Value::K_PtrOrObj); - EXPECT_TRUE(V3.isManuallyAlloc()); + 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"); } 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..06ec1eade2c2e --- /dev/null +++ b/compiler-rt/lib/orc/send_value.cpp @@ -0,0 +1,39 @@ +//===- 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 "debug.h" +#include "jit_dispatch.h" +#include "wrapper_function_utils.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( + JITDispatch(&__orc_rt_SendResultValue_tag), OptErr, ResultId, + ExecutorAddr::fromPtr(V))) { + cantFail(std::move(OptErr)); + cantFail(std::move(Err)); + } + consumeError(std::move(OptErr)); +}