https://github.com/SahilPatidar updated https://github.com/llvm/llvm-project/pull/144064
>From 05943c9542cd89ae672ddc0f14514e0c7b1e0bd7 Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Tue, 3 Dec 2024 15:07:56 +0530 Subject: [PATCH 1/4] Re-Land: [Clang-Repl] Add support for out-of-process execution. #110418 --- clang/include/clang/Interpreter/Interpreter.h | 7 +- .../clang/Interpreter/RemoteJITUtils.h | 38 +++ clang/lib/Interpreter/CMakeLists.txt | 1 + clang/lib/Interpreter/Interpreter.cpp | 37 ++- clang/lib/Interpreter/RemoteJITUtils.cpp | 267 ++++++++++++++++++ clang/tools/clang-repl/CMakeLists.txt | 2 + clang/tools/clang-repl/ClangRepl.cpp | 123 +++++++- 7 files changed, 465 insertions(+), 10 deletions(-) create mode 100644 clang/include/clang/Interpreter/RemoteJITUtils.h create mode 100644 clang/lib/Interpreter/RemoteJITUtils.cpp diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index f8663e3193a18..78dff1165dcf5 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -20,6 +20,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" #include "llvm/Support/Error.h" #include <memory> @@ -136,10 +137,14 @@ 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>> 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/include/clang/Interpreter/RemoteJITUtils.h b/clang/include/clang/Interpreter/RemoteJITUtils.h new file mode 100644 index 0000000000000..8705a3b1f669d --- /dev/null +++ b/clang/include/clang/Interpreter/RemoteJITUtils.h @@ -0,0 +1,38 @@ +//===-- 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_INTERPRETER_REMOTEJITUTILS_H +#define LLVM_CLANG_INTERPRETER_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> + +llvm::Expected<std::unique_ptr<llvm::orc::SimpleRemoteEPC>> +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, bool UseSharedMemory, + llvm::StringRef SlabAllocateSizeString); + +#endif // LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt index bf70cdfbee01e..38cf139fa86a6 100644 --- a/clang/lib/Interpreter/CMakeLists.txt +++ b/clang/lib/Interpreter/CMakeLists.txt @@ -27,6 +27,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 84feff82c63a7..30051a24662c6 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -46,6 +46,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Serialization/ObjectFilePCHContainerReader.h" #include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include "llvm/IR/Module.h" #include "llvm/Support/Errc.h" @@ -455,10 +456,11 @@ 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)); + auto Interp = std::unique_ptr<Interpreter>( + new Interpreter(std::move(CI), Err, JB ? std::move(JB) : nullptr)); if (Err) return std::move(Err); @@ -617,6 +619,25 @@ createJITTargetMachineBuilder(const std::string &TT) { return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT)); } +llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> +Interpreter::createLLJITBuilder( + std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC, + llvm::StringRef OrcRuntimePath) { + 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(); + + (*JB)->setExecutorProcessControl(std::move(EPC)); + (*JB)->setPlatformSetUp( + llvm::orc::ExecutorNativePlatform(OrcRuntimePath.str())); + + return std::move(*JB); +} + llvm::Error Interpreter::CreateExecutor() { if (IncrExecutor) return llvm::make_error<llvm::StringError>("Operation failed. " @@ -756,11 +777,11 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { if (!EE) return EE.takeError(); - auto &DL = EE->getDataLayout(); - - if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load( - name, DL.getGlobalPrefix())) - EE->getMainJITDylib().addGenerator(std::move(*DLSG)); + if (auto DLSG = llvm::orc::EPCDynamicLibrarySearchGenerator::Load( + EE->getExecutionSession(), name)) + // 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(); #endif diff --git a/clang/lib/Interpreter/RemoteJITUtils.cpp b/clang/lib/Interpreter/RemoteJITUtils.cpp new file mode 100644 index 0000000000000..24a5f729f2dcb --- /dev/null +++ b/clang/lib/Interpreter/RemoteJITUtils.cpp @@ -0,0 +1,267 @@ +//===-- 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 +// +//===----------------------------------------------------------------------===// +// +// FIXME: Unify this code with similar functionality in llvm-jitlink. +// +//===----------------------------------------------------------------------===// + +#include "clang/Interpreter/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/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" +#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<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()) { + if (auto S = getSlabAllocSize(SlabAllocateSizeString)) + SlabSize = *S; + else + return S.takeError(); + } + + return MapperJITLinkMemoryManager::CreateWithMapper<SharedMemoryMapper>( + SlabSize, SREPC, SAs); +} + +Expected<std::unique_ptr<SimpleRemoteEPC>> +launchExecutor(StringRef ExecutablePath, bool UseSharedMemory, + llvm::StringRef SlabAllocateSizeString) { +#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>( + "-" + ExecutablePath + + " requires threads, but LLVM was built with " + "LLVM_ENABLE_THREADS=Off", + inconvertibleErrorCode()); +#else + + 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()); + + ChildPID = fork(); + + if (ChildPID == 0) { + // In the child... + + // Close the parent ends of the pipes + close(ToExecutor[WriteEnd]); + close(FromExecutor[ReadEnd]); + + // Execute the child process. + std::unique_ptr<char[]> ExecutorPath, FDSpecifier; + { + ExecutorPath = std::make_unique<char[]>(ExecutablePath.size() + 1); + strcpy(ExecutorPath.get(), ExecutablePath.data()); + + 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[] = {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... + + // Close the child ends of the pipes + close(ToExecutor[ReadEnd]); + 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), + 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; + 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) { + // socket might fail, e.g. if the address family is not supported. Skip to + // the next addrinfo structure in such a case. + if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) + continue; + + // If connect returns null, we exit the loop with a working socket. + if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0) + break; + + close(SockFD); + } + freeaddrinfo(AI); + + // If we reached the end of the loop without connecting to a valid endpoint, + // dump the last error that was logged in socket() or connect(). + if (Server == nullptr) + return make_error<StringError>("invalid hostname", + inconvertibleErrorCode()); + + return SockFD; +} +#endif + +Expected<std::unique_ptr<SimpleRemoteEPC>> +connectTCPSocket(StringRef NetworkAddress, bool UseSharedMemory, + llvm::StringRef SlabAllocateSizeString) { +#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), + inconvertibleErrorCode()); + }; + + StringRef Host, PortStr; + std::tie(Host, PortStr) = NetworkAddress.split(':'); + if (Host.empty()) + return CreateErr("Host name for -" + NetworkAddress + " can not be empty"); + if (PortStr.empty()) + return CreateErr("Port number in -" + NetworkAddress + " can not be empty"); + int Port = 0; + if (PortStr.getAsInteger(10, Port)) + return CreateErr("Port number '" + PortStr + "' is not a valid integer"); + + Expected<int> SockFD = connectTCPSocketImpl(Host.str(), PortStr.str()); + if (!SockFD) + 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), + std::move(S), *SockFD, *SockFD); +#endif +} diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt index f9a911b0ae8e2..68d86dd98cac3 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 7af8e4f25d99e..e34a4c7ad1cd2 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -10,7 +10,11 @@ // //===----------------------------------------------------------------------===// +#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" @@ -24,8 +28,11 @@ #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" + // 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. @@ -34,10 +41,36 @@ 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"); +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"), + llvm::cl::init(""), llvm::cl::ValueOptional, + llvm::cl::cat(OOPCategory)); +static llvm::cl::opt<std::string> OOPExecutorConnect( + "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"), + llvm::cl::init(""), 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"), + 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"), @@ -47,6 +80,72 @@ 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() && + !!OOPExecutorConnect.getNumOccurrences()) + return llvm::make_error<llvm::StringError>( + "Only one of -" + OOPExecutor.ArgStr + " and -" + + 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. + // + // 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 require the ORC runtime. + if (OrcRuntimePath.empty() && (OOPExecutor.getNumOccurrences() || + OOPExecutorConnect.getNumOccurrences())) { + llvm::SmallString<256> BasePath(llvm::sys::fs::getMainExecutable( + 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::sys::path::append(BasePath, CLANG_INSTALL_LIBDIR_BASENAME, "clang", + CLANG_VERSION_MAJOR_STRING); + if (SystemTriple.isOSBinFormatELF()) + OrcRuntimePath = + 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. + 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); @@ -183,6 +282,25 @@ int main(int argc, const char **argv) { DeviceCI = ExitOnErr(CB.CreateCudaDevice()); } + ExitOnErr(sanitizeOopArguments(argv[0])); + + 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, UseSharedMemory, SlabAllocateSizeString)); + } else if (OOPExecutorConnect.getNumOccurrences()) { + EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnect, UseSharedMemory, + SlabAllocateSizeString)); + } + + std::unique_ptr<llvm::orc::LLJITBuilder> JB; + if (EPC) { + CB.SetTargetTriple(EPC->getTargetTriple().getTriple()); + JB = ExitOnErr( + clang::Interpreter::createLLJITBuilder(std::move(EPC), OrcRuntimePath)); + } + // 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; @@ -214,6 +332,9 @@ int main(int argc, const char **argv) { auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so"; ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str())); } + } else if (JB) { + Interp = + ExitOnErr(clang::Interpreter::create(std::move(CI), std::move(JB))); } else Interp = ExitOnErr(clang::Interpreter::create(std::move(CI))); >From 9422da581397ed71226234869d5b65cdda7408e4 Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Fri, 13 Jun 2025 17:58:41 +0530 Subject: [PATCH 2/4] Add test and introduce -host-support-out-of-process-jit flag --- clang/test/Interpreter/out-of-process.cpp | 88 +++++++++++++++++++++++ clang/test/lit.cfg.py | 27 +++++++ clang/tools/clang-repl/ClangRepl.cpp | 2 +- 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 clang/test/Interpreter/out-of-process.cpp diff --git a/clang/test/Interpreter/out-of-process.cpp b/clang/test/Interpreter/out-of-process.cpp new file mode 100644 index 0000000000000..6922ca6e82053 --- /dev/null +++ b/clang/test/Interpreter/out-of-process.cpp @@ -0,0 +1,88 @@ +// REQUIRES: host-supports-jit, host-supports-out-of-process-jit, x86_64-linux + +// 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 \ No newline at end of file diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index 2b35bb5dcbdaf..88ece96bbb1d6 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -116,6 +116,30 @@ if config.clang_examples: config.available_features.add("examples") +def have_host_out_of_process_jit_feature_support(): + clang_repl_exe = lit.util.which("clang-repl", config.clang_tools_dir) + + if not clang_repl_exe: + return False + + testcode = b'\n'.join([ + b"int i = 0;", + b"%quit" + ]) + + try: + clang_repl_cmd = subprocess.run( + [clang_repl_exe, "-orc-runtime", "-oop-executor"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + input=testcode + ) + except OSError: + return False + + if clang_repl_cmd.returncode == 0: + return True + + return False def have_host_jit_feature_support(feature_name): clang_repl_exe = lit.util.which("clang-repl", config.clang_tools_dir) @@ -169,6 +193,9 @@ def have_host_clang_repl_cuda(): if have_host_clang_repl_cuda(): config.available_features.add('host-supports-cuda') + if have_host_out_of_process_jit_feature_support(): + config.available_features.add('host-supports-out-of-process-jit') + if config.clang_staticanalyzer: config.available_features.add("staticanalyzer") tools.append("clang-check") diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index e34a4c7ad1cd2..54b27782d3db3 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -124,7 +124,7 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) { CLANG_VERSION_MAJOR_STRING); if (SystemTriple.isOSBinFormatELF()) OrcRuntimePath = - BasePath.str().str() + "lib/x86_64-unknown-linux-gnu/liborc_rt.a"; + 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 >From f0f67b29853063c7308794d6d7fb4df27b827aac Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Fri, 13 Jun 2025 18:20:39 +0530 Subject: [PATCH 3/4] Fix format --- clang/test/lit.cfg.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index 88ece96bbb1d6..0f82f2089ac58 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -122,16 +122,14 @@ def have_host_out_of_process_jit_feature_support(): if not clang_repl_exe: return False - testcode = b'\n'.join([ - b"int i = 0;", - b"%quit" - ]) + testcode = b"\n".join([b"int i = 0;", b"%quit"]) try: clang_repl_cmd = subprocess.run( [clang_repl_exe, "-orc-runtime", "-oop-executor"], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - input=testcode + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + input=testcode, ) except OSError: return False @@ -194,7 +192,7 @@ def have_host_clang_repl_cuda(): config.available_features.add('host-supports-cuda') if have_host_out_of_process_jit_feature_support(): - config.available_features.add('host-supports-out-of-process-jit') + config.available_features.add("host-supports-out-of-process-jit") if config.clang_staticanalyzer: config.available_features.add("staticanalyzer") >From 2efe5e9da69a60ed0e1782ac3c83481fd8daa35e Mon Sep 17 00:00:00 2001 From: SahilPatidar <patidarsahil2...@gmail.com> Date: Mon, 16 Jun 2025 10:38:56 +0530 Subject: [PATCH 4/4] Fix minor format issue --- clang/test/lit.cfg.py | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index 0f82f2089ac58..4c3fb0a8ee044 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -116,6 +116,7 @@ if config.clang_examples: config.available_features.add("examples") + def have_host_out_of_process_jit_feature_support(): clang_repl_exe = lit.util.which("clang-repl", config.clang_tools_dir) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits