[llvm] bb27e45 - [ORC] Add SimpleRemoteEPC: ExecutorProcessControl over SPS + abstract transport.

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 11 01:19:50 PDT 2021


Author: Lang Hames
Date: 2021-09-11T18:16:38+10:00
New Revision: bb27e4564355243e479cab40885d6e0f7f640572

URL: https://github.com/llvm/llvm-project/commit/bb27e4564355243e479cab40885d6e0f7f640572
DIFF: https://github.com/llvm/llvm-project/commit/bb27e4564355243e479cab40885d6e0f7f640572.diff

LOG: [ORC] Add SimpleRemoteEPC: ExecutorProcessControl over SPS + abstract transport.

SimpleRemoteEPC is an ExecutorProcessControl implementation (with corresponding
new server class) that uses ORC SimplePackedSerialization (SPS) to serialize and
deserialize EPC-messages to/from byte-buffers. The byte-buffers are sent and
received via a new SimpleRemoteEPCTransport interface that can be implemented to
run SimpleRemoteEPC over whatever underlying transport system (IPC, RPC, network
sockets, etc.) best suits your use case.

The SimpleRemoteEPCServer class provides executor-side support. It uses a
customizable SimpleRemoteEPCServer::Dispatcher object to dispatch wrapper
function calls to prevent the RPC thread from being blocked (a problem in some
earlier remote-JIT server implementations). Almost all functionality (beyond the
bare basics needed to bootstrap) is implemented as wrapper functions to keep the
implementation simple and uniform.

Compared to previous remote JIT utilities (OrcRemoteTarget*,
OrcRPCExecutorProcessControl), more consideration has been given to
disconnection and error handling behavior: Graceful disconnection is now always
initiated by the ORC side of the connection, and failure at either end (or in
the transport) will result in Errors being delivered to both ends to enable
controlled tear-down of the JIT and Executor (in the Executor's case this means
"as controlled as the JIT'd code allows").

The introduction of SimpleRemoteEPC will allow us to remove other remote-JIT
support from ORC (including the legacy OrcRemoteTarget* code used by lli, and
the OrcRPCExecutorProcessControl and OrcRPCEPCServer classes), and then remove
ORC RPC itself.

The llvm-jitlink and llvm-jitlink-executor tools have been updated to use
SimpleRemoteEPC over file descriptors. Future commits will move lli and other
tools and example code to this system, and remove ORC RPC.

Added: 
    llvm/include/llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h
    llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h
    llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h
    llvm/lib/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.cpp
    llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
    llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.cpp

Modified: 
    llvm/include/llvm/ExecutionEngine/Orc/Core.h
    llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h
    llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h
    llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
    llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp
    llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt
    llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
    llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp
    llvm/tools/llvm-jitlink/llvm-jitlink.cpp
    llvm/tools/llvm-jitlink/llvm-jitlink.h

Removed: 
    llvm/lib/ExecutionEngine/Orc/EPCGenericMemoryAccess.cpp


################################################################################
diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h
index 331a921c8abea..062b1f3e93e63 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Core.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h
@@ -1302,7 +1302,8 @@ class ExecutionSession {
   /// object.
   ExecutionSession(std::unique_ptr<ExecutorProcessControl> EPC);
 
-  /// End the session. Closes all JITDylibs.
+  /// End the session. Closes all JITDylibs and disconnects from the
+  /// executor.
   Error endSession();
 
   /// Get the ExecutorProcessControl object associated with this

diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h
index 0dd1e6a7562a5..48007129fd9f9 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h
@@ -38,11 +38,6 @@ class EPCGenericJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
   EPCGenericJITLinkMemoryManager(ExecutorProcessControl &EPC, FuncAddrs FAs)
       : EPC(EPC), FAs(FAs) {}
 
-  /// Create using the standard memory allocation function names from the
-  /// ORCTargetProcess library.
-  static Expected<std::unique_ptr<EPCGenericJITLinkMemoryManager>>
-  CreateUsingOrcRTFuncs(ExecutorProcessControl &EPC);
-
   Expected<std::unique_ptr<Allocation>>
   allocate(const jitlink::JITLinkDylib *JD,
            const SegmentsRequestMap &Request) override;

diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h
index fe9d1c13881cb..80a84ce9d0132 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h
@@ -39,11 +39,6 @@ class EPCGenericMemoryAccess : public ExecutorProcessControl::MemoryAccess {
   EPCGenericMemoryAccess(ExecutorProcessControl &EPC, FuncAddrs FAs)
       : EPC(EPC), FAs(FAs) {}
 
-  /// Create using the standard memory access function names from the
-  /// ORCTargetProcess library.
-  static Expected<std::unique_ptr<EPCGenericMemoryAccess>>
-  CreateUsingOrcRTFuncs(ExecutorProcessControl &EPC);
-
   void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
                         WriteResultFn OnWriteComplete) override {
     using namespace shared;

diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h
new file mode 100644
index 0000000000000..83929a82c6cce
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h
@@ -0,0 +1,249 @@
+//===--- SimpleRemoteEPCUtils.h - Utils for Simple Remote EPC ---*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Message definitions and other utilities for SimpleRemoteEPC and
+// SimpleRemoteEPCServer.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_SIMPLEREMOTEEPCUTILS_H
+#define LLVM_EXECUTIONENGINE_ORC_SHARED_SIMPLEREMOTEEPCUTILS_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
+#include "llvm/Support/Error.h"
+
+#include <string>
+#include <thread>
+
+namespace llvm {
+namespace orc {
+
+namespace SimpleRemoteEPCDefaultBootstrapSymbolNames {
+extern const char *ExecutorSessionObjectName;
+extern const char *DispatchFnName;
+} // end namespace SimpleRemoteEPCDefaultBootstrapSymbolNames
+
+enum class SimpleRemoteEPCOpcode : uint8_t {
+  FirstOpC,
+  Setup = FirstOpC,
+  Hangup,
+  Result,
+  CallWrapper,
+  LastOpC = CallWrapper
+};
+
+struct SimpleRemoteEPCExecutorInfo {
+  std::string TargetTriple;
+  uint64_t PageSize;
+  StringMap<ExecutorAddress> BootstrapSymbols;
+
+  Expected<ExecutorAddress> getBootstrapSymbol(StringRef Name) const {
+    auto I = BootstrapSymbols.find(Name);
+    if (I == BootstrapSymbols.end())
+      return make_error<StringError>("Symbol \"" + Name +
+                                         "\" not found in "
+                                         "bootstrap symbols map",
+                                     inconvertibleErrorCode());
+    return I->second;
+  }
+
+  Error getBootstrapSymbols(
+      ArrayRef<std::pair<ExecutorAddress &, StringRef>> Pairs) const {
+    for (auto &KV : Pairs) {
+      if (auto A = getBootstrapSymbol(KV.second))
+        KV.first = *A;
+      else
+        return A.takeError();
+    }
+    return Error::success();
+  }
+};
+
+using SimpleRemoteEPCArgBytesVector = SmallVector<char, 128>;
+
+class SimpleRemoteEPCTransportClient {
+public:
+  enum HandleMessageAction { ContinueSession, EndSession };
+
+  virtual ~SimpleRemoteEPCTransportClient();
+
+  /// Handle receipt of a message.
+  ///
+  /// Returns an Error if the message cannot be handled, 'EndSession' if the
+  /// client will not accept any further messages, and 'ContinueSession'
+  /// otherwise.
+  virtual Expected<HandleMessageAction>
+  handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
+                ExecutorAddress TagAddr,
+                SimpleRemoteEPCArgBytesVector ArgBytes) = 0;
+
+  /// Handle a disconnection from the underlying transport. No further messages
+  /// should be sent to handleMessage after this is called.
+  /// Err may contain an Error value indicating unexpected disconnection. This
+  /// allows clients to log such errors, but no attempt should be made at
+  /// recovery (which should be handled inside the transport class, if it is
+  /// supported at all).
+  virtual void handleDisconnect(Error Err) = 0;
+};
+
+class SimpleRemoteEPCTransport {
+public:
+  virtual ~SimpleRemoteEPCTransport();
+
+  /// Send a SimpleRemoteEPC message.
+  ///
+  /// This function may be called concurrently. Subclasses should implement
+  /// locking if required for the underlying transport.
+  virtual Error sendMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
+                            ExecutorAddress TagAddr,
+                            ArrayRef<char> ArgBytes) = 0;
+
+  /// Trigger disconnection from the transport. The implementation should
+  /// respond by calling handleDisconnect on the client once disconnection
+  /// is complete.
+  virtual void disconnect() = 0;
+};
+
+/// Uses read/write on FileDescriptors for transport.
+class FDSimpleRemoteEPCTransport : public SimpleRemoteEPCTransport {
+public:
+  /// Create a FDSimpleRemoteEPCTransport using the given FDs for
+  /// reading (InFD) and writing (OutFD).
+  static Expected<std::unique_ptr<FDSimpleRemoteEPCTransport>>
+  Create(SimpleRemoteEPCTransportClient &C, int InFD, int OutFD);
+
+  /// Create a FDSimpleRemoteEPCTransport using the given FD for both
+  /// reading and writing.
+  static Expected<std::unique_ptr<FDSimpleRemoteEPCTransport>>
+  Create(SimpleRemoteEPCTransportClient &C, int FD) {
+    return Create(C, FD, FD);
+  }
+
+  ~FDSimpleRemoteEPCTransport() override;
+
+  Error sendMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
+                    ExecutorAddress TagAddr, ArrayRef<char> ArgBytes) override;
+
+  void disconnect() override;
+
+private:
+  FDSimpleRemoteEPCTransport(SimpleRemoteEPCTransportClient &C, int InFD,
+                             int OutFD);
+
+  Error readBytes(char *Dst, size_t Size, bool *IsEOF = nullptr);
+  int writeBytes(const char *Src, size_t Size);
+  void listenLoop();
+
+  std::mutex M;
+  SimpleRemoteEPCTransportClient &C;
+  std::thread ListenerThread;
+  int InFD, OutFD;
+};
+
+struct RemoteSymbolLookupSetElement {
+  std::string Name;
+  bool Required;
+};
+
+using RemoteSymbolLookupSet = std::vector<RemoteSymbolLookupSetElement>;
+
+struct RemoteSymbolLookup {
+  uint64_t H;
+  RemoteSymbolLookupSet Symbols;
+};
+
+namespace shared {
+
+using SPSRemoteSymbolLookupSetElement = SPSTuple<SPSString, bool>;
+
+using SPSRemoteSymbolLookupSet = SPSSequence<SPSRemoteSymbolLookupSetElement>;
+
+using SPSRemoteSymbolLookup = SPSTuple<uint64_t, SPSRemoteSymbolLookupSet>;
+
+/// Tuple containing target triple, page size, and bootstrap symbols.
+using SPSSimpleRemoteEPCExecutorInfo =
+    SPSTuple<SPSString, uint64_t,
+             SPSSequence<SPSTuple<SPSString, SPSExecutorAddress>>>;
+
+template <>
+class SPSSerializationTraits<SPSRemoteSymbolLookupSetElement,
+                             RemoteSymbolLookupSetElement> {
+public:
+  static size_t size(const RemoteSymbolLookupSetElement &V) {
+    return SPSArgList<SPSString, bool>::size(V.Name, V.Required);
+  }
+
+  static size_t serialize(SPSOutputBuffer &OB,
+                          const RemoteSymbolLookupSetElement &V) {
+    return SPSArgList<SPSString, bool>::serialize(OB, V.Name, V.Required);
+  }
+
+  static size_t deserialize(SPSInputBuffer &IB,
+                            RemoteSymbolLookupSetElement &V) {
+    return SPSArgList<SPSString, bool>::deserialize(IB, V.Name, V.Required);
+  }
+};
+
+template <>
+class SPSSerializationTraits<SPSRemoteSymbolLookup, RemoteSymbolLookup> {
+public:
+  static size_t size(const RemoteSymbolLookup &V) {
+    return SPSArgList<uint64_t, SPSRemoteSymbolLookupSet>::size(V.H, V.Symbols);
+  }
+
+  static size_t serialize(SPSOutputBuffer &OB, const RemoteSymbolLookup &V) {
+    return SPSArgList<uint64_t, SPSRemoteSymbolLookupSet>::serialize(OB, V.H,
+                                                                     V.Symbols);
+  }
+
+  static size_t deserialize(SPSInputBuffer &IB, RemoteSymbolLookup &V) {
+    return SPSArgList<uint64_t, SPSRemoteSymbolLookupSet>::deserialize(
+        IB, V.H, V.Symbols);
+  }
+};
+
+template <>
+class SPSSerializationTraits<SPSSimpleRemoteEPCExecutorInfo,
+                             SimpleRemoteEPCExecutorInfo> {
+public:
+  static size_t size(const SimpleRemoteEPCExecutorInfo &SI) {
+    return SPSSimpleRemoteEPCExecutorInfo::AsArgList ::size(
+        SI.TargetTriple, SI.PageSize, SI.BootstrapSymbols);
+  }
+
+  static bool serialize(SPSOutputBuffer &OB,
+                        const SimpleRemoteEPCExecutorInfo &SI) {
+    return SPSSimpleRemoteEPCExecutorInfo::AsArgList ::serialize(
+        OB, SI.TargetTriple, SI.PageSize, SI.BootstrapSymbols);
+  }
+
+  static bool deserialize(SPSInputBuffer &IB, SimpleRemoteEPCExecutorInfo &SI) {
+    return SPSSimpleRemoteEPCExecutorInfo::AsArgList ::deserialize(
+        IB, SI.TargetTriple, SI.PageSize, SI.BootstrapSymbols);
+  }
+};
+
+using SPSRunAsMainSignature = int64_t(SPSExecutorAddress,
+                                      SPSSequence<SPSString>);
+
+using SPSLoadDylibSignature =
+    SPSExpected<SPSExecutorAddress>(SPSExecutorAddress, SPSString, uint64_t);
+
+using SPSLookupSymbolsSignature =
+    SPSExpected<SPSSequence<SPSSequence<SPSExecutorAddress>>>(
+        SPSExecutorAddress, SPSSequence<SPSRemoteSymbolLookup>);
+
+} // end namespace shared
+} // end namespace orc
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_SIMPLEREMOTEEPCUTILS_H

diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h b/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h
new file mode 100644
index 0000000000000..252474abecbd4
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h
@@ -0,0 +1,136 @@
+//===---- SimpleRemoteEPC.h - Simple remote executor control ----*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Simple remote executor process control.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_SIMPLEREMOTEEPC_H
+#define LLVM_EXECUTIONENGINE_ORC_SIMPLEREMOTEEPC_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/FunctionExtras.h"
+#include "llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h"
+#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MSVCErrorWorkarounds.h"
+
+#include <future>
+
+namespace llvm {
+namespace orc {
+
+class SimpleRemoteEPC : public ExecutorProcessControl,
+                        public SimpleRemoteEPCTransportClient {
+public:
+  /// Create a SimpleRemoteEPC using the given transport type and args.
+  template <typename TransportT, typename... TransportTCtorArgTs>
+  static Expected<std::unique_ptr<SimpleRemoteEPC>>
+  Create(TransportTCtorArgTs &&...TransportTCtorArgs) {
+    std::unique_ptr<SimpleRemoteEPC> SREPC(
+        new SimpleRemoteEPC(std::make_shared<SymbolStringPool>()));
+
+    // Prepare for setup packet.
+    std::promise<MSVCPExpected<SimpleRemoteEPCExecutorInfo>> EIP;
+    auto EIF = EIP.get_future();
+    SREPC->prepareToReceiveSetupMessage(EIP);
+    auto T = TransportT::Create(
+        *SREPC, std::forward<TransportTCtorArgTs>(TransportTCtorArgs)...);
+    if (!T)
+      return T.takeError();
+    auto EI = EIF.get();
+    if (!EI) {
+      (*T)->disconnect();
+      return EI.takeError();
+    }
+    if (auto Err = SREPC->setup(std::move(*T), std::move(*EI)))
+      return joinErrors(std::move(Err), SREPC->disconnect());
+    return std::move(SREPC);
+  }
+
+  SimpleRemoteEPC(const SimpleRemoteEPC &) = delete;
+  SimpleRemoteEPC &operator=(const SimpleRemoteEPC &) = delete;
+  SimpleRemoteEPC(SimpleRemoteEPC &&) = delete;
+  SimpleRemoteEPC &operator=(SimpleRemoteEPC &) = delete;
+  ~SimpleRemoteEPC();
+
+  /// Called at the end of the construction process to set up the instance.
+  ///
+  /// Override to set up custom memory manager and/or memory access objects.
+  /// This method must be called at the *end* of the subclass's
+  /// implementation.
+  virtual Error setup(std::unique_ptr<SimpleRemoteEPCTransport> T,
+                      const SimpleRemoteEPCExecutorInfo &EI);
+
+  Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override;
+
+  Expected<std::vector<tpctypes::LookupResult>>
+  lookupSymbols(ArrayRef<LookupRequest> Request) override;
+
+  Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr,
+                              ArrayRef<std::string> Args) override;
+
+  void callWrapperAsync(SendResultFunction OnComplete,
+                        JITTargetAddress WrapperFnAddr,
+                        ArrayRef<char> ArgBuffer) override;
+
+  Error disconnect() override;
+
+  Expected<HandleMessageAction>
+  handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
+                ExecutorAddress TagAddr,
+                SimpleRemoteEPCArgBytesVector ArgBytes) override;
+
+  void handleDisconnect(Error Err) override;
+
+protected:
+  void setMemoryManager(std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr);
+  void setMemoryAccess(std::unique_ptr<MemoryAccess> MemAccess);
+
+private:
+  SimpleRemoteEPC(std::shared_ptr<SymbolStringPool> SSP)
+      : ExecutorProcessControl(std::move(SSP)) {}
+
+  Error setupDefaultMemoryManager(const SimpleRemoteEPCExecutorInfo &EI);
+  Error setupDefaultMemoryAccess(const SimpleRemoteEPCExecutorInfo &EI);
+
+  Error handleSetup(uint64_t SeqNo, ExecutorAddress TagAddr,
+                    SimpleRemoteEPCArgBytesVector ArgBytes);
+  void prepareToReceiveSetupMessage(
+      std::promise<MSVCPExpected<SimpleRemoteEPCExecutorInfo>> &ExecInfoP);
+
+  Error handleResult(uint64_t SeqNo, ExecutorAddress TagAddr,
+                     SimpleRemoteEPCArgBytesVector ArgBytes);
+  void handleCallWrapper(uint64_t RemoteSeqNo, ExecutorAddress TagAddr,
+                         SimpleRemoteEPCArgBytesVector ArgBytes);
+
+  uint64_t getNextSeqNo() { return NextSeqNo++; }
+  void releaseSeqNo(uint64_t SeqNo) {}
+
+  using PendingCallWrapperResultsMap = DenseMap<uint64_t, SendResultFunction>;
+
+  std::atomic_bool Disconnected{false};
+  std::mutex SimpleRemoteEPCMutex;
+  std::unique_ptr<SimpleRemoteEPCTransport> T;
+  std::unique_ptr<jitlink::JITLinkMemoryManager> OwnedMemMgr;
+  std::unique_ptr<MemoryAccess> OwnedMemAccess;
+
+  ExecutorAddress LoadDylibAddr;
+  ExecutorAddress LookupSymbolsAddr;
+  ExecutorAddress RunAsMainAddr;
+
+  uint64_t NextSeqNo = 0;
+  PendingCallWrapperResultsMap PendingCallWrapperResults;
+};
+
+} // end namespace orc
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_SIMPLEREMOTEEPC_H

diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h
new file mode 100644
index 0000000000000..838e04c9ca1de
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h
@@ -0,0 +1,146 @@
+//===---- SimpleRemoteEPCServer.h - EPC over abstract channel ---*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// EPC over simple abstract channel.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_SIMPLEREMOTEEPCSERVER_H
+#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_SIMPLEREMOTEEPCSERVER_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/FunctionExtras.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
+#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
+#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Error.h"
+
+#include <condition_variable>
+#include <future>
+#include <memory>
+#include <mutex>
+
+namespace llvm {
+namespace orc {
+
+/// A simple EPC server implementation.
+class SimpleRemoteEPCServer : public SimpleRemoteEPCTransportClient {
+public:
+  using ReportErrorFunction = unique_function<void(Error)>;
+
+  class Dispatcher {
+  public:
+    virtual ~Dispatcher();
+    virtual void dispatch(unique_function<void()> Work) = 0;
+    virtual void shutdown() = 0;
+  };
+
+  class ThreadDispatcher : public Dispatcher {
+  public:
+    void dispatch(unique_function<void()> Work) override;
+    void shutdown() override;
+
+  private:
+    std::mutex DispatchMutex;
+    bool Running = true;
+    size_t Outstanding = 0;
+    std::condition_variable OutstandingCV;
+  };
+
+  static StringMap<ExecutorAddress> defaultBootstrapSymbols();
+
+  template <typename TransportT, typename... TransportTCtorArgTs>
+  static Expected<std::unique_ptr<SimpleRemoteEPCServer>>
+  Create(std::unique_ptr<Dispatcher> D,
+         StringMap<ExecutorAddress> BootstrapSymbols,
+         TransportTCtorArgTs &&...TransportTCtorArgs) {
+    auto SREPCServer = std::make_unique<SimpleRemoteEPCServer>();
+    SREPCServer->D = std::move(D);
+    SREPCServer->ReportError = [](Error Err) {
+      logAllUnhandledErrors(std::move(Err), errs(), "SimpleRemoteEPCServer ");
+    };
+    auto T = TransportT::Create(
+        *SREPCServer, std::forward<TransportTCtorArgTs>(TransportTCtorArgs)...);
+    if (!T)
+      return T.takeError();
+    SREPCServer->T = std::move(*T);
+    if (auto Err = SREPCServer->sendSetupMessage(std::move(BootstrapSymbols)))
+      return std::move(Err);
+    return std::move(SREPCServer);
+  }
+
+  /// Set an error reporter for this server.
+  void setErrorReporter(ReportErrorFunction ReportError) {
+    this->ReportError = std::move(ReportError);
+  }
+
+  /// Call to handle an incoming message.
+  ///
+  /// Returns 'Disconnect' if the message is a 'detach' message from the remote
+  /// otherwise returns 'Continue'. If the server has moved to an error state,
+  /// returns an error, which should be reported and treated as a 'Disconnect'.
+  Expected<HandleMessageAction>
+  handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
+                ExecutorAddress TagAddr,
+                SimpleRemoteEPCArgBytesVector ArgBytes) override;
+
+  Error waitForDisconnect();
+
+  void handleDisconnect(Error Err) override;
+
+private:
+  Error sendSetupMessage(StringMap<ExecutorAddress> BootstrapSymbols);
+
+  Error handleResult(uint64_t SeqNo, ExecutorAddress TagAddr,
+                     SimpleRemoteEPCArgBytesVector ArgBytes);
+  void handleCallWrapper(uint64_t RemoteSeqNo, ExecutorAddress TagAddr,
+                         SimpleRemoteEPCArgBytesVector ArgBytes);
+
+  static shared::detail::CWrapperFunctionResult
+  loadDylibWrapper(const char *ArgData, size_t ArgSize);
+
+  static shared::detail::CWrapperFunctionResult
+  lookupSymbolsWrapper(const char *ArgData, size_t ArgSize);
+
+  Expected<tpctypes::DylibHandle> loadDylib(const std::string &Path,
+                                            uint64_t Mode);
+
+  Expected<std::vector<std::vector<ExecutorAddress>>>
+  lookupSymbols(const std::vector<RemoteSymbolLookup> &L);
+
+  shared::WrapperFunctionResult
+  doJITDispatch(const void *FnTag, const char *ArgData, size_t ArgSize);
+
+  static shared::detail::CWrapperFunctionResult
+  jitDispatchEntry(void *DispatchCtx, const void *FnTag, const char *ArgData,
+                   size_t ArgSize);
+
+  uint64_t getNextSeqNo() { return NextSeqNo++; }
+  void releaseSeqNo(uint64_t) {}
+
+  using PendingJITDispatchResultsMap =
+      DenseMap<uint64_t, std::promise<shared::WrapperFunctionResult> *>;
+
+  std::mutex ServerStateMutex;
+  std::condition_variable ShutdownCV;
+  enum { ServerRunning, ServerShuttingDown, ServerShutDown } RunState;
+  Error ShutdownErr = Error::success();
+  std::unique_ptr<SimpleRemoteEPCTransport> T;
+  std::unique_ptr<Dispatcher> D;
+  ReportErrorFunction ReportError;
+
+  uint64_t NextSeqNo = 0;
+  PendingJITDispatchResultsMap PendingJITDispatchResults;
+  std::vector<sys::DynamicLibrary> Dylibs;
+};
+
+} // end namespace orc
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_SIMPLEREMOTEEPCSERVER_H

diff  --git a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
index 982a93cc60c0b..8e8e0d3ea7941 100644
--- a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
@@ -8,7 +8,6 @@ add_llvm_component_library(LLVMOrcJIT
   EPCDebugObjectRegistrar.cpp
   EPCEHFrameRegistrar.cpp
   EPCGenericJITLinkMemoryManager.cpp
-  EPCGenericMemoryAccess.cpp
   EPCIndirectionUtils.cpp
   ExecutionUtils.cpp
   IndirectionUtils.cpp
@@ -27,6 +26,7 @@ add_llvm_component_library(LLVMOrcJIT
   OrcABISupport.cpp
   OrcV2CBindings.cpp
   RTDyldObjectLinkingLayer.cpp
+  SimpleRemoteEPC.cpp
   Speculation.cpp
   SpeculateAnalyses.cpp
   ExecutorProcessControl.cpp

diff  --git a/llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp b/llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp
index ac6de224c9c5f..5303b7cf2e5a5 100644
--- a/llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp
@@ -86,32 +86,6 @@ class EPCGenericJITLinkMemoryManager::Alloc
   SegInfoMap Segs;
 };
 
-/// Create from a ExecutorProcessControl instance.
-Expected<std::unique_ptr<EPCGenericJITLinkMemoryManager>>
-EPCGenericJITLinkMemoryManager::CreateUsingOrcRTFuncs(
-    ExecutorProcessControl &EPC) {
-
-  auto H = EPC.loadDylib("");
-  if (!H)
-    return H.takeError();
-
-  StringRef GlobalPrefix = "";
-  if (EPC.getTargetTriple().isOSBinFormatMachO())
-    GlobalPrefix = "_";
-
-  FuncAddrs FAs;
-  if (auto Err = lookupAndRecordAddrs(
-          EPC, *H,
-          {{EPC.intern((GlobalPrefix + "__orc_rt_reserve").str()), &FAs.Reserve},
-           {EPC.intern((GlobalPrefix + "__orc_rt_finalize").str()),
-            &FAs.Finalize},
-           {EPC.intern((GlobalPrefix + "__orc_rt_deallocate").str()),
-            &FAs.Deallocate}}))
-    return std::move(Err);
-
-  return std::make_unique<EPCGenericJITLinkMemoryManager>(EPC, std::move(FAs));
-}
-
 Expected<std::unique_ptr<jitlink::JITLinkMemoryManager::Allocation>>
 EPCGenericJITLinkMemoryManager::allocate(const jitlink::JITLinkDylib *JD,
                                          const SegmentsRequestMap &Request) {

diff  --git a/llvm/lib/ExecutionEngine/Orc/EPCGenericMemoryAccess.cpp b/llvm/lib/ExecutionEngine/Orc/EPCGenericMemoryAccess.cpp
deleted file mode 100644
index e4ecd479e22d2..0000000000000
--- a/llvm/lib/ExecutionEngine/Orc/EPCGenericMemoryAccess.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-//===----- 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(ExecutorProcessControl &EPC) {
-
-  auto H = EPC.loadDylib("");
-  if (!H)
-    return H.takeError();
-
-  StringRef GlobalPrefix = "";
-  if (EPC.getTargetTriple().isOSBinFormatMachO())
-    GlobalPrefix = "_";
-
-  FuncAddrs FAs;
-  if (auto Err = lookupAndRecordAddrs(
-          EPC, *H,
-          {{EPC.intern((GlobalPrefix + "__orc_rt_write_uint8s_wrapper").str()),
-            &FAs.WriteUInt8s},
-           {EPC.intern((GlobalPrefix + "__orc_rt_write_uint16s_wrapper").str()),
-            &FAs.WriteUInt16s},
-           {EPC.intern((GlobalPrefix + "__orc_rt_write_uint32s_wrapper").str()),
-            &FAs.WriteUInt32s},
-           {EPC.intern((GlobalPrefix + "__orc_rt_write_uint64s_wrapper").str()),
-            &FAs.WriteUInt64s},
-           {EPC.intern((GlobalPrefix + "__orc_rt_write_buffers_wrapper").str()),
-            &FAs.WriteBuffers}}))
-    return std::move(Err);
-
-  return std::make_unique<EPCGenericMemoryAccess>(EPC, std::move(FAs));
-}
-
-} // end namespace orc
-} // end namespace llvm

