diff --git a/.github/actions/Build_LLVM/action.yml b/.github/actions/Build_LLVM/action.yml index 3d9461fe7..080eb4029 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-1-out-of-process.patch + echo "Apply clang20-1-out-of-process.patch:" + fi cd build cmake -DLLVM_ENABLE_PROJECTS="${{ matrix.llvm_enable_projects}}" \ -DLLVM_TARGETS_TO_BUILD="${{ matrix.llvm_targets_to_build }}" \ @@ -64,6 +68,14 @@ 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" + elif [[ "${{ matrix.os }}" == ubuntu* ]]; then + SUFFIX="-x86_64" + 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..5f419a108 100644 --- a/.github/actions/Build_and_Test_CppInterOp/action.yml +++ b/.github/actions/Build_and_Test_CppInterOp/action.yml @@ -23,6 +23,12 @@ runs: export CPPINTEROP_DIR="$CB_PYTHON_DIR/cppyy_backend" # Build CppInterOp next to cling and llvm-project. + if [[ "${{ matrix.oop-jit }}" == "On" ]]; then + if [[ -f VERSION ]]; then + sed -i.bak 's/1\.8\.0;dev/1.8.1;dev/' VERSION + rm -f VERSION.bak + fi + fi mkdir build && cd build export CPPINTEROP_BUILD_DIR=$PWD cling_on=$(echo "${{ matrix.cling }}" | tr '[:lower:]' '[:upper:]') @@ -60,6 +66,9 @@ runs: if [[ "${os}" != "macos"* ]]; then valgrind --show-error-list=yes --track-origins=yes --error-exitcode=1 unittests/CppInterOp/CppInterOpTests/unittests/bin/${{ env.BUILD_TYPE }}/CppInterOpTests fi + if [[ "${{ matrix.oop-jit }}" == "On" ]]; then + ./unittests/CppInterOp/CppInterOpTests/unittests/bin/${{ env.BUILD_TYPE }}/CppInterOpTests --use-oop-jit + fi fi echo "CB_PYTHON_DIR=$CB_PYTHON_DIR" >> $GITHUB_ENV echo "CPPINTEROP_BUILD_DIR=$CPPINTEROP_BUILD_DIR" >> $GITHUB_ENV diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5f8061f4a..c2652fcb0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -90,6 +90,15 @@ jobs: cppyy: Off llvm_enable_projects: "clang" llvm_targets_to_build: "host;NVPTX" + - name: ubu24-x86-gcc12-clang-repl-20-out-of-process + os: ubuntu-24.04 + 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-x86-gcc12-clang-repl-19-cppyy os: ubuntu-24.04 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-out-of-process + 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 @@ -281,9 +299,9 @@ jobs: id: cache with: path: | - llvm-project - ${{ matrix.cling=='On' && 'cling' || '' }} - key: ${{ env.CLING_HASH }}-${{ runner.os }}-${{ matrix.os }}-${{ matrix.compiler }}-clang-${{ matrix.clang-runtime }}.x-patch-${{ hashFiles(format('patches/llvm/clang{0}-*.patch', matrix.clang-runtime)) || 'none' }} + llvm-project + ${{ matrix.cling=='On' && 'cling' || '' }} + key: ${{ env.CLING_HASH }}-${{ runner.os }}-${{ matrix.os }}-${{ matrix.compiler }}-clang-${{ matrix.clang-runtime }}.x-patch-${{ hashFiles(format('patches/llvm/clang{0}-*.patch', matrix.clang-runtime)) || 'none' }}${{ matrix.oop-jit == 'On' && '-oop' || '' }} - name: Setup default Build Type uses: ./.github/actions/Miscellaneous/Select_Default_Build_Type @@ -299,14 +317,14 @@ jobs: with: cache-hit: ${{ steps.cache.outputs.cache-hit }} - - name: Cache LLVM-${{ matrix.clang-runtime }} and ${{ matrix.cling == 'On' && 'Cling' || 'Clang-REPL' }} build - uses: actions/cache/save@v4 - if: ${{ steps.cache.outputs.cache-hit != 'true' }} - with: - path: | - llvm-project - ${{ matrix.cling=='On' && 'cling' || '' }} - key: ${{ steps.cache.outputs.cache-primary-key }} + # - name: Cache LLVM-${{ matrix.clang-runtime }} and ${{ matrix.cling == 'On' && 'Cling' || 'Clang-REPL' }} build + # uses: actions/cache/save@v4 + # if: ${{ steps.cache.outputs.cache-hit != 'true' }} + # with: + # path: | + # llvm-project + # ${{ matrix.cling=='On' && 'cling' || '' }} + # key: ${{ steps.cache.outputs.cache-primary-key }} - name: Setup code coverage if: ${{ success() && (matrix.coverage == true) }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b2df5b4f..6031ec568 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,25 +72,26 @@ if (CPPINTEROP_USE_CLING) else() set(CLANG_MIN_SUPPORTED 16.0) endif(CPPINTEROP_USE_CLING) - set(CLANG_MAX_SUPPORTED "20.1.x") - set(CLANG_VERSION_UPPER_BOUND 21.0.0) + set(CLANG_MAX_SUPPORTED "21.1.x") + set(CLANG_VERSION_UPPER_BOUND 22.0.0) if (CPPINTEROP_USE_CLING) set(LLD_MIN_SUPPORTED 18.0) else() set(LLD_MIN_SUPPORTED 16.0) endif(CPPINTEROP_USE_CLING) - set(LLD_MAX_SUPPORTED "20.1.x") - set(LLD_VERSION_UPPER_BOUND 21.0.0) + set(LLD_MAX_SUPPORTED "21.1.x") + set(LLD_VERSION_UPPER_BOUND 22.0.0) if (CPPINTEROP_USE_CLING) set(LLVM_MIN_SUPPORTED 18.0) else() set(LLVM_MIN_SUPPORTED 16.0) endif(CPPINTEROP_USE_CLING) - set(LLVM_MAX_SUPPORTED "20.1.x") - set(LLVM_VERSION_UPPER_BOUND 21.0.0) + set(LLVM_MAX_SUPPORTED "21.1.x") + set(LLVM_VERSION_UPPER_BOUND 22.0.0) ## Set Cmake packages search order + set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL) set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC) @@ -310,6 +311,9 @@ include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS}) add_definitions(${LLVM_DEFINITIONS_LIST}) +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 @@ -391,10 +395,16 @@ string(REPLACE "-Wcovered-switch-default" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} file(STRINGS "VERSION" CPPINTEROP_VERSION) string(REPLACE "." ";" VERSION_LIST "${CPPINTEROP_VERSION}") +string(REPLACE "\;" ";" VERSION_LIST "${VERSION_LIST}") + list(GET VERSION_LIST 0 CPPINTEROP_VERSION_MAJOR) list(GET VERSION_LIST 1 CPPINTEROP_VERSION_MINOR) list(GET VERSION_LIST 2 CPPINTEROP_VERSION_PATCH) +if(NOT CPPINTEROP_VERSION_PATCH STREQUAL "0") + add_definitions(-DCPPINTEROP_VERSION_PATCH) +endif() + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CppInterOp/CppInterOpConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/CppInterOp/CppInterOpConfig.cmake diff --git a/README.md b/README.md index 534fc191e..9babdc56c 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-1-out-of-process.patch +``` + ##### Build Clang-REPL Clang-REPL is an interpreter that CppInterOp works alongside. Build Clang (and @@ -175,6 +182,28 @@ $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-x86_64 --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 @@ -326,6 +355,8 @@ cmake --build . --target install --parallel $(nproc --all) and +> Do make sure to apply the patch and change VERSION file to ``1.8.1;dev``, if you want to have out-of-process JIT execution feature enabled. + ```powershell cmake -DLLVM_DIR=$env:LLVM_DIR\build\lib\cmake\llvm -DClang_DIR=$env:LLVM_DIR\build\lib\cmake\clang -DCMAKE_INSTALL_PREFIX=$env:CPPINTEROP_DIR .. cmake --build . --target install --parallel $env:ncpus diff --git a/docs/InstallationAndUsage.rst b/docs/InstallationAndUsage.rst index 8063d9c61..d1bfa6ff2 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-1-out-of-process.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-x86_64 --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 apply the patch and change VERSION file to ``1.8.1;dev``, 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 7b428e4b2..618616e6f 100644 --- a/include/CppInterOp/CppInterOp.h +++ b/include/CppInterOp/CppInterOp.h @@ -19,6 +19,7 @@ #include #include #include +#include #include // The cross-platform CPPINTEROP_API macro definition @@ -903,6 +904,12 @@ CPPINTEROP_API void CodeComplete(std::vector& Results, ///\returns 0 on success, non-zero on failure. CPPINTEROP_API int Undo(unsigned N = 1); +#ifndef _WIN32 +/// Returns the process ID of the executor process. +/// \returns the PID of the executor process. +CPPINTEROP_API pid_t GetExecutorPID(); +#endif + } // end namespace Cpp #endif // CPPINTEROP_CPPINTEROP_H diff --git a/lib/CppInterOp/Compatibility.h b/lib/CppInterOp/Compatibility.h index 0c7c66f5b..086dcfd0c 100644 --- a/lib/CppInterOp/Compatibility.h +++ b/lib/CppInterOp/Compatibility.h @@ -205,10 +205,23 @@ inline void codeComplete(std::vector& Results, #include "llvm/Support/Error.h" +#ifdef CPPINTEROP_VERSION_PATCH +#include "clang/Basic/Version.h" +#include "clang/Interpreter/RemoteJITUtils.h" +#include "llvm/TargetParser/Host.h" + +#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h" +#endif + +#include + +static const llvm::ExitOnError ExitOnError; + namespace compat { inline std::unique_ptr -createClangInterpreter(std::vector& args) { +createClangInterpreter(std::vector& args, int stdin_fd = 0, + int stdout_fd = 1, int stderr_fd = 2) { auto has_arg = [](const char* x, llvm::StringRef match = "cuda") { llvm::StringRef Arg = x; Arg = Arg.trim().ltrim('-'); @@ -222,6 +235,15 @@ createClangInterpreter(std::vector& args) { bool CudaEnabled = !gpu_args.empty(); #endif +#if defined(_WIN32) + bool outOfProcess = false; +#else + bool outOfProcess = + std::any_of(args.begin(), args.end(), [](const char* arg) { + return llvm::StringRef(arg).trim() == "--use-oop-jit"; + }); +#endif + clang::IncrementalCompilerBuilder CB; CB.SetCompilerArgs({args.begin(), it}); @@ -246,16 +268,62 @@ createClangInterpreter(std::vector& args) { (*ciOrErr)->LoadRequestedPlugins(); if (CudaEnabled) DeviceCI->LoadRequestedPlugins(); + +#ifdef CPPINTEROP_VERSION_PATCH + 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, stdin_fd, + stdout_fd, stderr_fd)); + +#ifdef __APPLE__ + std::string OrcRuntimePath = + std::string(LLVM_SOURCE_DIR) + "/build/lib/clang/" + + std::to_string(LLVM_VERSION_MAJOR) + "/lib/darwin/liborc_rt_osx.a"; +#else + std::string OrcRuntimePath = std::string(LLVM_SOURCE_DIR) + + "/build/lib/clang/" + + std::to_string(LLVM_VERSION_MAJOR) + + "/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. Running in-process JIT execution." + << "(To enable recompile CppInterOp with patch applied and change " + "VERSION file to 1.8.1;dev." + << "\n"; + } 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 +439,12 @@ inline void codeComplete(std::vector& Results, #endif } +#ifndef _WIN32 +#ifdef CPPINTEROP_VERSION_PATCH +inline pid_t getExecutorPID() { return /*llvm*/ getLastLaunchedExecutorPID(); } +#endif +#endif + } // namespace compat #include "CppInterOpInterpreter.h" @@ -395,7 +469,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 cd776dbce..da1b64645 100755 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -63,12 +63,17 @@ #include #include #include +#include +#ifndef _WIN32 +#include +#endif // Stream redirect. #ifdef _WIN32 #include #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 +#define STDERR_FILENO 2 // For exec(). #include #define popen(x, y) (_popen(x, y)) @@ -76,7 +81,6 @@ #endif #else #include -#include #endif // WIN32 namespace Cpp { @@ -3103,8 +3107,9 @@ TInterp_t CreateInterpreter(const std::vector& Args /*={}*/, #ifdef CPPINTEROP_USE_CLING auto I = new compat::Interpreter(ClingArgv.size(), &ClingArgv[0]); #else - auto Interp = compat::Interpreter::create(static_cast(ClingArgv.size()), - ClingArgv.data()); + auto Interp = + compat::Interpreter::create(static_cast(ClingArgv.size()), + ClingArgv.data(), nullptr, {}, nullptr, true); if (!Interp) return nullptr; auto* I = Interp.release(); @@ -3828,12 +3833,12 @@ void Destruct(TCppObject_t This, TCppScope_t scope, bool withFree /*=true*/, } class StreamCaptureInfo { - struct file_deleter { - void operator()(FILE* fp) { pclose(fp); } - }; - std::unique_ptr m_TempFile; + + FILE* m_TempFile; int m_FD = -1; int m_DupFD = -1; + int mode = -1; + bool m_OwnsFile = false; public: #ifdef _MSC_VER @@ -3848,20 +3853,46 @@ class StreamCaptureInfo { }()}, m_FD(FD) { #else - StreamCaptureInfo(int FD) : m_TempFile{tmpfile()}, m_FD(FD) { + StreamCaptureInfo(int FD) : mode(FD) { +#endif +#if !defined(CPPINTEROP_USE_CLING) && !defined(_WIN32) + if (compat::Interpreter::isOutOfProcess() && FD == STDOUT_FILENO) { + m_TempFile = compat::Interpreter::getTempFileForOOP(FD); + ::fflush(m_TempFile); + m_FD = fileno(m_TempFile); + m_OwnsFile = false; + } else { + m_TempFile = tmpfile(); + m_FD = FD; + m_OwnsFile = true; + } +#else + m_TempFile = tmpfile(); + m_FD = FD; + m_OwnsFile = true; + (void)mode; #endif if (!m_TempFile) { perror("StreamCaptureInfo: Unable to create temp file"); return; } - m_DupFD = dup(FD); + m_DupFD = dup(m_FD); // Flush now or can drop the buffer when dup2 is called with Fd later. // This seems only necessary when piping stdout or stderr, but do it // for ttys to avoid over complicated code for minimal benefit. - ::fflush(FD == STDOUT_FILENO ? stdout : stderr); - if (dup2(fileno(m_TempFile.get()), FD) < 0) + if (m_FD == STDOUT_FILENO) { + ::fflush(stdout); + } else if (m_FD == STDERR_FILENO) { + ::fflush(stderr); + } else { +#ifndef _WIN32 + fsync(m_FD); +#endif + } + // ::fflush(FD == STDOUT_FILENO ? stdout : stderr); + if (dup2(fileno(m_TempFile), m_FD) < 0) perror("StreamCaptureInfo:"); } StreamCaptureInfo(const StreamCaptureInfo&) = delete; @@ -3869,7 +3900,12 @@ class StreamCaptureInfo { StreamCaptureInfo(StreamCaptureInfo&&) = delete; StreamCaptureInfo& operator=(StreamCaptureInfo&&) = delete; - ~StreamCaptureInfo() { assert(m_DupFD == -1 && "Captured output not used?"); } + ~StreamCaptureInfo() { + assert(m_DupFD == -1 && "Captured output not used?"); + if (m_TempFile && m_OwnsFile) { + fclose(m_TempFile); + } + } std::string GetCapturedString() { assert(m_DupFD != -1 && "Multiple calls to GetCapturedString"); @@ -3878,11 +3914,11 @@ class StreamCaptureInfo { if (dup2(m_DupFD, m_FD) < 0) perror("StreamCaptureInfo:"); // Go to the end of the file. - if (fseek(m_TempFile.get(), 0L, SEEK_END) != 0) + if (fseek(m_TempFile, 0L, SEEK_END) != 0) perror("StreamCaptureInfo:"); // Get the size of the file. - long bufsize = ftell(m_TempFile.get()); + long bufsize = ftell(m_TempFile); if (bufsize == -1) perror("StreamCaptureInfo:"); @@ -3890,13 +3926,12 @@ class StreamCaptureInfo { std::unique_ptr content(new char[bufsize + 1]); // Go back to the start of the file. - if (fseek(m_TempFile.get(), 0L, SEEK_SET) != 0) + if (fseek(m_TempFile, 0L, SEEK_SET) != 0) perror("StreamCaptureInfo:"); // Read the entire file into memory. - size_t newLen = - fread(content.get(), sizeof(char), bufsize, m_TempFile.get()); - if (ferror(m_TempFile.get()) != 0) + size_t newLen = fread(content.get(), sizeof(char), bufsize, m_TempFile); + if (ferror(m_TempFile) != 0) fputs("Error reading file", stderr); else content[newLen++] = '\0'; // Just to be safe. @@ -3904,6 +3939,14 @@ class StreamCaptureInfo { std::string result = content.get(); close(m_DupFD); m_DupFD = -1; +#if !defined(_WIN32) && !defined(CPPINTEROP_USE_CLING) + if (compat::Interpreter::isOutOfProcess() && mode != STDERR_FILENO) { + if (ftruncate(m_FD, 0) != 0) + perror("ftruncate"); + if (lseek(m_FD, 0, SEEK_SET) == -1) + perror("lseek"); + } +#endif return result; } }; @@ -3943,4 +3986,13 @@ int Undo(unsigned N) { #endif } +#ifndef _WIN32 +pid_t GetExecutorPID() { +#ifdef CPPINTEROP_VERSION_PATCH + return compat::getExecutorPID(); +#endif + return -1; +} +#endif + } // end namespace Cpp diff --git a/lib/CppInterOp/CppInterOpInterpreter.h b/lib/CppInterOp/CppInterOpInterpreter.h index d87eb33e1..3c45dbeb8 100644 --- a/lib/CppInterOp/CppInterOpInterpreter.h +++ b/lib/CppInterOp/CppInterOpInterpreter.h @@ -39,6 +39,12 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Triple.h" +#include +#ifndef _WIN32 +#include +#endif +#include +#include #include #include @@ -145,6 +151,43 @@ class Interpreter { Interpreter(std::unique_ptr CI) : inner(std::move(CI)) {} +public: + struct FileDeleter { + void operator()(FILE* f) { + if (f) + fclose(f); + } + }; + +private: + static std::unique_ptr& getStdinFile() { + static std::unique_ptr stdin_file = nullptr; + return stdin_file; + } + + static std::unique_ptr& getStdoutFile() { + static std::unique_ptr stdout_file = nullptr; + return stdout_file; + } + + static std::unique_ptr& getStderrFile() { + static std::unique_ptr stderr_file = nullptr; + return stderr_file; + } + + static bool& getOutOfProcess() { + static bool outOfProcess = false; + return outOfProcess; + } + + static bool initializeTempFiles() { + getStdinFile().reset(tmpfile()); + getStdoutFile().reset(tmpfile()); + getStderrFile().reset(tmpfile()); + + return getStdinFile() && getStdoutFile() && getStderrFile(); + } + public: static std::unique_ptr create(int argc, const char* const* argv, const char* llvmdir = nullptr, @@ -158,7 +201,34 @@ class Interpreter { llvm::InitializeAllAsmPrinters(); std::vector vargs(argv + 1, argv + argc); - auto CI = compat::createClangInterpreter(vargs); + + int stdin_fd = 0; + int stdout_fd = 1; + int stderr_fd = 2; + +#if defined(_WIN32) + getOutOfProcess() = false; +#else + getOutOfProcess() = + std::any_of(vargs.begin(), vargs.end(), [](const char* arg) { + return llvm::StringRef(arg).trim() == "--use-oop-jit"; + }); + if (getOutOfProcess()) { + bool init = initializeTempFiles(); + if (!init) { + llvm::errs() << "Can't start out-of-process JIT execution. Continuing " + "with in-process JIT execution.\n"; + getOutOfProcess() = false; + } else { + stdin_fd = fileno(getStdinFile().get()); + stdout_fd = fileno(getStdoutFile().get()); + stderr_fd = fileno(getStderrFile().get()); + } + } +#endif + + auto CI = + compat::createClangInterpreter(vargs, stdin_fd, stdout_fd, stderr_fd); if (!CI) { llvm::errs() << "Interpreter creation failed\n"; return nullptr; @@ -172,6 +242,24 @@ class Interpreter { operator const clang::Interpreter&() const { return *inner; } operator clang::Interpreter&() { return *inner; } + static bool isOutOfProcess() { return getOutOfProcess(); } + +#ifndef _WIN32 + static FILE* getTempFileForOOP(int FD) { + switch (FD) { + case (STDIN_FILENO): + return getStdinFile().get(); + case (STDOUT_FILENO): + return getStdoutFile().get(); + case (STDERR_FILENO): + return getStderrFile().get(); + default: + llvm::errs() << "No temp file for the FD\n"; + return nullptr; + } + } +#endif + ///\brief Describes the return result of the different routines that do the /// incremental compilation. /// diff --git a/patches/llvm/clang20-1-out-of-process.patch b/patches/llvm/clang20-1-out-of-process.patch new file mode 100644 index 000000000..1ab988ea7 --- /dev/null +++ b/patches/llvm/clang20-1-out-of-process.patch @@ -0,0 +1,835 @@ +diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h +index f8663e319..78dff1165 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 000000000..825143f00 +--- /dev/null ++++ b/clang/include/clang/Interpreter/RemoteJITUtils.h +@@ -0,0 +1,42 @@ ++//===-- RemoteJITUtils.h - Utilities for remote-JITing ----------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++// ++// Utilities for ExecutorProcessControl-based remote JITing with Orc and ++// JITLink. ++// ++//===----------------------------------------------------------------------===// ++ ++#ifndef LLVM_CLANG_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 ++#include ++ ++llvm::Expected> ++launchExecutor(llvm::StringRef ExecutablePath, bool UseSharedMemory, ++ llvm::StringRef SlabAllocateSizeString, int stdin_fd = STDIN_FILENO, int stdout_fd = STDOUT_FILENO, int stderr_fd = STDERR_FILENO); ++ ++/// 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. ++/// Returns PID of last launched executor. ++pid_t getLastLaunchedExecutorPID(); ++ ++/// Returns PID of nth launched executor. ++/// 1-based indexing. ++pid_t getNthLaunchedExecutorPID(int n); ++#endif // LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H +diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt +index bf70cdfbe..38cf139fa 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 3b81f9d70..f0a6c75d0 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); + +@@ -617,6 +619,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 +778,13 @@ 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 (llvm::Expected< ++ std::unique_ptr> ++ 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 000000000..8324aeaaf +--- /dev/null ++++ b/clang/lib/Interpreter/RemoteJITUtils.cpp +@@ -0,0 +1,297 @@ ++//===-- 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 ++#endif // LLVM_ON_UNIX ++ ++using namespace llvm; ++using namespace llvm::orc; ++ ++static std::vector LaunchedExecutorPID; ++ ++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 (Expected S = getSlabAllocSize(SlabAllocateSizeString)) ++ SlabSize = *S; ++ else ++ return S.takeError(); ++ } ++ ++ return MapperJITLinkMemoryManager::CreateWithMapper( ++ SlabSize, SREPC, SAs); ++} ++ ++Expected> ++launchExecutor(StringRef ExecutablePath, bool UseSharedMemory, ++ llvm::StringRef SlabAllocateSizeString, int stdin_fd, int stdout_fd, int stderr_fd) { ++#ifndef LLVM_ON_UNIX ++ // FIXME: Add support for Windows. ++ return make_error("-" + ExecutablePath + ++ " not supported on non-unix platforms", ++ inconvertibleErrorCode()); ++#elif !LLVM_ENABLE_THREADS ++ // Out of process mode using SimpleRemoteEPC depends on 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. ++ 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("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]); ++ ++ if (stdin_fd != STDIN_FILENO) { ++ dup2(stdin_fd, STDIN_FILENO); ++ ++ close(stdin_fd); ++ } ++ ++ if (stdout_fd != STDOUT_FILENO) { ++ dup2(stdout_fd, STDOUT_FILENO); ++ ++ close(stdout_fd); ++ ++ setvbuf(stdout, NULL, _IONBF, 0); ++ } ++ ++ if (stderr_fd != STDERR_FILENO) { ++ dup2(stderr_fd, STDERR_FILENO); ++ ++ close(stderr_fd); ++ ++ setvbuf(stderr, NULL, _IONBF, 0); ++ } ++ ++ // 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.push_back(ChildPID); ++ } ++ // else we're the parent... ++ ++ // Close the child ends of the pipes ++ close(ToExecutor[ReadEnd]); ++ close(FromExecutor[WriteEnd]); ++ ++ SimpleRemoteEPC::Setup 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), FromExecutor[ReadEnd], ToExecutor[WriteEnd]); ++#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(); ++ ++ SimpleRemoteEPC::Setup 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() { ++ if (!LaunchedExecutorPID.size()) ++ return -1; ++ return LaunchedExecutorPID.back(); ++} ++ ++pid_t getNthLaunchedExecutorPID(int n) { ++ if (n - 1 < 0 || n - 1 >= static_cast(LaunchedExecutorPID.size())) ++ return -1; ++ return LaunchedExecutorPID.at(n - 1); ++} +diff --git a/clang/test/Interpreter/out-of-process.cpp b/clang/test/Interpreter/out-of-process.cpp +new file mode 100644 +index 000000000..6922ca6e8 +--- /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 e4b39c4f7..e3bc17e76 100644 +--- a/clang/test/lit.cfg.py ++++ b/clang/test/lit.cfg.py +@@ -113,6 +113,29 @@ 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) + +@@ -165,6 +188,9 @@ if have_host_jit_feature_support('jit'): + 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/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt +index f9a911b0a..68d86dd98 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 7af8e4f25..54b27782d 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,25 @@ 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. ++ 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()); ++ 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 +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))); + +diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp +index 972c24abc..a75a0afa7 100644 +--- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp ++++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp +@@ -635,16 +635,19 @@ Error ORCPlatformSupport::initialize(orc::JITDylib &JD) { + int32_t result; + auto E = ES.callSPSWrapper(WrapperAddr->getAddress(), + result, DSOHandles[&JD]); +- if (result) ++ if (E) ++ return E; ++ else if (result) + return make_error("dlupdate failed", + inconvertibleErrorCode()); +- return E; +- } +- return ES.callSPSWrapper(WrapperAddr->getAddress(), +- DSOHandles[&JD], JD.getName(), +- int32_t(ORC_RT_RTLD_LAZY)); ++ } else ++ return ES.callSPSWrapper(WrapperAddr->getAddress(), ++ DSOHandles[&JD], JD.getName(), ++ int32_t(ORC_RT_RTLD_LAZY)); + } else + return WrapperAddr.takeError(); ++ ++ return Error::success(); + } + + Error ORCPlatformSupport::deinitialize(orc::JITDylib &JD) { + diff --git a/unittests/CppInterOp/CMakeLists.txt b/unittests/CppInterOp/CMakeLists.txt index 8cc63038a..ba678a9b9 100644 --- a/unittests/CppInterOp/CMakeLists.txt +++ b/unittests/CppInterOp/CMakeLists.txt @@ -4,13 +4,14 @@ if (EMSCRIPTEN) # Omitting CUDATest.cpp since Emscripten build currently has no GPU support # For Emscripten builds linking to gtest_main will not suffice for gtest to run # the tests and an explicitly main.cpp is needed - set(EXTRA_TEST_SOURCE_FILES main.cpp) else() # Do not need main.cpp for native builds, but we do have GPU support for native builds set(EXTRA_TEST_SOURCE_FILES CUDATest.cpp) set(EXTRA_PATH_TEST_BINARIES /CppInterOpTests/unittests/bin/$/) endif() +set(EXTRA_TEST_SOURCE_FILES main.cpp) + add_cppinterop_unittest(CppInterOpTests EnumReflectionTest.cpp FunctionReflectionTest.cpp @@ -89,7 +90,7 @@ if (NOT EMSCRIPTEN) set(EXTRA_PATH_TEST_BINARIES /TestSharedLib/unittests/bin/$/) endif() -add_cppinterop_unittest(DynamicLibraryManagerTests DynamicLibraryManagerTest.cpp ${EXTRA_TEST_SOURCE_FILES}) +add_cppinterop_unittest(DynamicLibraryManagerTests DynamicLibraryManagerTest.cpp ${EXTRA_TEST_SOURCE_FILES} Utils.cpp) target_link_libraries(DynamicLibraryManagerTests PRIVATE diff --git a/unittests/CppInterOp/CUDATest.cpp b/unittests/CppInterOp/CUDATest.cpp index 45b41c94d..9eb42b6ad 100644 --- a/unittests/CppInterOp/CUDATest.cpp +++ b/unittests/CppInterOp/CUDATest.cpp @@ -14,7 +14,7 @@ static bool HasCudaSDK() { // FIXME: Enable this for cling. return false; #endif - if (!Cpp::CreateInterpreter({}, {"--cuda"})) + if (!TestUtils::CreateInterpreter({}, {"--cuda"})) return false; return Cpp::Declare("__global__ void test_func() {}" "test_func<<<1,1>>>();") == 0; @@ -32,7 +32,7 @@ static bool HasCudaRuntime() { if (!HasCudaSDK()) return false; - if (!Cpp::CreateInterpreter({}, {"--cuda"})) + if (!TestUtils::CreateInterpreter({}, {"--cuda"})) return false; if (Cpp::Declare("__global__ void test_func() {}" "test_func<<<1,1>>>();")) @@ -54,7 +54,7 @@ TEST(CUDATest, Sanity) { #endif if (!HasCudaSDK()) GTEST_SKIP() << "Skipping CUDA tests as CUDA SDK not found"; - EXPECT_TRUE(Cpp::CreateInterpreter({}, {"--cuda"})); + EXPECT_TRUE(TestUtils::CreateInterpreter({}, {"--cuda"})); } TEST(CUDATest, CUDAH) { @@ -64,7 +64,7 @@ TEST(CUDATest, CUDAH) { if (!HasCudaSDK()) GTEST_SKIP() << "Skipping CUDA tests as CUDA SDK not found"; - Cpp::CreateInterpreter({}, {"--cuda"}); + TestUtils::CreateInterpreter({}, {"--cuda"}); bool success = !Cpp::Declare("#include "); EXPECT_TRUE(success); } diff --git a/unittests/CppInterOp/DynamicLibraryManagerTest.cpp b/unittests/CppInterOp/DynamicLibraryManagerTest.cpp index 4925af329..39c80ad76 100644 --- a/unittests/CppInterOp/DynamicLibraryManagerTest.cpp +++ b/unittests/CppInterOp/DynamicLibraryManagerTest.cpp @@ -1,3 +1,4 @@ +#include "Utils.h" #include "CppInterOp/CppInterOp.h" #include "clang/Basic/Version.h" @@ -29,7 +30,7 @@ TEST(DynamicLibraryManagerTest, Sanity) { GTEST_SKIP() << "Test fails with Cling on Windows"; #endif - EXPECT_TRUE(Cpp::CreateInterpreter()); + EXPECT_TRUE(TestUtils::CreateInterpreter()); EXPECT_FALSE(Cpp::GetFunctionAddress("ret_zero")); std::string BinaryPath = GetExecutablePath(/*Argv0=*/nullptr); @@ -75,7 +76,7 @@ TEST(DynamicLibraryManagerTest, BasicSymbolLookup) { #endif #endif - ASSERT_TRUE(Cpp::CreateInterpreter()); + ASSERT_TRUE(TestUtils::CreateInterpreter()); EXPECT_FALSE(Cpp::GetFunctionAddress("ret_zero")); // Load the library manually. Use known preload path (MEMFS path) diff --git a/unittests/CppInterOp/EnumReflectionTest.cpp b/unittests/CppInterOp/EnumReflectionTest.cpp index f4b3d47de..3b29aa312 100644 --- a/unittests/CppInterOp/EnumReflectionTest.cpp +++ b/unittests/CppInterOp/EnumReflectionTest.cpp @@ -338,7 +338,7 @@ TEST(EnumReflectionTest, GetEnums) { int myVariable; )"; - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); Interp->declare(code); std::vector enumNames1, enumNames2, enumNames3, enumNames4; Cpp::TCppScope_t globalscope = Cpp::GetScope("", 0); diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index 099e55aa2..6c5c3aeb4 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -655,7 +655,7 @@ TEST(FunctionReflectionTest, InstantiateTemplateFunctionFromString) { if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; std::vector interpreter_args = { "-include", "new" }; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); std::string code = R"(#include )"; Interp->process(code); const char* str = "std::make_unique"; @@ -1403,6 +1403,9 @@ TEST(FunctionReflectionTest, GetFunctionAddress) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } std::vector Decls, SubDecls; std::string code = "int f1(int i) { return i * i; }"; std::vector interpreter_args = {"-include", "new"}; @@ -1412,10 +1415,8 @@ TEST(FunctionReflectionTest, GetFunctionAddress) { testing::internal::CaptureStdout(); Interp->declare("#include "); - Interp->process( - "void * address = (void *) &f1; \n" - "std::cout << address; \n" - ); + Interp->process("void * address = (void *) &f1; \n" + "std::cout << address; \n"); std::string output = testing::internal::GetCapturedStdout(); std::stringstream address; @@ -1451,6 +1452,9 @@ TEST(FunctionReflectionTest, JitCallAdvanced) { GTEST_SKIP() << "Test fails for Emscipten builds"; #endif #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; @@ -1499,6 +1503,9 @@ TEST(FunctionReflectionTest, JitCallDebug) { GTEST_SKIP() << "Test fails for Emscipten builds"; #endif #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; @@ -1590,11 +1597,15 @@ TEST(FunctionReflectionTest, GetFunctionCallWrapper) { #ifdef EMSCRIPTEN GTEST_SKIP() << "Test fails for Emscipten builds"; #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; #if defined(CPPINTEROP_USE_CLING) && defined(_WIN32) GTEST_SKIP() << "Disabled, invoking functions containing printf does not work with Cling on Windows"; #endif + std::vector Decls; std::string code = R"( int f1(int i) { return i * i; } @@ -2116,6 +2127,9 @@ TEST(FunctionReflectionTest, Construct) { GTEST_SKIP() << "Test fails for Emscipten builds"; #endif #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; #ifdef _WIN32 @@ -2197,13 +2211,16 @@ TEST(FunctionReflectionTest, ConstructPOD) { GTEST_SKIP() << "Test fails for Emscipten builds"; #endif #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); Interp->declare(R"( namespace PODS { @@ -2245,9 +2262,12 @@ TEST(FunctionReflectionTest, ConstructNested) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); Interp->declare(R"( #include @@ -2308,8 +2328,11 @@ TEST(FunctionReflectionTest, ConstructArray) { #if defined(__APPLE__) && (CLANG_VERSION_MAJOR == 16) GTEST_SKIP() << "Test fails on Clang16 OS X"; #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); Interp->declare(R"( #include @@ -2361,9 +2384,12 @@ TEST(FunctionReflectionTest, Destruct) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); Interp->declare(R"( #include @@ -2421,9 +2447,12 @@ TEST(FunctionReflectionTest, DestructArray) { #if defined(__APPLE__) && (CLANG_VERSION_MAJOR == 16) GTEST_SKIP() << "Test fails on Clang16 OS X"; #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); Interp->declare(R"( #include @@ -2494,7 +2523,7 @@ TEST(FunctionReflectionTest, UndoTest) { #ifdef EMSCRIPTEN GTEST_SKIP() << "Test fails for Emscipten builds"; #else - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); EXPECT_EQ(Cpp::Process("int a = 5;"), 0); EXPECT_EQ(Cpp::Process("int b = 10;"), 0); EXPECT_EQ(Cpp::Process("int x = 5;"), 0); @@ -2521,7 +2550,7 @@ TEST(FunctionReflectionTest, FailingTest1) { #ifdef EMSCRIPTEN_SHARED_LIBRARY GTEST_SKIP() << "Test fails for Emscipten shared library builds"; #endif - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); EXPECT_FALSE(Cpp::Declare(R"( class WithOutEqualOp1 {}; class WithOutEqualOp2 {}; diff --git a/unittests/CppInterOp/InterpreterTest.cpp b/unittests/CppInterOp/InterpreterTest.cpp index 4ef046f92..f03decb65 100644 --- a/unittests/CppInterOp/InterpreterTest.cpp +++ b/unittests/CppInterOp/InterpreterTest.cpp @@ -36,7 +36,7 @@ TEST(InterpreterTest, DISABLED_DebugFlag) { #else TEST(InterpreterTest, DebugFlag) { #endif // NDEBUG - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); EXPECT_FALSE(Cpp::IsDebugOutputEnabled()); std::string cerrs; testing::internal::CaptureStderr(); @@ -65,6 +65,10 @@ TEST(InterpreterTest, Evaluate) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } + if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; // EXPECT_TRUE(Cpp::Evaluate(I, "") == 0); @@ -81,9 +85,12 @@ TEST(InterpreterTest, Evaluate) { } TEST(InterpreterTest, DeleteInterpreter) { - auto* I1 = Cpp::CreateInterpreter(); - auto* I2 = Cpp::CreateInterpreter(); - auto* I3 = Cpp::CreateInterpreter(); + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } + auto* I1 = TestUtils::CreateInterpreter(); + auto* I2 = TestUtils::CreateInterpreter(); + auto* I3 = TestUtils::CreateInterpreter(); EXPECT_TRUE(I1 && I2 && I3) << "Failed to create interpreters"; EXPECT_EQ(I3, Cpp::GetInterpreter()) << "I3 is not active"; @@ -102,10 +109,13 @@ TEST(InterpreterTest, ActivateInterpreter) { #ifdef EMSCRIPTEN_STATIC_LIBRARY GTEST_SKIP() << "Test fails for Emscipten static library build"; #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } EXPECT_FALSE(Cpp::ActivateInterpreter(nullptr)); - auto* Cpp14 = Cpp::CreateInterpreter({"-std=c++14"}); - auto* Cpp17 = Cpp::CreateInterpreter({"-std=c++17"}); - auto* Cpp20 = Cpp::CreateInterpreter({"-std=c++20"}); + auto* Cpp14 = TestUtils::CreateInterpreter({"-std=c++14"}); + auto* Cpp17 = TestUtils::CreateInterpreter({"-std=c++17"}); + auto* Cpp20 = TestUtils::CreateInterpreter({"-std=c++20"}); EXPECT_TRUE(Cpp14 && Cpp17 && Cpp20); EXPECT_TRUE(Cpp::Evaluate("__cplusplus") == 202002L) @@ -134,7 +144,7 @@ TEST(InterpreterTest, Process) { if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; std::vector interpreter_args = { "-include", "new" }; - auto* I = Cpp::CreateInterpreter(interpreter_args); + auto* I = TestUtils::CreateInterpreter(interpreter_args); EXPECT_TRUE(Cpp::Process("") == 0); EXPECT_TRUE(Cpp::Process("int a = 12;") == 0); EXPECT_FALSE(Cpp::Process("error_here;") == 0); @@ -142,14 +152,16 @@ TEST(InterpreterTest, Process) { EXPECT_FALSE(Cpp::Process("int f(); int res = f();") == 0); // C API - auto* CXI = clang_createInterpreterFromRawPtr(I); - clang_Interpreter_declare(CXI, "#include ", false); - clang_Interpreter_process(CXI, "int c = 42;"); - auto* CXV = clang_createValue(); - auto Res = clang_Interpreter_evaluate(CXI, "c", CXV); - EXPECT_EQ(Res, CXError_Success); - clang_Value_dispose(CXV); - clang_Interpreter_dispose(CXI); + if (!TestUtils::g_use_oop_jit) { + auto* CXI = clang_createInterpreterFromRawPtr(I); + clang_Interpreter_declare(CXI, "#include ", false); + clang_Interpreter_process(CXI, "int c = 42;"); + auto* CXV = clang_createValue(); + auto Res = clang_Interpreter_evaluate(CXI, "c", CXV); + EXPECT_EQ(Res, CXError_Success); + clang_Value_dispose(CXV); + clang_Interpreter_dispose(CXI); + } } TEST(InterpreterTest, EmscriptenExceptionHandling) { @@ -166,7 +178,7 @@ TEST(InterpreterTest, EmscriptenExceptionHandling) { "-mllvm", "-enable-emscripten-sjlj" }; - Cpp::CreateInterpreter(Args); + TestUtils::CreateInterpreter(Args); const char* tryCatchCode = R"( try { @@ -180,7 +192,7 @@ TEST(InterpreterTest, EmscriptenExceptionHandling) { } TEST(InterpreterTest, CreateInterpreter) { - auto* I = Cpp::CreateInterpreter(); + auto* I = TestUtils::CreateInterpreter(); EXPECT_TRUE(I); // Check if the default standard is c++14 @@ -192,7 +204,7 @@ TEST(InterpreterTest, CreateInterpreter) { EXPECT_TRUE(Cpp::GetNamed("cpp14")); EXPECT_FALSE(Cpp::GetNamed("cppUnknown")); - I = Cpp::CreateInterpreter({"-std=c++17"}); + I = TestUtils::CreateInterpreter({"-std=c++17"}); Cpp::Declare("#if __cplusplus==201703L\n" "int cpp17() { return 2017; }\n" "#else\n" @@ -244,7 +256,7 @@ TEST(InterpreterTest, DISABLED_DetectResourceDir) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); EXPECT_STRNE(Cpp::DetectResourceDir().c_str(), Cpp::GetResourceDir()); llvm::SmallString<256> Clang(LLVM_BINARY_DIR); llvm::sys::path::append(Clang, "bin", "clang"); @@ -269,6 +281,9 @@ TEST(InterpreterTest, DetectSystemCompilerIncludePaths) { } TEST(InterpreterTest, IncludePaths) { + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } std::vector includes; Cpp::GetIncludePaths(includes); EXPECT_FALSE(includes.empty()); @@ -294,7 +309,7 @@ TEST(InterpreterTest, IncludePaths) { TEST(InterpreterTest, CodeCompletion) { #if CLANG_VERSION_MAJOR >= 18 || defined(CPPINTEROP_USE_CLING) - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); std::vector cc; Cpp::Declare("int foo = 12;"); Cpp::CodeComplete(cc, "f", 1, 2); diff --git a/unittests/CppInterOp/JitTest.cpp b/unittests/CppInterOp/JitTest.cpp index a1b6909b7..fb9daf83a 100644 --- a/unittests/CppInterOp/JitTest.cpp +++ b/unittests/CppInterOp/JitTest.cpp @@ -20,6 +20,9 @@ TEST(JitTest, InsertOrReplaceJitSymbol) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } std::vector Decls; std::string code = R"( extern "C" int printf(const char*,...); @@ -42,6 +45,9 @@ TEST(JitTest, InsertOrReplaceJitSymbol) { TEST(Streams, StreamRedirect) { // printf and etc are fine here. // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg) + if (TestUtils::g_use_oop_jit) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } Cpp::BeginStdStreamCapture(Cpp::kStdOut); Cpp::BeginStdStreamCapture(Cpp::kStdErr); printf("StdOut is captured\n"); @@ -70,3 +76,30 @@ TEST(Streams, StreamRedirect) { EXPECT_STREQ(cerrs.c_str(), "Err\nStdErr\n"); // NOLINTEND(cppcoreguidelines-pro-type-vararg) } + +TEST(Streams, StreamRedirectJIT) { +#ifdef EMSCRIPTEN + GTEST_SKIP() << "Test fails for Emscipten builds"; +#endif + if (llvm::sys::RunningOnValgrind()) + GTEST_SKIP() << "XFAIL due to Valgrind report"; +#ifdef _WIN32 + GTEST_SKIP() << "Disabled on Windows. Needs fixing."; +#endif +#ifdef CPPINTEROP_USE_CLING + GTEST_SKIP() << "Test fails for cling builds"; +#endif + TestUtils::CreateInterpreter(); + + Cpp::BeginStdStreamCapture(Cpp::kStdOut); + Cpp::BeginStdStreamCapture(Cpp::kStdErr); + Interp->process(R"( + #include + printf("%s\n", "Hello World"); + fflush(stdout); + )"); + std::string CapturedStringErr = Cpp::EndStdStreamCapture(); + std::string CapturedStringOut = Cpp::EndStdStreamCapture(); + + EXPECT_STREQ(CapturedStringOut.c_str(), "Hello World\n"); +} \ No newline at end of file diff --git a/unittests/CppInterOp/ScopeReflectionTest.cpp b/unittests/CppInterOp/ScopeReflectionTest.cpp index 4ecdf706e..2c1371901 100644 --- a/unittests/CppInterOp/ScopeReflectionTest.cpp +++ b/unittests/CppInterOp/ScopeReflectionTest.cpp @@ -176,7 +176,7 @@ TEST(ScopeReflectionTest, IsBuiltin) { std::vector interpreter_args = { "-include", "new" }; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); ASTContext &C = Interp->getCI()->getASTContext(); EXPECT_TRUE(Cpp::IsBuiltin(C.BoolTy.getAsOpaquePtr())); EXPECT_TRUE(Cpp::IsBuiltin(C.CharTy.getAsOpaquePtr())); @@ -466,7 +466,7 @@ TEST(ScopeReflectionTest, GetScope) { typedef N::C T; )"; - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); Interp->declare(code); Cpp::TCppScope_t tu = Cpp::GetScope("", 0); Cpp::TCppScope_t ns_N = Cpp::GetScope("N", 0); @@ -491,7 +491,7 @@ TEST(ScopeReflectionTest, GetScopefromCompleteName) { } )"; - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); Interp->declare(code); EXPECT_EQ(Cpp::GetQualifiedName(Cpp::GetScopeFromCompleteName("N1")), "N1"); @@ -518,7 +518,7 @@ TEST(ScopeReflectionTest, GetNamed) { std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); Interp->declare(code); Cpp::TCppScope_t ns_N1 = Cpp::GetNamed("N1", nullptr); @@ -557,7 +557,7 @@ TEST(ScopeReflectionTest, GetParentScope) { } )"; - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); Interp->declare(code); Cpp::TCppScope_t ns_N1 = Cpp::GetNamed("N1"); @@ -899,7 +899,7 @@ TEST(ScopeReflectionTest, InstantiateTemplateFunctionFromString) { if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); std::string code = R"(#include )"; Interp->process(code); const char* str = "std::make_unique"; @@ -1048,7 +1048,7 @@ TEST(ScopeReflectionTest, IncludeVector) { #include )"; std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); Interp->declare(code); } @@ -1056,7 +1056,7 @@ TEST(ScopeReflectionTest, GetOperator) { if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); std::string code = R"( class MyClass { diff --git a/unittests/CppInterOp/TypeReflectionTest.cpp b/unittests/CppInterOp/TypeReflectionTest.cpp index 26eb2d621..8d9664211 100644 --- a/unittests/CppInterOp/TypeReflectionTest.cpp +++ b/unittests/CppInterOp/TypeReflectionTest.cpp @@ -109,7 +109,7 @@ TEST(TypeReflectionTest, GetCanonicalType) { } TEST(TypeReflectionTest, GetType) { - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); std::string code = R"( class A {}; @@ -346,7 +346,7 @@ TEST(TypeReflectionTest, IsUnderlyingTypeRecordType) { } TEST(TypeReflectionTest, GetComplexType) { - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); auto get_complex_type_as_string = [&](const std::string &element_type) { auto ElementQT = Cpp::GetType(element_type); @@ -559,7 +559,7 @@ TEST(TypeReflectionTest, IsSmartPtrType) { GTEST_SKIP() << "XFAIL due to Valgrind report"; std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); Interp->declare(R"( #include @@ -598,7 +598,7 @@ TEST(TypeReflectionTest, IsSmartPtrType) { TEST(TypeReflectionTest, IsFunctionPointerType) { std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); Interp->declare(R"( typedef int (*int_func)(int, int); diff --git a/unittests/CppInterOp/Utils.cpp b/unittests/CppInterOp/Utils.cpp index e048b0df5..3ea4dba2d 100644 --- a/unittests/CppInterOp/Utils.cpp +++ b/unittests/CppInterOp/Utils.cpp @@ -16,28 +16,30 @@ using namespace clang; using namespace llvm; +bool TestUtils::g_use_oop_jit = false; + void TestUtils::GetAllTopLevelDecls( const std::string& code, std::vector& Decls, bool filter_implicitGenerated /* = false */, const std::vector& interpreter_args /* = {} */) { - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); #ifdef CPPINTEROP_USE_CLING - cling::Transaction *T = nullptr; + cling::Transaction* T = nullptr; Interp->declare(code, &T); for (auto DCI = T->decls_begin(), E = T->decls_end(); DCI != E; ++DCI) { if (DCI->m_Call != cling::Transaction::kCCIHandleTopLevelDecl) continue; - for (Decl *D : DCI->m_DGR) { + for (Decl* D : DCI->m_DGR) { if (filter_implicitGenerated && D->isImplicit()) continue; Decls.push_back(D); } } #else - PartialTranslationUnit *T = nullptr; - Interp->process(code, /*Value*/nullptr, &T); - for (auto *D : T->TUPart->decls()) { + PartialTranslationUnit* T = nullptr; + Interp->process(code, /*Value*/ nullptr, &T); + for (auto* D : T->TUPart->decls()) { if (filter_implicitGenerated && D->isImplicit()) continue; Decls.push_back(D); @@ -45,18 +47,27 @@ void TestUtils::GetAllTopLevelDecls( #endif } -void TestUtils::GetAllSubDecls(Decl *D, std::vector& SubDecls, +void TestUtils::GetAllSubDecls(Decl* D, std::vector& SubDecls, bool filter_implicitGenerated /* = false */) { if (!isa_and_nonnull(D)) return; - DeclContext *DC = cast(D); - for (auto *Di : DC->decls()) { + DeclContext* DC = cast(D); + for (auto* Di : DC->decls()) { if (filter_implicitGenerated && Di->isImplicit()) continue; SubDecls.push_back(Di); } } +TInterp_t TestUtils::CreateInterpreter(const std::vector& Args, + const std::vector& GpuArgs) { + auto mergedArgs = Args; + if (TestUtils::g_use_oop_jit) { + mergedArgs.push_back("--use-oop-jit"); + } + return Cpp::CreateInterpreter(mergedArgs, GpuArgs); +} + const char* get_c_string(CXString string) { return static_cast(string.data); } diff --git a/unittests/CppInterOp/Utils.h b/unittests/CppInterOp/Utils.h index 2b7b12590..ddcae9faa 100644 --- a/unittests/CppInterOp/Utils.h +++ b/unittests/CppInterOp/Utils.h @@ -16,16 +16,19 @@ using namespace clang; using namespace llvm; namespace clang { - class Decl; +class Decl; } #define Interp (static_cast(Cpp::GetInterpreter())) namespace TestUtils { +extern bool g_use_oop_jit; void GetAllTopLevelDecls(const std::string& code, std::vector& Decls, bool filter_implicitGenerated = false, const std::vector& interpreter_args = {}); void GetAllSubDecls(clang::Decl* D, std::vector& SubDecls, bool filter_implicitGenerated = false); +TInterp_t CreateInterpreter(const std::vector& Args = {}, + const std::vector& GpuArgs = {}); } // end namespace TestUtils const char* get_c_string(CXString string); diff --git a/unittests/CppInterOp/VariableReflectionTest.cpp b/unittests/CppInterOp/VariableReflectionTest.cpp index f75d1839a..e98de81c1 100644 --- a/unittests/CppInterOp/VariableReflectionTest.cpp +++ b/unittests/CppInterOp/VariableReflectionTest.cpp @@ -180,7 +180,7 @@ TEST(VariableReflectionTest, GetTypeAsString) { } )"; - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); EXPECT_EQ(Cpp::Declare(code.c_str()), 0); Cpp::TCppScope_t wrapper = @@ -362,7 +362,7 @@ TEST(VariableReflectionTest, VariableOffsetsWithInheritance) { GTEST_SKIP() << "XFAIL due to Valgrind report"; std::vector interpreter_args = {"-include", "new"}; - Cpp::CreateInterpreter(interpreter_args); + TestUtils::CreateInterpreter(interpreter_args); Cpp::Declare("#include"); @@ -539,7 +539,7 @@ TEST(VariableReflectionTest, StaticConstExprDatamember) { GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); Cpp::Declare(R"( class MyClass { @@ -611,7 +611,7 @@ TEST(VariableReflectionTest, StaticConstExprDatamember) { } TEST(VariableReflectionTest, GetEnumConstantDatamembers) { - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); Cpp::Declare(R"( class MyEnumClass { @@ -635,7 +635,7 @@ TEST(VariableReflectionTest, GetEnumConstantDatamembers) { } TEST(VariableReflectionTest, Is_Get_Pointer) { - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); std::vector Decls; std::string code = R"( class A {}; @@ -667,7 +667,7 @@ TEST(VariableReflectionTest, Is_Get_Pointer) { } TEST(VariableReflectionTest, Is_Get_Reference) { - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); std::vector Decls; std::string code = R"( class A {}; @@ -705,7 +705,7 @@ TEST(VariableReflectionTest, Is_Get_Reference) { } TEST(VariableReflectionTest, GetPointerType) { - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); std::vector Decls; std::string code = R"( class A {}; diff --git a/unittests/CppInterOp/main.cpp b/unittests/CppInterOp/main.cpp index 09799f80a..b61fc7e47 100644 --- a/unittests/CppInterOp/main.cpp +++ b/unittests/CppInterOp/main.cpp @@ -1,6 +1,17 @@ +#include "Utils.h" #include +void parseCustomArguments(int argc, char** argv) { + for (int i = 1; i < argc; ++i) { + std::string arg(argv[i]); + if (arg == "--use-oop-jit") { + TestUtils::g_use_oop_jit = true; + } + } +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + parseCustomArguments(argc, argv); return RUN_ALL_TESTS(); } \ No newline at end of file