[clang] [llvm] [Clang-Repl] Add support for out-of-process execution. (PR #110418)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 29 02:47:00 PDT 2024
https://github.com/SahilPatidar created https://github.com/llvm/llvm-project/pull/110418
This PR introduces out-of-process (OOP) execution support for Clang-Repl. With this enhancement, two new flags, `oop-executor` and `oop-executor-connect`, are added to the Clang-Repl interface. These flags enable the launch of an external executor (`llvm-jitlink-executor`), which handles code execution in a separate process.
>From d3385116da9f9020bd6bb11b0e8ee8ed16348cfd Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Fri, 7 Jun 2024 13:30:08 +0530
Subject: [PATCH 1/5] [Clang-Repl] Implement out-of-process execution interface
for clang-repl
---
clang/include/clang/Interpreter/Interpreter.h | 9 +-
clang/lib/Interpreter/Interpreter.cpp | 33 ++-
clang/tools/clang-repl/CMakeLists.txt | 2 +
clang/tools/clang-repl/ClangRepl.cpp | 75 ++++++-
clang/tools/clang-repl/RemoteJITUtils.cpp | 208 ++++++++++++++++++
clang/tools/clang-repl/RemoteJITUtils.h | 42 ++++
llvm/lib/ExecutionEngine/Orc/LLJIT.cpp | 3 +
7 files changed, 364 insertions(+), 8 deletions(-)
create mode 100644 clang/tools/clang-repl/RemoteJITUtils.cpp
create mode 100644 clang/tools/clang-repl/RemoteJITUtils.h
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 1230a3a7016fae..147794c5870335 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/Shared/ExecutorAddress.h"
+#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
#include "llvm/Support/Error.h"
#include <memory>
#include <vector>
@@ -129,7 +130,13 @@ class Interpreter {
public:
virtual ~Interpreter();
static llvm::Expected<std::unique_ptr<Interpreter>>
- create(std::unique_ptr<CompilerInstance> CI);
+ create(std::unique_ptr<CompilerInstance> CI,
+ std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr);
+ static llvm::Expected<std::unique_ptr<Interpreter>>
+ createWithOOPExecutor(
+ std::unique_ptr<CompilerInstance> CI,
+ std::unique_ptr<llvm::orc::ExecutorProcessControl> EI,
+ llvm::StringRef OrcRuntimePath);
static llvm::Expected<std::unique_ptr<Interpreter>>
createWithCUDA(std::unique_ptr<CompilerInstance> CI,
std::unique_ptr<CompilerInstance> DCI);
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index bc96da811d44cb..21507906618c39 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -45,6 +45,7 @@
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
+#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
@@ -456,10 +457,12 @@ const char *const Runtimes = R"(
)";
llvm::Expected<std::unique_ptr<Interpreter>>
-Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
+Interpreter::create(std::unique_ptr<CompilerInstance> CI,
+ std::unique_ptr<llvm::orc::LLJITBuilder> JB) {
llvm::Error Err = llvm::Error::success();
auto Interp =
- std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err));
+ std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err,
+ JB? std::move(JB): nullptr));
if (Err)
return std::move(Err);
@@ -578,6 +581,26 @@ createJITTargetMachineBuilder(const std::string &TT) {
return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT));
}
+llvm::Expected<std::unique_ptr<Interpreter>>
+Interpreter::createWithOOPExecutor(
+ std::unique_ptr<CompilerInstance> CI,
+ std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
+ llvm::StringRef OrcRuntimePath) {
+ const std::string &TT = CI->getTargetOpts().Triple;
+ auto JTMB = createJITTargetMachineBuilder(TT);
+ if (!JTMB)
+ return JTMB.takeError();
+ auto JB = IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB));
+ if (!JB)
+ return JB.takeError();
+ if (EPC) {
+ JB.get()->setExecutorProcessControl(std::move(EPC));
+ JB.get()->setPlatformSetUp(llvm::orc::ExecutorNativePlatform(OrcRuntimePath.str()));
+ }
+
+ return Interpreter::create(std::move(CI), std::move(*JB));
+}
+
llvm::Error Interpreter::CreateExecutor() {
if (IncrExecutor)
return llvm::make_error<llvm::StringError>("Operation failed. "
@@ -702,10 +725,10 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
if (!EE)
return EE.takeError();
- auto &DL = EE->getDataLayout();
+ // auto &DL = EE->getDataLayout();
- if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load(
- name, DL.getGlobalPrefix()))
+ if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load(
+ EE->getExecutionSession(), name))
EE->getMainJITDylib().addGenerator(std::move(*DLSG));
else
return DLSG.takeError();
diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt
index 9ffe853d759caf..9c1eb1ea558c1f 100644
--- a/clang/tools/clang-repl/CMakeLists.txt
+++ b/clang/tools/clang-repl/CMakeLists.txt
@@ -4,7 +4,9 @@ set( LLVM_LINK_COMPONENTS
LineEditor
Option
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 08c54e6cafa901..21359df01c270f 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -10,6 +10,8 @@
//
//===----------------------------------------------------------------------===//
+#include "RemoteJITUtils.h"
+
#include "clang/Basic/Diagnostic.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendDiagnostic.h"
@@ -26,6 +28,8 @@
#include "llvm/Support/TargetSelect.h"
#include <optional>
+#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
+
// Disable LSan for this test.
// FIXME: Re-enable once we can assume GCC 13.2 or higher.
// https://llvm.org/github.com/llvm/llvm-project/issues/67586.
@@ -37,7 +41,18 @@ LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; }
static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);
static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
-
+static llvm::cl::OptionCategory OOPCategory("Out-of-process Execution Options (Only available on MachO)");
+static llvm::cl::opt<std::string> OOPExecutor(
+ "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> OOPExecutorConnectTCP(
+ "opp-connect",
+ llvm::cl::desc("Connect to an out-of-process executor through a TCP socket"),
+ llvm::cl::value_desc("<hostname>:<port>"));
+static llvm::cl::opt<std::string>
+ OrcRuntimePath("orc-runtime", llvm::cl::desc("Path to the ORC runtime"),
+ llvm::cl::cat(OOPCategory));
static llvm::cl::list<std::string>
ClangArgs("Xcc",
llvm::cl::desc("Argument to pass to the CompilerInvocation"),
@@ -47,6 +62,39 @@ 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::Error sanitizeOopArguments(const char *ArgV0) {
+ // Only one of -oop-executor and -oop-executor-connect can be used.
+ if (!!OOPExecutor.getNumOccurrences() &&
+ !!OOPExecutorConnectTCP.getNumOccurrences())
+ return llvm::make_error<llvm::StringError>(
+ "Only one of -" + OOPExecutor.ArgStr + " and -" +
+ OOPExecutorConnectTCP.ArgStr + " can be specified",
+ llvm::inconvertibleErrorCode());
+
+ // Out-of-process executors must run with the ORC runtime for destructor
+ // support.
+ if (OrcRuntimePath.empty() &&
+ (OOPExecutor.getNumOccurrences() ||
+ OOPExecutorConnectTCP.getNumOccurrences()))
+ return llvm::make_error<llvm::StringError>(
+ "ORC runtime required",
+ llvm::inconvertibleErrorCode());
+
+ // If -oop-executor was used but no value was specified then use a sensible
+ // default.
+ if (!!OOPExecutor.getNumOccurrences() &&
+ OOPExecutor.empty()) {
+ llvm::SmallString<256> OOPExecutorPath(llvm::sys::fs::getMainExecutable(
+ ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments)));
+ llvm::sys::path::remove_filename(OOPExecutorPath);
+ llvm::sys::path::append(OOPExecutorPath, "llvm-jitlink-executor");
+ OOPExecutor = OOPExecutorPath.str().str();
+ }
+
+ return llvm::Error::success();
+}
+
static void LLVMErrorHandler(void *UserData, const char *Message,
bool GenCrashDiag) {
auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
@@ -181,6 +229,25 @@ int main(int argc, const char **argv) {
DeviceCI = ExitOnErr(CB.CreateCudaDevice());
}
+ ExitOnErr(sanitizeOopArguments(argv[0]));
+
+
+ std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC;
+ // std::unique_ptr<llvm::orc::TaskDispatcher> D = nullptr;
+
+ // D = std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(std::nullopt);
+
+ // if (auto EPCOrErr =
+ // llvm::orc::SelfExecutorProcessControl::Create(nullptr, std::move(D), nullptr))
+ // EPC = std::move(*EPCOrErr);
+ if (OOPExecutor.getNumOccurrences()) {
+ // Launch an out-of-process executor locally in a child process.
+ int PID;
+ std::tie(EPC, PID) = ExitOnErr(launchLocalExecutor(OOPExecutor));
+ CB.SetTargetTriple(EPC->getTargetTriple().getTriple());
+ llvm::outs() << "executor process id = " << PID << "\n";
+ }
+
// FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
// can replace the boilerplate code for creation of the compiler instance.
std::unique_ptr<clang::CompilerInstance> CI;
@@ -212,11 +279,15 @@ int main(int argc, const char **argv) {
auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str()));
}
+ } else if (EPC) {
+ Interp = ExitOnErr(
+ clang::Interpreter::createWithOOPExecutor(std::move(CI),
+ std::move(EPC),
+ OrcRuntimePath));
} else
Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
bool HasError = false;
-
for (const std::string &input : OptInputs) {
if (auto Err = Interp->ParseAndExecute(input)) {
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
diff --git a/clang/tools/clang-repl/RemoteJITUtils.cpp b/clang/tools/clang-repl/RemoteJITUtils.cpp
new file mode 100644
index 00000000000000..7c896ab6d88e07
--- /dev/null
+++ b/clang/tools/clang-repl/RemoteJITUtils.cpp
@@ -0,0 +1,208 @@
+//===-- RemoteJITUtils.cpp - Utilities for remote-JITing --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RemoteJITUtils.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
+#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
+#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+#ifdef LLVM_ON_UNIX
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif // LLVM_ON_UNIX
+
+using namespace llvm;
+using namespace llvm::orc;
+
+Expected<std::unique_ptr<DefinitionGenerator>>
+loadDylib(ExecutionSession &ES, StringRef RemotePath) {
+ if (auto Handle = ES.getExecutorProcessControl().loadDylib(RemotePath.data()))
+ return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle);
+ else
+ return Handle.takeError();
+}
+
+static void findLocalExecutorHelper() {}
+std::string findLocalExecutor(const char *HostArgv0) {
+ // This just needs to be some static symbol in the binary; C++ doesn't
+ // allow taking the address of ::main however.
+ uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper);
+ void *VoidPtr = reinterpret_cast<void *>(UIntPtr);
+ SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr));
+ sys::path::remove_filename(FullName);
+ sys::path::append(FullName, "llvm-jitlink-executor");
+ return FullName.str().str();
+}
+
+#ifndef LLVM_ON_UNIX
+
+// FIXME: Add support for Windows.
+Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
+launchLocalExecutor(StringRef ExecutablePath) {
+ return make_error<StringError>(
+ "Remote JITing not yet supported on non-unix platforms",
+ inconvertibleErrorCode());
+}
+
+// FIXME: Add support for Windows.
+Expected<std::unique_ptr<SimpleRemoteEPC>>
+connectTCPSocket(StringRef NetworkAddress) {
+ return make_error<StringError>(
+ "Remote JITing not yet supported on non-unix platforms",
+ inconvertibleErrorCode());
+}
+
+#else
+
+Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
+launchLocalExecutor(StringRef ExecutablePath) {
+ constexpr int ReadEnd = 0;
+ constexpr int WriteEnd = 1;
+
+ if (!sys::fs::can_execute(ExecutablePath))
+ return make_error<StringError>(
+ formatv("Specified executor invalid: {0}", ExecutablePath),
+ inconvertibleErrorCode());
+
+ // Pipe FDs.
+ int ToExecutor[2];
+ int FromExecutor[2];
+
+ // Create pipes to/from the executor..
+ if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
+ return make_error<StringError>("Unable to create pipe for executor",
+ inconvertibleErrorCode());
+
+ pid_t ProcessID = fork();
+ if (ProcessID == 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[]> ExecPath, FDSpecifier, TestOutputFlag;
+ {
+ ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
+ strcpy(ExecPath.get(), ExecutablePath.data());
+
+ const char *TestOutputFlagStr = "test-jitloadergdb";
+ TestOutputFlag = std::make_unique<char[]>(strlen(TestOutputFlagStr) + 1);
+ strcpy(TestOutputFlag.get(), TestOutputFlagStr);
+
+ std::string FDSpecifierStr("filedescs=");
+ FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
+ FDSpecifierStr += ',';
+ FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
+ FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
+ strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
+ }
+
+ char *const Args[] = {ExecPath.get(), TestOutputFlag.get(),
+ FDSpecifier.get(), nullptr};
+ int RC = execvp(ExecPath.get(), Args);
+ if (RC != 0)
+ return make_error<StringError>(
+ "Unable to launch out-of-process executor '" + ExecutablePath + "'\n",
+ inconvertibleErrorCode());
+
+ llvm_unreachable("Fork won't return in success case");
+ }
+ // else we're the parent...
+
+ // Close the child ends of the pipes
+ close(ToExecutor[ReadEnd]);
+ close(FromExecutor[WriteEnd]);
+
+ auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
+ std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
+ SimpleRemoteEPC::Setup(),
+ FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
+ if (!EPC)
+ return EPC.takeError();
+
+ return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID));
+}
+
+static Expected<int> connectTCPSocketImpl(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 make_error<StringError>(
+ formatv("address resolution failed ({0})", gai_strerror(EC)),
+ inconvertibleErrorCode());
+
+ // 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) {
+ // If socket fails, maybe it's because the address family is not supported.
+ // Skip to the next addrinfo structure.
+ if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
+ continue;
+
+ // If connect works, we exit the loop with a working socket.
+ if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
+ break;
+
+ close(SockFD);
+ }
+ freeaddrinfo(AI);
+
+ // Did we reach the end of the loop without connecting to a valid endpoint?
+ if (Server == nullptr)
+ return make_error<StringError>("invalid hostname",
+ inconvertibleErrorCode());
+
+ return SockFD;
+}
+
+Expected<std::unique_ptr<SimpleRemoteEPC>>
+connectTCPSocket(StringRef NetworkAddress) {
+ auto CreateErr = [NetworkAddress](StringRef Details) {
+ return make_error<StringError>(
+ formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
+ Details),
+ inconvertibleErrorCode());
+ };
+
+ StringRef Host, PortStr;
+ std::tie(Host, PortStr) = NetworkAddress.split(':');
+ if (Host.empty())
+ return CreateErr("host name cannot be empty");
+ if (PortStr.empty())
+ return CreateErr("port cannot be empty");
+ int Port = 0;
+ if (PortStr.getAsInteger(10, Port))
+ return CreateErr("port number is not a valid integer");
+
+ Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
+ if (!SockFD)
+ return CreateErr(toString(SockFD.takeError()));
+
+ return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
+ std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
+ SimpleRemoteEPC::Setup(), *SockFD);
+}
+
+#endif
diff --git a/clang/tools/clang-repl/RemoteJITUtils.h b/clang/tools/clang-repl/RemoteJITUtils.h
new file mode 100644
index 00000000000000..ccf1f7b913eaab
--- /dev/null
+++ b/clang/tools/clang-repl/RemoteJITUtils.h
@@ -0,0 +1,42 @@
+//===-- RemoteJITUtils.h - Utilities for remote-JITing ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Utilities for ExecutorProcessControl-based remote JITing with Orc and
+// JITLink.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_REMOTEJITUTILS_H
+#define LLVM_CLANG_TOOLS_REMOTEJITUTILS_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/Layer.h"
+#include "llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h"
+#include "llvm/Support/Error.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+/// Find the default exectuable on disk and create a JITLinkExecutor for it.
+std::string findLocalExecutor(const char *HostArgv0);
+
+llvm::Expected<std::pair<std::unique_ptr<llvm::orc::SimpleRemoteEPC>, uint64_t>>
+launchLocalExecutor(llvm::StringRef ExecutablePath);
+
+/// Create a JITLinkExecutor that connects to the given network address
+/// through a TCP socket. A valid NetworkAddress provides hostname and port,
+/// e.g. localhost:20000.
+llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
+connectTCPSocket(llvm::StringRef NetworkAddress);
+
+llvm::Expected<std::unique_ptr<llvm::orc::DefinitionGenerator>>
+loadDylib(llvm::orc::ExecutionSession &ES, llvm::StringRef RemotePath);
+
+#endif
diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
index 19b3f3d6ea0380..3daeaafe038ec5 100644
--- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
@@ -1159,6 +1159,9 @@ Expected<JITDylibSP> ExecutorNativePlatform::operator()(LLJIT &J) {
auto &ES = J.getExecutionSession();
auto &PlatformJD = ES.createBareJITDylib("<Platform>");
+ if (auto DSGOrErr = EPCDynamicLibrarySearchGenerator::GetForTargetProcess(ES)) {
+ PlatformJD.addGenerator(std::move(*DSGOrErr));
+ }
PlatformJD.addToLinkOrder(*ProcessSymbolsJD);
J.setPlatformSupport(std::make_unique<ORCPlatformSupport>(J));
>From beee5bb3f68b92ecaa2e9508041ab1b0d1425079 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Fri, 28 Jun 2024 14:47:04 +0530
Subject: [PATCH 2/5] Refactor and improve initial code structure
---
clang/include/clang/Interpreter/Interpreter.h | 9 +-
.../clang/Interpreter}/RemoteJITUtils.h | 16 +-
clang/lib/Interpreter/CMakeLists.txt | 1 +
clang/lib/Interpreter/Interpreter.cpp | 26 ++--
.../Interpreter}/RemoteJITUtils.cpp | 145 ++++++++----------
clang/tools/clang-repl/ClangRepl.cpp | 75 +++++----
6 files changed, 129 insertions(+), 143 deletions(-)
rename clang/{tools/clang-repl => include/clang/Interpreter}/RemoteJITUtils.h (68%)
rename clang/{tools/clang-repl => lib/Interpreter}/RemoteJITUtils.cpp (57%)
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 147794c5870335..db482f1b7e4153 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -133,13 +133,12 @@ class Interpreter {
create(std::unique_ptr<CompilerInstance> CI,
std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr);
static llvm::Expected<std::unique_ptr<Interpreter>>
- createWithOOPExecutor(
- std::unique_ptr<CompilerInstance> CI,
- std::unique_ptr<llvm::orc::ExecutorProcessControl> EI,
- llvm::StringRef OrcRuntimePath);
- static llvm::Expected<std::unique_ptr<Interpreter>>
createWithCUDA(std::unique_ptr<CompilerInstance> CI,
std::unique_ptr<CompilerInstance> DCI);
+ static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
+ createLLJITBuilder(
+ std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
+ llvm::StringRef OrcRuntimePath);
const ASTContext &getASTContext() const;
ASTContext &getASTContext();
const CompilerInstance *getCompilerInstance() const;
diff --git a/clang/tools/clang-repl/RemoteJITUtils.h b/clang/include/clang/Interpreter/RemoteJITUtils.h
similarity index 68%
rename from clang/tools/clang-repl/RemoteJITUtils.h
rename to clang/include/clang/Interpreter/RemoteJITUtils.h
index ccf1f7b913eaab..c0f3f424629fbd 100644
--- a/clang/tools/clang-repl/RemoteJITUtils.h
+++ b/clang/include/clang/Interpreter/RemoteJITUtils.h
@@ -11,8 +11,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_TOOLS_REMOTEJITUTILS_H
-#define LLVM_CLANG_TOOLS_REMOTEJITUTILS_H
+#ifndef LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H
+#define LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H
#include "llvm/ADT/StringRef.h"
#include "llvm/ExecutionEngine/Orc/Core.h"
@@ -24,11 +24,8 @@
#include <memory>
#include <string>
-/// Find the default exectuable on disk and create a JITLinkExecutor for it.
-std::string findLocalExecutor(const char *HostArgv0);
-
-llvm::Expected<std::pair<std::unique_ptr<llvm::orc::SimpleRemoteEPC>, uint64_t>>
-launchLocalExecutor(llvm::StringRef ExecutablePath);
+llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
+launchExecutor(llvm::StringRef ExecutablePath);
/// Create a JITLinkExecutor that connects to the given network address
/// through a TCP socket. A valid NetworkAddress provides hostname and port,
@@ -36,7 +33,4 @@ launchLocalExecutor(llvm::StringRef ExecutablePath);
llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
connectTCPSocket(llvm::StringRef NetworkAddress);
-llvm::Expected<std::unique_ptr<llvm::orc::DefinitionGenerator>>
-loadDylib(llvm::orc::ExecutionSession &ES, llvm::StringRef RemotePath);
-
-#endif
+#endif // LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index 2cc7c59b61d318..4055898af3f35a 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -24,6 +24,7 @@ add_clang_library(clangInterpreter
Interpreter.cpp
InterpreterValuePrinter.cpp
InterpreterUtils.cpp
+ RemoteJITUtils.cpp
Value.cpp
${WASM_SRC}
PARTIAL_SOURCES_INTENDED
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 21507906618c39..c281c917cff4b5 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -44,8 +44,8 @@
#include "clang/Sema/Lookup.h"
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
-#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
+#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorHandling.h"
@@ -460,9 +460,8 @@ llvm::Expected<std::unique_ptr<Interpreter>>
Interpreter::create(std::unique_ptr<CompilerInstance> CI,
std::unique_ptr<llvm::orc::LLJITBuilder> JB) {
llvm::Error Err = llvm::Error::success();
- auto Interp =
- std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err,
- JB? std::move(JB): nullptr));
+ auto Interp = std::unique_ptr<Interpreter>(
+ new Interpreter(std::move(CI), Err, JB ? std::move(JB) : nullptr));
if (Err)
return std::move(Err);
@@ -581,24 +580,23 @@ createJITTargetMachineBuilder(const std::string &TT) {
return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT));
}
-llvm::Expected<std::unique_ptr<Interpreter>>
-Interpreter::createWithOOPExecutor(
- std::unique_ptr<CompilerInstance> CI,
+llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
+Interpreter::createLLJITBuilder(
std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
llvm::StringRef OrcRuntimePath) {
- const std::string &TT = CI->getTargetOpts().Triple;
+ const std::string &TT = EPC->getTargetTriple().getTriple();
auto JTMB = createJITTargetMachineBuilder(TT);
if (!JTMB)
return JTMB.takeError();
auto JB = IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB));
if (!JB)
return JB.takeError();
- if (EPC) {
- JB.get()->setExecutorProcessControl(std::move(EPC));
- JB.get()->setPlatformSetUp(llvm::orc::ExecutorNativePlatform(OrcRuntimePath.str()));
- }
- return Interpreter::create(std::move(CI), std::move(*JB));
+ (*JB)->setExecutorProcessControl(std::move(EPC));
+ (*JB)->setPlatformSetUp(
+ llvm::orc::ExecutorNativePlatform(OrcRuntimePath.str()));
+
+ return std::move(*JB);
}
llvm::Error Interpreter::CreateExecutor() {
@@ -725,8 +723,6 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
if (!EE)
return EE.takeError();
- // auto &DL = EE->getDataLayout();
-
if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load(
EE->getExecutionSession(), name))
EE->getMainJITDylib().addGenerator(std::move(*DLSG));
diff --git a/clang/tools/clang-repl/RemoteJITUtils.cpp b/clang/lib/Interpreter/RemoteJITUtils.cpp
similarity index 57%
rename from clang/tools/clang-repl/RemoteJITUtils.cpp
rename to clang/lib/Interpreter/RemoteJITUtils.cpp
index 7c896ab6d88e07..75f2ccf069729a 100644
--- a/clang/tools/clang-repl/RemoteJITUtils.cpp
+++ b/clang/lib/Interpreter/RemoteJITUtils.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "RemoteJITUtils.h"
+#include "clang/Interpreter/RemoteJITUtils.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
@@ -27,67 +27,44 @@
using namespace llvm;
using namespace llvm::orc;
-Expected<std::unique_ptr<DefinitionGenerator>>
-loadDylib(ExecutionSession &ES, StringRef RemotePath) {
- if (auto Handle = ES.getExecutorProcessControl().loadDylib(RemotePath.data()))
- return std::make_unique<EPCDynamicLibrarySearchGenerator>(ES, *Handle);
- else
- return Handle.takeError();
-}
-
-static void findLocalExecutorHelper() {}
-std::string findLocalExecutor(const char *HostArgv0) {
- // This just needs to be some static symbol in the binary; C++ doesn't
- // allow taking the address of ::main however.
- uintptr_t UIntPtr = reinterpret_cast<uintptr_t>(&findLocalExecutorHelper);
- void *VoidPtr = reinterpret_cast<void *>(UIntPtr);
- SmallString<256> FullName(sys::fs::getMainExecutable(HostArgv0, VoidPtr));
- sys::path::remove_filename(FullName);
- sys::path::append(FullName, "llvm-jitlink-executor");
- return FullName.str().str();
-}
-
-#ifndef LLVM_ON_UNIX
-
-// FIXME: Add support for Windows.
-Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
-launchLocalExecutor(StringRef ExecutablePath) {
- return make_error<StringError>(
- "Remote JITing not yet supported on non-unix platforms",
- inconvertibleErrorCode());
-}
-
-// FIXME: Add support for Windows.
Expected<std::unique_ptr<SimpleRemoteEPC>>
-connectTCPSocket(StringRef NetworkAddress) {
+launchExecutor(StringRef ExecutablePath) {
+#ifndef LLVM_ON_UNIX
+ // FIXME: Add support for Windows.
+ return make_error<StringError>("-" + ExecutablePath +
+ " not supported on non-unix platforms",
+ inconvertibleErrorCode());
+#elif !LLVM_ENABLE_THREADS
+ // Out of process mode using SimpleRemoteEPC depends on threads.
return make_error<StringError>(
- "Remote JITing not yet supported on non-unix platforms",
+ "-" + ExecutablePath +
+ " requires threads, but LLVM was built with "
+ "LLVM_ENABLE_THREADS=Off",
inconvertibleErrorCode());
-}
-
#else
-Expected<std::pair<std::unique_ptr<SimpleRemoteEPC>, uint64_t>>
-launchLocalExecutor(StringRef ExecutablePath) {
- constexpr int ReadEnd = 0;
- constexpr int WriteEnd = 1;
-
if (!sys::fs::can_execute(ExecutablePath))
return make_error<StringError>(
formatv("Specified executor invalid: {0}", ExecutablePath),
inconvertibleErrorCode());
+ 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 make_error<StringError>("Unable to create pipe for executor",
inconvertibleErrorCode());
- pid_t ProcessID = fork();
- if (ProcessID == 0) {
+ ChildPID = fork();
+
+ if (ChildPID == 0) {
// In the child...
// Close the parent ends of the pipes
@@ -95,14 +72,10 @@ launchLocalExecutor(StringRef ExecutablePath) {
close(FromExecutor[ReadEnd]);
// Execute the child process.
- std::unique_ptr<char[]> ExecPath, FDSpecifier, TestOutputFlag;
+ std::unique_ptr<char[]> ExecutorPath, FDSpecifier;
{
- ExecPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
- strcpy(ExecPath.get(), ExecutablePath.data());
-
- const char *TestOutputFlagStr = "test-jitloadergdb";
- TestOutputFlag = std::make_unique<char[]>(strlen(TestOutputFlagStr) + 1);
- strcpy(TestOutputFlag.get(), TestOutputFlagStr);
+ ExecutorPath = std::make_unique<char[]>(ExecutablePath.size() + 1);
+ strcpy(ExecutorPath.get(), ExecutablePath.data());
std::string FDSpecifierStr("filedescs=");
FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
@@ -112,15 +85,13 @@ launchLocalExecutor(StringRef ExecutablePath) {
strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
}
- char *const Args[] = {ExecPath.get(), TestOutputFlag.get(),
- FDSpecifier.get(), nullptr};
- int RC = execvp(ExecPath.get(), Args);
- if (RC != 0)
- return make_error<StringError>(
- "Unable to launch out-of-process executor '" + ExecutablePath + "'\n",
- inconvertibleErrorCode());
-
- llvm_unreachable("Fork won't return in success case");
+ char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};
+ int RC = execvp(ExecutorPath.get(), Args);
+ if (RC != 0) {
+ errs() << "unable to launch out-of-process executor \""
+ << ExecutorPath.get() << "\"\n";
+ exit(1);
+ }
}
// else we're the parent...
@@ -128,16 +99,16 @@ launchLocalExecutor(StringRef ExecutablePath) {
close(ToExecutor[ReadEnd]);
close(FromExecutor[WriteEnd]);
- auto EPC = SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
- std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
- SimpleRemoteEPC::Setup(),
- FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
- if (!EPC)
- return EPC.takeError();
+ auto S = SimpleRemoteEPC::Setup();
- return std::make_pair(std::move(*EPC), static_cast<uint64_t>(ProcessID));
+ return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
+ std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
+ std::move(S), FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
+#endif
}
+#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
+
static Expected<int> connectTCPSocketImpl(std::string Host,
std::string PortStr) {
addrinfo *AI;
@@ -150,18 +121,17 @@ static Expected<int> connectTCPSocketImpl(std::string Host,
return make_error<StringError>(
formatv("address resolution failed ({0})", gai_strerror(EC)),
inconvertibleErrorCode());
-
// 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) {
- // If socket fails, maybe it's because the address family is not supported.
- // Skip to the next addrinfo structure.
+ // 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 works, we exit the loop with a working socket.
+ // If connect returns null, we exit the loop with a working socket.
if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
break;
@@ -169,17 +139,33 @@ static Expected<int> connectTCPSocketImpl(std::string Host,
}
freeaddrinfo(AI);
- // Did we reach the end of the loop without connecting to a valid endpoint?
+ // 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 make_error<StringError>("invalid hostname",
inconvertibleErrorCode());
return SockFD;
}
+#endif
Expected<std::unique_ptr<SimpleRemoteEPC>>
connectTCPSocket(StringRef NetworkAddress) {
- auto CreateErr = [NetworkAddress](StringRef Details) {
+#ifndef LLVM_ON_UNIX
+ // FIXME: Add TCP support for Windows.
+ return make_error<StringError>("-" + NetworkAddress +
+ " not supported on non-unix platforms",
+ inconvertibleErrorCode());
+#elif !LLVM_ENABLE_THREADS
+ // Out of process mode using SimpleRemoteEPC depends on threads.
+ return make_error<StringError>(
+ "-" + NetworkAddress +
+ " requires threads, but LLVM was built with "
+ "LLVM_ENABLE_THREADS=Off",
+ inconvertibleErrorCode());
+#else
+
+ auto CreateErr = [NetworkAddress](Twine Details) {
return make_error<StringError>(
formatv("Failed to connect TCP socket '{0}': {1}", NetworkAddress,
Details),
@@ -189,20 +175,21 @@ connectTCPSocket(StringRef NetworkAddress) {
StringRef Host, PortStr;
std::tie(Host, PortStr) = NetworkAddress.split(':');
if (Host.empty())
- return CreateErr("host name cannot be empty");
+ return CreateErr("Host name for -" + NetworkAddress + " can not be empty");
if (PortStr.empty())
- return CreateErr("port cannot be empty");
+ return CreateErr("Port number in -" + NetworkAddress + " can not be empty");
int Port = 0;
if (PortStr.getAsInteger(10, Port))
- return CreateErr("port number is not a valid integer");
+ return CreateErr("Port number '" + PortStr + "' is not a valid integer");
Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str());
if (!SockFD)
- return CreateErr(toString(SockFD.takeError()));
+ return SockFD.takeError();
+
+ auto S = SimpleRemoteEPC::Setup();
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
- SimpleRemoteEPC::Setup(), *SockFD);
-}
-
+ std::move(S), *SockFD, *SockFD);
#endif
+}
\ No newline at end of file
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index 21359df01c270f..3502bec5f9f99c 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -10,7 +10,7 @@
//
//===----------------------------------------------------------------------===//
-#include "RemoteJITUtils.h"
+#include "clang/Interpreter/RemoteJITUtils.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Frontend/CompilerInstance.h"
@@ -26,6 +26,7 @@
#include "llvm/Support/ManagedStatic.h" // llvm_shutdown
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/TargetParser/Host.h"
#include <optional>
#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
@@ -38,17 +39,21 @@
LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; }
#endif
+#define DEBUG_TYPE "clang-repl"
+
static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);
static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
-static llvm::cl::OptionCategory OOPCategory("Out-of-process Execution Options (Only available on MachO)");
-static llvm::cl::opt<std::string> OOPExecutor(
- "oop-executor",
- llvm::cl::desc("Launch an out-of-process executor to run code"),
- llvm::cl::ValueOptional, llvm::cl::cat(OOPCategory));
+static llvm::cl::OptionCategory
+ OOPCategory("Out-of-process Execution Options");
+static llvm::cl::opt<std::string>
+ OOPExecutor("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> OOPExecutorConnectTCP(
- "opp-connect",
- llvm::cl::desc("Connect to an out-of-process executor through a TCP socket"),
+ "oop-executor-connect",
+ llvm::cl::desc(
+ "Connect to an out-of-process executor through a TCP socket"),
llvm::cl::value_desc("<hostname>:<port>"));
static llvm::cl::opt<std::string>
OrcRuntimePath("orc-runtime", llvm::cl::desc("Path to the ORC runtime"),
@@ -62,7 +67,6 @@ 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::Error sanitizeOopArguments(const char *ArgV0) {
// Only one of -oop-executor and -oop-executor-connect can be used.
if (!!OOPExecutor.getNumOccurrences() &&
@@ -74,17 +78,25 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) {
// Out-of-process executors must run with the ORC runtime for destructor
// support.
- if (OrcRuntimePath.empty() &&
- (OOPExecutor.getNumOccurrences() ||
- OOPExecutorConnectTCP.getNumOccurrences()))
- return llvm::make_error<llvm::StringError>(
- "ORC runtime required",
- llvm::inconvertibleErrorCode());
+ if (OrcRuntimePath.empty() && (OOPExecutor.getNumOccurrences() ||
+ OOPExecutorConnectTCP.getNumOccurrences())) {
+ llvm::SmallString<256> OrcPath(llvm::sys::fs::getMainExecutable(
+ 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::Triple SystemTriple(llvm::sys::getProcessTriple());
+ llvm::StringRef Path;
+ if (SystemTriple.isOSBinFormatELF())
+ Path = "lib/clang/19/lib/x86_64-unknown-linux-gnu/liborc_rt.a";
+ else if (SystemTriple.isOSBinFormatMachO())
+ Path = "lib/clang/19/lib/darwin/liborc_rt_osx.a";
+ llvm::sys::path::append(OrcPath, Path);
+ OrcRuntimePath = OrcPath.str().str();
+ }
// If -oop-executor was used but no value was specified then use a sensible
// default.
- if (!!OOPExecutor.getNumOccurrences() &&
- OOPExecutor.empty()) {
+ if (!!OOPExecutor.getNumOccurrences() && OOPExecutor.empty()) {
llvm::SmallString<256> OOPExecutorPath(llvm::sys::fs::getMainExecutable(
ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments)));
llvm::sys::path::remove_filename(OOPExecutorPath);
@@ -231,21 +243,19 @@ int main(int argc, const char **argv) {
ExitOnErr(sanitizeOopArguments(argv[0]));
-
std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC;
- // std::unique_ptr<llvm::orc::TaskDispatcher> D = nullptr;
-
- // D = std::make_unique<llvm::orc::DynamicThreadPoolTaskDispatcher>(std::nullopt);
-
- // if (auto EPCOrErr =
- // llvm::orc::SelfExecutorProcessControl::Create(nullptr, std::move(D), nullptr))
- // EPC = std::move(*EPCOrErr);
if (OOPExecutor.getNumOccurrences()) {
// Launch an out-of-process executor locally in a child process.
- int PID;
- std::tie(EPC, PID) = ExitOnErr(launchLocalExecutor(OOPExecutor));
+ EPC = ExitOnErr(launchExecutor(OOPExecutor));
+ } else if (OOPExecutorConnectTCP.getNumOccurrences()) {
+ EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnectTCP));
+ }
+
+ std::unique_ptr<llvm::orc::LLJITBuilder> JB;
+ if (EPC) {
CB.SetTargetTriple(EPC->getTargetTriple().getTriple());
- llvm::outs() << "executor process id = " << PID << "\n";
+ JB = ExitOnErr(
+ clang::Interpreter::createLLJITBuilder(std::move(EPC), OrcRuntimePath));
}
// FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
@@ -279,15 +289,14 @@ int main(int argc, const char **argv) {
auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str()));
}
- } else if (EPC) {
- Interp = ExitOnErr(
- clang::Interpreter::createWithOOPExecutor(std::move(CI),
- std::move(EPC),
- OrcRuntimePath));
+ } else if (JB) {
+ Interp =
+ ExitOnErr(clang::Interpreter::create(std::move(CI), std::move(JB)));
} else
Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
bool HasError = false;
+
for (const std::string &input : OptInputs) {
if (auto Err = Interp->ParseAndExecute(input)) {
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
>From 1fdff47ad179ffb7014cc8fce4a97cac4d833976 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 17 Jul 2024 10:40:53 +0530
Subject: [PATCH 3/5] Add shared memory support
---
.../clang/Interpreter/RemoteJITUtils.h | 6 +-
clang/lib/Interpreter/RemoteJITUtils.cpp | 69 ++++++++++++++++++-
clang/tools/clang-repl/ClangRepl.cpp | 16 ++++-
3 files changed, 85 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Interpreter/RemoteJITUtils.h b/clang/include/clang/Interpreter/RemoteJITUtils.h
index c0f3f424629fbd..8705a3b1f669de 100644
--- a/clang/include/clang/Interpreter/RemoteJITUtils.h
+++ b/clang/include/clang/Interpreter/RemoteJITUtils.h
@@ -25,12 +25,14 @@
#include <string>
llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
-launchExecutor(llvm::StringRef ExecutablePath);
+launchExecutor(llvm::StringRef ExecutablePath, bool UseSharedMemory,
+ llvm::StringRef SlabAllocateSizeString);
/// Create a JITLinkExecutor that connects to the given network address
/// through a TCP socket. A valid NetworkAddress provides hostname and port,
/// e.g. localhost:20000.
llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>>
-connectTCPSocket(llvm::StringRef NetworkAddress);
+connectTCPSocket(llvm::StringRef NetworkAddress, bool UseSharedMemory,
+ llvm::StringRef SlabAllocateSizeString);
#endif // LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H
diff --git a/clang/lib/Interpreter/RemoteJITUtils.cpp b/clang/lib/Interpreter/RemoteJITUtils.cpp
index 75f2ccf069729a..a103307ebf0eae 100644
--- a/clang/lib/Interpreter/RemoteJITUtils.cpp
+++ b/clang/lib/Interpreter/RemoteJITUtils.cpp
@@ -12,6 +12,8 @@
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
+#include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
#include "llvm/Support/FileSystem.h"
@@ -27,8 +29,62 @@
using namespace llvm;
using namespace llvm::orc;
+static ExitOnError ExitOnErr;
+
+Expected<uint64_t> getSlabAllocSize(StringRef SizeString) {
+ SizeString = SizeString.trim();
+
+ uint64_t Units = 1024;
+
+ if (SizeString.ends_with_insensitive("kb"))
+ SizeString = SizeString.drop_back(2).rtrim();
+ else if (SizeString.ends_with_insensitive("mb")) {
+ Units = 1024 * 1024;
+ SizeString = SizeString.drop_back(2).rtrim();
+ } else if (SizeString.ends_with_insensitive("gb")) {
+ Units = 1024 * 1024 * 1024;
+ SizeString = SizeString.drop_back(2).rtrim();
+ }
+
+ uint64_t SlabSize = 0;
+ if (SizeString.getAsInteger(10, SlabSize))
+ return make_error<StringError>("Invalid numeric format for slab size",
+ inconvertibleErrorCode());
+
+ return SlabSize * Units;
+}
+
+Expected<std::unique_ptr<jitlink::JITLinkMemoryManager>>
+createSharedMemoryManager(SimpleRemoteEPC &SREPC, StringRef SlabAllocateSizeString) {
+ SharedMemoryMapper::SymbolAddrs SAs;
+ if (auto Err = SREPC.getBootstrapSymbols(
+ {{SAs.Instance, rt::ExecutorSharedMemoryMapperServiceInstanceName},
+ {SAs.Reserve,
+ rt::ExecutorSharedMemoryMapperServiceReserveWrapperName},
+ {SAs.Initialize,
+ rt::ExecutorSharedMemoryMapperServiceInitializeWrapperName},
+ {SAs.Deinitialize,
+ rt::ExecutorSharedMemoryMapperServiceDeinitializeWrapperName},
+ {SAs.Release,
+ rt::ExecutorSharedMemoryMapperServiceReleaseWrapperName}}))
+ return std::move(Err);
+
+#ifdef _WIN32
+ size_t SlabSize = 1024 * 1024;
+#else
+ size_t SlabSize = 1024 * 1024 * 1024;
+#endif
+
+ if (!SlabAllocateSizeString.empty())
+ SlabSize = ExitOnErr(getSlabAllocSize(SlabAllocateSizeString));
+
+ return MapperJITLinkMemoryManager::CreateWithMapper<SharedMemoryMapper>(
+ SlabSize, SREPC, SAs);
+}
+
Expected<std::unique_ptr<SimpleRemoteEPC>>
-launchExecutor(StringRef ExecutablePath) {
+launchExecutor(StringRef ExecutablePath, bool UseSharedMemory,
+ llvm::StringRef SlabAllocateSizeString) {
#ifndef LLVM_ON_UNIX
// FIXME: Add support for Windows.
return make_error<StringError>("-" + ExecutablePath +
@@ -100,6 +156,10 @@ launchExecutor(StringRef ExecutablePath) {
close(FromExecutor[WriteEnd]);
auto S = SimpleRemoteEPC::Setup();
+ if (UseSharedMemory)
+ S.CreateMemoryManager = [SlabAllocateSizeString](SimpleRemoteEPC &EPC) {
+ return createSharedMemoryManager(EPC, SlabAllocateSizeString);
+ };
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
@@ -150,7 +210,8 @@ static Expected<int> connectTCPSocketImpl(std::string Host,
#endif
Expected<std::unique_ptr<SimpleRemoteEPC>>
-connectTCPSocket(StringRef NetworkAddress) {
+connectTCPSocket(StringRef NetworkAddress, bool UseSharedMemory,
+ llvm::StringRef SlabAllocateSizeString) {
#ifndef LLVM_ON_UNIX
// FIXME: Add TCP support for Windows.
return make_error<StringError>("-" + NetworkAddress +
@@ -187,6 +248,10 @@ connectTCPSocket(StringRef NetworkAddress) {
return SockFD.takeError();
auto S = SimpleRemoteEPC::Setup();
+ if (UseSharedMemory)
+ S.CreateMemoryManager = [SlabAllocateSizeString](SimpleRemoteEPC &EPC) {
+ return createSharedMemoryManager(EPC, SlabAllocateSizeString);
+ };
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index 3502bec5f9f99c..6099dfb9af98b1 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -46,6 +46,12 @@ static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
static llvm::cl::OptionCategory
OOPCategory("Out-of-process Execution Options");
+static llvm::cl::opt<std::string> SlabAllocateSizeString(
+ "slab-allocate",
+ llvm::cl::desc("Allocate from a slab of the given size "
+ "(allowable suffixes: Kb, Mb, Gb. default = "
+ "Kb)"),
+ llvm::cl::init(""), llvm::cl::cat(OOPCategory));
static llvm::cl::opt<std::string>
OOPExecutor("oop-executor",
llvm::cl::desc("Launch an out-of-process executor to run code"),
@@ -58,6 +64,10 @@ static llvm::cl::opt<std::string> OOPExecutorConnectTCP(
static llvm::cl::opt<std::string>
OrcRuntimePath("orc-runtime", llvm::cl::desc("Path to the ORC runtime"),
llvm::cl::cat(OOPCategory));
+static llvm::cl::opt<bool> UseSharedMemory(
+ "use-shared-memory",
+ llvm::cl::desc("Use shared memory to transfer generated code and data"),
+ llvm::cl::init(false), llvm::cl::cat(OOPCategory));
static llvm::cl::list<std::string>
ClangArgs("Xcc",
llvm::cl::desc("Argument to pass to the CompilerInvocation"),
@@ -246,9 +256,11 @@ int main(int argc, const char **argv) {
std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC;
if (OOPExecutor.getNumOccurrences()) {
// Launch an out-of-process executor locally in a child process.
- EPC = ExitOnErr(launchExecutor(OOPExecutor));
+ EPC = ExitOnErr(
+ launchExecutor(OOPExecutor, UseSharedMemory, SlabAllocateSizeString));
} else if (OOPExecutorConnectTCP.getNumOccurrences()) {
- EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnectTCP));
+ EPC = ExitOnErr(connectTCPSocket(
+ OOPExecutorConnectTCP, UseSharedMemory, SlabAllocateSizeString));
}
std::unique_ptr<llvm::orc::LLJITBuilder> JB;
>From 97e314e98d4c2235490067f661c7dafb436fcec3 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 29 Sep 2024 14:26:58 +0530
Subject: [PATCH 4/5] Fix code format
---
clang/lib/Interpreter/RemoteJITUtils.cpp | 3 ++-
clang/tools/clang-repl/ClangRepl.cpp | 11 +++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Interpreter/RemoteJITUtils.cpp b/clang/lib/Interpreter/RemoteJITUtils.cpp
index a103307ebf0eae..450748eb6df445 100644
--- a/clang/lib/Interpreter/RemoteJITUtils.cpp
+++ b/clang/lib/Interpreter/RemoteJITUtils.cpp
@@ -55,7 +55,8 @@ Expected<uint64_t> getSlabAllocSize(StringRef SizeString) {
}
Expected<std::unique_ptr<jitlink::JITLinkMemoryManager>>
-createSharedMemoryManager(SimpleRemoteEPC &SREPC, StringRef SlabAllocateSizeString) {
+createSharedMemoryManager(SimpleRemoteEPC &SREPC,
+ StringRef SlabAllocateSizeString) {
SharedMemoryMapper::SymbolAddrs SAs;
if (auto Err = SREPC.getBootstrapSymbols(
{{SAs.Instance, rt::ExecutorSharedMemoryMapperServiceInstanceName},
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index 6099dfb9af98b1..68c0d309a158c3 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -44,13 +44,12 @@ LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; }
static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);
static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
-static llvm::cl::OptionCategory
- OOPCategory("Out-of-process Execution Options");
+static llvm::cl::OptionCategory OOPCategory("Out-of-process Execution Options");
static llvm::cl::opt<std::string> SlabAllocateSizeString(
"slab-allocate",
llvm::cl::desc("Allocate from a slab of the given size "
- "(allowable suffixes: Kb, Mb, Gb. default = "
- "Kb)"),
+ "(allowable suffixes: Kb, Mb, Gb. default = "
+ "Kb)"),
llvm::cl::init(""), llvm::cl::cat(OOPCategory));
static llvm::cl::opt<std::string>
OOPExecutor("oop-executor",
@@ -259,8 +258,8 @@ int main(int argc, const char **argv) {
EPC = ExitOnErr(
launchExecutor(OOPExecutor, UseSharedMemory, SlabAllocateSizeString));
} else if (OOPExecutorConnectTCP.getNumOccurrences()) {
- EPC = ExitOnErr(connectTCPSocket(
- OOPExecutorConnectTCP, UseSharedMemory, SlabAllocateSizeString));
+ EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnectTCP, UseSharedMemory,
+ SlabAllocateSizeString));
}
std::unique_ptr<llvm::orc::LLJITBuilder> JB;
>From fa27f86f0630da403f1b5143ac36a9a6ed934af7 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 29 Sep 2024 14:53:48 +0530
Subject: [PATCH 5/5] Fix orc runtime path
---
clang/tools/clang-repl/ClangRepl.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index 68c0d309a158c3..9f4d4cb69bb467 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -96,9 +96,9 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) {
llvm::Triple SystemTriple(llvm::sys::getProcessTriple());
llvm::StringRef Path;
if (SystemTriple.isOSBinFormatELF())
- Path = "lib/clang/19/lib/x86_64-unknown-linux-gnu/liborc_rt.a";
+ Path = "lib/clang/20/lib/x86_64-unknown-linux-gnu/liborc_rt.a";
else if (SystemTriple.isOSBinFormatMachO())
- Path = "lib/clang/19/lib/darwin/liborc_rt_osx.a";
+ Path = "lib/clang/20/lib/darwin/liborc_rt_osx.a";
llvm::sys::path::append(OrcPath, Path);
OrcRuntimePath = OrcPath.str().str();
}
More information about the cfe-commits
mailing list