Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 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
11 changes: 11 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/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,13 @@ runs:
-DLLVM_INCLUDE_TESTS=OFF \
../llvm
ninja clang clangInterpreter clangStaticAnalyzerCore -j ${{ env.ncpus }}
if [[ "${{ matrix.oop-jit }}" == "On" ]]; then
if [[ "${{ runner.os }}" == "Linux" ]]; then
ninja clang clang-repl llvm-jitlink-executor orc_rt -j $(nproc --all)
elif [[ "${{ runner.os }}" == "macOS" ]]; then
ninja clang clang-repl llvm-jitlink-executor orc_rt_osx -j $(nproc --all)
fi
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
16 changes: 15 additions & 1 deletion .github/actions/Build_and_Test_CppInterOp/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,29 @@ runs:
-DLLVM_ENABLE_WERROR=On \
../
else
cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
if [[ "${{ matrix.oop-jit }}" == "On" ]]; then
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_WITH_OOP_JIT=ON \
../
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_WITH_OOP_JIT=OFF \
Copy link
Collaborator

Choose a reason for hiding this comment

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

Feels like CPPINTEROP_WITH_OOP_JIT should have a default value, like we have for cppinterop_use_repl/cppinterop_use_cling

../
fi
fi
docs_on=$(echo "${{ matrix.documentation }}" | tr '[:lower:]' '[:upper:]')
if [[ "${docs_on}" == "ON" ]]; then
Expand Down
12 changes: 8 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,19 @@ jobs:
clang-runtime: '20'
cling: Off
cppyy: Off
llvm_enable_projects: "clang"
llvm_enable_projects: "clang;compiler-rt"
llvm_targets_to_build: "host;NVPTX"
coverage: true
oop-jit: "On"
- name: ubu24-arm-gcc12-clang-repl-20
os: ubuntu-24.04-arm
compiler: gcc-12
clang-runtime: '20'
cling: Off
cppyy: Off
llvm_enable_projects: "clang"
llvm_enable_projects: "clang;compiler-rt"
llvm_targets_to_build: "host;NVPTX"
oop-jit: "On"
- name: ubu24-arm-gcc12-clang-repl-19-cppyy
os: ubuntu-24.04-arm
compiler: gcc-12
Expand Down Expand Up @@ -138,8 +140,9 @@ jobs:
clang-runtime: '20'
cling: Off
cppyy: Off
llvm_enable_projects: "clang"
llvm_enable_projects: "clang;compiler-rt"
llvm_targets_to_build: "host"
oop-jit: "On"
- name: osx15-arm-clang-clang-repl-19-cppyy
os: macos-15
compiler: clang
Expand Down Expand Up @@ -188,8 +191,9 @@ jobs:
clang-runtime: '20'
cling: Off
cppyy: Off
llvm_enable_projects: "clang"
llvm_enable_projects: "clang;compiler-rt"
llvm_targets_to_build: "host"
oop-jit: "On"
- name: osx13-x86-clang-clang-repl-19-cppyy
os: macos-13
compiler: clang
Expand Down
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,13 @@ else()
message(FATAL_ERROR "We need either CPPINTEROP_USE_CLING or CPPINTEROP_USE_REPL")
endif()

if(CPPINTEROP_WITH_OOP_JIT)
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?


include_directories(SYSTEM ${CLANG_INCLUDE_DIRS})
include_directories(SYSTEM ${LLVM_INCLUDE_DIRS})
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
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/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 $(nproc --all)
```

#### 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
2 changes: 1 addition & 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
51 changes: 50 additions & 1 deletion lib/CppInterOp/Compatibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ static inline char* GetEnv(const char* Var_Name) {

// std::regex breaks pytorch's jit: pytorch/pytorch#49460
#include "llvm/Support/Regex.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"

#ifdef CPPINTEROP_USE_CLING

Expand Down Expand Up @@ -205,10 +206,21 @@ inline void codeComplete(std::vector<std::string>& Results,

#include "llvm/Support/Error.h"

#ifdef CPPINTEROP_WITH_OOP_JIT
#include "clang/Interpreter/RemoteJITUtils.h"
#include "clang/Basic/Version.h"
#include "llvm/TargetParser/Host.h"

#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
#endif


static 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,53 @@ createClangInterpreter(std::vector<const char*>& args) {
(*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";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will this work if LLVM is installed with conda or is installed with the system's package installer.
Could you make sure if working in the other cases too.
You can first check if the file exists; if not, try other paths like the CONDA_PREFIX or /usr/bin.

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"
<< "\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(),
Expand Down
6 changes: 3 additions & 3 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3031,11 +3031,11 @@ 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 is_out_of_process) {
std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr);
std::string ResourceDir = MakeResourcesPath();
std::vector<const char*> ClingArgv = {"-resource-dir", ResourceDir.c_str(),
"-std=c++14"};
"-std=c++14", "-gdwarf-4", "-O0"};
ClingArgv.insert(ClingArgv.begin(), MainExecutableName.c_str());
#ifdef _WIN32
// FIXME : Workaround Sema::PushDeclContext assert on windows
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, is_out_of_process);
if (!Interp)
return nullptr;
auto* I = Interp.release();
Expand Down
4 changes: 2 additions & 2 deletions lib/CppInterOp/CppInterOpInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,15 @@ class Interpreter {
create(int argc, const char* const* argv, const char* llvmdir = nullptr,
const std::vector<std::shared_ptr<clang::ModuleFileExtension>>&
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();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmPrinters();

std::vector<const char*> 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;
Expand Down
Loading
Loading