diff  --git a/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt
index dddfda1a89539..74805720678c9 100644
--- a/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_llvm_component_library(LLVMOrcShared
   OrcError.cpp
   RPCError.cpp
+  SimpleRemoteEPCUtils.cpp
   ADDITIONAL_HEADER_DIRS
   ${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc
 

diff  --git a/llvm/lib/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.cpp
new file mode 100644
index 0000000000000..a9a77ae688318
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.cpp
@@ -0,0 +1,241 @@
+//===------ SimpleRemoteEPCUtils.cpp - Utils for Simple Remote EPC --------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Message definitions and other utilities for SimpleRemoteEPC and
+// SimpleRemoteEPCServer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+namespace {
+
+struct FDMsgHeader {
+  static constexpr unsigned MsgSizeOffset = 0;
+  static constexpr unsigned OpCOffset = MsgSizeOffset + sizeof(uint64_t);
+  static constexpr unsigned SeqNoOffset = OpCOffset + sizeof(uint64_t);
+  static constexpr unsigned TagAddrOffset = SeqNoOffset + sizeof(uint64_t);
+  static constexpr unsigned Size = TagAddrOffset + sizeof(uint64_t);
+};
+
+} // namespace
+
+namespace llvm {
+namespace orc {
+namespace SimpleRemoteEPCDefaultBootstrapSymbolNames {
+
+const char *ExecutorSessionObjectName =
+    "__llvm_orc_SimpleRemoteEPC_dispatch_ctx";
+const char *DispatchFnName = "__llvm_orc_SimpleRemoteEPC_dispatch_fn";
+
+} // end namespace SimpleRemoteEPCDefaultBootstrapSymbolNames
+
+SimpleRemoteEPCTransportClient::~SimpleRemoteEPCTransportClient() {}
+SimpleRemoteEPCTransport::~SimpleRemoteEPCTransport() {}
+
+Expected<std::unique_ptr<FDSimpleRemoteEPCTransport>>
+FDSimpleRemoteEPCTransport::Create(SimpleRemoteEPCTransportClient &C, int InFD,
+                                   int OutFD) {
+  if (InFD == -1)
+    return make_error<StringError>("Invalid input file descriptor " +
+                                       Twine(InFD),
+                                   inconvertibleErrorCode());
+  if (OutFD == -1)
+    return make_error<StringError>("Invalid output file descriptor " +
+                                       Twine(OutFD),
+                                   inconvertibleErrorCode());
+  std::unique_ptr<FDSimpleRemoteEPCTransport> FDT(
+      new FDSimpleRemoteEPCTransport(C, InFD, OutFD));
+  return FDT;
+}
+
+FDSimpleRemoteEPCTransport::FDSimpleRemoteEPCTransport(
+    SimpleRemoteEPCTransportClient &C, int InFD, int OutFD)
+    : C(C), InFD(InFD), OutFD(OutFD) {
+  ListenerThread = std::thread([this]() { listenLoop(); });
+}
+
+FDSimpleRemoteEPCTransport::~FDSimpleRemoteEPCTransport() {
+  ListenerThread.join();
+}
+
+Error FDSimpleRemoteEPCTransport::sendMessage(SimpleRemoteEPCOpcode OpC,
+                                              uint64_t SeqNo,
+                                              ExecutorAddress TagAddr,
+                                              ArrayRef<char> ArgBytes) {
+  char HeaderBuffer[FDMsgHeader::Size];
+
+  *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::MsgSizeOffset)) =
+      FDMsgHeader::Size + ArgBytes.size();
+  *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::OpCOffset)) =
+      static_cast<uint64_t>(OpC);
+  *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::SeqNoOffset)) = SeqNo;
+  *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::TagAddrOffset)) =
+      TagAddr.getValue();
+
+  std::lock_guard<std::mutex> Lock(M);
+  if (OutFD == -1)
+    return make_error<StringError>("FD-transport disconnected",
+                                   inconvertibleErrorCode());
+  if (int ErrNo = writeBytes(HeaderBuffer, FDMsgHeader::Size))
+    return errorCodeToError(std::error_code(ErrNo, std::generic_category()));
+  if (int ErrNo = writeBytes(ArgBytes.data(), ArgBytes.size()))
+    return errorCodeToError(std::error_code(ErrNo, std::generic_category()));
+  return Error::success();
+}
+
+void FDSimpleRemoteEPCTransport::disconnect() {
+  int CloseInFD = -1, CloseOutFD = -1;
+  {
+    std::lock_guard<std::mutex> Lock(M);
+    std::swap(InFD, CloseInFD);
+    std::swap(OutFD, CloseOutFD);
+  }
+
+  // If CloseOutFD == CloseInFD then set CloseOutFD to -1 up-front so that we
+  // don't double-close.
+  if (CloseOutFD == CloseInFD)
+    CloseOutFD = -1;
+
+  // Close InFD.
+  if (CloseInFD != -1)
+    while (close(CloseInFD) == -1) {
+      if (errno == EBADF)
+        break;
+    }
+
+  // Close OutFD.
+  if (CloseOutFD != -1) {
+    while (close(CloseOutFD) == -1) {
+      if (errno == EBADF)
+        break;
+    }
+  }
+}
+
+static Error makeUnexpectedEOFError() {
+  return make_error<StringError>("Unexpected end-of-file",
+                                 inconvertibleErrorCode());
+}
+
+Error FDSimpleRemoteEPCTransport::readBytes(char *Dst, size_t Size,
+                                            bool *IsEOF) {
+  assert(Dst && "Attempt to read into null.");
+  ssize_t Completed = 0;
+  while (Completed < static_cast<ssize_t>(Size)) {
+    ssize_t Read = ::read(InFD, Dst + Completed, Size - Completed);
+    if (Read <= 0) {
+      auto ErrNo = errno;
+      if (Read == 0) {
+        if (Completed == 0 && IsEOF) {
+          *IsEOF = true;
+          return Error::success();
+        } else
+          return makeUnexpectedEOFError();
+      } else if (ErrNo == EAGAIN || ErrNo == EINTR)
+        continue;
+      else {
+        std::lock_guard<std::mutex> Lock(M);
+        if (InFD == -1 && IsEOF) { // Disconnected locally. Pretend this is EOF.
+          *IsEOF = true;
+          return Error::success();
+        }
+        return errorCodeToError(
+            std::error_code(ErrNo, std::generic_category()));
+      }
+    }
+    Completed += Read;
+  }
+  return Error::success();
+}
+
+int FDSimpleRemoteEPCTransport::writeBytes(const char *Src, size_t Size) {
+  assert(Src && "Attempt to append from null.");
+  ssize_t Completed = 0;
+  while (Completed < static_cast<ssize_t>(Size)) {
+    ssize_t Written = ::write(OutFD, Src + Completed, Size - Completed);
+    if (Written < 0) {
+      auto ErrNo = errno;
+      if (ErrNo == EAGAIN || ErrNo == EINTR)
+        continue;
+      else
+        return ErrNo;
+    }
+    Completed += Written;
+  }
+  return 0;
+}
+
+void FDSimpleRemoteEPCTransport::listenLoop() {
+  Error Err = Error::success();
+  do {
+
+    char HeaderBuffer[FDMsgHeader::Size];
+    // Read the header buffer.
+    {
+      bool IsEOF;
+      if (auto Err2 = readBytes(HeaderBuffer, FDMsgHeader::Size, &IsEOF)) {
+        Err = joinErrors(std::move(Err), std::move(Err2));
+        break;
+      }
+      if (IsEOF)
+        break;
+    }
+
+    // Decode header buffer.
+    uint64_t MsgSize;
+    SimpleRemoteEPCOpcode OpC;
+    uint64_t SeqNo;
+    ExecutorAddress TagAddr;
+
+    MsgSize =
+        *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::MsgSizeOffset));
+    OpC = static_cast<SimpleRemoteEPCOpcode>(static_cast<uint64_t>(
+        *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::OpCOffset))));
+    SeqNo =
+        *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::SeqNoOffset));
+    TagAddr.setValue(
+        *((support::ulittle64_t *)(HeaderBuffer + FDMsgHeader::TagAddrOffset)));
+
+    if (MsgSize < FDMsgHeader::Size) {
+      Err = joinErrors(std::move(Err),
+                       make_error<StringError>("Mesasge size too small",
+                                               inconvertibleErrorCode()));
+      break;
+    }
+
+    // Read the argument bytes.
+    SimpleRemoteEPCArgBytesVector ArgBytes;
+    ArgBytes.resize(MsgSize - FDMsgHeader::Size);
+    if (auto Err2 = readBytes(ArgBytes.data(), ArgBytes.size())) {
+      Err = joinErrors(std::move(Err), std::move(Err2));
+      break;
+    }
+
+    if (auto Action = C.handleMessage(OpC, SeqNo, TagAddr, ArgBytes)) {
+      if (*Action == SimpleRemoteEPCTransportClient::EndSession)
+        break;
+    } else {
+      Err = joinErrors(std::move(Err), Action.takeError());
+      break;
+    }
+  } while (true);
+
+  C.handleDisconnect(std::move(Err));
+}
+
+} // end namespace orc
+} // end namespace llvm

