https://github.com/SahilPatidar updated https://github.com/llvm/llvm-project/pull/110418
>From 519dd2c99bc2200d364a184e416ec03b609e1910 Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Fri, 7 Jun 2024 13:30:08 +0530 Subject: [PATCH 01/14] [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 7aebbe7a19436a..11164e3b8f5c65 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 b31f4629565493..2d88f11836eea4 100644 --- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp +++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp @@ -1167,6 +1167,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 621420fa968c03b78eeafa023c33917c901e85eb Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Fri, 28 Jun 2024 14:47:04 +0530 Subject: [PATCH 02/14] 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 d5ffe78251d253..eb99e316ebc0a7 100644 --- a/clang/lib/Interpreter/CMakeLists.txt +++ b/clang/lib/Interpreter/CMakeLists.txt @@ -25,6 +25,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 94dec75ae26f07b7f178abab52a02ec9167fd950 Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Wed, 17 Jul 2024 10:40:53 +0530 Subject: [PATCH 03/14] 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 b29ea8201d130c092552b3b3383f24d72c34b150 Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Sun, 29 Sep 2024 14:26:58 +0530 Subject: [PATCH 04/14] 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 89d0fe02fb054ed8483e74d96e56083b5c7258cf Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Sun, 29 Sep 2024 14:53:48 +0530 Subject: [PATCH 05/14] 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(); } >From b02b0da59002ca424446d57e84667c3417003b09 Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Sun, 29 Sep 2024 17:14:50 +0530 Subject: [PATCH 06/14] Refactor Code --- clang/include/clang/Interpreter/Interpreter.h | 7 +++---- llvm/lib/ExecutionEngine/Orc/LLJIT.cpp | 3 --- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index db482f1b7e4153..42486304083f4e 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -20,8 +20,8 @@ #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/ExecutionEngine/Orc/Shared/ExecutorAddress.h" #include "llvm/Support/Error.h" #include <memory> #include <vector> @@ -136,9 +136,8 @@ class 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); + createLLJITBuilder(std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC, + llvm::StringRef OrcRuntimePath); const ASTContext &getASTContext() const; ASTContext &getASTContext(); const CompilerInstance *getCompilerInstance() const; diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp index 2d88f11836eea4..b31f4629565493 100644 --- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp +++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp @@ -1167,9 +1167,6 @@ 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 a4e48bc2626bbda4a1cb5cf5fa6f95f8041175e8 Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Mon, 30 Sep 2024 15:24:54 +0530 Subject: [PATCH 07/14] Add checks in sanitizeOopArguments to handle cases where remote execution doesn't support slab allocation. --- clang/tools/clang-repl/ClangRepl.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index 9f4d4cb69bb467..7fcd5f1730141d 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -55,7 +55,7 @@ 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( +static llvm::cl::opt<std::string> OOPExecutorConnect( "oop-executor-connect", llvm::cl::desc( "Connect to an out-of-process executor through a TCP socket"), @@ -79,16 +79,29 @@ static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional, static llvm::Error sanitizeOopArguments(const char *ArgV0) { // Only one of -oop-executor and -oop-executor-connect can be used. if (!!OOPExecutor.getNumOccurrences() && - !!OOPExecutorConnectTCP.getNumOccurrences()) + !!OOPExecutorConnect.getNumOccurrences()) return llvm::make_error<llvm::StringError>( "Only one of -" + OOPExecutor.ArgStr + " and -" + - OOPExecutorConnectTCP.ArgStr + " can be specified", + OOPExecutorConnect.ArgStr + " can be specified", llvm::inconvertibleErrorCode()); + // If -slab-allocate is passed, check that we're not trying to use it in + // -oop-executor or -oop-executor-connect mode. + // + // FIXME: Remove once we enable remote slab allocation. + if (SlabAllocateSizeString != "") { + if (OOPExecutor.getNumOccurrences() || + OOPExecutorConnect.getNumOccurrences()) + return llvm::make_error<llvm::StringError>( + "-slab-allocate cannot be used with -oop-executor or " + "-oop-executor-connect", + llvm::inconvertibleErrorCode()); + } + // Out-of-process executors must run with the ORC runtime for destructor // support. if (OrcRuntimePath.empty() && (OOPExecutor.getNumOccurrences() || - OOPExecutorConnectTCP.getNumOccurrences())) { + OOPExecutorConnect.getNumOccurrences())) { llvm::SmallString<256> OrcPath(llvm::sys::fs::getMainExecutable( ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments))); llvm::sys::path::remove_filename(OrcPath); // Remove clang-repl filename. @@ -257,8 +270,8 @@ int main(int argc, const char **argv) { // Launch an out-of-process executor locally in a child process. EPC = ExitOnErr( launchExecutor(OOPExecutor, UseSharedMemory, SlabAllocateSizeString)); - } else if (OOPExecutorConnectTCP.getNumOccurrences()) { - EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnectTCP, UseSharedMemory, + } else if (OOPExecutorConnect.getNumOccurrences()) { + EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnect, UseSharedMemory, SlabAllocateSizeString)); } >From 3143d8810c4af4d99e481605705245420ac38737 Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Wed, 16 Oct 2024 11:38:40 +0530 Subject: [PATCH 08/14] Update LoadDynamicLibrary method to use EPC Search Generator for out-of-process execution only --- clang/include/clang/Interpreter/Interpreter.h | 2 +- clang/lib/Interpreter/Interpreter.cpp | 13 ++++++++++--- clang/tools/clang-repl/ClangRepl.cpp | 4 +++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 42486304083f4e..f7083742a672e7 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -152,7 +152,7 @@ class Interpreter { llvm::Error Undo(unsigned N = 1); /// Link a dynamic library - llvm::Error LoadDynamicLibrary(const char *name); + llvm::Error LoadDynamicLibrary(const char *name, bool UseEPC = false); /// \returns the \c ExecutorAddr of a \c GlobalDecl. This interface uses /// the CodeGenModule's internal mangling cache to avoid recomputing the diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index c281c917cff4b5..faa1727d12731d 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -718,13 +718,20 @@ llvm::Error Interpreter::Undo(unsigned N) { return llvm::Error::success(); } -llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { +llvm::Error Interpreter::LoadDynamicLibrary(const char *name, bool UseEPC) { auto EE = getExecutionEngine(); if (!EE) return EE.takeError(); - if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load( - EE->getExecutionSession(), name)) + auto &DL = EE->getDataLayout(); + if (UseEPC) { + if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load( + EE->getExecutionSession(), name)) + EE->getMainJITDylib().addGenerator(std::move(*DLSG)); + else + return DLSG.takeError(); + } else if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load( + name, DL.getGlobalPrefix())) EE->getMainJITDylib().addGenerator(std::move(*DLSG)); else return DLSG.takeError(); diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index 7fcd5f1730141d..13414da7341273 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -276,10 +276,12 @@ int main(int argc, const char **argv) { } std::unique_ptr<llvm::orc::LLJITBuilder> JB; + bool UseEPCSearchGen = false; if (EPC) { CB.SetTargetTriple(EPC->getTargetTriple().getTriple()); JB = ExitOnErr( clang::Interpreter::createLLJITBuilder(std::move(EPC), OrcRuntimePath)); + UseEPCSearchGen = true; } // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It @@ -352,7 +354,7 @@ int main(int argc, const char **argv) { if (auto Err = Interp->Undo()) llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); } else if (Input.rfind("%lib ", 0) == 0) { - if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5)) + if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5, UseEPCSearchGen)) llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); } else if (auto Err = Interp->ParseAndExecute(Input)) { llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); >From 9673ec83817756bfa1ff660f6b08056204e65d1c Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Wed, 6 Nov 2024 14:47:38 +0530 Subject: [PATCH 09/14] Update: Propagate error instead of using `ExitOnErr` --- clang/lib/Interpreter/RemoteJITUtils.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/clang/lib/Interpreter/RemoteJITUtils.cpp b/clang/lib/Interpreter/RemoteJITUtils.cpp index 450748eb6df445..5b0c37205c695e 100644 --- a/clang/lib/Interpreter/RemoteJITUtils.cpp +++ b/clang/lib/Interpreter/RemoteJITUtils.cpp @@ -29,8 +29,6 @@ using namespace llvm; using namespace llvm::orc; -static ExitOnError ExitOnErr; - Expected<uint64_t> getSlabAllocSize(StringRef SizeString) { SizeString = SizeString.trim(); @@ -76,8 +74,12 @@ createSharedMemoryManager(SimpleRemoteEPC &SREPC, size_t SlabSize = 1024 * 1024 * 1024; #endif - if (!SlabAllocateSizeString.empty()) - SlabSize = ExitOnErr(getSlabAllocSize(SlabAllocateSizeString)); + if (!SlabAllocateSizeString.empty()) { + if (auto S = getSlabAllocSize(SlabAllocateSizeString)) + SlabSize = *S; + else + return S.takeError(); + } return MapperJITLinkMemoryManager::CreateWithMapper<SharedMemoryMapper>( SlabSize, SREPC, SAs); >From a50d7d3979ec6b349e87fbb93e7b15d6c2558548 Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Wed, 6 Nov 2024 16:26:34 +0530 Subject: [PATCH 10/14] Fix code format --- clang/tools/clang-repl/ClangRepl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index 13414da7341273..2a6e912f171adf 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -354,7 +354,8 @@ int main(int argc, const char **argv) { if (auto Err = Interp->Undo()) llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); } else if (Input.rfind("%lib ", 0) == 0) { - if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5, UseEPCSearchGen)) + if (auto Err = + Interp->LoadDynamicLibrary(Input.data() + 5, UseEPCSearchGen)) llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); } else if (auto Err = Interp->ParseAndExecute(Input)) { llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); >From 06132c8250fe60e90ca65424fb3b78521ce93c34 Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Fri, 8 Nov 2024 10:32:02 +0530 Subject: [PATCH 11/14] Refactor: Improve logic and remove hardcoded Clang version --- clang/include/clang/Interpreter/Interpreter.h | 2 +- clang/lib/Interpreter/Interpreter.cpp | 13 +-- clang/lib/Interpreter/RemoteJITUtils.cpp | 6 +- clang/test/Interpreter/out-of-process.cpp | 88 +++++++++++++++++++ clang/tools/clang-repl/ClangRepl.cpp | 28 +++--- 5 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 clang/test/Interpreter/out-of-process.cpp diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index f7083742a672e7..42486304083f4e 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -152,7 +152,7 @@ class Interpreter { llvm::Error Undo(unsigned N = 1); /// Link a dynamic library - llvm::Error LoadDynamicLibrary(const char *name, bool UseEPC = false); + llvm::Error LoadDynamicLibrary(const char *name); /// \returns the \c ExecutorAddr of a \c GlobalDecl. This interface uses /// the CodeGenModule's internal mangling cache to avoid recomputing the diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index faa1727d12731d..c281c917cff4b5 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -718,20 +718,13 @@ llvm::Error Interpreter::Undo(unsigned N) { return llvm::Error::success(); } -llvm::Error Interpreter::LoadDynamicLibrary(const char *name, bool UseEPC) { +llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { auto EE = getExecutionEngine(); if (!EE) return EE.takeError(); - auto &DL = EE->getDataLayout(); - if (UseEPC) { - if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load( - EE->getExecutionSession(), name)) - EE->getMainJITDylib().addGenerator(std::move(*DLSG)); - else - return DLSG.takeError(); - } else 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/lib/Interpreter/RemoteJITUtils.cpp b/clang/lib/Interpreter/RemoteJITUtils.cpp index 5b0c37205c695e..24a5f729f2dcbc 100644 --- a/clang/lib/Interpreter/RemoteJITUtils.cpp +++ b/clang/lib/Interpreter/RemoteJITUtils.cpp @@ -5,6 +5,10 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +// FIXME: Unify this code with similar functionality in llvm-jitlink. +// +//===----------------------------------------------------------------------===// #include "clang/Interpreter/RemoteJITUtils.h" @@ -260,4 +264,4 @@ connectTCPSocket(StringRef NetworkAddress, bool UseSharedMemory, std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt), std::move(S), *SockFD, *SockFD); #endif -} \ No newline at end of file +} diff --git a/clang/test/Interpreter/out-of-process.cpp b/clang/test/Interpreter/out-of-process.cpp new file mode 100644 index 00000000000000..372750b20b470c --- /dev/null +++ b/clang/test/Interpreter/out-of-process.cpp @@ -0,0 +1,88 @@ +// REQUIRES: host-supports-jit + +// RUN: cat %s | clang-repl -oop-executor -orc-runtime | FileCheck %s + +extern "C" int printf(const char *, ...); + +int intVar = 0; +double doubleVar = 3.14; +%undo +double doubleVar = 2.71; + +auto r1 = printf("intVar = %d\n", intVar); +// CHECK: intVar = 0 +auto r2 = printf("doubleVar = %.2f\n", doubleVar); +// CHECK: doubleVar = 2.71 + +// Test redefinition with inline and static functions. +int add(int a, int b, int c) { return a + b + c; } +%undo // Revert to the initial version of add +inline int add(int a, int b) { return a + b; } + +auto r3 = printf("add(1, 2) = %d\n", add(1, 2)); +// CHECK-NEXT: add(1, 2) = 3 + +// Test inline and lambda functions with variations. +inline int square(int x) { return x * x; } +auto lambdaSquare = [](int x) { return x * x; }; +auto lambdaMult = [](int a, int b) { return a * b; }; + +auto r4 = printf("square(4) = %d\n", square(4)); +// CHECK-NEXT: square(4) = 16 +auto lambda_r1 = printf("lambdaSquare(5) = %d\n", lambdaSquare(5)); +// CHECK-NEXT: lambdaSquare(5) = 25 +auto lambda_r2 = printf("lambdaMult(2, 3) = %d\n", lambdaMult(2, 3)); +// CHECK-NEXT: lambdaMult(2, 3) = 6 + +%undo // Undo previous lambda assignments +auto lambda_r3 = lambdaMult(3, 4); // Should fail or revert to the original lambda + +// Test weak and strong symbol linkage. +int __attribute__((weak)) weakFunc() { return 42; } +int strongFunc() { return 100; } +%undo // Revert the weak function + +auto r5 = printf("weakFunc() = %d\n", weakFunc()); +// CHECK: weakFunc() = 42 +auto r6 = printf("strongFunc() = %d\n", strongFunc()); +// CHECK-NEXT: strongFunc() = 100 + +// Weak variable linkage with different types. +int varA = 20; +static __typeof(varA) weakVarA __attribute__((__weakref__("varA"))); +char charVar = 'c'; +static __typeof(charVar) weakCharVar __attribute__((__weakref__("charVar"))); +auto r7 = printf("weakVarA = %d\n", weakVarA); +// CHECK: weakVarA = 20 +auto r8 = printf("weakCharVar = %c\n", weakCharVar); +// CHECK-NEXT: weakCharVar = c + +// Test complex lambdas with captures. +int captureVar = 5; +auto captureLambda = [](int x) { return x + captureVar; }; +int result1 = captureLambda(10); +%undo // Undo capture lambda + +auto r9 = printf("captureLambda(10) = %d\n", result1); +// CHECK: captureLambda(10) = 15 + +// Multiline statement test with arithmetic operations. +int sum = \ + 5 + \ + 10; +int prod = sum * 2; +auto r10 = printf("sum = %d, prod = %d\n", sum, prod); +// CHECK: sum = 15, prod = 30 + +// Test multiline functions and macro behavior. +#define MULTIPLY(a, b) ((a) * (b)) + +int complexFunc(int x) \ +{ \ + return MULTIPLY(x, 2) + x; \ +} + +auto r11 = printf("complexFunc(5) = %d\n", complexFunc(5)); +// CHECK: complexFunc(5) = 15 + +%quit diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index 2a6e912f171adf..1b1304c97506c0 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -13,6 +13,8 @@ #include "clang/Interpreter/RemoteJITUtils.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/Version.h" +#include "clang/Config/config.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Interpreter/CodeCompletion.h" @@ -62,7 +64,7 @@ static llvm::cl::opt<std::string> OOPExecutorConnect( 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)); + llvm::cl::ValueOptional, 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"), @@ -98,22 +100,21 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) { llvm::inconvertibleErrorCode()); } - // Out-of-process executors must run with the ORC runtime for destructor - // support. + // Out-of-process executors require the ORC runtime. if (OrcRuntimePath.empty() && (OOPExecutor.getNumOccurrences() || OOPExecutorConnect.getNumOccurrences())) { - llvm::SmallString<256> OrcPath(llvm::sys::fs::getMainExecutable( + llvm::SmallString<256> BasePath(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::sys::path::remove_filename(BasePath); // Remove clang-repl filename. + llvm::sys::path::remove_filename(BasePath); // Remove ./bin directory. llvm::Triple SystemTriple(llvm::sys::getProcessTriple()); - llvm::StringRef Path; + llvm::sys::path::append(BasePath, CLANG_INSTALL_LIBDIR_BASENAME, "clang", + CLANG_VERSION_MAJOR_STRING); if (SystemTriple.isOSBinFormatELF()) - Path = "lib/clang/20/lib/x86_64-unknown-linux-gnu/liborc_rt.a"; + OrcRuntimePath = + BasePath.str().str() + "lib/x86_64-unknown-linux-gnu/liborc_rt.a"; else if (SystemTriple.isOSBinFormatMachO()) - Path = "lib/clang/20/lib/darwin/liborc_rt_osx.a"; - llvm::sys::path::append(OrcPath, Path); - OrcRuntimePath = OrcPath.str().str(); + OrcRuntimePath = BasePath.str().str() + "/lib/darwin/liborc_rt_osx.a"; } // If -oop-executor was used but no value was specified then use a sensible @@ -276,12 +277,10 @@ int main(int argc, const char **argv) { } std::unique_ptr<llvm::orc::LLJITBuilder> JB; - bool UseEPCSearchGen = false; if (EPC) { CB.SetTargetTriple(EPC->getTargetTriple().getTriple()); JB = ExitOnErr( clang::Interpreter::createLLJITBuilder(std::move(EPC), OrcRuntimePath)); - UseEPCSearchGen = true; } // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It @@ -354,8 +353,7 @@ int main(int argc, const char **argv) { if (auto Err = Interp->Undo()) llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); } else if (Input.rfind("%lib ", 0) == 0) { - if (auto Err = - Interp->LoadDynamicLibrary(Input.data() + 5, UseEPCSearchGen)) + if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5)) llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); } else if (auto Err = Interp->ParseAndExecute(Input)) { llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); >From 7fa02cc51825f251ea236e3a0a9dc9a2fc12966b Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Fri, 8 Nov 2024 11:16:09 +0530 Subject: [PATCH 12/14] Fix: Apply lhames suggested fix for Clang-REPL crash --- clang/lib/Interpreter/Interpreter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index c281c917cff4b5..8eacbc6b713a19 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -725,7 +725,9 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load( EE->getExecutionSession(), name)) - EE->getMainJITDylib().addGenerator(std::move(*DLSG)); + // FIXME: Eventually we should put each library in its own JITDylib and + // turn off process symbols by default. + EE->getProcessSymbolsJITDylib()->addGenerator(std::move(*DLSG)); else return DLSG.takeError(); >From 527d39fdc8c4a0f00b05b0fe5b61476ecd222a5b Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Fri, 8 Nov 2024 14:41:48 +0530 Subject: [PATCH 13/14] Add platform support check in `sanitizeOopArguments` --- clang/tools/clang-repl/ClangRepl.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index 1b1304c97506c0..700ab41c8798e9 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -87,6 +87,17 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) { OOPExecutorConnect.ArgStr + " can be specified", llvm::inconvertibleErrorCode()); + llvm::Triple SystemTriple(llvm::sys::getProcessTriple()); + // TODO: Remove once out-of-process execution support is implemented for + // non-Unix platforms. + if ((!SystemTriple.isOSBinFormatELF() && + !SystemTriple.isOSBinFormatMachO()) && + (OOPExecutor.getNumOccurrences() || + OOPExecutorConnect.getNumOccurrences())) + return llvm::make_error<llvm::StringError>( + "Out-of-process execution is only supported on Unix platforms", + llvm::inconvertibleErrorCode()); + // If -slab-allocate is passed, check that we're not trying to use it in // -oop-executor or -oop-executor-connect mode. // @@ -100,6 +111,7 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) { llvm::inconvertibleErrorCode()); } + // Out-of-process executors require the ORC runtime. if (OrcRuntimePath.empty() && (OOPExecutor.getNumOccurrences() || OOPExecutorConnect.getNumOccurrences())) { @@ -107,7 +119,6 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) { ArgV0, reinterpret_cast<void *>(&sanitizeOopArguments))); llvm::sys::path::remove_filename(BasePath); // Remove clang-repl filename. llvm::sys::path::remove_filename(BasePath); // Remove ./bin directory. - llvm::Triple SystemTriple(llvm::sys::getProcessTriple()); llvm::sys::path::append(BasePath, CLANG_INSTALL_LIBDIR_BASENAME, "clang", CLANG_VERSION_MAJOR_STRING); if (SystemTriple.isOSBinFormatELF()) @@ -115,7 +126,11 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) { BasePath.str().str() + "lib/x86_64-unknown-linux-gnu/liborc_rt.a"; else if (SystemTriple.isOSBinFormatMachO()) OrcRuntimePath = BasePath.str().str() + "/lib/darwin/liborc_rt_osx.a"; - } + else + return llvm::make_error<llvm::StringError>( + "Out-of-process execution is not supported on non-unix platforms", + llvm::inconvertibleErrorCode()); + } // If -oop-executor was used but no value was specified then use a sensible // default. >From 14d6e133e755ea81fdc90b1b1e7daec5191cbffe Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Sun, 10 Nov 2024 16:51:31 +0530 Subject: [PATCH 14/14] Fix code format --- clang/tools/clang-repl/ClangRepl.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index 700ab41c8798e9..a1568624e14b52 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -111,7 +111,6 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) { llvm::inconvertibleErrorCode()); } - // Out-of-process executors require the ORC runtime. if (OrcRuntimePath.empty() && (OOPExecutor.getNumOccurrences() || OOPExecutorConnect.getNumOccurrences())) { @@ -130,7 +129,7 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) { return llvm::make_error<llvm::StringError>( "Out-of-process execution is not supported on non-unix platforms", llvm::inconvertibleErrorCode()); - } + } // If -oop-executor was used but no value was specified then use a sensible // default. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits