diff --git a/.github/actions/Build_LLVM/action.yml b/.github/actions/Build_LLVM/action.yml index 53fd301a8..106400b3e 100644 --- a/.github/actions/Build_LLVM/action.yml +++ b/.github/actions/Build_LLVM/action.yml @@ -43,6 +43,11 @@ runs: ninja LLVMOrcDebugging -j ${{ env.ncpus }} ninja clingInterpreter -j ${{ env.ncpus }} else + llvm_vers=$(echo "${{ matrix.clang-runtime }}" | tr '[:lower:]' '[:upper:]') + if [[ "${{ matrix.oop-jit }}" == "On" && "${llvm_vers}" == "20" ]]; 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 }}" \ @@ -58,6 +63,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..20e28a4a2 100644 --- a/.github/actions/Build_and_Test_CppInterOp/action.yml +++ b/.github/actions/Build_and_Test_CppInterOp/action.yml @@ -48,6 +48,7 @@ runs: -DCODE_COVERAGE=${{ env.CODE_COVERAGE }} \ -DCMAKE_INSTALL_PREFIX=$CPPINTEROP_DIR \ -DLLVM_ENABLE_WERROR=On \ + -DLLVM_BUILT_WITH_OOP_JIT=${{ matrix.oop-jit }} \ ../ fi docs_on=$(echo "${{ matrix.documentation }}" | tr '[:lower:]' '[:upper:]') @@ -60,6 +61,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 bc40f0bea..fcc037324 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,15 +23,6 @@ jobs: matrix: include: # Ubuntu Arm Jobs - - name: ubu22-arm-gcc12-clang-repl-20-coverage - os: ubuntu-22.04-arm - compiler: gcc-12 - clang-runtime: '20' - cling: Off - cppyy: Off - llvm_enable_projects: "clang" - llvm_targets_to_build: "host;NVPTX" - coverage: true - name: ubu24-arm-gcc12-clang-repl-20 os: ubuntu-24.04-arm compiler: gcc-12 @@ -66,6 +57,16 @@ jobs: llvm_enable_projects: "clang" llvm_targets_to_build: "host;NVPTX" # Ubuntu X86 Jobs + - name: ubu22-x86-gcc12-clang-repl-20-coverage + os: ubuntu-22.04 + compiler: gcc-12 + clang-runtime: '20' + cling: Off + cppyy: Off + llvm_enable_projects: "clang;compiler-rt" + llvm_targets_to_build: "host;NVPTX" + coverage: true + oop-jit: On - name: ubu24-x86-gcc12-clang-repl-20 os: ubuntu-24.04 compiler: gcc-12 @@ -74,6 +75,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 @@ -100,6 +110,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 @@ -218,9 +237,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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 981c57ced..78bf140b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -298,6 +298,18 @@ 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(LLVM_BUILT_WITH_OOP_JIT) + if((CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") OR + (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")) + add_definitions(-DLLVM_BUILT_WITH_OOP_JIT) + else() + message(FATAL_ERROR "LLVM_BUILT_WITH_OOP_JIT is only supported on Darwin arm64 or Linux x86_64. Build aborted.") + endif() +endif() + # 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 @@ -379,6 +391,7 @@ string(REPLACE "-Wcovered-switch-default" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} file(STRINGS "VERSION" CPPINTEROP_VERSION) string(REPLACE "." ";" VERSION_LIST "${CPPINTEROP_VERSION}") + list(GET VERSION_LIST 0 CPPINTEROP_VERSION_MAJOR) list(GET VERSION_LIST 1 CPPINTEROP_VERSION_MINOR) list(GET VERSION_LIST 2 CPPINTEROP_VERSION_PATCH) diff --git a/README.md b/README.md index 02d9fd923..1d6361f70 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,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 x86_64 and Macos amr64 +```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 x86_64 +cmake --build . --target clang clang-repl llvm-jitlink-executor orc_rt-x86_64 --parallel $(nproc --all) +## For MacOS arm64 +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 @@ -311,6 +333,8 @@ cmake --build . --target install --parallel $(nproc --all) and +> Do make sure to pass ``DLLVM_BUILT_WITH_OOP_JIT=ON``, 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 3ec6470b4..506e65ec3 100644 --- a/docs/InstallationAndUsage.rst +++ b/docs/InstallationAndUsage.rst @@ -99,6 +99,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 x86_64 and Macos arm64 + +.. 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 x86_64 + cmake --build . --target clang clang-repl llvm-jitlink-executor orc_rt-x86_64 --parallel $(nproc --all) + + # For MacOS arm64 + cmake --build . --target clang clang-repl llvm-jitlink-executor orc_rt_osx --parallel $(sysctl -n hw.ncpu) + ************************************** Build Cling and related dependencies ************************************** @@ -263,6 +293,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 pass ``DLLVM_BUILT_WITH_OOP_JIT=ON``, if you want to have out-of-process JIT execution feature enabled. + and .. code:: powershell diff --git a/include/CppInterOp/CppInterOp.h b/include/CppInterOp/CppInterOp.h index 534a8f45c..2791cc912 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 @@ -907,6 +908,16 @@ 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(); + +/// Returns the process ID of the nth executor process. +/// \returns the PID of the nth executor process. +CPPINTEROP_API pid_t GetNthExecutorPID(int n); +#endif + } // end namespace Cpp #endif // CPPINTEROP_CPPINTEROP_H diff --git a/lib/CppInterOp/Compatibility.h b/lib/CppInterOp/Compatibility.h index 562b42b38..10d0dc3ae 100644 --- a/lib/CppInterOp/Compatibility.h +++ b/lib/CppInterOp/Compatibility.h @@ -198,10 +198,23 @@ inline void codeComplete(std::vector& Results, #include "llvm/Support/Error.h" +#ifdef LLVM_BUILT_WITH_OOP_JIT +#include "clang/Basic/Version.h" +#include "clang/Interpreter/RemoteJITUtils.h" +#include "llvm/TargetParser/Host.h" + +#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h" +#endif + +#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('-'); @@ -215,6 +228,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}); @@ -239,16 +261,76 @@ createClangInterpreter(std::vector& args) { (*ciOrErr)->LoadRequestedPlugins(); if (CudaEnabled) DeviceCI->LoadRequestedPlugins(); + +#ifdef LLVM_BUILT_WITH_OOP_JIT + std::unique_ptr JB; + + if (outOfProcess) { + std::string OOPExecutor = + std::string(LLVM_SOURCE_DIR) + "/build/bin/llvm-jitlink-executor"; + bool UseSharedMemory = false; + std::string SlabAllocateSizeString = ""; + std::unique_ptr EPC; + + EPC = ExitOnError(launchExecutor(OOPExecutor, UseSharedMemory, + SlabAllocateSizeString, + [=] { // Lambda defined inline + auto redirect = [](int from, int to) { + if (from != to) { + dup2(from, to); + close(from); + } + }; + + redirect(0, STDIN_FILENO); + redirect(stdout_fd, STDOUT_FILENO); + redirect(2, STDERR_FILENO); + + setvbuf(stdout, nullptr, _IONBF, 0); + setvbuf(stderr, nullptr, _IONBF, 0); + })); + +#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(), @@ -360,6 +442,14 @@ inline void codeComplete(std::vector& Results, Results.push_back(r.str()); } +#if defined(LLVM_BUILT_WITH_OOP_JIT) && !defined(_WIN32) +inline pid_t getExecutorPID() { return /*llvm*/ getLastLaunchedExecutorPID(); } + +inline pid_t getNthExecutorPID(int n) { + return /*llvm*/ getNthLaunchedExecutorPID(n); +} +#endif + } // namespace compat #include "CppInterOpInterpreter.h" @@ -384,7 +474,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 6ee99240f..7ea8f2c72 100755 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -64,12 +65,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)) @@ -77,7 +83,6 @@ #endif #else #include -#include #endif // WIN32 namespace Cpp { @@ -3084,8 +3089,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(); @@ -3801,12 +3807,12 @@ bool Destruct(TCppObject_t This, TCppConstScope_t scope, } 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 @@ -3821,20 +3827,47 @@ 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) + auto& I = getInterp(); + if (I.isOutOfProcess() && FD == STDOUT_FILENO) { + m_TempFile = I.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; @@ -3842,7 +3875,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"); @@ -3851,11 +3889,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:"); @@ -3863,13 +3901,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. @@ -3877,6 +3914,15 @@ class StreamCaptureInfo { std::string result = content.get(); close(m_DupFD); m_DupFD = -1; +#if !defined(_WIN32) && !defined(CPPINTEROP_USE_CLING) + auto& I = getInterp(); + if (I.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; } }; @@ -3916,4 +3962,20 @@ int Undo(unsigned N) { #endif } +#ifndef _WIN32 +pid_t GetExecutorPID() { +#ifdef LLVM_BUILT_WITH_OOP_JIT + return compat::getExecutorPID(); +#endif + return -1; +} + +pid_t GetNthExecutorPID(int n) { +#ifdef LLVM_BUILT_WITH_OOP_JIT + return compat::getNthExecutorPID(n); +#endif + return -1; +} +#endif + } // end namespace Cpp diff --git a/lib/CppInterOp/CppInterOpInterpreter.h b/lib/CppInterOp/CppInterOpInterpreter.h index d87eb33e1..c4dca26dd 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" +#ifndef _WIN32 +#include +#endif +#include +#include +#include #include #include @@ -141,9 +147,32 @@ namespace Cpp { /// class Interpreter { private: + struct FileDeleter { + void operator()(FILE* f /* owns */) { + if (f) + fclose(f); + } + }; + struct IOContext { + std::unique_ptr stdin_file; + std::unique_ptr stdout_file; + std::unique_ptr stderr_file; + bool outOfProcess = false; + + bool initializeTempFiles() { + stdin_file.reset(tmpfile()); // NOLINT(cppcoreguidelines-owning-memory) + stdout_file.reset(tmpfile()); // NOLINT(cppcoreguidelines-owning-memory) + stderr_file.reset(tmpfile()); // NOLINT(cppcoreguidelines-owning-memory) + return stdin_file && stdout_file && stderr_file; + } + }; + std::unique_ptr inner; + std::unique_ptr io_context; - Interpreter(std::unique_ptr CI) : inner(std::move(CI)) {} + Interpreter(std::unique_ptr CI, + std::unique_ptr ctx = nullptr) + : inner(std::move(CI)), io_context(std::move(ctx)) {} public: static std::unique_ptr @@ -158,13 +187,44 @@ class Interpreter { llvm::InitializeAllAsmPrinters(); std::vector vargs(argv + 1, argv + argc); - auto CI = compat::createClangInterpreter(vargs); + + auto io_ctx = std::make_unique(); + + int stdin_fd = 0; + int stdout_fd = 1; + int stderr_fd = 2; + +#if defined(_WIN32) + io_ctx->outOfProcess = false; +#else + io_ctx->outOfProcess = + std::any_of(vargs.begin(), vargs.end(), [](const char* arg) { + return llvm::StringRef(arg).trim() == "--use-oop-jit"; + }); + + if (io_ctx->outOfProcess) { + bool init = io_ctx->initializeTempFiles(); + if (!init) { + llvm::errs() << "Can't start out-of-process JIT execution. Continuing " + "with in-process JIT execution.\n"; + io_ctx->outOfProcess = false; + } else { + stdin_fd = fileno(io_ctx->stdin_file.get()); + stdout_fd = fileno(io_ctx->stdout_file.get()); + stderr_fd = fileno(io_ctx->stderr_file.get()); + } + } +#endif + + auto CI = + compat::createClangInterpreter(vargs, stdin_fd, stdout_fd, stderr_fd); if (!CI) { llvm::errs() << "Interpreter creation failed\n"; return nullptr; } - return std::unique_ptr(new Interpreter(std::move(CI))); + return std::unique_ptr( + new Interpreter(std::move(CI), std::move(io_ctx))); } ~Interpreter() {} @@ -172,6 +232,28 @@ class Interpreter { operator const clang::Interpreter&() const { return *inner; } operator clang::Interpreter&() { return *inner; } + bool isOutOfProcess() const { + return io_context ? io_context->outOfProcess : false; + } + +#ifndef _WIN32 + FILE* getTempFileForOOP(int FD) { + if (!io_context) + return nullptr; + switch (FD) { + case (STDIN_FILENO): + return io_context->stdin_file.get(); + case (STDOUT_FILENO): + return io_context->stdout_file.get(); + case (STDERR_FILENO): + return io_context->stderr_file.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..21594b110 --- /dev/null +++ b/patches/llvm/clang20-1-out-of-process.patch @@ -0,0 +1,816 @@ +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,48 @@ ++//===-- 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, std::function CustomizeFork = nullptr); ++ ++/// 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,285 @@ ++//===-- 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, std::function CustomizeFork) { ++#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 (CustomizeFork) ++ CustomizeFork(); ++ ++ // 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 4b4b43bdd..14684619b 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 983ef8e95..09c8211ac 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::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::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::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::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; } @@ -2110,6 +2121,9 @@ TEST(FunctionReflectionTest, Construct) { GTEST_SKIP() << "Test fails for Emscipten builds"; #endif #endif + if (TestUtils::use_oop_jit()) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } if (llvm::sys::RunningOnValgrind()) GTEST_SKIP() << "XFAIL due to Valgrind report"; #ifdef _WIN32 @@ -2191,13 +2205,16 @@ TEST(FunctionReflectionTest, ConstructPOD) { GTEST_SKIP() << "Test fails for Emscipten builds"; #endif #endif + if (TestUtils::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 { @@ -2239,9 +2256,12 @@ TEST(FunctionReflectionTest, ConstructNested) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif + if (TestUtils::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 @@ -2299,8 +2319,11 @@ TEST(FunctionReflectionTest, ConstructArray) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif + if (TestUtils::use_oop_jit()) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } - Cpp::CreateInterpreter(); + TestUtils::CreateInterpreter(); Interp->declare(R"( #include @@ -2352,9 +2375,12 @@ TEST(FunctionReflectionTest, Destruct) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif + if (TestUtils::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 @@ -2423,9 +2449,12 @@ TEST(FunctionReflectionTest, DestructArray) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; #endif - + if (TestUtils::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 @@ -2496,7 +2525,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); @@ -2523,7 +2552,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 {}; @@ -2550,3 +2579,30 @@ TEST(FunctionReflectionTest, FailingTest1) { EXPECT_FALSE(Cpp::Declare("int x = 1;")); EXPECT_FALSE(Cpp::Declare("int y = x;")); } + +#ifndef _WIN32 +TEST(FunctionReflectionTest, GetExecutorPIDTest) { +#ifdef EMSCRIPTEN + GTEST_SKIP() << "Test fails for Emscipten builds"; +#endif +#ifdef CPPINTEROP_USE_CLING + GTEST_SKIP() << "Test fails for cling builds"; +#endif + if (llvm::sys::RunningOnValgrind()) + GTEST_SKIP() << "XFAIL due to Valgrind report"; + TestUtils::CreateInterpreter(); + pid_t pid = Cpp::GetExecutorPID(); + if (TestUtils::use_oop_jit()) { + EXPECT_NE(pid, -1); + } else { + EXPECT_EQ(pid, -1); + } + + pid = Cpp::GetNthExecutorPID(1); + if (TestUtils::use_oop_jit()) { + EXPECT_NE(pid, -1); + } else { + EXPECT_EQ(pid, -1); + } +} +#endif diff --git a/unittests/CppInterOp/InterpreterTest.cpp b/unittests/CppInterOp/InterpreterTest.cpp index 4ef046f92..622740b8e 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::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::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::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,20 +152,25 @@ 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::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) { #ifndef EMSCRIPTEN GTEST_SKIP() << "This test is intended to check exception handling for Emscripten builds."; #endif + if (TestUtils::use_oop_jit()) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } std::vector Args = { "-std=c++20", @@ -166,7 +181,7 @@ TEST(InterpreterTest, EmscriptenExceptionHandling) { "-mllvm", "-enable-emscripten-sjlj" }; - Cpp::CreateInterpreter(Args); + TestUtils::CreateInterpreter(Args); const char* tryCatchCode = R"( try { @@ -180,7 +195,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 +207,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 +259,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 +284,9 @@ TEST(InterpreterTest, DetectSystemCompilerIncludePaths) { } TEST(InterpreterTest, IncludePaths) { + if (TestUtils::use_oop_jit()) { + GTEST_SKIP() << "Test fails for OOP JIT builds"; + } std::vector includes; Cpp::GetIncludePaths(includes); EXPECT_FALSE(includes.empty()); @@ -294,7 +312,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..539619d7e 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::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::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,31 @@ 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"); + EXPECT_STREQ(CapturedStringErr.c_str(), ""); +} \ No newline at end of file diff --git a/unittests/CppInterOp/ScopeReflectionTest.cpp b/unittests/CppInterOp/ScopeReflectionTest.cpp index 7fd68c038..68ec2351a 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..a1a20e9d7 100644 --- a/unittests/CppInterOp/Utils.cpp +++ b/unittests/CppInterOp/Utils.cpp @@ -8,6 +8,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" +#include "clang/Interpreter/PartialTranslationUnit.h" #include #include @@ -16,28 +17,33 @@ using namespace clang; using namespace llvm; +bool& TestUtils::use_oop_jit() { + static bool flag = false; + return flag; +} + 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 +51,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::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..710430612 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 { +bool& 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..792bcff09 100644 --- a/unittests/CppInterOp/main.cpp +++ b/unittests/CppInterOp/main.cpp @@ -1,6 +1,18 @@ +#include "Utils.h" #include +#include + +static void parseCustomArguments(int argc, char** argv) { + for (int i = 1; i < argc; ++i) { + std::string arg(argv[i]); + if (arg == "--use-oop-jit") { + TestUtils::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