diff  --git a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
new file mode 100644
index 0000000000000..d60a6a2d06904
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
@@ -0,0 +1,324 @@
+//===------- SimpleRemoteEPC.cpp -- Simple remote executor control --------===//
+//
+// 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/SimpleRemoteEPC.h"
+#include "llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#define DEBUG_TYPE "orc"
+
+namespace llvm {
+namespace orc {
+namespace shared {
+
+template <>
+class SPSSerializationTraits<SPSRemoteSymbolLookupSetElement,
+                             SymbolLookupSet::value_type> {
+public:
+  static size_t size(const SymbolLookupSet::value_type &V) {
+    return SPSArgList<SPSString, bool>::size(
+        *V.first, V.second == SymbolLookupFlags::RequiredSymbol);
+  }
+
+  static bool serialize(SPSOutputBuffer &OB,
+                        const SymbolLookupSet::value_type &V) {
+    return SPSArgList<SPSString, bool>::serialize(
+        OB, *V.first, V.second == SymbolLookupFlags::RequiredSymbol);
+  }
+};
+
+template <>
+class TrivialSPSSequenceSerialization<SPSRemoteSymbolLookupSetElement,
+                                      SymbolLookupSet> {
+public:
+  static constexpr bool available = true;
+};
+
+template <>
+class SPSSerializationTraits<SPSRemoteSymbolLookup,
+                             ExecutorProcessControl::LookupRequest> {
+  using MemberSerialization =
+      SPSArgList<SPSExecutorAddress, SPSRemoteSymbolLookupSet>;
+
+public:
+  static size_t size(const ExecutorProcessControl::LookupRequest &LR) {
+    return MemberSerialization::size(ExecutorAddress(LR.Handle), LR.Symbols);
+  }
+
+  static bool serialize(SPSOutputBuffer &OB,
+                        const ExecutorProcessControl::LookupRequest &LR) {
+    return MemberSerialization::serialize(OB, ExecutorAddress(LR.Handle),
+                                          LR.Symbols);
+  }
+};
+
+} // end namespace shared
+
+SimpleRemoteEPC::~SimpleRemoteEPC() {
+  assert(Disconnected && "Destroyed without disconnection");
+}
+
+Error SimpleRemoteEPC::setup(std::unique_ptr<SimpleRemoteEPCTransport> T,
+                             const SimpleRemoteEPCExecutorInfo &EI) {
+  using namespace SimpleRemoteEPCDefaultBootstrapSymbolNames;
+  LLVM_DEBUG({
+    dbgs() << "SimpleRemoteEPC received setup message:\n"
+           << "  Triple: " << EI.TargetTriple << "\n"
+           << "  Page size: " << EI.PageSize << "\n"
+           << "  Bootstrap symbols:\n";
+    for (const auto &KV : EI.BootstrapSymbols)
+      dbgs() << "    " << KV.first() << ": "
+             << formatv("{0:x16}", KV.second.getValue()) << "\n";
+  });
+  this->T = std::move(T);
+  TargetTriple = Triple(EI.TargetTriple);
+  PageSize = EI.PageSize;
+
+  if (auto Err = EI.getBootstrapSymbols(
+          {{JDI.JITDispatchContextAddress, ExecutorSessionObjectName},
+           {JDI.JITDispatchFunctionAddress, DispatchFnName},
+           {LoadDylibAddr, "__llvm_orc_load_dylib"},
+           {LookupSymbolsAddr, "__llvm_orc_lookup_symbols"},
+           {RunAsMainAddr, "__llvm_orc_run_as_main"}}))
+    return Err;
+
+  if (!MemMgr)
+    if (auto Err = setupDefaultMemoryManager(EI))
+      return Err;
+  if (!MemAccess)
+    if (auto Err = setupDefaultMemoryAccess(EI))
+      return Err;
+
+  return Error::success();
+}
+
+Expected<tpctypes::DylibHandle>
+SimpleRemoteEPC::loadDylib(const char *DylibPath) {
+  Expected<tpctypes::DylibHandle> H((tpctypes::DylibHandle()));
+  if (auto Err = callSPSWrapper<shared::SPSLoadDylibSignature>(
+          LoadDylibAddr.getValue(), H, JDI.JITDispatchContextAddress,
+          StringRef(DylibPath), (uint64_t)0))
+    return std::move(Err);
+  return H;
+}
+
+Expected<std::vector<tpctypes::LookupResult>>
+SimpleRemoteEPC::lookupSymbols(ArrayRef<LookupRequest> Request) {
+  Expected<std::vector<tpctypes::LookupResult>> R(
+      (std::vector<tpctypes::LookupResult>()));
+
+  if (auto Err = callSPSWrapper<shared::SPSLookupSymbolsSignature>(
+          LookupSymbolsAddr.getValue(), R, JDI.JITDispatchContextAddress,
+          Request))
+    return std::move(Err);
+  return R;
+}
+
+Expected<int32_t> SimpleRemoteEPC::runAsMain(JITTargetAddress MainFnAddr,
+                                             ArrayRef<std::string> Args) {
+  int64_t Result = 0;
+  if (auto Err = callSPSWrapper<shared::SPSRunAsMainSignature>(
+          RunAsMainAddr.getValue(), Result, ExecutorAddress(MainFnAddr), Args))
+    return std::move(Err);
+  return Result;
+}
+
+void SimpleRemoteEPC::callWrapperAsync(SendResultFunction OnComplete,
+                                       JITTargetAddress WrapperFnAddr,
+                                       ArrayRef<char> ArgBuffer) {
+  uint64_t SeqNo;
+  {
+    std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
+    SeqNo = getNextSeqNo();
+    assert(!PendingCallWrapperResults.count(SeqNo) && "SeqNo already in use");
+    PendingCallWrapperResults[SeqNo] = std::move(OnComplete);
+  }
+
+  if (auto Err = T->sendMessage(SimpleRemoteEPCOpcode::CallWrapper, SeqNo,
+                                ExecutorAddress(WrapperFnAddr), ArgBuffer)) {
+    getExecutionSession().reportError(std::move(Err));
+  }
+}
+
+Error SimpleRemoteEPC::disconnect() {
+  Disconnected = true;
+  T->disconnect();
+  return Error::success();
+}
+
+Expected<SimpleRemoteEPCTransportClient::HandleMessageAction>
+SimpleRemoteEPC::handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
+                               ExecutorAddress TagAddr,
+                               SimpleRemoteEPCArgBytesVector ArgBytes) {
+  using UT = std::underlying_type_t<SimpleRemoteEPCOpcode>;
+  if (static_cast<UT>(OpC) < static_cast<UT>(SimpleRemoteEPCOpcode::FirstOpC) ||
+      static_cast<UT>(OpC) > static_cast<UT>(SimpleRemoteEPCOpcode::LastOpC))
+    return make_error<StringError>("Unexpected opcode",
+                                   inconvertibleErrorCode());
+
+  switch (OpC) {
+  case SimpleRemoteEPCOpcode::Setup:
+    if (auto Err = handleSetup(SeqNo, TagAddr, std::move(ArgBytes)))
+      return std::move(Err);
+    break;
+  case SimpleRemoteEPCOpcode::Hangup:
+    // FIXME: Put EPC into 'detached' state.
+    return SimpleRemoteEPCTransportClient::EndSession;
+  case SimpleRemoteEPCOpcode::Result:
+    if (auto Err = handleResult(SeqNo, TagAddr, std::move(ArgBytes)))
+      return std::move(Err);
+    break;
+  case SimpleRemoteEPCOpcode::CallWrapper:
+    handleCallWrapper(SeqNo, TagAddr, std::move(ArgBytes));
+    break;
+  }
+  return ContinueSession;
+}
+
+void SimpleRemoteEPC::handleDisconnect(Error Err) {
+  PendingCallWrapperResultsMap TmpPending;
+
+  {
+    std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
+    std::swap(TmpPending, PendingCallWrapperResults);
+  }
+
+  for (auto &KV : TmpPending)
+    KV.second(
+        shared::WrapperFunctionResult::createOutOfBandError("disconnecting"));
+
+  if (Err) {
+    // FIXME: Move ReportError to EPC.
+    if (ES)
+      ES->reportError(std::move(Err));
+    else
+      logAllUnhandledErrors(std::move(Err), errs(), "SimpleRemoteEPC: ");
+  }
+}
+
+void SimpleRemoteEPC::setMemoryManager(
+    std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr) {
+  OwnedMemMgr = std::move(MemMgr);
+  this->MemMgr = OwnedMemMgr.get();
+}
+
+void SimpleRemoteEPC::setMemoryAccess(std::unique_ptr<MemoryAccess> MemAccess) {
+  OwnedMemAccess = std::move(MemAccess);
+  this->MemAccess = OwnedMemAccess.get();
+}
+
+Error SimpleRemoteEPC::setupDefaultMemoryManager(
+    const SimpleRemoteEPCExecutorInfo &EI) {
+
+  EPCGenericJITLinkMemoryManager::FuncAddrs FAs;
+
+  if (auto Err = EI.getBootstrapSymbols(
+          {{FAs.Reserve, "__llvm_orc_memory_reserve"},
+           {FAs.Finalize, "__llvm_orc_memory_finalize"},
+           {FAs.Deallocate, "__llvm_orc_memory_deallocate"}}))
+    return Err;
+
+  setMemoryManager(
+      std::make_unique<EPCGenericJITLinkMemoryManager>(*this, FAs));
+  return Error::success();
+}
+
+Error SimpleRemoteEPC::setupDefaultMemoryAccess(
+    const SimpleRemoteEPCExecutorInfo &EI) {
+
+  return Error::success();
+}
+
+Error SimpleRemoteEPC::handleSetup(uint64_t SeqNo, ExecutorAddress TagAddr,
+                                   SimpleRemoteEPCArgBytesVector ArgBytes) {
+  if (SeqNo != 0)
+    return make_error<StringError>("Setup packet SeqNo not zero",
+                                   inconvertibleErrorCode());
+
+  if (TagAddr)
+    return make_error<StringError>("Setup packet TagAddr not zero",
+                                   inconvertibleErrorCode());
+
+  std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
+  auto I = PendingCallWrapperResults.find(0);
+  assert(PendingCallWrapperResults.size() == 1 &&
+         I != PendingCallWrapperResults.end() &&
+         "Setup message handler not connectly set up");
+  auto SetupMsgHandler = std::move(I->second);
+  PendingCallWrapperResults.erase(I);
+
+  auto WFR =
+      shared::WrapperFunctionResult::copyFrom(ArgBytes.data(), ArgBytes.size());
+  SetupMsgHandler(std::move(WFR));
+  return Error::success();
+}
+
+void SimpleRemoteEPC::prepareToReceiveSetupMessage(
+    std::promise<MSVCPExpected<SimpleRemoteEPCExecutorInfo>> &ExecInfoP) {
+  PendingCallWrapperResults[0] =
+      [&](shared::WrapperFunctionResult SetupMsgBytes) {
+        if (const char *ErrMsg = SetupMsgBytes.getOutOfBandError()) {
+          ExecInfoP.set_value(
+              make_error<StringError>(ErrMsg, inconvertibleErrorCode()));
+          return;
+        }
+        using SPSSerialize =
+            shared::SPSArgList<shared::SPSSimpleRemoteEPCExecutorInfo>;
+        shared::SPSInputBuffer IB(SetupMsgBytes.data(), SetupMsgBytes.size());
+        SimpleRemoteEPCExecutorInfo EI;
+        if (SPSSerialize::deserialize(IB, EI))
+          ExecInfoP.set_value(EI);
+        else
+          ExecInfoP.set_value(make_error<StringError>(
+              "Could not deserialize setup message", inconvertibleErrorCode()));
+      };
+}
+
+Error SimpleRemoteEPC::handleResult(uint64_t SeqNo, ExecutorAddress TagAddr,
+                                    SimpleRemoteEPCArgBytesVector ArgBytes) {
+  SendResultFunction SendResult;
+
+  if (TagAddr)
+    return make_error<StringError>("Unexpected TagAddr in result message",
+                                   inconvertibleErrorCode());
+
+  {
+    std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
+    auto I = PendingCallWrapperResults.find(SeqNo);
+    if (I == PendingCallWrapperResults.end())
+      return make_error<StringError>("No call for sequence number " +
+                                         Twine(SeqNo),
+                                     inconvertibleErrorCode());
+    SendResult = std::move(I->second);
+    PendingCallWrapperResults.erase(I);
+    releaseSeqNo(SeqNo);
+  }
+
+  auto WFR =
+      shared::WrapperFunctionResult::copyFrom(ArgBytes.data(), ArgBytes.size());
+  SendResult(std::move(WFR));
+  return Error::success();
+}
+
+void SimpleRemoteEPC::handleCallWrapper(
+    uint64_t RemoteSeqNo, ExecutorAddress TagAddr,
+    SimpleRemoteEPCArgBytesVector ArgBytes) {
+  assert(ES && "No ExecutionSession attached");
+  ES->runJITDispatchHandler(
+      [this, RemoteSeqNo](shared::WrapperFunctionResult WFR) {
+        if (auto Err =
+                T->sendMessage(SimpleRemoteEPCOpcode::Result, RemoteSeqNo,
+                               ExecutorAddress(), {WFR.data(), WFR.size()}))
+          getExecutionSession().reportError(std::move(Err));
+      },
+      TagAddr.getValue(), ArgBytes);
+}
+
+} // end namespace orc
+} // end namespace llvm

