diff --git a/.github/actions/Build_LLVM/action.yml b/.github/actions/Build_LLVM/action.yml index 3d9461fe7..ea690fa20 100644 --- a/.github/actions/Build_LLVM/action.yml +++ b/.github/actions/Build_LLVM/action.yml @@ -49,6 +49,10 @@ runs: git apply -v ../patches/llvm/clang${{ matrix.clang-runtime }}-*.patch echo "Apply clang${{ matrix.clang-runtime }}-*.patch patches:" fi + if [[ "${{ matrix.oop-jit }}" == "On" ]]; then + git apply -v ../patches/llvm/clang20-2-out-of-process-jit-execution.patch + echo "Apply out-of-process-jit-execution.patch:" + fi cd build cmake -DLLVM_ENABLE_PROJECTS="${{ matrix.llvm_enable_projects}}" \ -DLLVM_TARGETS_TO_BUILD="${{ matrix.llvm_targets_to_build }}" \ @@ -64,6 +68,10 @@ runs: -DLLVM_INCLUDE_TESTS=OFF \ ../llvm ninja clang clangInterpreter clangStaticAnalyzerCore -j ${{ env.ncpus }} + if [[ "${{ matrix.oop-jit }}" == "On" ]]; then + if [ "${{ matrix.os }}}" == "macos"* ]; then SUFFIX="_osx"; fi + ninja clang_repl llvm-jitlink-executor orc_rt${SUFFIX} -j ${{ env.ncpus }} + fi cd ./tools/ rm -rf $(find . -maxdepth 1 ! -name "clang" ! -name ".") cd .. diff --git a/.github/actions/Build_and_Test_CppInterOp/action.yml b/.github/actions/Build_and_Test_CppInterOp/action.yml index b97d2e8ae..c933ad8c3 100644 --- a/.github/actions/Build_and_Test_CppInterOp/action.yml +++ b/.github/actions/Build_and_Test_CppInterOp/action.yml @@ -41,14 +41,15 @@ runs: ../ else cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \ - -DCPPINTEROP_INCLUDE_DOCS=${{ matrix.documentation }} \ - -DLLVM_DIR=$LLVM_BUILD_DIR/lib/cmake/llvm \ - -DClang_DIR=$LLVM_BUILD_DIR/lib/cmake/clang \ - -DBUILD_SHARED_LIBS=ON \ - -DCODE_COVERAGE=${{ env.CODE_COVERAGE }} \ - -DCMAKE_INSTALL_PREFIX=$CPPINTEROP_DIR \ - -DLLVM_ENABLE_WERROR=On \ - ../ + -DCPPINTEROP_INCLUDE_DOCS=${{ matrix.documentation }} \ + -DLLVM_DIR=$LLVM_BUILD_DIR/lib/cmake/llvm \ + -DClang_DIR=$LLVM_BUILD_DIR/lib/cmake/clang \ + -DBUILD_SHARED_LIBS=ON \ + -DCODE_COVERAGE=${{ env.CODE_COVERAGE }} \ + -DCMAKE_INSTALL_PREFIX=$CPPINTEROP_DIR \ + -DLLVM_ENABLE_WERROR=On \ + -DCPPINTEROP_WITH_OOP_JIT=${{ matrix.oop-jit }} \ + ../ fi docs_on=$(echo "${{ matrix.documentation }}" | tr '[:lower:]' '[:upper:]') if [[ "${docs_on}" == "ON" ]]; then diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 669958da3..f60857849 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,6 +32,15 @@ jobs: llvm_enable_projects: "clang" llvm_targets_to_build: "host;NVPTX" coverage: true + - name: ubu24-arm-gcc12-clang-repl-20-oop + os: ubuntu-24.04-arm + compiler: gcc-12 + clang-runtime: '20' + cling: Off + cppyy: Off + llvm_enable_projects: "clang;compiler-rt" + llvm_targets_to_build: "host;NVPTX" + oop-jit: On - name: ubu24-arm-gcc12-clang-repl-20 os: ubuntu-24.04-arm compiler: gcc-12 @@ -132,6 +141,15 @@ jobs: llvm_enable_projects: "clang" llvm_targets_to_build: "host;NVPTX" # MacOS Arm Jobs + - name: osx15-arm-clang-clang-repl-20-oop + os: macos-15 + compiler: clang + clang-runtime: '20' + cling: Off + cppyy: Off + llvm_enable_projects: "clang;compiler-rt" + llvm_targets_to_build: "host" + oop-jit: On - name: osx15-arm-clang-clang-repl-20 os: macos-15 compiler: clang @@ -182,6 +200,15 @@ jobs: llvm_enable_projects: "clang" llvm_targets_to_build: "host;NVPTX" # MacOS X86 Jobs + - name: osx13-x86-clang-clang-repl-20-oop + os: macos-13 + compiler: clang + clang-runtime: '20' + cling: Off + cppyy: Off + llvm_enable_projects: "clang;compiler-rt" + llvm_targets_to_build: "host" + oop-jit: On - name: osx13-x86-clang-clang-repl-20 os: macos-13 compiler: clang diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b2df5b4f..57cc29342 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -310,6 +310,16 @@ include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS}) add_definitions(${LLVM_DEFINITIONS_LIST}) +if(CPPINTEROP_WITH_OOP_JIT) + if(WIN32 OR EMSCRIPTEN) + message(FATAL_ERROR "CPPINTEROP_WITH_OOP_JIT is not supported on Windows or Emscripten platforms.") + endif() + add_definitions(-DCPPINTEROP_WITH_OOP_JIT) +endif() + +string(REGEX REPLACE "/build/lib/cmake/llvm$" "" LLVM_SOURCE_DIR "${LLVM_DIR}") +add_definitions(-DLLVM_SOURCE_DIR="${LLVM_SOURCE_DIR}") + # If the llvm sources are present add them with higher priority. if (LLVM_BUILD_MAIN_SRC_DIR) # LLVM_INCLUDE_DIRS contains the include paths to both LLVM's source and diff --git a/README.md b/README.md index 534fc191e..a367765a4 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,13 @@ git apply -v clang{version}-*.patch on Windows. +If you want to have out-of-process JIT execution enabled in CppInterOp, then apply this patch on Linux and MacOS environment. +> Note that this patch will not work for Windows because out-of-process JIT execution is currently implemented for Linux and MacOS only. + +```bash +git apply -v ../CppInterOp/patches/llvm/clang20-2-out-of-process-jit-execution.patch +``` + ##### Build Clang-REPL Clang-REPL is an interpreter that CppInterOp works alongside. Build Clang (and @@ -175,6 +182,30 @@ $env:LLVM_DIR= $PWD.Path cd ..\ ``` +##### Build Clang-REPL with Out-of-Process JIT Execution + +To have ``Out-of-Process JIT Execution`` enabled, run following commands to build clang and clang-repl to support this feature: +> Only for Linux and Macos +```bash +mkdir build +cd build +cmake -DLLVM_ENABLE_PROJECTS="clang;compiler-rt \ + -DLLVM_TARGETS_TO_BUILD="host;NVPTX" \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_ENABLE_ASSERTIONS=ON \ + -DCLANG_ENABLE_STATIC_ANALYZER=OFF \ + -DCLANG_ENABLE_ARCMT=OFF \ + -DCLANG_ENABLE_FORMAT=OFF \ + -DCLANG_ENABLE_BOOTSTRAP=OFF \ + ../llvm + +## For Linux +cmake --build . --target clang clang-repl llvm-jitlink-executor orc_rt --parallel $(nproc --all) + +## For MacOS +cmake --build . --target clang clang-repl llvm-jitlink-executor orc_rt_osx --parallel $(sysctl -n hw.ncpu) +``` + #### Build Cling and related dependencies Besides the Clang-REPL interpreter, CppInterOp also works alongside the Cling @@ -324,6 +355,8 @@ cmake -DBUILD_SHARED_LIBS=ON -DLLVM_DIR=$LLVM_DIR/build/lib/cmake/llvm -DClang_D cmake --build . --target install --parallel $(nproc --all) ``` +> Do make sure to add ``-DCPPINTEROP_WITH_OOP_JIT=ON``, if you want to have out-of-process JIT execution feature enabled. + and ```powershell diff --git a/docs/InstallationAndUsage.rst b/docs/InstallationAndUsage.rst index 8063d9c61..27ed4bfa0 100644 --- a/docs/InstallationAndUsage.rst +++ b/docs/InstallationAndUsage.rst @@ -58,6 +58,15 @@ and on Windows. +If you want to have out-of-process JIT execution enabled in CppInterOp, then apply this patch on Linux and MacOS environment. +.. note:: + + This patch will not work for Windows because out-of-process JIT execution is currently implemented for Linux and MacOS only. + +.. code:: bash + + git apply -v ../CppInterOp/patches/llvm/clang20-2-out-of-process-jit-execution.patch + ****************** Build Clang-REPL ****************** @@ -116,6 +125,36 @@ On Windows you execute the following $env:LLVM_DIR= $PWD.Path cd ..\ +*************************************************** +Build Clang-REPL with Out-of-Process JIT Execution +*************************************************** + +To have `Out-of-Process JIT Execution` enabled, run following commands to build clang and clang-repl to support this feature: + +.. note:: + + Only for Linux and Macos + +.. code:: bash + + mkdir build + cd build + cmake -DLLVM_ENABLE_PROJECTS="clang;compiler-rt \ + -DLLVM_TARGETS_TO_BUILD="host;NVPTX" \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_ENABLE_ASSERTIONS=ON \ + -DCLANG_ENABLE_STATIC_ANALYZER=OFF \ + -DCLANG_ENABLE_ARCMT=OFF \ + -DCLANG_ENABLE_FORMAT=OFF \ + -DCLANG_ENABLE_BOOTSTRAP=OFF \ + ../llvm + + # For Linux + cmake --build . --target clang clang-repl llvm-jitlink-executor orc_rt --parallel $(nproc --all) + + # For MacOS + cmake --build . --target clang clang-repl llvm-jitlink-executor orc_rt_osx --parallel $(sysctl -n hw.ncpu) + ************************************** Build Cling and related dependencies ************************************** @@ -280,6 +319,10 @@ commands on Linux and MacOS cmake -DBUILD_SHARED_LIBS=ON -DLLVM_DIR=$LLVM_DIR/build/lib/cmake/llvm -DClang_DIR=$LLVM_DIR/build/lib/cmake/clang -DCMAKE_INSTALL_PREFIX=$CPPINTEROP_DIR .. cmake --build . --target install --parallel $(nproc --all) +.. note:: + + Do make sure to add `-DCPPINTEROP_WITH_OOP_JIT=ON`, if you want to have out-of-process JIT execution feature enabled. + and .. code:: powershell diff --git a/include/CppInterOp/CppInterOp.h b/include/CppInterOp/CppInterOp.h index 2f3d330db..431296986 100644 --- a/include/CppInterOp/CppInterOp.h +++ b/include/CppInterOp/CppInterOp.h @@ -665,7 +665,7 @@ CPPINTEROP_API void GetOperator(TCppScope_t scope, Operator op, ///\returns nullptr on failure. CPPINTEROP_API TInterp_t CreateInterpreter(const std::vector& Args = {}, - const std::vector& GpuArgs = {}); + const std::vector& GpuArgs = {}, bool outOfProcess = false); /// Deletes an instance of an interpreter. ///\param[in] I - the interpreter to be deleted, if nullptr, deletes the last. @@ -901,6 +901,10 @@ CPPINTEROP_API void CodeComplete(std::vector& Results, ///\returns 0 on success, non-zero on failure. CPPINTEROP_API int Undo(unsigned N = 1); +#ifdef CPPINTEROP_WITH_OOP_JIT +CPPINTEROP_API pid_t GetExecutorPID(); +#endif // CPPINTEROP_WITH_OOP_JIT + } // end namespace Cpp #endif // CPPINTEROP_CPPINTEROP_H diff --git a/lib/CppInterOp/Compatibility.h b/lib/CppInterOp/Compatibility.h index 0c7c66f5b..c31937de3 100644 --- a/lib/CppInterOp/Compatibility.h +++ b/lib/CppInterOp/Compatibility.h @@ -205,10 +205,22 @@ inline void codeComplete(std::vector& Results, #include "llvm/Support/Error.h" +#include + +#ifdef CPPINTEROP_WITH_OOP_JIT +#include "clang/Basic/Version.h" +#include "clang/Interpreter/RemoteJITUtils.h" +#include "llvm/TargetParser/Host.h" + +#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h" +#endif + +static const llvm::ExitOnError ExitOnError; + namespace compat { inline std::unique_ptr -createClangInterpreter(std::vector& args) { +createClangInterpreter(std::vector& args, bool outOfProcess) { auto has_arg = [](const char* x, llvm::StringRef match = "cuda") { llvm::StringRef Arg = x; Arg = Arg.trim().ltrim('-'); @@ -246,16 +258,62 @@ createClangInterpreter(std::vector& args) { (*ciOrErr)->LoadRequestedPlugins(); if (CudaEnabled) DeviceCI->LoadRequestedPlugins(); + +#ifdef CPPINTEROP_WITH_OOP_JIT + std::unique_ptr JB; + + if (outOfProcess) { + std::string OOPExecutor = + std::string(LLVM_SOURCE_DIR) + "/build/bin/llvm-jitlink-executor"; + bool UseSharedMemory = false; + std::string SlabAllocateSizeString = ""; + std::unique_ptr EPC; + EPC = ExitOnError( + launchExecutor(OOPExecutor, UseSharedMemory, SlabAllocateSizeString)); + +#ifdef __APPLE__ + std::string OrcRuntimePath = + std::string(LLVM_SOURCE_DIR) + + "/build/lib/clang/20/lib/darwin/liborc_rt_osx.a"; +#else + std::string OrcRuntimePath = + std::string(LLVM_SOURCE_DIR) + + "/build/lib/x86_64-unknown-linux-gnu/liborc_rt.a"; +#endif + if (EPC) { + + CB.SetTargetTriple(EPC->getTargetTriple().getTriple()); + JB = ExitOnError(clang::Interpreter::createLLJITBuilder(std::move(EPC), + OrcRuntimePath)); + } + } + + auto innerOrErr = + CudaEnabled + ? clang::Interpreter::createWithCUDA(std::move(*ciOrErr), + std::move(DeviceCI)) + : clang::Interpreter::create(std::move(*ciOrErr), std::move(JB)); +#else + if (outOfProcess) { + llvm::errs() + << "[CreateClangInterpreter]: No compatibility with out-of-process JIT" + << "(To enable recompile CppInterOp with " + "`-DCPPINTEROP_WITH_OOP_JIT=ON`)" + << "\n"; + return nullptr; + } auto innerOrErr = CudaEnabled ? clang::Interpreter::createWithCUDA(std::move(*ciOrErr), std::move(DeviceCI)) : clang::Interpreter::create(std::move(*ciOrErr)); +#endif if (!innerOrErr) { llvm::logAllUnhandledErrors(innerOrErr.takeError(), llvm::errs(), "Failed to build Interpreter:"); return nullptr; } + if (CudaEnabled) { if (auto Err = (*innerOrErr)->LoadDynamicLibrary("libcudart.so")) { llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), @@ -371,6 +429,12 @@ inline void codeComplete(std::vector& Results, #endif } +#ifdef CPPINTEROP_WITH_OOP_JIT +inline pid_t getExecutorPID() { + return getLastLaunchedExecutorPID(); +} +#endif // CPPINTEROP_WITH_OOP_JIT + } // namespace compat #include "CppInterOpInterpreter.h" @@ -395,7 +459,7 @@ class SynthesizingCodeRAII { "Failed to generate PTU:"); } }; -} +} // namespace compat #endif // CPPINTEROP_USE_REPL diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index bcbc12359..a6851ea90 100755 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -3031,7 +3031,7 @@ static std::string MakeResourcesPath() { } // namespace TInterp_t CreateInterpreter(const std::vector& Args /*={}*/, - const std::vector& GpuArgs /*={}*/) { + const std::vector& GpuArgs /*={}*/, bool outOfProcess) { std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr); std::string ResourceDir = MakeResourcesPath(); std::vector ClingArgv = {"-resource-dir", ResourceDir.c_str(), @@ -3075,7 +3075,7 @@ TInterp_t CreateInterpreter(const std::vector& Args /*={}*/, auto I = new compat::Interpreter(ClingArgv.size(), &ClingArgv[0]); #else auto Interp = compat::Interpreter::create(static_cast(ClingArgv.size()), - ClingArgv.data()); + ClingArgv.data(), nullptr, {}, nullptr, true, outOfProcess); if (!Interp) return nullptr; auto* I = Interp.release(); @@ -3909,4 +3909,10 @@ int Undo(unsigned N) { #endif } +#ifdef CPPINTEROP_WITH_OOP_JIT +pid_t GetExecutorPID() { + return compat::getExecutorPID(); +} +#endif // CPPINTEROP_WITH_OOP_JIT + } // end namespace Cpp diff --git a/lib/CppInterOp/CppInterOpInterpreter.h b/lib/CppInterOp/CppInterOpInterpreter.h index d87eb33e1..9f3ab9cc1 100644 --- a/lib/CppInterOp/CppInterOpInterpreter.h +++ b/lib/CppInterOp/CppInterOpInterpreter.h @@ -150,7 +150,7 @@ class Interpreter { create(int argc, const char* const* argv, const char* llvmdir = nullptr, const std::vector>& moduleExtensions = {}, - void* extraLibHandle = nullptr, bool noRuntime = true) { + void* extraLibHandle = nullptr, bool noRuntime = true, bool outOfProcess = false) { // Initialize all targets (required for device offloading) llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargets(); @@ -158,7 +158,7 @@ class Interpreter { llvm::InitializeAllAsmPrinters(); std::vector vargs(argv + 1, argv + argc); - auto CI = compat::createClangInterpreter(vargs); + auto CI = compat::createClangInterpreter(vargs, outOfProcess); if (!CI) { llvm::errs() << "Interpreter creation failed\n"; return nullptr; diff --git a/patches/llvm/clang20-2-out-of-process-jit-execution.patch b/patches/llvm/clang20-2-out-of-process-jit-execution.patch new file mode 100644 index 000000000..22dca3fae --- /dev/null +++ b/patches/llvm/clang20-2-out-of-process-jit-execution.patch @@ -0,0 +1,780 @@ +diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h +index f8663e3193..78dff1165d 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 +@@ -136,10 +137,14 @@ protected: + public: + virtual ~Interpreter(); + static llvm::Expected> +- create(std::unique_ptr CI); ++ create(std::unique_ptr CI, ++ std::unique_ptr JITBuilder = nullptr); + static llvm::Expected> + createWithCUDA(std::unique_ptr CI, + std::unique_ptr DCI); ++ static llvm::Expected> ++ createLLJITBuilder(std::unique_ptr 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 0000000000..ae978be2c8 +--- /dev/null ++++ b/clang/include/clang/Interpreter/RemoteJITUtils.h +@@ -0,0 +1,43 @@ ++//===-- 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 ++#include ++#include ++ ++llvm::Expected> ++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> ++connectTCPSocket(llvm::StringRef NetworkAddress, bool UseSharedMemory, ++ llvm::StringRef SlabAllocateSizeString); ++ ++ ++/// Get the PID of the last launched executor. ++/// This is useful for debugging or for cleanup purposes. ++pid_t getLastLaunchedExecutorPID(); ++ ++#endif // LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H +diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt +index bf70cdfbee..38cf139fa8 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 3b81f9d701..eaa5c31cb6 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> +-Interpreter::create(std::unique_ptr CI) { ++Interpreter::create(std::unique_ptr CI, ++ std::unique_ptr JB) { + llvm::Error Err = llvm::Error::success(); +- auto Interp = +- std::unique_ptr(new Interpreter(std::move(CI), Err)); ++ auto Interp = std::unique_ptr( ++ new Interpreter(std::move(CI), Err, JB ? std::move(JB) : nullptr)); + if (Err) + return std::move(Err); + +@@ -467,6 +469,8 @@ Interpreter::create(std::unique_ptr CI) { + auto PTU = Interp->Parse(Runtimes); + if (!PTU) + return PTU.takeError(); ++ ++ if (llvm::Error Err = Interp->Execute(*PTU)) return Err; + Interp->markUserCodeStart(); + + Interp->ValuePrintingInfo.resize(4); +@@ -617,6 +621,25 @@ createJITTargetMachineBuilder(const std::string &TT) { + return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT)); + } + ++llvm::Expected> ++Interpreter::createLLJITBuilder( ++ std::unique_ptr 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("Operation failed. " +@@ -757,11 +780,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 0000000000..2cc160625b +--- /dev/null ++++ b/clang/lib/Interpreter/RemoteJITUtils.cpp +@@ -0,0 +1,319 @@ ++//===-- 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 ++#include ++#include ++#include ++#include ++#endif // LLVM_ON_UNIX ++ ++using namespace llvm; ++using namespace llvm::orc; ++ ++static std::atomic LaunchedExecutorPID{-1}; ++ ++Expected 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("Invalid numeric format for slab size", ++ inconvertibleErrorCode()); ++ ++ return SlabSize * Units; ++} ++ ++Expected> ++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( ++ SlabSize, SREPC, SAs); ++} ++ ++Expected> ++launchExecutor(StringRef ExecutablePath, bool UseSharedMemory, ++ llvm::StringRef SlabAllocateSizeString) { ++#ifndef LLVM_ON_UNIX ++ return make_error("-" + ExecutablePath + ++ " not supported on non-unix platforms", ++ inconvertibleErrorCode()); ++#elif !LLVM_ENABLE_THREADS ++ return make_error( ++ "-" + ExecutablePath + ++ " requires threads, but LLVM was built with " ++ "LLVM_ENABLE_THREADS=Off", ++ inconvertibleErrorCode()); ++#else ++ if (!sys::fs::can_execute(ExecutablePath)) ++ return make_error( ++ formatv("Specified executor invalid: {0}", ExecutablePath), ++ inconvertibleErrorCode()); ++ ++ constexpr int ReadEnd = 0; ++ constexpr int WriteEnd = 1; ++ ++ // Pipe FDs for RPC ++ int ToExecutor[2]; ++ int FromExecutor[2]; ++ ++ // New pipes for stdout and stderr ++ int StdoutPipe[2]; ++ int StderrPipe[2]; ++ ++ pid_t ChildPID; ++ ++ // Create pipes ++ if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0 || ++ pipe(StdoutPipe) != 0 || pipe(StderrPipe) != 0) ++ return make_error("Unable to create pipes", ++ inconvertibleErrorCode()); ++ ++ ChildPID = fork(); ++ ++ if (ChildPID == 0) { ++ // In the child... ++ ++ // Close parent ends of RPC pipes ++ close(ToExecutor[WriteEnd]); ++ close(FromExecutor[ReadEnd]); ++ ++ // Close read ends of stdout and stderr pipes ++ close(StdoutPipe[ReadEnd]); ++ close(StderrPipe[ReadEnd]); ++ ++ // Redirect stdout and stderr to the pipes ++ dup2(StdoutPipe[WriteEnd], STDOUT_FILENO); ++ dup2(StderrPipe[WriteEnd], STDERR_FILENO); ++ ++ // Close write ends after redirection ++ close(StdoutPipe[WriteEnd]); ++ close(StderrPipe[WriteEnd]); ++ ++ // Execute the child process ++ std::unique_ptr ExecutorPath, FDSpecifier; ++ { ++ ExecutorPath = std::make_unique(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(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 { ++ LaunchedExecutorPID = ChildPID; ++ } ++ ++ // Close child ends of RPC pipes ++ close(ToExecutor[ReadEnd]); ++ close(FromExecutor[WriteEnd]); ++ ++ // Close write ends of stdout and stderr pipes ++ close(StdoutPipe[WriteEnd]); ++ close(StderrPipe[WriteEnd]); ++ ++ auto S = SimpleRemoteEPC::Setup(); ++ if (UseSharedMemory) ++ S.CreateMemoryManager = [SlabAllocateSizeString](SimpleRemoteEPC &EPC) { ++ return createSharedMemoryManager(EPC, SlabAllocateSizeString); ++ }; ++ ++ auto EPC = SimpleRemoteEPC::Create( ++ std::make_unique(std::nullopt), ++ std::move(S), FromExecutor[ReadEnd], ToExecutor[WriteEnd]); ++ ++ // Read stdout ++ std::thread([readFd = StdoutPipe[ReadEnd]] { ++ char buffer[1024]; ++ ssize_t bytesRead; ++ while ((bytesRead = read(readFd, buffer, sizeof(buffer) - 1)) > 0) { ++ buffer[bytesRead] = '\0'; ++ fwrite(buffer, 1, bytesRead, stdout); ++ fflush(stdout); ++ } ++ close(readFd); ++ }).detach(); ++ ++ // Spawn a background thread to read stderr ++ std::thread([readFd = StderrPipe[ReadEnd]] { ++ char buffer[1024]; ++ ssize_t bytesRead; ++ while ((bytesRead = read(readFd, buffer, sizeof(buffer) - 1)) > 0) { ++ buffer[bytesRead] = '\0'; ++ fwrite(buffer, 1, bytesRead, stderr); ++ fflush(stderr); ++ } ++ close(readFd); ++ }).detach(); ++ ++ return EPC; ++#endif ++} ++ ++#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS ++ ++static Expected 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( ++ 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("invalid hostname", ++ inconvertibleErrorCode()); ++ ++ return SockFD; ++} ++#endif ++ ++Expected> ++connectTCPSocket(StringRef NetworkAddress, bool UseSharedMemory, ++ llvm::StringRef SlabAllocateSizeString) { ++#ifndef LLVM_ON_UNIX ++ // FIXME: Add TCP support for Windows. ++ return make_error("-" + NetworkAddress + ++ " not supported on non-unix platforms", ++ inconvertibleErrorCode()); ++#elif !LLVM_ENABLE_THREADS ++ // Out of process mode using SimpleRemoteEPC depends on threads. ++ return make_error( ++ "-" + NetworkAddress + ++ " requires threads, but LLVM was built with " ++ "LLVM_ENABLE_THREADS=Off", ++ inconvertibleErrorCode()); ++#else ++ ++ auto CreateErr = [NetworkAddress](Twine Details) { ++ return make_error( ++ 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 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( ++ std::make_unique(std::nullopt), ++ std::move(S), *SockFD, *SockFD); ++#endif ++} ++ ++pid_t getLastLaunchedExecutorPID() { ++ return LaunchedExecutorPID; ++} +diff --git a/clang/test/Interpreter/out-of-process.cpp b/clang/test/Interpreter/out-of-process.cpp +new file mode 100644 +index 0000000000..edc4c3fee0 +--- /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/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt +index f9a911b0ae..68d86dd98c 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 7af8e4f25d..6113cc2a99 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 + ++#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 CudaEnabled("cuda", llvm::cl::Hidden); + static llvm::cl::opt CudaPath("cuda-path", llvm::cl::Hidden); + static llvm::cl::opt OffloadArch("offload-arch", llvm::cl::Hidden); +- ++static llvm::cl::OptionCategory OOPCategory("Out-of-process Execution Options"); ++static llvm::cl::opt 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 ++ 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 OOPExecutorConnect( ++ "oop-executor-connect", ++ llvm::cl::desc( ++ "Connect to an out-of-process executor through a TCP socket"), ++ llvm::cl::value_desc(":")); ++static llvm::cl::opt ++ 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 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 + ClangArgs("Xcc", + llvm::cl::desc("Argument to pass to the CompilerInvocation"), +@@ -47,6 +80,72 @@ static llvm::cl::opt OptHostSupportsJit("host-supports-jit", + static llvm::cl::list 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( ++ "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( ++ "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( ++ "-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(&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( ++ "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(&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(UserData); +@@ -183,6 +282,29 @@ int main(int argc, const char **argv) { + DeviceCI = ExitOnErr(CB.CreateCudaDevice()); + } + ++ ExitOnErr(sanitizeOopArguments(argv[0])); ++ ++ std::unique_ptr EPC; ++ if (OOPExecutor.getNumOccurrences()) { ++ // Launch an out-of-process executor locally in a child process. ++ std::cout << "OOPExecutor: " << OOPExecutor << std::endl; ++ std::cout << "UseSharedMemory: " << UseSharedMemory << std::endl; ++ std::cout << "SlabAllocateSizeString: " << SlabAllocateSizeString << std::endl; ++ EPC = ExitOnErr( ++ launchExecutor(OOPExecutor, UseSharedMemory, SlabAllocateSizeString)); ++ } else if (OOPExecutorConnect.getNumOccurrences()) { ++ EPC = ExitOnErr(connectTCPSocket(OOPExecutorConnect, UseSharedMemory, ++ SlabAllocateSizeString)); ++ } ++ ++ std::unique_ptr JB; ++ if (EPC) { ++ CB.SetTargetTriple(EPC->getTargetTriple().getTriple()); ++ std::cout << "OrcRuntimePath: " << OrcRuntimePath << std::endl; ++ 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 CI; +@@ -214,6 +336,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))); + diff --git a/unittests/CppInterOp/InterpreterTest.cpp b/unittests/CppInterOp/InterpreterTest.cpp index 841913c54..36c128391 100644 --- a/unittests/CppInterOp/InterpreterTest.cpp +++ b/unittests/CppInterOp/InterpreterTest.cpp @@ -146,6 +146,23 @@ TEST(InterpreterTest, Process) { clang_Interpreter_dispose(CXI); } +#ifdef CPPINTEROP_WITH_OOP_JIT +TEST(InterpreterTest, OopJITProcess) { +#ifdef _WIN32 + GTEST_SKIP() << "Disabled on Windows. Needs fixing."; +#endif + if (llvm::sys::RunningOnValgrind()) + GTEST_SKIP() << "XFAIL due to Valgrind report"; + std::vector interpreter_args = { "-include", "new" }; + auto* I = Cpp::CreateInterpreter(interpreter_args, {}, true); + EXPECT_TRUE(Cpp::Process("") == 0); + EXPECT_TRUE(Cpp::Process("int a = 12;") == 0); + EXPECT_FALSE(Cpp::Process("error_here;") == 0); + // Linker/JIT error. + EXPECT_FALSE(Cpp::Process("int f(); int res = f();") == 0); +} +#endif + TEST(InterpreterTest, EmscriptenExceptionHandling) { #ifndef EMSCRIPTEN GTEST_SKIP() << "This test is intended to check exception handling for Emscripten builds.";