[clang] [llvm] [compiler-rt] [clang-repl] [ORC] Add support for out-of-process execution on ELF (PR #79936)

via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 29 19:05:48 PST 2024


https://github.com/jameshu15869 created https://github.com/llvm/llvm-project/pull/79936

This PR adds initial support for out-of-process execution through llvm-jitlink-executor to clang-repl on ELF. The out-of-process executor can be invoked with "-oop-executor" or "-oop-executor-connect=<host>" on ELF platforms. Now, clang-repl depends upon the ORC runtime to support static initializers and deinitializers. 

This PR modifies the ORC ELFNix platform to allow JITDylibs to be reinitialized through the "__orc_rt_jit_dlupdate" function, since clang-repl incrementally adds static initializers to a single JITDylib. 

On x86_64 linux, the following tests pass with the out-of-process executor flag. However, a new new testing file (out-of-process.cpp) was added with select tests to restrict the test to ELF only. 

- code-undo.cpp
- const.cpp
- execute-stmts.cpp
- execute-weak.cpp
- global-dtor.cpp
- incremental-mode.cpp
- inline-virtual.cpp
- lambda.cpp
- multiline.cpp
- sanity.cpp

>From 5efb122bc1d3b732bd58eb636ec770dfc572611f Mon Sep 17 00:00:00 2001
From: James Hu <jhudson15869 at gmail.com>
Date: Sun, 28 Jan 2024 20:01:20 -0500
Subject: [PATCH 1/4] [clang-repl][ORC] Add support for out-of-process
 execution on ELF

---
 clang/include/clang/Interpreter/Interpreter.h |  10 +
 clang/lib/Interpreter/IncrementalExecutor.cpp |  29 +++
 clang/lib/Interpreter/IncrementalExecutor.h   |   3 +
 clang/lib/Interpreter/Interpreter.cpp         |  41 +++-
 clang/test/Interpreter/code-undo.cpp          |   1 +
 clang/test/Interpreter/const.cpp              |   1 +
 clang/test/Interpreter/execute-stmts.cpp      |   1 +
 clang/test/Interpreter/execute-weak.cpp       |   1 +
 clang/test/Interpreter/global-dtor.cpp        |   1 +
 clang/test/Interpreter/incremental-mode.cpp   |   1 +
 clang/test/Interpreter/inline-virtual.cpp     |   1 +
 clang/test/Interpreter/lambda.cpp             |   1 +
 clang/test/Interpreter/multiline.cpp          |   1 +
 clang/test/Interpreter/sanity.c               |   4 +
 clang/tools/clang-repl/CMakeLists.txt         |   1 +
 clang/tools/clang-repl/ClangRepl.cpp          | 220 ++++++++++++++++++
 compiler-rt/lib/orc/dlfcn_wrapper.cpp         |  14 ++
 compiler-rt/lib/orc/elfnix_platform.cpp       |  39 ++++
 compiler-rt/lib/orc/elfnix_platform.h         |   1 +
 .../ExecutionEngine/Orc/ELFNixPlatform.cpp    |  10 +-
 llvm/lib/ExecutionEngine/Orc/LLJIT.cpp        |  21 +-
 21 files changed, 392 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 43573fb1a4b89..314beb7b72dac 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -21,6 +21,7 @@
 
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
 #include "llvm/Support/Error.h"
 #include <memory>
@@ -81,6 +82,11 @@ class Interpreter {
   // An optional parser for CUDA offloading
   std::unique_ptr<IncrementalParser> DeviceParser;
 
+  // An optional parameter for an out-of-process executor
+  std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC;
+
+  llvm::StringRef OrcRuntimePath;
+
   Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err);
 
   llvm::Error CreateExecutor();
@@ -98,6 +104,10 @@ class Interpreter {
   static llvm::Expected<std::unique_ptr<Interpreter>>
   createWithCUDA(std::unique_ptr<CompilerInstance> CI,
                  std::unique_ptr<CompilerInstance> DCI);
+  static llvm::Expected<std::unique_ptr<Interpreter>>
+  createWithOutOfProcessExecutor(
+      std::unique_ptr<CompilerInstance> CI,
+      std::unique_ptr<llvm::orc::ExecutorProcessControl> EI, llvm::StringRef OrcRuntimePath);
   const ASTContext &getASTContext() const;
   ASTContext &getASTContext();
   const CompilerInstance *getCompilerInstance() const;
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 40bcef94797d4..30b24caa4a594 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -63,6 +63,35 @@ IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC,
   }
 }
 
+IncrementalExecutor::IncrementalExecutor(
+    llvm::orc::ThreadSafeContext &TSC, llvm::Error &Err,
+    const clang::TargetInfo &TI,
+    std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC, llvm::StringRef OrcRuntimePath)
+    : TSCtx(TSC) {
+  using namespace llvm::orc;
+  llvm::ErrorAsOutParameter EAO(&Err);
+
+  auto JTMB = JITTargetMachineBuilder(TI.getTriple());
+  JTMB.addFeatures(TI.getTargetOpts().Features);
+  LLJITBuilder Builder;
+  Builder.setJITTargetMachineBuilder(JTMB);
+  Builder.setPrePlatformSetup([](LLJIT &J) {
+    // Try to enable debugging of JIT'd code (only works with JITLink for
+    // ELF and MachO).
+    consumeError(enableDebuggerSupport(J));
+    return llvm::Error::success();
+  });
+  Builder.setExecutorProcessControl(std::move(EPC));
+  Builder.setPlatformSetUp(llvm::orc::ExecutorNativePlatform(OrcRuntimePath.str()));
+
+  if (auto JitOrErr = Builder.create()) {
+    Jit = std::move(*JitOrErr);
+  } else {
+    Err = JitOrErr.takeError();
+    return;
+  }
+}
+
 IncrementalExecutor::~IncrementalExecutor() {}
 
 llvm::Error IncrementalExecutor::addModule(PartialTranslationUnit &PTU) {
diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h
index dd0a210a06141..a73ba9035182c 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.h
+++ b/clang/lib/Interpreter/IncrementalExecutor.h
@@ -46,6 +46,9 @@ class IncrementalExecutor {
 
   IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, llvm::Error &Err,
                       const clang::TargetInfo &TI);
+  IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, llvm::Error &Err,
+                      const clang::TargetInfo &TI,
+                      std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC, llvm::StringRef OrcRuntimePath);
   ~IncrementalExecutor();
 
   llvm::Error addModule(PartialTranslationUnit &PTU);
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 7968c62cbd3e7..50d1ddbfcf07b 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -36,6 +36,7 @@
 #include "clang/Lex/PreprocessorOptions.h"
 #include "clang/Sema/Lookup.h"
 #include "llvm/ExecutionEngine/JITSymbol.h"
+#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Errc.h"
@@ -242,6 +243,15 @@ Interpreter::~Interpreter() {
           llvm::Twine("Failed to clean up IncrementalExecutor: ") +
           toString(std::move(Err)));
   }
+
+    if (EPC) {
+    if (auto Err = EPC->disconnect()) {
+      llvm::report_fatal_error(
+          llvm::Twine("Failed to clean up EPC (IncrementalExecutor has not yet "
+                      "been created): ") +
+          toString(std::move(Err)));
+    }
+  }
 }
 
 // These better to put in a runtime header but we can't. This is because we
