[ORC] Add EPCGenericMemoryAccess: generic executor memory access via EPC calls.
authorLang Hames <lhames@gmail.com>
Fri, 20 Aug 2021 05:52:42 +0000 (15:52 +1000)
committerLang Hames <lhames@gmail.com>
Sat, 21 Aug 2021 09:33:39 +0000 (19:33 +1000)
All ExecutorProcessControl subclasses must provide an
ExecutorProcessControl::MemoryAccess object that can be used to access executor
memory from the JIT process. The EPCGenericMemoryAccess class provides an
off-the-shelf MemoryAccess implementation for JITs that do not need (or cannot
provide) a specialized MemoryAccess implementation. This simplifies the process
of creating new ExecutorProcessControl implementations.

llvm/include/llvm/ExecutionEngine/Orc/Core.h
llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h [new file with mode: 0644]
llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
llvm/include/llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h
llvm/include/llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h
llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
llvm/include/llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h
llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
llvm/lib/ExecutionEngine/Orc/EPCGenericMemoryAccess.cpp [new file with mode: 0644]
llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp [new file with mode: 0644]

index 03a7b4b..e047e5e 100644 (file)
@@ -1489,14 +1489,8 @@ public:
   void callSPSWrapperAsync(SendResultT &&SendResult,
                            JITTargetAddress WrapperFnAddr,
                            const ArgTs &...Args) {
-    shared::WrapperFunction<SPSSignature>::callAsync(
-        [this,
-         WrapperFnAddr](ExecutorProcessControl::SendResultFunction SendResult,
-                        const char *ArgData, size_t ArgSize) {
-          callWrapperAsync(std::move(SendResult), WrapperFnAddr,
-                           ArrayRef<char>(ArgData, ArgSize));
-        },
-        std::move(SendResult), Args...);
+    EPC->callSPSWrapperAsync<SPSSignature, SendResultT, ArgTs...>(
+        std::forward<SendResultT>(SendResult), WrapperFnAddr, Args...);
   }
 
   /// Run a wrapper function using SPS to serialize the arguments and
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h
new file mode 100644 (file)
index 0000000..20a4784
--- /dev/null
@@ -0,0 +1,90 @@
+//===- EPCGenericMemoryAccess.h - Generic EPC MemoryAccess impl -*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements ExecutorProcessControl::MemoryAccess by making calls to
+// ExecutorProcessControl::callWrapperAsync.
+//
+// This simplifies the implementaton of new ExecutorProcessControl instances,
+// as this implementation will always work (at the cost of some performance
+// overhead for the calls).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_EPCGENERICMEMORYACCESS_H
+#define LLVM_EXECUTIONENGINE_ORC_EPCGENERICMEMORYACCESS_H
+
+#include "llvm/ExecutionEngine/Orc/Core.h"
+
+namespace llvm {
+namespace orc {
+
+class EPCGenericMemoryAccess : public ExecutorProcessControl::MemoryAccess {
+public:
+  /// Function addresses for memory access.
+  struct FuncAddrs {
+    ExecutorAddress WriteUInt8s;
+    ExecutorAddress WriteUInt16s;
+    ExecutorAddress WriteUInt32s;
+    ExecutorAddress WriteUInt64s;
+    ExecutorAddress WriteBuffers;
+  };
+
+  /// Create an EPCGenericMemoryAccess instance from a given set of
+  /// function addrs.
+  EPCGenericMemoryAccess(ExecutorProcessControl &EPC, FuncAddrs FAs)
+      : EPC(EPC), FAs(FAs) {}
+
+  /// Create using the standard memory access function names from the ORC
+  /// runtime.
+  static Expected<std::unique_ptr<EPCGenericMemoryAccess>>
+  CreateUsingOrcRTFuncs(ExecutionSession &ES, JITDylib &OrcRuntimeJD);
+
+  void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
+                        WriteResultFn OnWriteComplete) override {
+    using namespace shared;
+    EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessUInt8Write>)>(
+        std::move(OnWriteComplete), FAs.WriteUInt8s.getValue(), Ws);
+  }
+
+  void writeUInt16sAsync(ArrayRef<tpctypes::UInt16Write> Ws,
+                         WriteResultFn OnWriteComplete) override {
+    using namespace shared;
+    EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessUInt16Write>)>(
+        std::move(OnWriteComplete), FAs.WriteUInt16s.getValue(), Ws);
+  }
+
+  void writeUInt32sAsync(ArrayRef<tpctypes::UInt32Write> Ws,
+                         WriteResultFn OnWriteComplete) override {
+    using namespace shared;
+    EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessUInt32Write>)>(
+        std::move(OnWriteComplete), FAs.WriteUInt32s.getValue(), Ws);
+  }
+
+  void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
+                         WriteResultFn OnWriteComplete) override {
+    using namespace shared;
+    EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessUInt64Write>)>(
+        std::move(OnWriteComplete), FAs.WriteUInt64s.getValue(), Ws);
+  }
+
+  void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
+                         WriteResultFn OnWriteComplete) override {
+    using namespace shared;
+    EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessBufferWrite>)>(
+        std::move(OnWriteComplete), FAs.WriteBuffers.getValue(), Ws);
+  }
+
+private:
+  ExecutorProcessControl &EPC;
+  FuncAddrs FAs;
+};
+
+} // end namespace orc
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_EPCGENERICMEMORYACCESS_H
index a98ff46..9b00369 100644 (file)
@@ -168,8 +168,7 @@ public:
   /// The result of the lookup is a 2-dimentional array of target addresses
   /// that correspond to the lookup order. If a required symbol is not
   /// found then this method will return an error. If a weakly referenced