diff  --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
index b04c09c502445..548bf947974ab 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_llvm_component_library(LLVMOrcTargetProcess
   JITLoaderGDB.cpp
   RegisterEHFrames.cpp
+  SimpleRemoteEPCServer.cpp
   TargetExecutionUtils.cpp
 
   ADDITIONAL_HEADER_DIRS

diff  --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.cpp
new file mode 100644
index 0000000000000..e1b00630c3414
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.cpp
@@ -0,0 +1,402 @@
+//===------- SimpleEPCServer.cpp - EPC over simple abstract channel -------===//
+//
+// 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/TargetProcess/SimpleRemoteEPCServer.h"
+
+#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/Process.h"
+
+#define DEBUG_TYPE "orc"
+
+using namespace llvm::orc::shared;
+
+namespace llvm {
+namespace orc {
+
+static llvm::orc::shared::detail::CWrapperFunctionResult
+reserveWrapper(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<SPSOrcTargetProcessAllocate>::handle(
+             ArgData, ArgSize,
+             [](uint64_t Size) -> Expected<ExecutorAddress> {
+               std::error_code EC;
+               auto MB = sys::Memory::allocateMappedMemory(
+                   Size, 0, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
+               if (EC)
+                 return errorCodeToError(EC);
+               return ExecutorAddress::fromPtr(MB.base());
+             })
+      .release();
+}
+
+static llvm::orc::shared::detail::CWrapperFunctionResult
+finalizeWrapper(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<SPSOrcTargetProcessFinalize>::handle(
+             ArgData, ArgSize,
+             [](const tpctypes::FinalizeRequest &FR) -> Error {
+               for (auto &Seg : FR) {
+                 char *Mem = Seg.Addr.toPtr<char *>();
+                 memcpy(Mem, Seg.Content.data(), Seg.Content.size());
+                 memset(Mem + Seg.Content.size(), 0,
+                        Seg.Size - Seg.Content.size());
+                 assert(Seg.Size <= std::numeric_limits<size_t>::max());
+                 if (auto EC = sys::Memory::protectMappedMemory(
+                         {Mem, static_cast<size_t>(Seg.Size)},
+                         tpctypes::fromWireProtectionFlags(Seg.Prot)))
+                   return errorCodeToError(EC);
+                 if (Seg.Prot & tpctypes::WPF_Exec)
+                   sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);
+               }
+               return Error::success();
+             })
+      .release();
+}
+
+static llvm::orc::shared::detail::CWrapperFunctionResult
+deallocateWrapper(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<SPSOrcTargetProcessDeallocate>::handle(
+             ArgData, ArgSize,
+             [](ExecutorAddress Base, uint64_t Size) -> Error {
+               sys::MemoryBlock MB(Base.toPtr<void *>(), Size);
+               if (auto EC = sys::Memory::releaseMappedMemory(MB))
+                 return errorCodeToError(EC);
+               return Error::success();
+             })
+      .release();
+}
+
+template <typename WriteT, typename SPSWriteT>
+static llvm::orc::shared::detail::CWrapperFunctionResult
+writeUIntsWrapper(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();
+}
+
+static llvm::orc::shared::detail::CWrapperFunctionResult
+writeBuffersWrapper(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();
+}
+
+static llvm::orc::shared::detail::CWrapperFunctionResult
+runAsMainWrapper(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<SPSRunAsMainSignature>::handle(
+             ArgData, ArgSize,
+             [](ExecutorAddress MainAddr,
+                std::vector<std::string> Args) -> int64_t {
+               return runAsMain(MainAddr.toPtr<int (*)(int, char *[])>(), Args);
+             })
+      .release();
+}
+
+SimpleRemoteEPCServer::Dispatcher::~Dispatcher() {}
+
+void SimpleRemoteEPCServer::ThreadDispatcher::dispatch(
+    unique_function<void()> Work) {
+  {
+    std::lock_guard<std::mutex> Lock(DispatchMutex);
+    if (!Running)
+      return;
+    ++Outstanding;
+  }
+
+  std::thread([this, Work = std::move(Work)]() mutable {
+    Work();
+    std::lock_guard<std::mutex> Lock(DispatchMutex);
+    --Outstanding;
+    OutstandingCV.notify_all();
+  }).detach();
+}
+
+void SimpleRemoteEPCServer::ThreadDispatcher::shutdown() {
+  std::unique_lock<std::mutex> Lock(DispatchMutex);
+  Running = false;
+  OutstandingCV.wait(Lock, [this]() { return Outstanding == 0; });
+}
+
+StringMap<ExecutorAddress> SimpleRemoteEPCServer::defaultBootstrapSymbols() {
+  StringMap<ExecutorAddress> DBS;
+
+  DBS["__llvm_orc_memory_reserve"] = ExecutorAddress::fromPtr(&reserveWrapper);
+  DBS["__llvm_orc_memory_finalize"] =
+      ExecutorAddress::fromPtr(&finalizeWrapper);
+  DBS["__llvm_orc_memory_deallocate"] =
+      ExecutorAddress::fromPtr(&deallocateWrapper);
+  DBS["__llvm_orc_memory_write_uint8s"] = ExecutorAddress::fromPtr(
+      &writeUIntsWrapper<tpctypes::UInt8Write,
+                         shared::SPSMemoryAccessUInt8Write>);
+  DBS["__llvm_orc_memory_write_uint16s"] = ExecutorAddress::fromPtr(
+      &writeUIntsWrapper<tpctypes::UInt16Write,
+                         shared::SPSMemoryAccessUInt16Write>);
+  DBS["__llvm_orc_memory_write_uint32s"] = ExecutorAddress::fromPtr(
+      &writeUIntsWrapper<tpctypes::UInt32Write,
+                         shared::SPSMemoryAccessUInt32Write>);
+  DBS["__llvm_orc_memory_write_uint64s"] = ExecutorAddress::fromPtr(
+      &writeUIntsWrapper<tpctypes::UInt64Write,
+                         shared::SPSMemoryAccessUInt64Write>);
+  DBS["__llvm_orc_memory_write_buffers"] =
+      ExecutorAddress::fromPtr(&writeBuffersWrapper);
+  DBS["__llvm_orc_run_as_main"] = ExecutorAddress::fromPtr(&runAsMainWrapper);
+  DBS["__llvm_orc_load_dylib"] = ExecutorAddress::fromPtr(&loadDylibWrapper);
+  DBS["__llvm_orc_lookup_symbols"] =
+      ExecutorAddress::fromPtr(&lookupSymbolsWrapper);
+  return DBS;
+}
+
+Expected<SimpleRemoteEPCTransportClient::HandleMessageAction>
+SimpleRemoteEPCServer::handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
+                                     ExecutorAddress TagAddr,
+                                     SimpleRemoteEPCArgBytesVector ArgBytes) {
+  using UT = std::underlying_type_t<SimpleRemoteEPCOpcode>;
+  if (static_cast<UT>(OpC) < static_cast<UT>(SimpleRemoteEPCOpcode::FirstOpC) ||
+      static_cast<UT>(OpC) > static_cast<UT>(SimpleRemoteEPCOpcode::LastOpC))
+    return make_error<StringError>("Unexpected opcode",
+                                   inconvertibleErrorCode());
+
+  // TODO: Clean detach message?
+  switch (OpC) {
+  case SimpleRemoteEPCOpcode::Setup:
+    return make_error<StringError>("Unexpected Setup opcode",
+                                   inconvertibleErrorCode());
+  case SimpleRemoteEPCOpcode::Hangup:
+    return SimpleRemoteEPCTransportClient::EndSession;
+  case SimpleRemoteEPCOpcode::Result:
+    if (auto Err = handleResult(SeqNo, TagAddr, std::move(ArgBytes)))
+      return std::move(Err);
+    break;
+  case SimpleRemoteEPCOpcode::CallWrapper:
+    handleCallWrapper(SeqNo, TagAddr, std::move(ArgBytes));
+    break;
+  }
+  return ContinueSession;
+}
+
+Error SimpleRemoteEPCServer::waitForDisconnect() {
+  std::unique_lock<std::mutex> Lock(ServerStateMutex);
+  ShutdownCV.wait(Lock, [this]() { return RunState == ServerShutDown; });
+  return std::move(ShutdownErr);
+}
+
+void SimpleRemoteEPCServer::handleDisconnect(Error Err) {
+  PendingJITDispatchResultsMap TmpPending;
+
+  {
+    std::lock_guard<std::mutex> Lock(ServerStateMutex);
+    std::swap(TmpPending, PendingJITDispatchResults);
+    RunState = ServerShuttingDown;
+  }
+
+  // Send out-of-band errors to any waiting threads.
+  for (auto &KV : TmpPending)
+    KV.second->set_value(
+        shared::WrapperFunctionResult::createOutOfBandError("disconnecting"));
+
+  // TODO: Free attached resources.
+  // 1. Close libraries in DylibHandles.
+
+  // Wait for dispatcher to clear.
+  D->shutdown();
+
+  std::lock_guard<std::mutex> Lock(ServerStateMutex);
+  ShutdownErr = joinErrors(std::move(ShutdownErr), std::move(Err));
+  RunState = ServerShutDown;
+  ShutdownCV.notify_all();
+}
+
+Error SimpleRemoteEPCServer::sendSetupMessage(
+    StringMap<ExecutorAddress> BootstrapSymbols) {
+
+  using namespace SimpleRemoteEPCDefaultBootstrapSymbolNames;
+
+  std::vector<char> SetupPacket;
+  SimpleRemoteEPCExecutorInfo EI;
+  EI.TargetTriple = sys::getProcessTriple();
+  if (auto PageSize = sys::Process::getPageSize())
+    EI.PageSize = *PageSize;
+  else
+    return PageSize.takeError();
+  EI.BootstrapSymbols = std::move(BootstrapSymbols);
+
+  assert(!EI.BootstrapSymbols.count(ExecutorSessionObjectName) &&
+         "Dispatch context name should not be set");
+  assert(!EI.BootstrapSymbols.count(DispatchFnName) &&
+         "Dispatch function name should not be set");
+  EI.BootstrapSymbols[ExecutorSessionObjectName] =
+      ExecutorAddress::fromPtr(this);
+  EI.BootstrapSymbols[DispatchFnName] =
+      ExecutorAddress::fromPtr(jitDispatchEntry);
+
+  using SPSSerialize =
+      shared::SPSArgList<shared::SPSSimpleRemoteEPCExecutorInfo>;
+  auto SetupPacketBytes =
+      shared::WrapperFunctionResult::allocate(SPSSerialize::size(EI));
+  shared::SPSOutputBuffer OB(SetupPacketBytes.data(), SetupPacketBytes.size());
+  if (!SPSSerialize::serialize(OB, EI))
+    return make_error<StringError>("Could not send setup packet",
+                                   inconvertibleErrorCode());
+
+  return T->sendMessage(SimpleRemoteEPCOpcode::Setup, 0, ExecutorAddress(),
+                        {SetupPacketBytes.data(), SetupPacketBytes.size()});
+}
+
+Error SimpleRemoteEPCServer::handleResult(
+    uint64_t SeqNo, ExecutorAddress TagAddr,
+    SimpleRemoteEPCArgBytesVector ArgBytes) {
+  std::promise<shared::WrapperFunctionResult> *P = nullptr;
+  {
+    std::lock_guard<std::mutex> Lock(ServerStateMutex);
+    auto I = PendingJITDispatchResults.find(SeqNo);
+    if (I == PendingJITDispatchResults.end())
+      return make_error<StringError>("No call for sequence number " +
+                                         Twine(SeqNo),
+                                     inconvertibleErrorCode());
+    P = I->second;
+    PendingJITDispatchResults.erase(I);
+    releaseSeqNo(SeqNo);
+  }
+  auto R = shared::WrapperFunctionResult::allocate(ArgBytes.size());
+  memcpy(R.data(), ArgBytes.data(), ArgBytes.size());
+  P->set_value(std::move(R));
+  return Error::success();
+}
+
+void SimpleRemoteEPCServer::handleCallWrapper(
+    uint64_t RemoteSeqNo, ExecutorAddress TagAddr,
+    SimpleRemoteEPCArgBytesVector ArgBytes) {
+  D->dispatch([this, RemoteSeqNo, TagAddr, ArgBytes = std::move(ArgBytes)]() {
+    using WrapperFnTy =
+        shared::detail::CWrapperFunctionResult (*)(const char *, size_t);
+    auto *Fn = TagAddr.toPtr<WrapperFnTy>();
+    shared::WrapperFunctionResult ResultBytes(
+        Fn(ArgBytes.data(), ArgBytes.size()));
+    if (auto Err = T->sendMessage(SimpleRemoteEPCOpcode::Result, RemoteSeqNo,
+                                  ExecutorAddress(),
+                                  {ResultBytes.data(), ResultBytes.size()}))
+      ReportError(std::move(Err));
+  });
+}
+
+shared::detail::CWrapperFunctionResult
+SimpleRemoteEPCServer::loadDylibWrapper(const char *ArgData, size_t ArgSize) {
+  return shared::WrapperFunction<shared::SPSLoadDylibSignature>::handle(
+             ArgData, ArgSize,
+             [](ExecutorAddress ExecutorSessionObj, std::string Path,
+                uint64_t Flags) -> Expected<uint64_t> {
+               return ExecutorSessionObj.toPtr<SimpleRemoteEPCServer *>()
+                   ->loadDylib(Path, Flags);
+             })
+      .release();
+}
+
+shared::detail::CWrapperFunctionResult
+SimpleRemoteEPCServer::lookupSymbolsWrapper(const char *ArgData,
+                                            size_t ArgSize) {
+  return shared::WrapperFunction<shared::SPSLookupSymbolsSignature>::handle(
+             ArgData, ArgSize,
+             [](ExecutorAddress ExecutorSessionObj,
+                std::vector<RemoteSymbolLookup> Lookup) {
+               return ExecutorSessionObj.toPtr<SimpleRemoteEPCServer *>()
+                   ->lookupSymbols(Lookup);
+             })
+      .release();
+}
+
+Expected<tpctypes::DylibHandle>
+SimpleRemoteEPCServer::loadDylib(const std::string &Path, uint64_t Mode) {
+  std::string ErrMsg;
+  const char *P = Path.empty() ? nullptr : Path.c_str();
+  auto DL = sys::DynamicLibrary::getPermanentLibrary(P, &ErrMsg);
+  if (!DL.isValid())
+    return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode());
+  std::lock_guard<std::mutex> Lock(ServerStateMutex);
+  uint64_t Id = Dylibs.size();
+  Dylibs.push_back(std::move(DL));
+  return Id;
+}
+
+Expected<std::vector<std::vector<ExecutorAddress>>>
+SimpleRemoteEPCServer::lookupSymbols(const std::vector<RemoteSymbolLookup> &L) {
+  std::vector<std::vector<ExecutorAddress>> Result;
+
+  for (const auto &E : L) {
+    if (E.H >= Dylibs.size())
+      return make_error<StringError>("Unrecognized handle",
+                                     inconvertibleErrorCode());
+    auto &DL = Dylibs[E.H];
+    Result.push_back({});
+
+    for (const auto &Sym : E.Symbols) {
+
+      const char *DemangledSymName = Sym.Name.c_str();
+#ifdef __APPLE__
+      if (*DemangledSymName == '_')
+        ++DemangledSymName;
+#endif
+
+      void *Addr = DL.getAddressOfSymbol(DemangledSymName);
+      if (!Addr && Sym.Required)
+        return make_error<StringError>(Twine("Missing definition for ") +
+                                           DemangledSymName,
+                                       inconvertibleErrorCode());
+
+      Result.back().push_back(ExecutorAddress::fromPtr(Addr));
+    }
+  }
+
+  return std::move(Result);
+}
+
+shared::WrapperFunctionResult
+SimpleRemoteEPCServer::doJITDispatch(const void *FnTag, const char *ArgData,
+                                     size_t ArgSize) {
+  uint64_t SeqNo;
+  std::promise<shared::WrapperFunctionResult> ResultP;
+  auto ResultF = ResultP.get_future();
+  {
+    std::lock_guard<std::mutex> Lock(ServerStateMutex);
+    if (RunState != ServerRunning)
+      return shared::WrapperFunctionResult::createOutOfBandError(
+          "jit_dispatch not available (EPC server shut down)");
+
+    SeqNo = getNextSeqNo();
+    assert(!PendingJITDispatchResults.count(SeqNo) && "SeqNo already in use");
+    PendingJITDispatchResults[SeqNo] = &ResultP;
+  }
+
+  if (auto Err =
+          T->sendMessage(SimpleRemoteEPCOpcode::CallWrapper, SeqNo,
+                         ExecutorAddress::fromPtr(FnTag), {ArgData, ArgSize}))
+    ReportError(std::move(Err));
+
+  return ResultF.get();
+}
+
+shared::detail::CWrapperFunctionResult
+SimpleRemoteEPCServer::jitDispatchEntry(void *DispatchCtx, const void *FnTag,
+                                        const char *ArgData, size_t ArgSize) {
+  return reinterpret_cast<SimpleRemoteEPCServer *>(DispatchCtx)
+      ->doJITDispatch(FnTag, ArgData, ArgSize)
+      .release();
+}
+
+} // end namespace orc
+} // end namespace llvm