@@ -315,6 +325,20 @@ Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI,
   return Interp;
 }
 
+
+llvm::Expected<std::unique_ptr<Interpreter>>
+Interpreter::createWithOutOfProcessExecutor(
+    std::unique_ptr<CompilerInstance> CI,
+    std::unique_ptr<llvm::orc::ExecutorProcessControl> EI, llvm::StringRef OrcRuntimePath) {
+  auto Interp = create(std::move(CI));
+  if (auto E = Interp.takeError()) {
+    return std::move(E);
+  }
+  (*Interp)->EPC = std::move(EI);
+  (*Interp)->OrcRuntimePath = OrcRuntimePath;
+  return Interp;
+}
+
 const CompilerInstance *Interpreter::getCompilerInstance() const {
   return IncrParser->getCI();
 }
@@ -363,7 +387,13 @@ llvm::Error Interpreter::CreateExecutor() {
   const clang::TargetInfo &TI =
       getCompilerInstance()->getASTContext().getTargetInfo();
   llvm::Error Err = llvm::Error::success();
-  auto Executor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
+  std::unique_ptr<IncrementalExecutor> Executor;
+  if (EPC) {
+    Executor =
+        std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI, std::move(EPC), OrcRuntimePath);
+  } else {
+    Executor = std::make_unique<IncrementalExecutor>(*TSCtx, Err, TI);
+  }
   if (!Err)
     IncrExecutor = std::move(Executor);
 
@@ -460,13 +490,8 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
   if (!EE)
     return EE.takeError();
 
-  auto &DL = EE->getDataLayout();
-
-  if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load(
-          name, DL.getGlobalPrefix()))
-    EE->getMainJITDylib().addGenerator(std::move(*DLSG));
-  else
-    return DLSG.takeError();
+  if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load(
+          EE->getExecutionSession(), name))
 
   return llvm::Error::success();
 }
diff --git a/clang/test/Interpreter/code-undo.cpp b/clang/test/Interpreter/code-undo.cpp
index 83ade0ec9158b..6a4b99fde2899 100644
--- a/clang/test/Interpreter/code-undo.cpp
+++ b/clang/test/Interpreter/code-undo.cpp
@@ -1,5 +1,6 @@
 // UNSUPPORTED: system-aix
 // RUN: cat %s | clang-repl | FileCheck %s
+// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 extern "C" int printf(const char *, ...);
 int x1 = 0;
 int x2 = 42;
diff --git a/clang/test/Interpreter/const.cpp b/clang/test/Interpreter/const.cpp
index 4b6ce65e3643e..b4aa3ef9d87ee 100644
--- a/clang/test/Interpreter/const.cpp
+++ b/clang/test/Interpreter/const.cpp
@@ -3,6 +3,7 @@
 // XFAIL: system-windows
 
 // RUN: cat %s | clang-repl | FileCheck %s
+// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 // RUN: cat %s | clang-repl -Xcc -O2 | FileCheck %s
 
 extern "C" int printf(const char*, ...);
diff --git a/clang/test/Interpreter/execute-stmts.cpp b/clang/test/Interpreter/execute-stmts.cpp
index 2d4c17e0c91e6..defd128683e64 100644
--- a/clang/test/Interpreter/execute-stmts.cpp
+++ b/clang/test/Interpreter/execute-stmts.cpp
@@ -1,6 +1,7 @@
 // REQUIRES: host-supports-jit
 // UNSUPPORTED: system-aix
 // RUN: cat %s | clang-repl -Xcc -Xclang -Xcc  -verify | FileCheck %s
+// RUN: cat %s | clang-repl -oop-executor -Xcc -Xclang -Xcc -verify | FileCheck %s
 // RUN: %clang_cc1 -verify -fincremental-extensions -emit-llvm -o -  %s \
 // RUN:           | FileCheck --check-prefix=CODEGEN-CHECK %s
 
diff --git a/clang/test/Interpreter/execute-weak.cpp b/clang/test/Interpreter/execute-weak.cpp
index 85fa5d276f5f4..75e58e5e2c295 100644
--- a/clang/test/Interpreter/execute-weak.cpp
+++ b/clang/test/Interpreter/execute-weak.cpp
@@ -1,5 +1,6 @@
 // UNSUPPORTED: system-aix, system-windows
 // RUN: cat %s | clang-repl | FileCheck %s
+// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 
 extern "C" int printf(const char *, ...);
 int __attribute__((weak)) bar() { return 42; }
diff --git a/clang/test/Interpreter/global-dtor.cpp b/clang/test/Interpreter/global-dtor.cpp
index 1f241d9f19317..2a01cdb4d8c7b 100644
--- a/clang/test/Interpreter/global-dtor.cpp
+++ b/clang/test/Interpreter/global-dtor.cpp
@@ -4,6 +4,7 @@
 // Tests that a global destructor is ran on platforms with gnu exception support.
 //
 // RUN: cat %s | clang-repl | FileCheck %s
+// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 
 extern "C" int printf(const char *, ...);
 
diff --git a/clang/test/Interpreter/incremental-mode.cpp b/clang/test/Interpreter/incremental-mode.cpp
index e6350d237ef57..54153b74b2d83 100644
--- a/clang/test/Interpreter/incremental-mode.cpp
+++ b/clang/test/Interpreter/incremental-mode.cpp
@@ -1,3 +1,4 @@
 // RUN: clang-repl -Xcc -E
 // RUN: clang-repl -Xcc -emit-llvm 
+// RUN: clang-repl -oop-executor -Xcc -E
 // expected-no-diagnostics
diff --git a/clang/test/Interpreter/inline-virtual.cpp b/clang/test/Interpreter/inline-virtual.cpp
index 79ab8ed337ffe..557ba47a68356 100644
--- a/clang/test/Interpreter/inline-virtual.cpp
+++ b/clang/test/Interpreter/inline-virtual.cpp
@@ -4,6 +4,7 @@
 // We disable RTTI to avoid problems on Windows for non-RTTI builds of LLVM
 // where the JIT cannot find ??_7type_info@@6B at .
 // RUN: cat %s | clang-repl -Xcc -fno-rtti | FileCheck %s
+// RUN: cat %s | clang-repl -oop-executor -Xcc -fno-rtti | FileCheck %s
 // RUN: cat %s | clang-repl -Xcc -fno-rtti -Xcc -O2 | FileCheck %s
 
 extern "C" int printf(const char *, ...);
diff --git a/clang/test/Interpreter/lambda.cpp b/clang/test/Interpreter/lambda.cpp
index df75274a050b2..fa926925d7eda 100644
--- a/clang/test/Interpreter/lambda.cpp
+++ b/clang/test/Interpreter/lambda.cpp
@@ -1,6 +1,7 @@
 // REQUIRES: host-supports-jit
 // UNSUPPORTED: system-aix
 // RUN: cat %s | clang-repl | FileCheck %s
+// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 // RUN: cat %s | clang-repl -Xcc -O2 | FileCheck %s
 extern "C" int printf(const char *, ...);
 
