diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h --- a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h @@ -32,6 +32,7 @@ ExecutorAddr WriteUInt32s; ExecutorAddr WriteUInt64s; ExecutorAddr WriteBuffers; + ExecutorAddr WritePointers; }; /// Create an EPCGenericMemoryAccess instance from a given set of @@ -74,6 +75,13 @@ FAs.WriteBuffers, std::move(OnWriteComplete), Ws); } + void writePointersAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override { + using namespace shared; + EPC.callSPSWrapperAsync)>( + FAs.WritePointers, std::move(OnWriteComplete), Ws); + } + private: ExecutorProcessControl &EPC; FuncAddrs FAs; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h --- a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h @@ -120,6 +120,9 @@ virtual void writeBuffersAsync(ArrayRef Ws, WriteResultFn OnWriteComplete) = 0; + virtual void writePointersAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) = 0; + Error writeUInt8s(ArrayRef Ws) { std::promise ResultP; auto ResultF = ResultP.get_future(); @@ -159,6 +162,14 @@ [&](Error Err) { ResultP.set_value(std::move(Err)); }); return ResultF.get(); } + + Error writePointers(ArrayRef Ws) { + std::promise ResultP; + auto ResultF = ResultP.get_future(); + writePointersAsync(Ws, + [&](Error Err) { ResultP.set_value(std::move(Err)); }); + return ResultF.get(); + } }; /// A pair of a dylib and a set of symbols to be looked up. @@ -403,21 +414,48 @@ StringMap BootstrapSymbols; }; +class InProcessMemoryAccess : public ExecutorProcessControl::MemoryAccess { +public: + InProcessMemoryAccess(bool IsArch64Bit) : IsArch64Bit(IsArch64Bit) {} + void writeUInt8sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; + + void writeUInt16sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; + + void writeUInt32sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; + + void writeUInt64sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; + + void writeBuffersAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; + + void writePointersAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) override; + +private: + bool IsArch64Bit; +}; + /// A ExecutorProcessControl instance that asserts if any of its methods are /// used. Suitable for use is unit tests, and by ORC clients who haven't moved /// to ExecutorProcessControl-based APIs yet. -class UnsupportedExecutorProcessControl : public ExecutorProcessControl { +class UnsupportedExecutorProcessControl : public ExecutorProcessControl, + private InProcessMemoryAccess { public: UnsupportedExecutorProcessControl( std::shared_ptr SSP = nullptr, - std::unique_ptr D = nullptr, - const std::string &TT = "", unsigned PageSize = 0) - : ExecutorProcessControl(SSP ? std::move(SSP) - : std::make_shared(), - D ? std::move(D) - : std::make_unique()) { + std::unique_ptr D = nullptr, const std::string &TT = "", + unsigned PageSize = 0) + : ExecutorProcessControl( + SSP ? std::move(SSP) : std::make_shared(), + D ? std::move(D) : std::make_unique()), + InProcessMemoryAccess(Triple(TT).isArch64Bit()) { this->TargetTriple = Triple(TT); this->PageSize = PageSize; + this->MemAccess = this; } Expected loadDylib(const char *DylibPath) override { @@ -452,9 +490,8 @@ }; /// A ExecutorProcessControl implementation targeting the current process. -class SelfExecutorProcessControl - : public ExecutorProcessControl, - private ExecutorProcessControl::MemoryAccess { +class SelfExecutorProcessControl : public ExecutorProcessControl, + private InProcessMemoryAccess { public: SelfExecutorProcessControl( std::shared_ptr SSP, std::unique_ptr D, @@ -490,21 +527,6 @@ Error disconnect() override; private: - void writeUInt8sAsync(ArrayRef Ws, - WriteResultFn OnWriteComplete) override; - - void writeUInt16sAsync(ArrayRef Ws, - WriteResultFn OnWriteComplete) override; - - void writeUInt32sAsync(ArrayRef Ws, - WriteResultFn OnWriteComplete) override; - - void writeUInt64sAsync(ArrayRef Ws, - WriteResultFn OnWriteComplete) override; - - void writeBuffersAsync(ArrayRef Ws, - WriteResultFn OnWriteComplete) override; - static shared::CWrapperFunctionResult jitDispatchViaWrapperFunctionManager(void *Ctx, const void *FnTag, const char *Data, size_t Size); diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h --- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h @@ -100,6 +100,17 @@ StringRef Buffer; }; +/// Describes a write to a pointer. +/// For use with TargetProcessControl::MemoryAccess objects. +struct PointerWrite { + PointerWrite() = default; + PointerWrite(ExecutorAddr Addr, ExecutorAddr Value) + : Addr(Addr), Value(Value) {} + + ExecutorAddr Addr; + ExecutorAddr Value; +}; + /// A handle used to represent a loaded dylib in the target process. using DylibHandle = ExecutorAddr; @@ -133,6 +144,7 @@ using SPSMemoryAccessUInt64Write = SPSMemoryAccessUIntWrite; using SPSMemoryAccessBufferWrite = SPSTuple>; +using SPSMemoryAccessPointerWrite = SPSTuple; template <> class SPSSerializationTraits { @@ -302,6 +314,26 @@ } }; +template <> +class SPSSerializationTraits { +public: + static size_t size(const tpctypes::PointerWrite &W) { + return SPSTuple::AsArgList::size(W.Addr, + W.Value); + } + + static bool serialize(SPSOutputBuffer &OB, const tpctypes::PointerWrite &W) { + return SPSTuple::AsArgList::serialize( + OB, W.Addr, W.Value); + } + + static bool deserialize(SPSInputBuffer &IB, tpctypes::PointerWrite &W) { + return SPSTuple::AsArgList::deserialize( + IB, W.Addr, W.Value); + } +}; + } // end namespace shared } // end namespace orc } // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp b/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp --- a/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp +++ b/llvm/lib/ExecutionEngine/Orc/ExecutorProcessControl.cpp @@ -29,7 +29,8 @@ std::shared_ptr SSP, std::unique_ptr D, Triple TargetTriple, unsigned PageSize, std::unique_ptr MemMgr) - : ExecutorProcessControl(std::move(SSP), std::move(D)) { + : ExecutorProcessControl(std::move(SSP), std::move(D)), + InProcessMemoryAccess(TargetTriple.isArch64Bit()) { OwnedMemMgr = std::move(MemMgr); if (!OwnedMemMgr) @@ -146,41 +147,54 @@ return Error::success(); } -void SelfExecutorProcessControl::writeUInt8sAsync( - ArrayRef Ws, WriteResultFn OnWriteComplete) { +void InProcessMemoryAccess::writeUInt8sAsync(ArrayRef Ws, + WriteResultFn OnWriteComplete) { for (auto &W : Ws) *W.Addr.toPtr() = W.Value; OnWriteComplete(Error::success()); } -void SelfExecutorProcessControl::writeUInt16sAsync( +void InProcessMemoryAccess::writeUInt16sAsync( ArrayRef Ws, WriteResultFn OnWriteComplete) { for (auto &W : Ws) *W.Addr.toPtr() = W.Value; OnWriteComplete(Error::success()); } -void SelfExecutorProcessControl::writeUInt32sAsync( +void InProcessMemoryAccess::writeUInt32sAsync( ArrayRef Ws, WriteResultFn OnWriteComplete) { for (auto &W : Ws) *W.Addr.toPtr() = W.Value; OnWriteComplete(Error::success()); } -void SelfExecutorProcessControl::writeUInt64sAsync( +void InProcessMemoryAccess::writeUInt64sAsync( ArrayRef Ws, WriteResultFn OnWriteComplete) { for (auto &W : Ws) *W.Addr.toPtr() = W.Value; OnWriteComplete(Error::success()); } -void SelfExecutorProcessControl::writeBuffersAsync( +void InProcessMemoryAccess::writeBuffersAsync( ArrayRef Ws, WriteResultFn OnWriteComplete) { for (auto &W : Ws) memcpy(W.Addr.toPtr(), W.Buffer.data(), W.Buffer.size()); OnWriteComplete(Error::success()); } +void InProcessMemoryAccess::writePointersAsync( + ArrayRef Ws, WriteResultFn OnWriteComplete) { + if (IsArch64Bit) { + for (auto &W : Ws) + *W.Addr.toPtr() = W.Value.getValue(); + } else { + for (auto &W : Ws) + *W.Addr.toPtr() = static_cast(W.Value.getValue()); + } + + OnWriteComplete(Error::success()); +} + shared::CWrapperFunctionResult SelfExecutorProcessControl::jitDispatchViaWrapperFunctionManager( void *Ctx, const void *FnTag, const char *Data, size_t Size) { diff --git a/llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp b/llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp --- a/llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp +++ b/llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp @@ -41,6 +41,17 @@ .release(); } +llvm::orc::shared::CWrapperFunctionResult testWritePointers(const char *ArgData, + size_t ArgSize) { + return WrapperFunction)>:: + handle(ArgData, ArgSize, + [](std::vector Ws) { + for (auto &W : Ws) + *W.Addr.template toPtr() = W.Value.getValue(); + }) + .release(); +} + TEST(EPCGenericMemoryAccessTest, MemWrites) { auto SelfEPC = cantFail(SelfExecutorProcessControl::Create()); @@ -54,6 +65,7 @@ FAs.WriteUInt64s = ExecutorAddr::fromPtr( &testWriteUInts); FAs.WriteBuffers = ExecutorAddr::fromPtr(&testWriteBuffers); + FAs.WritePointers = ExecutorAddr::fromPtr(&testWritePointers); auto MemAccess = std::make_unique(*SelfEPC, FAs); @@ -62,6 +74,7 @@ uint16_t Test_UInt16 = 0; uint32_t Test_UInt32 = 0; uint64_t Test_UInt64 = 0; + uint64_t Test_Pointer = 0; char Test_Buffer[21]; auto Err1 = @@ -93,6 +106,11 @@ EXPECT_THAT_ERROR(std::move(Err5), Succeeded()); EXPECT_EQ(StringRef(Test_Buffer, TestMsg.size()), TestMsg); + auto Err6 = MemAccess->writePointers( + {{ExecutorAddr::fromPtr(&Test_Pointer), ExecutorAddr(1U)}}); + EXPECT_THAT_ERROR(std::move(Err6), Succeeded()); + EXPECT_EQ(Test_Pointer, 1U); + cantFail(SelfEPC->disconnect()); }