[clang] [llvm] [compiler-rt] [clang-repl] [ORC] Add support for out-of-process execution on ELF (PR #79936)
via llvm-commits
llvm-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 llvm-commits
mailing list