[LLI] Replace the LLI remote-JIT support with the new ORC remote-JIT components.
[oota-llvm.git] / tools / lli / lli.cpp
index 9f714060c17a5b36aec8fad810d04d789626d69a..b83a1a86e0d325240cd1bf5abb07f6dd10b905b8 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
-#include "llvm/IR/LLVMContext.h"
 #include "OrcLazyJIT.h"
-#include "RemoteMemoryManager.h"
-#include "RemoteTarget.h"
-#include "RemoteTargetExternal.h"
+#include "RemoteJITUtils.h"
+#include "llvm/IR/LLVMContext.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/Bitcode/ReaderWriter.h"
 #include "llvm/CodeGen/LinkAllCodegenComponents.h"
@@ -28,6 +26,7 @@
 #include "llvm/ExecutionEngine/ObjectCache.h"
 #include "llvm/ExecutionEngine/OrcMCJITReplacement.h"
 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IR/Type.h"
@@ -449,7 +448,7 @@ int main(int argc, char **argv, char * const *envp) {
   RTDyldMemoryManager *RTDyldMM = nullptr;
   if (!ForceInterpreter) {
     if (RemoteMCJIT)
-      RTDyldMM = new RemoteMemoryManager();
+      RTDyldMM = new ForwardingMemoryManager();
     else
       RTDyldMM = new SectionMemoryManager();
 
@@ -582,6 +581,27 @@ int main(int argc, char **argv, char * const *envp) {
 
   int Result;
 
+  // Sanity check use of remote-jit: LLI currently only supports use of the
+  // remote JIT on Unix platforms.
+  // FIXME: Remove this pointless fallback mode which causes tests to "pass"
+  // on platforms where they should XFAIL.
+  if (RemoteMCJIT) {
+#ifndef LLVM_ON_UNIX
+    errs() << "Warning: host does not support external remote targets.\n"
+           << "  Defaulting to local execution execution\n";
+    RemoteMCJIT = false;
+#else
+    if (ChildExecPath.empty()) {
+      errs() << "-remote-mcjit requires -mcjit-remote-process.\n";
+      exit(1);
+    } else if (!sys::fs::can_execute(ChildExecPath)) {
+      errs() << "Unable to find usable child executable: '" << ChildExecPath
+             << "'\n";
+      return -1;
+    }
+#endif
+  }
+
   if (!RemoteMCJIT) {
     // If the program doesn't explicitly call exit, we will need the Exit
     // function later on to make an explicit call, so get the function now.
@@ -629,66 +649,123 @@ int main(int argc, char **argv, char * const *envp) {
     // Remote target MCJIT doesn't (yet) support static constructors. No reason
     // it couldn't. This is a limitation of the LLI implemantation, not the
     // MCJIT itself. FIXME.
-    //
-    RemoteMemoryManager *MM = static_cast<RemoteMemoryManager*>(RTDyldMM);
-    // Everything is prepared now, so lay out our program for the target
-    // address space, assign the section addresses to resolve any relocations,
-    // and send it to the target.
-
-    std::unique_ptr<RemoteTarget> Target;
-    if (!ChildExecPath.empty()) { // Remote execution on a child process
-#ifndef LLVM_ON_UNIX
-      // FIXME: Remove this pointless fallback mode which causes tests to "pass"
-      // on platforms where they should XFAIL.
-      errs() << "Warning: host does not support external remote targets.\n"
-             << "  Defaulting to simulated remote execution\n";
-      Target.reset(new RemoteTarget);
-#else
-      if (!sys::fs::can_execute(ChildExecPath)) {
-        errs() << "Unable to find usable child executable: '" << ChildExecPath
-               << "'\n";
-        return -1;
-      }
-      Target.reset(new RemoteTargetExternal(ChildExecPath));
-#endif
-    } else {
-      // No child process name provided, use simulated remote execution.
-      Target.reset(new RemoteTarget);
+
+    // Lanch the remote process and get a channel to it.
+    std::unique_ptr<FDRPCChannel> C = launchRemote();
+    if (!C) {
+      errs() << "Failed to launch remote JIT.\n";
+      exit(1);
     }
 
-    // Give the memory manager a pointer to our remote target interface object.
-    MM->setRemoteTarget(Target.get());
+    // Create a remote target client running over the channel.
+    typedef orc::remote::OrcRemoteTargetClient<orc::remote::RPCChannel> MyRemote;
+    ErrorOr<MyRemote> R = MyRemote::Create(*C);
+    if (!R) {
+      errs() << "Could not create remote: " << R.getError().message() << "\n";
+      exit(1);
+    }
 
-    // Create the remote target.
-    if (!Target->create()) {
-      errs() << "ERROR: " << Target->getErrorMsg() << "\n";
-      return EXIT_FAILURE;
+    // Create a remote memory manager.
+    std::unique_ptr<MyRemote::RCMemoryManager> RemoteMM;
+    if (auto EC = R->createRemoteMemoryManager(RemoteMM)) {
+      errs() << "Could not create remote memory manager: " << EC.message() << "\n";
+      exit(1);
     }
 
-    // Since we're executing in a (at least simulated) remote address space,
-    // we can't use the ExecutionEngine::runFunctionAsMain(). We have to
-    // grab the function address directly here and tell the remote target
-    // to execute the function.
-    //
-    // Our memory manager will map generated code into the remote address
-    // space as it is loaded and copy the bits over during the finalizeMemory
-    // operation.
-    //
+    // Forward MCJIT's memory manager calls to the remote memory manager.
+    static_cast<ForwardingMemoryManager*>(RTDyldMM)->setMemMgr(
+      std::move(RemoteMM));
+
+    // Forward MCJIT's symbol resolution calls to the remote.
+    static_cast<ForwardingMemoryManager*>(RTDyldMM)->setResolver(
+      orc::createLambdaResolver(
+        [&](const std::string &Name) {
+          orc::TargetAddress Addr = 0;
+          if (auto EC = R->getSymbolAddress(Addr, Name)) {
+            errs() << "Failure during symbol lookup: " << EC.message() << "\n";
+            exit(1);
+          }
+          return RuntimeDyld::SymbolInfo(Addr, JITSymbolFlags::Exported);
+        },
+        [](const std::string &Name) { return nullptr; }
+      ));
+
+    // Grab the target address of the JIT'd main function on the remote and call
+    // it.
     // FIXME: argv and envp handling.
-    uint64_t Entry = EE->getFunctionAddress(EntryFn->getName().str());
-
+    orc::TargetAddress Entry = EE->getFunctionAddress(EntryFn->getName().str());
+    EE->finalizeObject();
     DEBUG(dbgs() << "Executing '" << EntryFn->getName() << "' at 0x"
                  << format("%llx", Entry) << "\n");
-
-    if (!Target->executeCode(Entry, Result))
-      errs() << "ERROR: " << Target->getErrorMsg() << "\n";
+    if (auto EC = R->callIntVoid(Result, Entry))
+      errs() << "ERROR: " << EC.message() << "\n";
 
     // Like static constructors, the remote target MCJIT support doesn't handle
     // this yet. It could. FIXME.
 
-    // Stop the remote target
-    Target->stop();
+    // Delete the EE - we need to tear it down *before* we terminate the session
+    // with the remote, otherwise it'll crash when it tries to release resources
+    // on a remote that has already been disconnected.
+    delete EE;
+    EE = nullptr;
+
+    // Signal the remote target that we're done JITing.
+    R->terminateSession();
   }
 
   return Result;
 }
+
+std::unique_ptr<FDRPCChannel> launchRemote() {
+#ifndef LLVM_ON_UNIX
+  llvm_unreachable("launchRemote not supported on non-Unix platforms");
+#else
+  int PipeFD[2][2];
+  pid_t ChildPID;
+
+  // Create two pipes.
+  if (pipe(PipeFD[0]) != 0 || pipe(PipeFD[1]) != 0)
+    perror("Error creating pipe: ");
+
+  ChildPID = fork();
+
+  if (ChildPID == 0) {
+    // In the child...
+
+    // Close the parent ends of the pipes
+    close(PipeFD[0][1]);
+    close(PipeFD[1][0]);
+
+
+    // Execute the child process.
+    std::unique_ptr<char[]> ChildPath, ChildIn, ChildOut;
+    {
+      ChildPath.reset(new char[ChildExecPath.size() + 1]);
+      std::copy(ChildExecPath.begin(), ChildExecPath.end(), &ChildPath[0]);
+      ChildPath[ChildExecPath.size()] = '\0';
+      std::string ChildInStr = std::to_string(PipeFD[0][0]);
+      ChildIn.reset(new char[ChildInStr.size() + 1]);
+      std::copy(ChildInStr.begin(), ChildInStr.end(), &ChildIn[0]);
+      ChildIn[ChildInStr.size()] = '\0';
+      std::string ChildOutStr = std::to_string(PipeFD[1][1]);
+      ChildOut.reset(new char[ChildOutStr.size() + 1]);
+      std::copy(ChildOutStr.begin(), ChildOutStr.end(), &ChildOut[0]);
+      ChildOut[ChildOutStr.size()] = '\0';
+    }
+
+    char * const args[] = { &ChildPath[0], &ChildIn[0], &ChildOut[0], nullptr };
+    int rc = execv(ChildExecPath.c_str(), args);
+    if (rc != 0)
+      perror("Error executing child process: ");
+    llvm_unreachable("Error executing child process");
+  }
+  // else we're the parent...
+
+  // Close the child ends of the pipes
+  close(PipeFD[0][0]);
+  close(PipeFD[1][1]);
+
+  // Return an RPC channel connected to our end of the pipes.
+  return llvm::make_unique<FDRPCChannel>(PipeFD[1][0], PipeFD[0][1]);
+#endif
+}