diff --git a/clang/test/Interpreter/multiline.cpp b/clang/test/Interpreter/multiline.cpp
index 054e61a7e3d62..797a6cdba1361 100644
--- a/clang/test/Interpreter/multiline.cpp
+++ b/clang/test/Interpreter/multiline.cpp
@@ -1,6 +1,7 @@
 // REQUIRES: host-supports-jit
 // UNSUPPORTED: system-aix
 // RUN: cat %s | clang-repl | FileCheck %s
+// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 
 extern "C" int printf(const char*,...);
 int i = \
diff --git a/clang/test/Interpreter/sanity.c b/clang/test/Interpreter/sanity.c
index 8893a12f9216a..85f44558f5b6a 100644
--- a/clang/test/Interpreter/sanity.c
+++ b/clang/test/Interpreter/sanity.c
@@ -2,6 +2,10 @@
 // RUN:   clang-repl -Xcc -fno-color-diagnostics -Xcc -Xclang -Xcc -ast-dump \
 // RUN:            -Xcc -Xclang -Xcc -ast-dump-filter -Xcc -Xclang -Xcc Test 2>&1| \
 // RUN:         FileCheck %s
+// RUN: cat %s | \
+// RUN:   clang-repl -oop-executor -Xcc -fno-color-diagnostics -Xcc -Xclang -Xcc -ast-dump \
+// RUN:            -Xcc -Xclang -Xcc -ast-dump-filter -Xcc -Xclang -Xcc Test 2>&1| \
+// RUN:         FileCheck %s
 
 int TestVar = 12;
 // CHECK: Dumping TestVar:
diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt
index 2ccbe292fd49e..d33d0273f6abb 100644
--- a/clang/tools/clang-repl/CMakeLists.txt
+++ b/clang/tools/clang-repl/CMakeLists.txt
@@ -4,6 +4,7 @@ set( LLVM_LINK_COMPONENTS
   LineEditor
   Option
   OrcJIT
+  OrcShared
   Support
   )
 
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index 5663c2c5a6c92..b79f3a4dff5be 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -16,13 +16,20 @@
 #include "clang/Interpreter/CodeCompletion.h"
 #include "clang/Interpreter/Interpreter.h"
 
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
+#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
 #include "llvm/LineEditor/LineEditor.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ManagedStatic.h" // llvm_shutdown
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/TargetSelect.h"
+#include <netdb.h>
 #include <optional>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 // Disable LSan for this test.
 // FIXME: Re-enable once we can assume GCC 13.2 or higher.
@@ -45,6 +52,20 @@ static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
 static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
                                              llvm::cl::desc("[code to run]"));
 
+static llvm::cl::OptionCategory OOPCategory("Out-of-process Execution Options");
+static llvm::cl::opt<std::string> OutOfProcessExecutor(
+    "oop-executor",
+    llvm::cl::desc("Launch an out-of-process executor to run code"),
+    llvm::cl::ValueOptional, llvm::cl::cat(OOPCategory));
+static llvm::cl::opt<std::string> OutOfProcessExecutorConnect(
+    "oop-executor-connect",
+    llvm::cl::desc("Connect to an out-of-process executor via TCP"),
+    llvm::cl::cat(OOPCategory));
+static llvm::cl::opt<std::string> OrcRuntimePath(
+    "orc-runtime",
+    llvm::cl::desc("Path to the ORC runtime"),
+    llvm::cl::cat(OOPCategory));
+
 static void LLVMErrorHandler(void *UserData, const char *Message,
                              bool GenCrashDiag) {
   auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
@@ -143,6 +164,193 @@ ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos,
   return Comps;
 }
 
