Skip to content

Adds Out-Of-Process JIT execution for CppInterOp #635

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/actions/Build_LLVM/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should apply this patch regardless of whether matrix.oop-jit=on. If you only apply it if CPPINTEROP_WITH_OOP_JIT=ON , this would imply if you apply the patch, DCPPINTEROP_WITH_OOP_JIT=OFF, then somehow something will go wrong.

Also, why are we not applying this patch on Windows, and testing CPPINTEROP_WITH_OOP_JIT=ON/OFF?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. The thing is, I would want CppInterOp to be compatible with upstream LLVM as well as the patched LLVM. Therefore, I was thinking about having 2 different CI workflows for version 20 with OOP-JIT and without OOP-JIT.
OOP-JIT is not supported on Windows.


Ideally, this is temporary till the patch is directly merged into LLVM and a new version is released.

Copy link
Collaborator

@mcbarton mcbarton Jun 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could be wrong, and @vgvassilev can correct me if I am wrong, but for such large patches, we have had them merged into llvm first, before applying them to previous versions in CppInterOp.

You could have a new llvm cache like you want. One where you build OOP-JIT and without OOP-JIT. This could only really be done after I find a way to make space in the cache, since we currently are unlikely to have enough space for all these new llvm cache builds. I have an idea on how to free up some space, and just need to find some time to do. I will try and do it tomorrow, as I am busy today.

cd build
cmake -DLLVM_ENABLE_PROJECTS="${{ matrix.llvm_enable_projects}}" \
-DLLVM_TARGETS_TO_BUILD="${{ matrix.llvm_targets_to_build }}" \
Expand All @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have target name for orc_rt difference for MacOS? The target name should be independent of the system your building on.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is different in LLVM itself. I guess it might have to do with cross-compiling.

cd ./tools/
rm -rf $(find . -maxdepth 1 ! -name "clang" ! -name ".")
cd ..
Expand Down
17 changes: 9 additions & 8 deletions .github/actions/Build_and_Test_CppInterOp/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Comment on lines +320 to +321
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
string(REGEX REPLACE "/build/lib/cmake/llvm$" "" LLVM_SOURCE_DIR "${LLVM_DIR}")
add_definitions(-DLLVM_SOURCE_DIR="${LLVM_SOURCE_DIR}")
string(REGEX REPLACE "/build/lib/cmake/llvm$" "" LLVM_BINARY_DIR "${LLVM_DIR}")
add_definitions(-DLLVM_SOURCE_DIR="${LLVM_BINARY_DIR}")

I guess is a better name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLVM_BINARY_DIR is already an env variable

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reuse LLVM_BINARY_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
Expand Down
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
43 changes: 43 additions & 0 deletions docs/InstallationAndUsage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
******************
Expand Down Expand Up @@ -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
**************************************
Expand Down Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion include/CppInterOp/CppInterOp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<const char*>& Args = {},
const std::vector<const char*>& GpuArgs = {});
const std::vector<const char*>& GpuArgs = {}, bool outOfProcess = false);

/// Deletes an instance of an interpreter.
///\param[in] I - the interpreter to be deleted, if nullptr, deletes the last.
Expand Down Expand Up @@ -901,6 +901,10 @@ CPPINTEROP_API void CodeComplete(std::vector<std::string>& 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
68 changes: 66 additions & 2 deletions lib/CppInterOp/Compatibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,22 @@

#include "llvm/Support/Error.h"

#include <vector>

#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<clang::Interpreter>
createClangInterpreter(std::vector<const char*>& args) {
createClangInterpreter(std::vector<const char*>& args, bool outOfProcess) {
auto has_arg = [](const char* x, llvm::StringRef match = "cuda") {
llvm::StringRef Arg = x;
Arg = Arg.trim().ltrim('-');
Expand Down Expand Up @@ -246,16 +258,62 @@
(*ciOrErr)->LoadRequestedPlugins();
if (CudaEnabled)
DeviceCI->LoadRequestedPlugins();

#ifdef CPPINTEROP_WITH_OOP_JIT
std::unique_ptr<llvm::orc::LLJITBuilder> 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<llvm::orc::ExecutorProcessControl> 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"

Check warning on line 299 in lib/CppInterOp/Compatibility.h

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/Compatibility.h#L298-L299

Added lines #L298 - L299 were not covered by tests
<< "(To enable recompile CppInterOp with "
"`-DCPPINTEROP_WITH_OOP_JIT=ON`)"
<< "\n";
return nullptr;

Check warning on line 303 in lib/CppInterOp/Compatibility.h

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/Compatibility.h#L301-L303

Added lines #L301 - L303 were not covered by tests
}
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(),
Expand Down Expand Up @@ -371,6 +429,12 @@
#endif
}

#ifdef CPPINTEROP_WITH_OOP_JIT
inline pid_t getExecutorPID() {
return getLastLaunchedExecutorPID();
}
#endif // CPPINTEROP_WITH_OOP_JIT

} // namespace compat

#include "CppInterOpInterpreter.h"
Expand All @@ -395,7 +459,7 @@
"Failed to generate PTU:");
}
};
}
} // namespace compat

#endif // CPPINTEROP_USE_REPL

Expand Down
10 changes: 8 additions & 2 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3031,7 +3031,7 @@ static std::string MakeResourcesPath() {
} // namespace

TInterp_t CreateInterpreter(const std::vector<const char*>& Args /*={}*/,
const std::vector<const char*>& GpuArgs /*={}*/) {
const std::vector<const char*>& GpuArgs /*={}*/, bool outOfProcess) {
std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr);
std::string ResourceDir = MakeResourcesPath();
std::vector<const char*> ClingArgv = {"-resource-dir", ResourceDir.c_str(),
Expand Down Expand Up @@ -3075,7 +3075,7 @@ TInterp_t CreateInterpreter(const std::vector<const char*>& Args /*={}*/,
auto I = new compat::Interpreter(ClingArgv.size(), &ClingArgv[0]);
#else
auto Interp = compat::Interpreter::create(static_cast<int>(ClingArgv.size()),
ClingArgv.data());
ClingArgv.data(), nullptr, {}, nullptr, true, outOfProcess);
if (!Interp)
return nullptr;
auto* I = Interp.release();
Expand Down Expand Up @@ -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
Loading
Loading