diff  --git a/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp
index 7f197a50c3902..bed443bc374b5 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp
@@ -13,8 +13,8 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h"
 #include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
-#include "llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h"
 #include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h"
 #include "llvm/Support/DynamicLibrary.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/MathExtras.h"
@@ -90,8 +90,6 @@ int openListener(std::string Host, std::string PortStr) {
   static constexpr int ConnectionQueueLen = 1;
   listen(SockFD, ConnectionQueueLen);
 
-  outs() << "Listening at " << Host << ":" << PortStr << "\n";
-
 #if defined(_AIX)
   assert(Hi_32(AI->ai_addrlen) == 0 && "Field is a size_t on 64-bit AIX");
   socklen_t AddrLen = Lo_32(AI->ai_addrlen);
@@ -133,24 +131,15 @@ int main(int argc, char *argv[]) {
                           "' is not a valid integer");
 
       InFD = OutFD = openListener(Host.str(), PortStr.str());
-      outs() << "Connection established. Running OrcRPCTPCServer...\n";
     } else
       printErrorAndExit("invalid specifier type \"" + SpecifierType + "\"");
   }
 
-  ExitOnErr.setBanner(std::string(argv[0]) + ":");
-
-  using JITLinkExecutorEndpoint =
-      shared::SingleThreadedRPCEndpoint<shared::FDRawByteChannel>;
-
-  shared::registerStringError<shared::FDRawByteChannel>();
-
-  shared::FDRawByteChannel C(InFD, OutFD);
-  JITLinkExecutorEndpoint EP(C, true);
-  OrcRPCTPCServer<JITLinkExecutorEndpoint> Server(EP);
-  Server.setProgramName(std::string("llvm-jitlink-executor"));
-
-  ExitOnErr(Server.run());
+  auto Server =
+      ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>(
+          std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>(),
+          SimpleRemoteEPCServer::defaultBootstrapSymbols(), InFD, OutFD));
 