-  /// symbol is not found then it be assigned a '0' value in the result.
-  /// that correspond to the lookup order.
+  /// symbol is not found then it be assigned a '0' value.
   virtual Expected<std::vector<tpctypes::LookupResult>>
   lookupSymbols(ArrayRef<LookupRequest> Request) = 0;
 
@@ -190,6 +189,22 @@ public:
                                 JITTargetAddress WrapperFnAddr,
                                 ArrayRef<char> ArgBuffer) = 0;
 
+  /// Run a wrapper function using SPS to serialize the arguments and
+  /// deserialize the results.
+  template <typename SPSSignature, typename SendResultT, typename... ArgTs>
+  void callSPSWrapperAsync(SendResultT &&SendResult,
+                           JITTargetAddress WrapperFnAddr,
+                           const ArgTs &...Args) {
+    shared::WrapperFunction<SPSSignature>::callAsync(
+        [this,
+         WrapperFnAddr](ExecutorProcessControl::SendResultFunction SendResult,
+                        const char *ArgData, size_t ArgSize) {
+          callWrapperAsync(std::move(SendResult), WrapperFnAddr,
+                           ArrayRef<char>(ArgData, ArgSize));
+        },
+        std::move(SendResult), Args...);
+  }
+
   /// Disconnect from the target process.
   ///
   /// This should be called after the JIT session is shut down.
index c100005..181d735 100644 (file)
@@ -61,7 +61,8 @@ Error lookupAndRecordAddrs(
 /// weak.
 Error lookupAndRecordAddrs(
     ExecutorProcessControl &EPC, tpctypes::DylibHandle H,
-    std::vector<std::pair<SymbolStringPtr, ExecutorAddress *>> Pairs);
+    std::vector<std::pair<SymbolStringPtr, ExecutorAddress *>> Pairs,
+    SymbolLookupFlags LookupFlags = SymbolLookupFlags::RequiredSymbol);
 
 } // End namespace orc
 } // End namespace llvm
index 854f109..ea86304 100644 (file)
@@ -289,6 +289,13 @@ public:
   }
 };
 
+/// Trivial ArrayRef<T> -> SPSSequence<SPSElementTagT> serialization.
+template <typename SPSElementTagT, typename T>
+class TrivialSPSSequenceSerialization<SPSElementTagT, ArrayRef<T>> {
+public:
+  static constexpr bool available = true;
+};
+
 /// 'Trivial' sequence serialization: Sequence is serialized as a uint64_t size
 /// followed by a for-earch loop over the elements of the sequence to serialize
 /// each of them.