+static llvm::Error sanitizeArguments(const char *ArgV0) {
+  // Only one of -oop-executor and -oop-executor-connect can be used.
+  if (!!OutOfProcessExecutor.getNumOccurrences() &&
+      !!OutOfProcessExecutorConnect.getNumOccurrences())
+    return llvm::make_error<llvm::StringError>(
+        "Only one of -" + OutOfProcessExecutor.ArgStr + " and -" +
+            OutOfProcessExecutorConnect.ArgStr + " can be specified",
+        llvm::inconvertibleErrorCode());
+
+  // If -oop-executor was used but no value was specified then use a sensible
+  // default.
+  if (!!OutOfProcessExecutor.getNumOccurrences() &&
+      OutOfProcessExecutor.empty()) {
+    llvm::SmallString<256> OOPExecutorPath(llvm::sys::fs::getMainExecutable(
+        ArgV0, reinterpret_cast<void *>(&sanitizeArguments)));
+    llvm::sys::path::remove_filename(OOPExecutorPath);
+    llvm::sys::path::append(OOPExecutorPath, "llvm-jitlink-executor");
+    OutOfProcessExecutor = OOPExecutorPath.str().str();
+  }
+
+  // Out-of-process executors must run with the ORC runtime for destructor support.
+  if (OrcRuntimePath.empty() && (OutOfProcessExecutor.getNumOccurrences() || OutOfProcessExecutorConnect.getNumOccurrences())) {
+    llvm::SmallString<256> OrcPath(llvm::sys::fs::getMainExecutable(
+        ArgV0, reinterpret_cast<void *>(&sanitizeArguments)));
+    llvm::sys::path::remove_filename(OrcPath); // Remove clang-repl filename.
+    llvm::sys::path::remove_filename(OrcPath); // Remove ./bin directory.
+    llvm::sys::path::append(OrcPath, "lib/clang/18/lib/x86_64-unknown-linux-gnu/liborc_rt.a");
+    OrcRuntimePath = OrcPath.str().str();
+  }
+
+  return llvm::Error::success();
+}
+
+static llvm::Expected<std::unique_ptr<llvm::orc::ExecutorProcessControl>>
+launchExecutor() {
+  constexpr int ReadEnd = 0;
+  constexpr int WriteEnd = 1;
+
+  // Pipe FDs.
+  int ToExecutor[2];
+  int FromExecutor[2];
+
+  pid_t ChildPID;
+
+  // Create pipes to/from the executor..
+  if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
+    return llvm::make_error<llvm::StringError>(
+        "Unable to create pipe for executor", llvm::inconvertibleErrorCode());
+
+  ChildPID = fork();
+
+  if (ChildPID == 0) {
+    // In the child...
+
+    // Close the parent ends of the pipes
+    close(ToExecutor[WriteEnd]);
+    close(FromExecutor[ReadEnd]);
+
+    // Execute the child process.
+    std::unique_ptr<char[]> ExecutorPath, FDSpecifier;
+    {
+      ExecutorPath = std::make_unique<char[]>(OutOfProcessExecutor.size() + 1);
+      strcpy(ExecutorPath.get(), OutOfProcessExecutor.data());
+
+      std::string FDSpecifierStr("filedescs=");
+      FDSpecifierStr += llvm::utostr(ToExecutor[ReadEnd]);
+      FDSpecifierStr += ',';
+      FDSpecifierStr += llvm::utostr(FromExecutor[WriteEnd]);
+      FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
+      strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
+    }
+
+    char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};
+    int RC = execvp(ExecutorPath.get(), Args);
+    if (RC != 0) {
+      llvm::errs() << "unable to launch out-of-process executor \""
+                   << ExecutorPath.get() << "\"\n";
+      exit(1);
+    }
+  }
+  // else we're the parent...
+
+  // Close the child ends of the pipes
+  close(ToExecutor[ReadEnd]);
+  close(FromExecutor[WriteEnd]);
+
+  auto S = llvm::orc::SimpleRemoteEPC::Setup();
+
+  return llvm::orc::SimpleRemoteEPC::Create<
+      llvm::orc::FDSimpleRemoteEPCTransport>(
+      std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(),
+      std::move(S), FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
+}
+
+#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
+static llvm::Error createTCPSocketError(llvm::Twine Details) {
+  return llvm::make_error<llvm::StringError>(
+      formatv("Failed to connect TCP socket '{0}': {1}",
+              OutOfProcessExecutorConnect, Details),
+      llvm::inconvertibleErrorCode());
+}
+
+static llvm::Expected<int> connectTCPSocket(std::string Host,
+                                            std::string PortStr) {
+  addrinfo *AI;
+  addrinfo Hints{};
+  Hints.ai_family = AF_INET;
+  Hints.ai_socktype = SOCK_STREAM;
+  Hints.ai_flags = AI_NUMERICSERV;
+
+  if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
+    return createTCPSocketError("Address resolution failed (" +
+                                llvm::StringRef(gai_strerror(EC)) + ")");
+
+  // Cycle through the returned addrinfo structures and connect to the first
+  // reachable endpoint.
+  int SockFD;
+  addrinfo *Server;
+  for (Server = AI; Server != nullptr; Server = Server->ai_next) {
+    // socket might fail, e.g. if the address family is not supported. Skip to
+    // the next addrinfo structure in such a case.
+    if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
+      continue;
+
+    // If connect returns null, we exit the loop with a working socket.
+    if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
+      break;
+
+    close(SockFD);
+  }
+  freeaddrinfo(AI);
+
+  // If we reached the end of the loop without connecting to a valid endpoint,
+  // dump the last error that was logged in socket() or connect().
+  if (Server == nullptr)
+    return createTCPSocketError(std::strerror(errno));
+
+  return SockFD;
+}
+#endif
+
+static llvm::Expected<std::unique_ptr<llvm::orc::ExecutorProcessControl>>
+connectToExecutor() {
+#ifndef LLVM_ON_UNIX
+  // FIXME: Add TCP support for Windows.
+  return llvm::make_error<StringError>(
+      "-" + OutOfProcessExecutorConnect.ArgStr +
+          " not supported on non-unix platforms",
+      inconvertibleErrorCode());
+#elif !LLVM_ENABLE_THREADS
+  // Out of process mode using SimpleRemoteEPC depends on threads.
+  return llvm::make_error<StringError>(
+      "-" + OutOfProcessExecutorConnect.ArgStr +
+          " requires threads, but LLVM was built with "
+          "LLVM_ENABLE_THREADS=Off",
+      inconvertibleErrorCode());
+#else
+
+  llvm::StringRef Host, PortStr;
+  std::tie(Host, PortStr) =
+      llvm::StringRef(OutOfProcessExecutorConnect).split(':');
+  if (Host.empty())
+    return createTCPSocketError("Host name for -" +
+                                OutOfProcessExecutorConnect.ArgStr +
+                                " can not be empty");
+  if (PortStr.empty())
+    return createTCPSocketError("Port number in -" +
+                                OutOfProcessExecutorConnect.ArgStr +
+                                " can not be empty");
+  int Port = 0;
+  if (PortStr.getAsInteger(10, Port))
+    return createTCPSocketError("Port number '" + PortStr +
+                                "' is not a valid integer");
+
+  llvm::Expected<int> SockFD = connectTCPSocket(Host.str(), PortStr.str());
+  if (!SockFD)
+    return SockFD.takeError();
+
+  auto S = llvm::orc::SimpleRemoteEPC::Setup();
+
+  return llvm::orc::SimpleRemoteEPC::Create<
+      llvm::orc::FDSimpleRemoteEPCTransport>(
+      std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(),
+      std::move(S), *SockFD, *SockFD);
+#endif
+}
+
 llvm::ExitOnError ExitOnErr;
 int main(int argc, const char **argv) {
   ExitOnErr.setBanner("clang-repl: ");
@@ -205,6 +413,8 @@ int main(int argc, const char **argv) {
   if (CudaEnabled)
     DeviceCI->LoadRequestedPlugins();
 
+  ExitOnErr(sanitizeArguments(argv[0]));
+
   std::unique_ptr<clang::Interpreter> Interp;
 
   if (CudaEnabled) {
@@ -217,6 +427,16 @@ int main(int argc, const char **argv) {
       auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
       ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str()));
     }
+  } else if (OutOfProcessExecutor.getNumOccurrences()) {
+    // Create an instance of llvm-jitlink-executor in a separate process.
+    auto oopExecutor = ExitOnErr(launchExecutor());
+    Interp = ExitOnErr(clang::Interpreter::createWithOutOfProcessExecutor(
+        std::move(CI), std::move(oopExecutor), OrcRuntimePath));
+  } else if (OutOfProcessExecutorConnect.getNumOccurrences()) {
+    /// If -oop-executor-connect is passed then connect to the executor.
+    auto REPC = ExitOnErr(connectToExecutor());
+    Interp = ExitOnErr(clang::Interpreter::createWithOutOfProcessExecutor(
+        std::move(CI), std::move(REPC), OrcRuntimePath));
   } else
     Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
 
diff --git a/compiler-rt/lib/orc/dlfcn_wrapper.cpp b/compiler-rt/lib/orc/dlfcn_wrapper.cpp
index ece63da2cb48e..a8b207d27157e 100644
--- a/compiler-rt/lib/orc/dlfcn_wrapper.cpp
+++ b/compiler-rt/lib/orc/dlfcn_wrapper.cpp
@@ -20,6 +20,7 @@ using namespace __orc_rt;
 
 extern "C" const char *__orc_rt_jit_dlerror();
 extern "C" void *__orc_rt_jit_dlopen(const char *path, int mode);
+extern "C" void *__orc_rt_jit_dlupdate(const char *path, int mode);
 extern "C" int __orc_rt_jit_dlclose(void *dso_handle);
 
 ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult
@@ -41,6 +42,19 @@ __orc_rt_jit_dlopen_wrapper(const char *ArgData, size_t ArgSize) {
       .release();
 }
 
+ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult
+__orc_rt_jit_dlupdate_wrapper(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<SPSExecutorAddr(SPSString, int32_t)>::handle(
+             ArgData, ArgSize,
+             [](const std::string &Path, int32_t mode) {
+               return ExecutorAddr::fromPtr(
+                   __orc_rt_jit_dlupdate(Path.c_str(), mode));
+             })
+      .release();
+}
+
+
+
 ORC_RT_INTERFACE orc_rt_CWrapperFunctionResult
 __orc_rt_jit_dlclose_wrapper(const char *ArgData, size_t ArgSize) {
   return WrapperFunction<int32_t(SPSExecutorAddr)>::handle(
diff --git a/compiler-rt/lib/orc/elfnix_platform.cpp b/compiler-rt/lib/orc/elfnix_platform.cpp
index c087e71038f95..6f9fdb670f558 100644
--- a/compiler-rt/lib/orc/elfnix_platform.cpp
+++ b/compiler-rt/lib/orc/elfnix_platform.cpp
@@ -116,6 +116,7 @@ class ELFNixPlatformRuntimeState {
 
   const char *dlerror();
   void *dlopen(std::string_view Name, int Mode);
+  void *dlupdate(std::string_view Name, int Mode);
   int dlclose(void *DSOHandle);
   void *dlsym(void *DSOHandle, std::string_view Symbol);
 
@@ -142,6 +143,7 @@ class ELFNixPlatformRuntimeState {
   Expected<ELFNixJITDylibInitializerSequence>
   getJITDylibInitializersByName(std::string_view Path);
   Expected<void *> dlopenInitialize(std::string_view Path, int Mode);
+  Expected<void *> dlupdateInitialize(std::string_view Path, int Mode);
   Error initializeJITDylib(ELFNixJITDylibInitializers &MOJDIs);
 
   static ELFNixPlatformRuntimeState *MOPS;
@@ -237,6 +239,18 @@ void *ELFNixPlatformRuntimeState::dlopen(std::string_view Path, int Mode) {
   return *H;
 }
 
+void *ELFNixPlatformRuntimeState::dlupdate(std::string_view Path, int Mode) {
+  std::lock_guard<std::recursive_mutex> Lock(JDStatesMutex);
+
+  auto H = dlupdateInitialize(Path, Mode);
+  if (!H) {
+    DLFcnError = toString(H.takeError());
+    return nullptr;
+  }
+
+  return *H;
+}
+
 int ELFNixPlatformRuntimeState::dlclose(void *DSOHandle) {
   runAtExits(DSOHandle);
   return 0;
@@ -401,6 +415,27 @@ ELFNixPlatformRuntimeState::dlopenInitialize(std::string_view Path, int Mode) {
   return JDS->Header;
 }
 
+Expected<void *>
+ELFNixPlatformRuntimeState::dlupdateInitialize(std::string_view Path,
+                                               int Mode) {
+  auto InitSeq = getJITDylibInitializersByName(Path);
+  if (!InitSeq)
+    return InitSeq.takeError();
+
+  // Init sequences can be empty becaue we are (possibly) reinitializing
+  if (InitSeq->empty())
+    return getJITDylibStateByName(Path)->Header;
+
+  for (auto &MOJDIs : *InitSeq)
+    if (auto Err = initializeJITDylib(MOJDIs))
+      return std::move(Err);
+
+  auto *JDS = getJITDylibStateByHeaderAddr(
+      InitSeq->back().DSOHandleAddress.toPtr<void *>());
+  assert(JDS && "Missing state entry for JD");
+  return JDS->Header;
+}
+
 long getPriority(const std::string &name) {
   auto pos = name.find_last_not_of("0123456789");
   if (pos == name.size() - 1)
@@ -583,6 +618,10 @@ void *__orc_rt_elfnix_jit_dlopen(const char *path, int mode) {
   return ELFNixPlatformRuntimeState::get().dlopen(path, mode);
 }
 
+void *__orc_rt_elfnix_jit_dlupdate(const char *path, int mode) {
+  return ELFNixPlatformRuntimeState::get().dlupdate(path, mode);
+}
+
 int __orc_rt_elfnix_jit_dlclose(void *dso_handle) {
   return ELFNixPlatformRuntimeState::get().dlclose(dso_handle);
 }
diff --git a/compiler-rt/lib/orc/elfnix_platform.h b/compiler-rt/lib/orc/elfnix_platform.h
index e0ee9591dfc62..569a3cfb5e11b 100644
--- a/compiler-rt/lib/orc/elfnix_platform.h
+++ b/compiler-rt/lib/orc/elfnix_platform.h
@@ -25,6 +25,7 @@ ORC_RT_INTERFACE void __orc_rt_elfnix_cxa_finalize(void *dso_handle);
 // dlfcn functions.
 ORC_RT_INTERFACE const char *__orc_rt_elfnix_jit_dlerror();
 ORC_RT_INTERFACE void *__orc_rt_elfnix_jit_dlopen(const char *path, int mode);
+ORC_RT_INTERFACE void *__orc_rt_elfnix_jit_dlupdate(const char *path, int mode);
 ORC_RT_INTERFACE int __orc_rt_elfnix_jit_dlclose(void *dso_handle);
 ORC_RT_INTERFACE void *__orc_rt_elfnix_jit_dlsym(void *dso_handle,
                                                  const char *symbol);
diff --git a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
index 2b6c4b9e7f431..b32d9d4c1046d 100644
--- a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
@@ -237,6 +237,7 @@ ELFNixPlatform::standardRuntimeUtilityAliases() {
           {"__orc_rt_run_program", "__orc_rt_elfnix_run_program"},
           {"__orc_rt_jit_dlerror", "__orc_rt_elfnix_jit_dlerror"},
           {"__orc_rt_jit_dlopen", "__orc_rt_elfnix_jit_dlopen"},
+          {"__orc_rt_jit_dlupdate", "__orc_rt_elfnix_jit_dlupdate"},
           {"__orc_rt_jit_dlclose", "__orc_rt_elfnix_jit_dlclose"},
           {"__orc_rt_jit_dlsym", "__orc_rt_elfnix_jit_dlsym"},
           {"__orc_rt_log_error", "__orc_rt_log_error_to_stderr"}};
@@ -544,10 +545,17 @@ Error ELFNixPlatform::registerInitInfo(
 
       auto SearchOrder =
           JD.withLinkOrderDo([](const JITDylibSearchOrder &SO) { return SO; });
-      if (auto Err = ES.lookup(SearchOrder, DSOHandleSymbol).takeError())
+      auto SymOrErr = ES.lookup(SearchOrder, DSOHandleSymbol);
+      if (auto Err = SymOrErr.takeError()) {
         return Err;
+      }
 
       Lock.lock();
+      // We can allow reinitialization by reinserting the handles to each 
+      // JITDylib into InitSeqs.
+      InitSeqs.insert(std::make_pair(
+          &JD,
+          ELFNixJITDylibInitializers(JD.getName(), (*SymOrErr).getAddress())));
       I = InitSeqs.find(&JD);
       assert(I != InitSeqs.end() &&
              "Entry missing after header symbol lookup?");
diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
index 1a4251353e2da..54030c83b7eb0 100644
--- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
@@ -105,9 +105,18 @@ class ORCPlatformSupport : public LLJIT::PlatformSupport {
     auto MainSearchOrder = J.getMainJITDylib().withLinkOrderDo(
         [](const JITDylibSearchOrder &SO) { return SO; });
 
+    std::string WrapperToCall = "__orc_rt_jit_dlopen_wrapper";
+    auto Triple = ES.getTargetTriple();
+    if (Triple.isOSBinFormatELF()) {
+      WrapperToCall = FirstInitialization ? "__orc_rt_jit_dlopen_wrapper" : "__orc_rt_jit_dlupdate_wrapper";
+      if (FirstInitialization) {
+        FirstInitialization = false;
+      }
+    }
+
     if (auto WrapperAddr =
             ES.lookup(MainSearchOrder,
-                      J.mangleAndIntern("__orc_rt_jit_dlopen_wrapper"))) {
+                      J.mangleAndIntern(WrapperToCall))) {
       return ES.callSPSWrapper<SPSDLOpenSig>(WrapperAddr->getAddress(),
                                              DSOHandles[&JD], JD.getName(),
                                              int32_t(ORC_RT_RTLD_LAZY));
@@ -143,6 +152,7 @@ class ORCPlatformSupport : public LLJIT::PlatformSupport {
 private:
   orc::LLJIT &J;
   DenseMap<orc::JITDylib *, orc::ExecutorAddr> DSOHandles;
+  bool FirstInitialization = true; // Used to track if reinitialization is needed for ELF
 };
 
 class GenericLLVMIRPlatformSupport;
@@ -1109,6 +1119,15 @@ Expected<JITDylibSP> ExecutorNativePlatform::operator()(LLJIT &J) {
 
   auto &ES = J.getExecutionSession();
   auto &PlatformJD = ES.createBareJITDylib("<Platform>");
+
+  // Required to help out-of-process executors find definitions for setting
+  // up the ORC platform
+  if (auto DylibSearchGeneratorOrErr =
+          llvm::orc::EPCDynamicLibrarySearchGenerator::GetForTargetProcess(
+              ES)) {
+    PlatformJD.addGenerator(std::move(*DylibSearchGeneratorOrErr));
+  }
+
   PlatformJD.addToLinkOrder(*ProcessSymbolsJD);
 
   J.setPlatformSupport(std::make_unique<ORCPlatformSupport>(J));

>From 246d0373e7ed35803f1200213a5415360b1787ab Mon Sep 17 00:00:00 2001
From: James Hu <jhudson15869 at gmail.com>
Date: Mon, 29 Jan 2024 21:17:29 -0500
Subject: [PATCH 2/4] [clang-repl] Add ELF-only test and checks

---
 clang/test/Interpreter/code-undo.cpp        |  1 -
 clang/test/Interpreter/const.cpp            |  1 -
 clang/test/Interpreter/execute-stmts.cpp    |  1 -
 clang/test/Interpreter/execute-weak.cpp     |  1 -
 clang/test/Interpreter/global-dtor.cpp      |  1 -
 clang/test/Interpreter/incremental-mode.cpp |  1 -
 clang/test/Interpreter/inline-virtual.cpp   |  1 -
 clang/test/Interpreter/lambda.cpp           |  1 -
 clang/test/Interpreter/multiline.cpp        |  1 -
 clang/test/Interpreter/out-of-process.cpp   | 70 +++++++++++++++++++++
 clang/test/Interpreter/sanity.c             |  4 --
 clang/tools/clang-repl/CMakeLists.txt       |  1 +
 clang/tools/clang-repl/ClangRepl.cpp        | 20 ++++--
 13 files changed, 86 insertions(+), 18 deletions(-)
 create mode 100644 clang/test/Interpreter/out-of-process.cpp

diff --git a/clang/test/Interpreter/code-undo.cpp b/clang/test/Interpreter/code-undo.cpp
index 6a4b99fde2899..83ade0ec9158b 100644
--- a/clang/test/Interpreter/code-undo.cpp
+++ b/clang/test/Interpreter/code-undo.cpp
@@ -1,6 +1,5 @@
 // UNSUPPORTED: system-aix
 // RUN: cat %s | clang-repl | FileCheck %s
-// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 extern "C" int printf(const char *, ...);
 int x1 = 0;
 int x2 = 42;
diff --git a/clang/test/Interpreter/const.cpp b/clang/test/Interpreter/const.cpp
index b4aa3ef9d87ee..4b6ce65e3643e 100644
--- a/clang/test/Interpreter/const.cpp
+++ b/clang/test/Interpreter/const.cpp
@@ -3,7 +3,6 @@
 // XFAIL: system-windows
 
 // RUN: cat %s | clang-repl | FileCheck %s
-// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 // RUN: cat %s | clang-repl -Xcc -O2 | FileCheck %s
 
 extern "C" int printf(const char*, ...);
diff --git a/clang/test/Interpreter/execute-stmts.cpp b/clang/test/Interpreter/execute-stmts.cpp
index defd128683e64..2d4c17e0c91e6 100644
--- a/clang/test/Interpreter/execute-stmts.cpp
+++ b/clang/test/Interpreter/execute-stmts.cpp
@@ -1,7 +1,6 @@
 // REQUIRES: host-supports-jit
 // UNSUPPORTED: system-aix
 // RUN: cat %s | clang-repl -Xcc -Xclang -Xcc  -verify | FileCheck %s
-// RUN: cat %s | clang-repl -oop-executor -Xcc -Xclang -Xcc -verify | FileCheck %s
 // RUN: %clang_cc1 -verify -fincremental-extensions -emit-llvm -o -  %s \
 // RUN:           | FileCheck --check-prefix=CODEGEN-CHECK %s
 
diff --git a/clang/test/Interpreter/execute-weak.cpp b/clang/test/Interpreter/execute-weak.cpp
index 75e58e5e2c295..85fa5d276f5f4 100644
--- a/clang/test/Interpreter/execute-weak.cpp
+++ b/clang/test/Interpreter/execute-weak.cpp
@@ -1,6 +1,5 @@
 // UNSUPPORTED: system-aix, system-windows
 // RUN: cat %s | clang-repl | FileCheck %s
-// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 
 extern "C" int printf(const char *, ...);
 int __attribute__((weak)) bar() { return 42; }
diff --git a/clang/test/Interpreter/global-dtor.cpp b/clang/test/Interpreter/global-dtor.cpp
index 2a01cdb4d8c7b..1f241d9f19317 100644
--- a/clang/test/Interpreter/global-dtor.cpp
+++ b/clang/test/Interpreter/global-dtor.cpp
@@ -4,7 +4,6 @@
 // Tests that a global destructor is ran on platforms with gnu exception support.
 //
 // RUN: cat %s | clang-repl | FileCheck %s
-// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 
 extern "C" int printf(const char *, ...);
 
diff --git a/clang/test/Interpreter/incremental-mode.cpp b/clang/test/Interpreter/incremental-mode.cpp
index 54153b74b2d83..e6350d237ef57 100644
--- a/clang/test/Interpreter/incremental-mode.cpp
+++ b/clang/test/Interpreter/incremental-mode.cpp
@@ -1,4 +1,3 @@
 // RUN: clang-repl -Xcc -E
 // RUN: clang-repl -Xcc -emit-llvm 
-// RUN: clang-repl -oop-executor -Xcc -E
 // expected-no-diagnostics
diff --git a/clang/test/Interpreter/inline-virtual.cpp b/clang/test/Interpreter/inline-virtual.cpp
index 557ba47a68356..79ab8ed337ffe 100644
--- a/clang/test/Interpreter/inline-virtual.cpp
+++ b/clang/test/Interpreter/inline-virtual.cpp
@@ -4,7 +4,6 @@
 // We disable RTTI to avoid problems on Windows for non-RTTI builds of LLVM
 // where the JIT cannot find ??_7type_info@@6B at .
 // RUN: cat %s | clang-repl -Xcc -fno-rtti | FileCheck %s
-// RUN: cat %s | clang-repl -oop-executor -Xcc -fno-rtti | FileCheck %s
 // RUN: cat %s | clang-repl -Xcc -fno-rtti -Xcc -O2 | FileCheck %s
 
 extern "C" int printf(const char *, ...);
diff --git a/clang/test/Interpreter/lambda.cpp b/clang/test/Interpreter/lambda.cpp
index fa926925d7eda..df75274a050b2 100644
--- a/clang/test/Interpreter/lambda.cpp
+++ b/clang/test/Interpreter/lambda.cpp
@@ -1,7 +1,6 @@
 // REQUIRES: host-supports-jit
 // UNSUPPORTED: system-aix
 // RUN: cat %s | clang-repl | FileCheck %s
-// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 // RUN: cat %s | clang-repl -Xcc -O2 | FileCheck %s
 extern "C" int printf(const char *, ...);
 
diff --git a/clang/test/Interpreter/multiline.cpp b/clang/test/Interpreter/multiline.cpp
index 797a6cdba1361..054e61a7e3d62 100644
--- a/clang/test/Interpreter/multiline.cpp
+++ b/clang/test/Interpreter/multiline.cpp
@@ -1,7 +1,6 @@
 // REQUIRES: host-supports-jit
 // UNSUPPORTED: system-aix
 // RUN: cat %s | clang-repl | FileCheck %s
-// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
 
 extern "C" int printf(const char*,...);
 int i = \
diff --git a/clang/test/Interpreter/out-of-process.cpp b/clang/test/Interpreter/out-of-process.cpp
new file mode 100644
index 0000000000000..19db924cb32f1
--- /dev/null
+++ b/clang/test/Interpreter/out-of-process.cpp
@@ -0,0 +1,70 @@
+// REQUIRES: host-supports-jit, x86_64-linux
+// RUN: cat %s | clang-repl | FileCheck %s
+// RUN: cat %s | clang-repl -oop-executor | FileCheck %s
+
+extern "C" int printf(const char *, ...);
+
+// Test code undo.
+int x1 = 0;
+int x2 = 42;
+%undo
+int x2 = 24;
+auto r1 = printf("x1 = %d\n", x1);
+// CHECK: x1 = 0
+auto r2 = printf("x2 = %d\n", x2);
+// CHECK-NEXT: x2 = 24
+int foo() { return 1; }
+%undo
+int foo() { return 2; }
+auto r3 = printf("foo() = %d\n", foo());
+// CHECK-NEXT: foo() = 2
+inline int bar() { return 42;}
+auto r4 = bar();
+%undo
+auto r5 = bar();
+
+// Test weak execution.
+int __attribute__((weak)) weak_bar() { return 42; }
+auto r6 = printf("bar() = %d\n", weak_bar());
+// CHECK: bar() = 42
+int a = 12;
+static __typeof(a) b __attribute__((__weakref__("a")));
+int c = b;
+
+// Test dynamic libraries.
+extern "C" int ultimate_answer;
+extern "C" int calculate_answer();
+%lib libdynamic-library-test.so
+printf("Return value: %d\n", calculate_answer());
+// CHECK: Return value: 5
+printf("Variable: %d\n", ultimate_answer);
+// CHECK-NEXT: Variable: 42
+
+// Test lambdas.
+auto l1 = []() { printf("ONE\n"); return 42; };
+auto l2 = []() { printf("TWO\n"); return 17; };
+auto lambda_r1 = l1();
+// CHECK: ONE
+auto lambda_r2 = l2();
+// CHECK: TWO
+auto lambda_r3 = l2();
+// CHECK: TWO
+
+// Test multiline.
+int i = \
+  12;
+printf("i=%d\n", i);
+// CHECK: i=12
+void f(int x) \ 
+{                                               \
+  printf("x=\
+          %d", x); \
+}
+f(i);
+// CHECK: x=12
+
+// Test global destructor.
+struct D { float f = 1.0; D *m = nullptr; D(){} ~D() { printf("D[f=%f, m=0x%llx]\n", f, reinterpret_cast<unsigned long long>(m)); }} d;
+// CHECK: D[f=1.000000, m=0x0]
+
+%quit
\ No newline at end of file
diff --git a/clang/test/Interpreter/sanity.c b/clang/test/Interpreter/sanity.c
index 85f44558f5b6a..8893a12f9216a 100644
--- a/clang/test/Interpreter/sanity.c
+++ b/clang/test/Interpreter/sanity.c
@@ -2,10 +2,6 @@
 // RUN:   clang-repl -Xcc -fno-color-diagnostics -Xcc -Xclang -Xcc -ast-dump \
 // RUN:            -Xcc -Xclang -Xcc -ast-dump-filter -Xcc -Xclang -Xcc Test 2>&1| \
 // RUN:         FileCheck %s
-// RUN: cat %s | \
-// RUN:   clang-repl -oop-executor -Xcc -fno-color-diagnostics -Xcc -Xclang -Xcc -ast-dump \
-// RUN:            -Xcc -Xclang -Xcc -ast-dump-filter -Xcc -Xclang -Xcc Test 2>&1| \
-// RUN:         FileCheck %s
 
 int TestVar = 12;
 // CHECK: Dumping TestVar:
diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt
index d33d0273f6abb..adf4ea4180cba 100644
--- a/clang/tools/clang-repl/CMakeLists.txt
+++ b/clang/tools/clang-repl/CMakeLists.txt
@@ -6,6 +6,7 @@ set( LLVM_LINK_COMPONENTS
   OrcJIT
   OrcShared
   Support
+  TargetParser
   )
 
 add_clang_tool(clang-repl
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index b79f3a4dff5be..1c43e7b503646 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -25,6 +25,7 @@
 #include "llvm/Support/ManagedStatic.h" // llvm_shutdown
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/TargetSelect.h"
+#include "llvm/TargetParser/Host.h"
 #include <netdb.h>
 #include <optional>
 #include <sys/socket.h>
@@ -52,7 +53,8 @@ static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
 static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
                                              llvm::cl::desc("[code to run]"));
 
-static llvm::cl::OptionCategory OOPCategory("Out-of-process Execution Options");
+static llvm::cl::OptionCategory OOPCategory(
+    "Out-of-process Execution Options (Only available on ELF/Linux)");
 static llvm::cl::opt<std::string> OutOfProcessExecutor(
     "oop-executor",
     llvm::cl::desc("Launch an out-of-process executor to run code"),
@@ -164,7 +166,15 @@ ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos,
   return Comps;
 }
 
-static llvm::Error sanitizeArguments(const char *ArgV0) {
+static llvm::Error sanitizeOopArguments(const char *ArgV0) {
+  llvm::Triple SystemTriple(llvm::sys::getProcessTriple());
+  if ((OutOfProcessExecutor.getNumOccurrences() ||
+       OutOfProcessExecutorConnect.getNumOccurrences()) &&
+      (!SystemTriple.isOSBinFormatELF()))
+    return llvm::make_error<llvm::StringError>(
+        "Out-process-executors are currently only supported on ELF",
+        llvm::inconvertibleErrorCode());
+
   // Only one of -oop-executor and -oop-executor-connect can be used.
   if (!!OutOfProcessExecutor.getNumOccurrences() &&
       !!OutOfProcessExecutorConnect.getNumOccurrences())
@@ -178,7 +188,7 @@ static llvm::Error sanitizeArguments(const char *ArgV0) {
   if (!!OutOfProcessExecutor.getNumOccurrences() &&
       OutOfProcessExecutor.empty()) {
     llvm::SmallString<256> OOPExecutorPath(llvm::sys::fs::getMainExecutable(
-        ArgV0, reinterpret_cast<void *>(&sanitizeArguments)));
+        ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments)));
     llvm::sys::path::remove_filename(OOPExecutorPath);
     llvm::sys::path::append(OOPExecutorPath, "llvm-jitlink-executor");
     OutOfProcessExecutor = OOPExecutorPath.str().str();
@@ -187,7 +197,7 @@ static llvm::Error sanitizeArguments(const char *ArgV0) {
   // Out-of-process executors must run with the ORC runtime for destructor support.
   if (OrcRuntimePath.empty() && (OutOfProcessExecutor.getNumOccurrences() || OutOfProcessExecutorConnect.getNumOccurrences())) {
     llvm::SmallString<256> OrcPath(llvm::sys::fs::getMainExecutable(
-        ArgV0, reinterpret_cast<void *>(&sanitizeArguments)));
+        ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments)));
     llvm::sys::path::remove_filename(OrcPath); // Remove clang-repl filename.
     llvm::sys::path::remove_filename(OrcPath); // Remove ./bin directory.
     llvm::sys::path::append(OrcPath, "lib/clang/18/lib/x86_64-unknown-linux-gnu/liborc_rt.a");
@@ -413,7 +423,7 @@ int main(int argc, const char **argv) {
   if (CudaEnabled)
     DeviceCI->LoadRequestedPlugins();
 
-  ExitOnErr(sanitizeArguments(argv[0]));
+  ExitOnErr(sanitizeOopArguments(argv[0]));
 
   std::unique_ptr<clang::Interpreter> Interp;
 

>From c7dbe9ae97083478a8183abf297e5fdffc828916 Mon Sep 17 00:00:00 2001
From: James Hu <jhudson15869 at gmail.com>
Date: Mon, 29 Jan 2024 21:18:21 -0500
Subject: [PATCH 3/4] [clang-repl] Add trailing whitespace

---
 clang/test/Interpreter/out-of-process.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Interpreter/out-of-process.cpp b/clang/test/Interpreter/out-of-process.cpp
index 19db924cb32f1..39ae1442f1b13 100644
--- a/clang/test/Interpreter/out-of-process.cpp
+++ b/clang/test/Interpreter/out-of-process.cpp
@@ -67,4 +67,4 @@ f(i);
 struct D { float f = 1.0; D *m = nullptr; D(){} ~D() { printf("D[f=%f, m=0x%llx]\n", f, reinterpret_cast<unsigned long long>(m)); }} d;
 // CHECK: D[f=1.000000, m=0x0]
 
-%quit
\ No newline at end of file
+%quit

>From 158cc5ec91bf085ec9914de26a1554606a1e3338 Mon Sep 17 00:00:00 2001
From: James Hu <jhudson15869 at gmail.com>
Date: Mon, 29 Jan 2024 21:59:46 -0500
Subject: [PATCH 4/4] [clang-repl] Refactor reinitialization check and
 reorganize tests

---
 clang/lib/Interpreter/Interpreter.cpp      |  3 +++
 clang/test/Interpreter/dynamic-library.cpp |  1 +
 clang/test/Interpreter/out-of-process.cpp  |  9 ---------
 llvm/lib/ExecutionEngine/Orc/LLJIT.cpp     | 13 ++++++++-----
 4 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 50d1ddbfcf07b..13e6be3b54abc 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -492,6 +492,9 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
 
   if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load(
           EE->getExecutionSession(), name))
+    EE->getMainJITDylib().addGenerator(std::move(*DLSG));
+  else
+    return DLSG.takeError();
 
   return llvm::Error::success();
 }
diff --git a/clang/test/Interpreter/dynamic-library.cpp b/clang/test/Interpreter/dynamic-library.cpp
index 6c4621f729c1c..c2d186f71ebfe 100644
--- a/clang/test/Interpreter/dynamic-library.cpp
+++ b/clang/test/Interpreter/dynamic-library.cpp
@@ -15,6 +15,7 @@
 // }
 
 // RUN: cat %s | env LD_LIBRARY_PATH=%S/Inputs:$LD_LIBRARY_PATH clang-repl | FileCheck %s
+// RUN: cat %s | env LD_LIBRARY_PATH=%S/Inputs:$LD_LIBRARY_PATH clang-repl -oop-executor | FileCheck %s
 
 extern "C" int printf(const char* format, ...);
 
diff --git a/clang/test/Interpreter/out-of-process.cpp b/clang/test/Interpreter/out-of-process.cpp
index 39ae1442f1b13..ddbf86ec65881 100644
--- a/clang/test/Interpreter/out-of-process.cpp
+++ b/clang/test/Interpreter/out-of-process.cpp
@@ -31,15 +31,6 @@ int a = 12;
 static __typeof(a) b __attribute__((__weakref__("a")));
 int c = b;
 
-// Test dynamic libraries.
-extern "C" int ultimate_answer;
-extern "C" int calculate_answer();
-%lib libdynamic-library-test.so
-printf("Return value: %d\n", calculate_answer());
-// CHECK: Return value: 5
-printf("Variable: %d\n", ultimate_answer);
-// CHECK-NEXT: Variable: 42
-
 // Test lambdas.
 auto l1 = []() { printf("ONE\n"); return 42; };
 auto l2 = []() { printf("TWO\n"); return 17; };
diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
index 54030c83b7eb0..9bfbe951727c8 100644
--- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h"
 #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
 #include "llvm/ExecutionEngine/Orc/COFFPlatform.h"
@@ -108,10 +109,10 @@ class ORCPlatformSupport : public LLJIT::PlatformSupport {
     std::string WrapperToCall = "__orc_rt_jit_dlopen_wrapper";
     auto Triple = ES.getTargetTriple();
     if (Triple.isOSBinFormatELF()) {
-      WrapperToCall = FirstInitialization ? "__orc_rt_jit_dlopen_wrapper" : "__orc_rt_jit_dlupdate_wrapper";
-      if (FirstInitialization) {
-        FirstInitialization = false;
-      }
+      WrapperToCall = !InitializedJITDylibs.contains(&JD)
+                          ? "__orc_rt_jit_dlopen_wrapper"
+                          : "__orc_rt_jit_dlupdate_wrapper";
+      InitializedJITDylibs.insert(&JD);
     }
 
     if (auto WrapperAddr =
@@ -152,7 +153,9 @@ class ORCPlatformSupport : public LLJIT::PlatformSupport {
 private:
   orc::LLJIT &J;
   DenseMap<orc::JITDylib *, orc::ExecutorAddr> DSOHandles;
-  bool FirstInitialization = true; // Used to track if reinitialization is needed for ELF
+  llvm::SmallPtrSet<orc::JITDylib const *, 8>
+      InitializedJITDylibs; // Used to track if reinitialization is required for
+                            // ELF.
 };
 
 class GenericLLVMIRPlatformSupport;



More information about the cfe-commits mailing list