+  ExitOnErr(Server->waitForDisconnect());
   return 0;
 }

diff  --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
index dd97bafbdfe2f..2e4b9cfc0d7f7 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -635,8 +635,7 @@ static Error loadDylibs(Session &S) {
   return Error::success();
 }
 
-Expected<std::unique_ptr<ExecutorProcessControl>>
-LLVMJITLinkRemoteExecutorProcessControl::LaunchExecutor() {
+static Expected<std::unique_ptr<ExecutorProcessControl>> launchExecutor() {
 #ifndef LLVM_ON_UNIX
   // FIXME: Add support for Windows.
   return make_error<StringError>("-" + OutOfProcessExecutor.ArgStr +
@@ -644,8 +643,6 @@ LLVMJITLinkRemoteExecutorProcessControl::LaunchExecutor() {
                                  inconvertibleErrorCode());
 #else
 
-  shared::registerStringError<LLVMJITLinkChannel>();
-
   constexpr int ReadEnd = 0;
   constexpr int WriteEnd = 1;
 
@@ -697,24 +694,8 @@ LLVMJITLinkRemoteExecutorProcessControl::LaunchExecutor() {
   close(ToExecutor[ReadEnd]);
   close(FromExecutor[WriteEnd]);
 
-  // Return an RPC channel connected to our end of the pipes.
-  auto SSP = std::make_shared<SymbolStringPool>();
-  auto Channel = std::make_unique<shared::FDRawByteChannel>(
+  return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
       FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
-  auto Endpoint = std::make_unique<LLVMJITLinkRPCEndpoint>(*Channel, true);
-
-  auto ReportError = [](Error Err) {
-    logAllUnhandledErrors(std::move(Err), errs(), "");
-  };
-
-  Error Err = Error::success();
-  std::unique_ptr<LLVMJITLinkRemoteExecutorProcessControl> REPC(
-      new LLVMJITLinkRemoteExecutorProcessControl(
-          std::move(SSP), std::move(Channel), std::move(Endpoint),
-          std::move(ReportError), Err));
-  if (Err)
-    return std::move(Err);
-  return std::move(REPC);
 #endif
 }
 
@@ -764,8 +745,7 @@ static Expected<int> connectTCPSocket(std::string Host, std::string PortStr) {
 }
 #endif
 
-Expected<std::unique_ptr<ExecutorProcessControl>>
-LLVMJITLinkRemoteExecutorProcessControl::ConnectToExecutor() {
+static Expected<std::unique_ptr<ExecutorProcessControl>> connectToExecutor() {
 #ifndef LLVM_ON_UNIX
   // FIXME: Add TCP support for Windows.
   return make_error<StringError>("-" + OutOfProcessExecutorConnect.ArgStr +
@@ -773,8 +753,6 @@ LLVMJITLinkRemoteExecutorProcessControl::ConnectToExecutor() {
                                  inconvertibleErrorCode());
 #else
 
-  shared::registerStringError<LLVMJITLinkChannel>();
-
   StringRef Host, PortStr;
   std::tie(Host, PortStr) = StringRef(OutOfProcessExecutorConnect).split(':');
   if (Host.empty())
@@ -794,37 +772,10 @@ LLVMJITLinkRemoteExecutorProcessControl::ConnectToExecutor() {
   if (!SockFD)
     return SockFD.takeError();
 
-  auto SSP = std::make_shared<SymbolStringPool>();
-  auto Channel = std::make_unique<shared::FDRawByteChannel>(*SockFD, *SockFD);
-  auto Endpoint = std::make_unique<LLVMJITLinkRPCEndpoint>(*Channel, true);
-
-  auto ReportError = [](Error Err) {
-    logAllUnhandledErrors(std::move(Err), errs(), "");
-  };
-
-  Error Err = Error::success();
-  std::unique_ptr<LLVMJITLinkRemoteExecutorProcessControl> REPC(
-      new LLVMJITLinkRemoteExecutorProcessControl(
-          std::move(SSP), std::move(Channel), std::move(Endpoint),
-          std::move(ReportError), Err));
-  if (Err)
-    return std::move(Err);
-  return std::move(REPC);
+  return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(*SockFD, *SockFD);
 #endif
 }
 
-Error LLVMJITLinkRemoteExecutorProcessControl::disconnect() {
-  std::promise<MSVCPError> P;
-  auto F = P.get_future();
-  auto Err = closeConnection([&](Error Err) -> Error {
-    P.set_value(std::move(Err));
-    Finished = true;
-    return Error::success();
-  });
-  ListenerThread.join();
-  return joinErrors(std::move(Err), F.get());
-}
-
 class PhonyExternalsGenerator : public DefinitionGenerator {
 public:
   Error tryToGenerate(LookupState &LS, LookupKind K, JITDylib &JD,
@@ -846,14 +797,13 @@ Expected<std::unique_ptr<Session>> Session::Create(Triple TT) {
   std::unique_ptr<ExecutorProcessControl> EPC;
   if (OutOfProcessExecutor.getNumOccurrences()) {
     /// If -oop-executor is passed then launch the executor.
-    if (auto REPC = LLVMJITLinkRemoteExecutorProcessControl::LaunchExecutor())
+    if (auto REPC = launchExecutor())
       EPC = std::move(*REPC);
     else
       return REPC.takeError();
   } else if (OutOfProcessExecutorConnect.getNumOccurrences()) {
     /// If -oop-executor-connect is passed then connect to the executor.
-    if (auto REPC =
-            LLVMJITLinkRemoteExecutorProcessControl::ConnectToExecutor())
+    if (auto REPC = connectToExecutor())
       EPC = std::move(*REPC);
     else
       return REPC.takeError();

diff  --git a/llvm/tools/llvm-jitlink/llvm-jitlink.h b/llvm/tools/llvm-jitlink/llvm-jitlink.h
index acb64a9a39ca3..0a4654a8c864a 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.h
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.h
@@ -19,9 +19,9 @@
 #include "llvm/ExecutionEngine/Orc/Core.h"
 #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
 #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
-#include "llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h"
 #include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h"
 #include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
+#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
 #include "llvm/ExecutionEngine/RuntimeDyldChecker.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/Regex.h"
@@ -48,66 +48,6 @@ class LLVMJITLinkObjectLinkingLayer : public orc::ObjectLinkingLayer {
   Session &S;
 };
 
-using LLVMJITLinkChannel = orc::shared::FDRawByteChannel;
-using LLVMJITLinkRPCEndpoint =
-    orc::shared::MultiThreadedRPCEndpoint<LLVMJITLinkChannel>;
-using LLVMJITLinkRemoteMemoryAccess =
-    orc::OrcRPCEPCMemoryAccess<LLVMJITLinkRPCEndpoint>;
-
-class LLVMJITLinkRemoteExecutorProcessControl
-    : public orc::OrcRPCExecutorProcessControlBase<LLVMJITLinkRPCEndpoint> {
-public:
-  using BaseT = orc::OrcRPCExecutorProcessControlBase<LLVMJITLinkRPCEndpoint>;
-  static Expected<std::unique_ptr<ExecutorProcessControl>> LaunchExecutor();
-
-  static Expected<std::unique_ptr<ExecutorProcessControl>> ConnectToExecutor();
-
-  Error disconnect() override;
-
-private:
-  using LLVMJITLinkRemoteMemoryAccess =
-      orc::OrcRPCEPCMemoryAccess<LLVMJITLinkRemoteExecutorProcessControl>;
-
-  using LLVMJITLinkRemoteMemoryManager = orc::OrcRPCEPCJITLinkMemoryManager<
-      LLVMJITLinkRemoteExecutorProcessControl>;
-
-  LLVMJITLinkRemoteExecutorProcessControl(
-      std::shared_ptr<orc::SymbolStringPool> SSP,
-      std::unique_ptr<LLVMJITLinkChannel> Channel,
-      std::unique_ptr<LLVMJITLinkRPCEndpoint> Endpoint,
-      ErrorReporter ReportError, Error &Err)
-      : BaseT(std::move(SSP), *Endpoint, std::move(ReportError)),
-        Channel(std::move(Channel)), Endpoint(std::move(Endpoint)) {
-    ErrorAsOutParameter _(&Err);
-
-    ListenerThread = std::thread([&]() {
-      while (!Finished) {
-        if (auto Err = this->Endpoint->handleOne()) {
-          reportError(std::move(Err));
-          return;
-        }
-      }
-    });
-
-    if (auto Err2 = initializeORCRPCEPCBase()) {
-      Err = joinErrors(std::move(Err2), disconnect());
-      return;
-    }
-
-    OwnedMemAccess = std::make_unique<LLVMJITLinkRemoteMemoryAccess>(*this);
-    MemAccess = OwnedMemAccess.get();
-    OwnedMemMgr = std::make_unique<LLVMJITLinkRemoteMemoryManager>(*this);
-    MemMgr = OwnedMemMgr.get();
-  }
-
-  std::unique_ptr<LLVMJITLinkChannel> Channel;
-  std::unique_ptr<LLVMJITLinkRPCEndpoint> Endpoint;
-  std::unique_ptr<ExecutorProcessControl::MemoryAccess> OwnedMemAccess;
-  std::unique_ptr<jitlink::JITLinkMemoryManager> OwnedMemMgr;
-  std::atomic<bool> Finished{false};
-  std::thread ListenerThread;
-};
-
 struct Session {
   orc::ExecutionSession ES;
   orc::JITDylib *MainJD = nullptr;


        


More information about the llvm-commits mailing list