index a44bcd4..fe9b7b1 100644 (file)
@@ -62,6 +62,62 @@ using DylibHandle = JITTargetAddress;
 using LookupResult = std::vector<JITTargetAddress>;
 
 } // end namespace tpctypes
+
+namespace shared {
+
+template <typename T>
+using SPSMemoryAccessUIntWrite = SPSTuple<SPSExecutorAddress, T>;
+
+using SPSMemoryAccessUInt8Write = SPSMemoryAccessUIntWrite<uint8_t>;
+using SPSMemoryAccessUInt16Write = SPSMemoryAccessUIntWrite<uint16_t>;
+using SPSMemoryAccessUInt32Write = SPSMemoryAccessUIntWrite<uint32_t>;
+using SPSMemoryAccessUInt64Write = SPSMemoryAccessUIntWrite<uint64_t>;
+
+using SPSMemoryAccessBufferWrite =
+    SPSTuple<SPSExecutorAddress, SPSSequence<char>>;
+
+template <typename T>
+class SPSSerializationTraits<SPSMemoryAccessUIntWrite<T>,
+                             tpctypes::UIntWrite<T>> {
+public:
+  static size_t size(const tpctypes::UIntWrite<T> &W) {
+    return SPSTuple<SPSExecutorAddress, T>::AsArgList::size(W.Address, W.Value);
+  }
+
+  static bool serialize(SPSOutputBuffer &OB, const tpctypes::UIntWrite<T> &W) {
+    return SPSTuple<SPSExecutorAddress, T>::AsArgList::serialize(OB, W.Address,
+                                                                 W.Value);
+  }
+
+  static bool deserialize(SPSInputBuffer &IB, tpctypes::UIntWrite<T> &W) {
+    return SPSTuple<SPSExecutorAddress, T>::AsArgList::deserialize(
+        IB, W.Address, W.Value);
+  }
+};
+
+template <>
+class SPSSerializationTraits<SPSMemoryAccessBufferWrite,
+                             tpctypes::BufferWrite> {
+public:
+  static size_t size(const tpctypes::BufferWrite &W) {
+    return SPSTuple<SPSExecutorAddress, SPSSequence<char>>::AsArgList::size(
+        W.Address, W.Buffer);
+  }
+
+  static bool serialize(SPSOutputBuffer &OB, const tpctypes::BufferWrite &W) {
+    return SPSTuple<SPSExecutorAddress,
+                    SPSSequence<char>>::AsArgList ::serialize(OB, W.Address,
+                                                              W.Buffer);
+  }
+
+  static bool deserialize(SPSInputBuffer &IB, tpctypes::BufferWrite &W) {
+    return SPSTuple<SPSExecutorAddress,
+                    SPSSequence<char>>::AsArgList ::deserialize(IB, W.Address,
+                                                                W.Buffer);
+  }
+};
+
+} // end namespace shared
 } // end namespace orc
 } // end namespace llvm
 
index d1d7d54..3154bf6 100644 (file)
@@ -486,7 +486,7 @@ public:
     }
 
     auto SendSerializedResult = [SDR = std::move(SendDeserializedResult)](
-                                    WrapperFunctionResult R) {
+                                    WrapperFunctionResult R) mutable {
       RetT RetVal = detail::ResultDeserializer<SPSRetTagT, RetT>::makeValue();
       detail::ResultDeserializer<SPSRetTagT, RetT>::makeSafe(RetVal);
 
@@ -554,8 +554,10 @@ public:
                         const ArgTs &...Args) {
     WrapperFunction<SPSEmpty(SPSTagTs...)>::callAsync(
         Caller,
-        [SDR = std::move(SendDeserializedResult)](
-            Error SerializeErr, SPSEmpty E) { SDR(std::move(SerializeErr)); },
+        [SDR = std::move(SendDeserializedResult)](Error SerializeErr,
+                                                  SPSEmpty E) mutable {
+          SDR(std::move(SerializeErr));
+        },
         Args...);
   }
 
index 6dc12da..fb69781 100644 (file)
@@ -7,6 +7,7 @@ add_llvm_component_library(LLVMOrcJIT
   EPCDynamicLibrarySearchGenerator.cpp
   EPCDebugObjectRegistrar.cpp
   EPCEHFrameRegistrar.cpp
+  EPCGenericMemoryAccess.cpp
   EPCIndirectionUtils.cpp
   ExecutionUtils.cpp
   IndirectionUtils.cpp
diff --git a/llvm/lib/ExecutionEngine/Orc/EPCGenericMemoryAccess.cpp b/llvm/lib/ExecutionEngine/Orc/EPCGenericMemoryAccess.cpp
new file mode 100644 (file)
index 0000000..319909f
--- /dev/null
@@ -0,0 +1,44 @@
+//===----- EPCGenericMemoryAccess.cpp - Generic EPC MemoryAccess impl -----===//
+//
+// 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 "llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h"
+#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
+
+namespace llvm {
+namespace orc {
+
+/// Create from a ExecutorProcessControl instance.
+Expected<std::unique_ptr<EPCGenericMemoryAccess>>
+EPCGenericMemoryAccess::CreateUsingOrcRTFuncs(ExecutionSession &ES,
+                                              JITDylib &OrcRuntimeJD) {
+
+  StringRef GlobalPrefix = "";
+  if (ES.getExecutorProcessControl().getTargetTriple().isOSBinFormatMachO())
+    GlobalPrefix = "_";
+
+  FuncAddrs FAs;
+  if (auto Err = lookupAndRecordAddrs(
+          ES, LookupKind::Static, makeJITDylibSearchOrder(&OrcRuntimeJD),
+          {{ES.intern((GlobalPrefix + "__orc_rt_write_uint8s_wrapper").str()),
+            &FAs.WriteUInt8s},
+           {ES.intern((GlobalPrefix + "__orc_rt_write_uint16s_wrapper").str()),
+            &FAs.WriteUInt16s},
+           {ES.intern((GlobalPrefix + "__orc_rt_write_uint32s_wrapper").str()),
+            &FAs.WriteUInt32s},
+           {ES.intern((GlobalPrefix + "__orc_rt_write_uint64s_wrapper").str()),
+            &FAs.WriteUInt64s},
+           {ES.intern((GlobalPrefix + "__orc_rt_write_buffers_wrapper").str()),
+            &FAs.WriteBuffers}}))
+    return std::move(Err);
+
+  return std::make_unique<EPCGenericMemoryAccess>(
+      ES.getExecutorProcessControl(), FAs);
+}
+
+} // end namespace orc
+} // end namespace llvm
index 8f7097a..79c16cb 100644 (file)
@@ -17,6 +17,7 @@ set(LLVM_LINK_COMPONENTS
 add_llvm_unittest(OrcJITTests
   CoreAPIsTest.cpp
   ExecutionSessionWrapperFunctionCallsTest.cpp
+  EPCGenericMemoryAccessTest.cpp
   IndirectionUtilsTest.cpp
   JITTargetMachineBuilderTest.cpp
   LazyCallThroughAndReexportsTest.cpp
diff --git a/llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp b/llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp
new file mode 100644 (file)
index 0000000..f5209d6
--- /dev/null
@@ -0,0 +1,98 @@
+//===- EPCGenericMemoryAccessTest.cpp -- Tests for EPCGenericMemoryAccess -===//
+//
+// 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 "OrcTestCommon.h"
+
+#include "llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h"
+#include "llvm/Testing/Support/Error.h"
+
+using namespace llvm;
+using namespace llvm::orc;
+using namespace llvm::orc::shared;
+
+namespace {
+
+template <typename WriteT, typename SPSWriteT>
+llvm::orc::shared::detail::CWrapperFunctionResult
+testWriteUInts(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<void(SPSSequence<SPSWriteT>)>::handle(
+             ArgData, ArgSize,
+             [](std::vector<WriteT> Ws) {
+               for (auto &W : Ws)
+                 *jitTargetAddressToPointer<decltype(W.Value) *>(W.Address) =
+                     W.Value;
+             })
+      .release();
+}
+
+llvm::orc::shared::detail::CWrapperFunctionResult
+testWriteBuffers(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<void(SPSSequence<SPSMemoryAccessBufferWrite>)>::handle(
+             ArgData, ArgSize,
+             [](std::vector<tpctypes::BufferWrite> Ws) {
+               for (auto &W : Ws)
+                 memcpy(jitTargetAddressToPointer<char *>(W.Address),
+                        W.Buffer.data(), W.Buffer.size());
+             })
+      .release();
+}
+
+TEST(EPCGenericMemoryAccessTest, MemWrites) {
+  auto SelfEPC = cantFail(SelfExecutorProcessControl::Create());
+
+  EPCGenericMemoryAccess::FuncAddrs FAs;
+  FAs.WriteUInt8s = ExecutorAddress::fromPtr(
+      &testWriteUInts<tpctypes::UInt8Write, SPSMemoryAccessUInt8Write>);
+  FAs.WriteUInt16s = ExecutorAddress::fromPtr(
+      &testWriteUInts<tpctypes::UInt16Write, SPSMemoryAccessUInt16Write>);
+  FAs.WriteUInt32s = ExecutorAddress::fromPtr(
+      &testWriteUInts<tpctypes::UInt32Write, SPSMemoryAccessUInt32Write>);
+  FAs.WriteUInt64s = ExecutorAddress::fromPtr(
+      &testWriteUInts<tpctypes::UInt64Write, SPSMemoryAccessUInt64Write>);
+  FAs.WriteBuffers = ExecutorAddress::fromPtr(&testWriteBuffers);
+
+  auto MemAccess = std::make_unique<EPCGenericMemoryAccess>(*SelfEPC, FAs);
+
+  uint8_t Test_UInt8_1 = 0;
+  uint8_t Test_UInt8_2 = 0;
+  uint16_t Test_UInt16 = 0;
+  uint32_t Test_UInt32 = 0;
+  uint64_t Test_UInt64 = 0;
+  char Test_Buffer[21];
+
+  auto Err1 = MemAccess->writeUInt8s(
+      {{pointerToJITTargetAddress(&Test_UInt8_1), 1},
+       {pointerToJITTargetAddress(&Test_UInt8_2), 0xFE}});
+
+  EXPECT_THAT_ERROR(std::move(Err1), Succeeded());
+  EXPECT_EQ(Test_UInt8_1, 1U);
+  EXPECT_EQ(Test_UInt8_2, 0xFE);
+
+  auto Err2 =
+      MemAccess->writeUInt16s({{pointerToJITTargetAddress(&Test_UInt16), 1}});
+  EXPECT_THAT_ERROR(std::move(Err2), Succeeded());
+  EXPECT_EQ(Test_UInt16, 1U);
+
+  auto Err3 =
+      MemAccess->writeUInt32s({{pointerToJITTargetAddress(&Test_UInt32), 1}});
+  EXPECT_THAT_ERROR(std::move(Err3), Succeeded());
+  EXPECT_EQ(Test_UInt32, 1U);
+
+  auto Err4 =
+      MemAccess->writeUInt64s({{pointerToJITTargetAddress(&Test_UInt64), 1}});
+  EXPECT_THAT_ERROR(std::move(Err4), Succeeded());
+  EXPECT_EQ(Test_UInt64, 1U);
+
+  StringRef TestMsg("test-message");
+  auto Err5 = MemAccess->writeBuffers(
+      {{pointerToJITTargetAddress(&Test_Buffer), TestMsg}});
+  EXPECT_THAT_ERROR(std::move(Err5), Succeeded());
+  EXPECT_EQ(StringRef(Test_Buffer, TestMsg.size()), TestMsg);
+}
+
+} // namespace