diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 83d2962cbf3ba..cfc5ead85e79b 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -15,6 +15,8 @@ #define LLVM_CLANG_INTERPRETER_INTERPRETER_H #include "clang/AST/GlobalDecl.h" +#include "clang/Driver/ToolChain.h" +#include "clang/Interpreter/OutOfProcessJITConfig.h" #include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/Interpreter/Value.h" @@ -136,15 +138,19 @@ class Interpreter { public: virtual ~Interpreter(); - static llvm::Expected> - create(std::unique_ptr CI, - std::unique_ptr JITBuilder = nullptr); + static llvm::Expected> create( + std::unique_ptr CI, + std::optional OutOfProcessConfig = std::nullopt); static llvm::Expected> createWithCUDA(std::unique_ptr CI, std::unique_ptr DCI); static llvm::Expected> createLLJITBuilder(std::unique_ptr EPC, llvm::StringRef OrcRuntimePath); + static std::unique_ptr + outOfProcessJITBuilder(OutOfProcessJITConfig OutOfProcessConfig); + static llvm::Expected + getOrcRuntimePath(const driver::ToolChain &TC); const ASTContext &getASTContext() const; ASTContext &getASTContext(); const CompilerInstance *getCompilerInstance() const; diff --git a/clang/include/clang/Interpreter/OutOfProcessJITConfig.h b/clang/include/clang/Interpreter/OutOfProcessJITConfig.h new file mode 100644 index 0000000000000..0996e758bab41 --- /dev/null +++ b/clang/include/clang/Interpreter/OutOfProcessJITConfig.h @@ -0,0 +1,45 @@ +//===-- OutOfProcessJITConfig.h - Struct for Out-Of-Process JIT--*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines a struct that holds configuration options for +// out-of-process JIT execution. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INTERPRETER_OUTOFPROCESSJITCONFIG_H +#define LLVM_CLANG_INTERPRETER_OUTOFPROCESSJITCONFIG_H + +#include +#include +#include + +namespace clang { + +/// \brief Configuration options for out-of-process JIT execution. +struct OutOfProcessJITConfig { + /// Indicates whether out-of-process JIT execution is enabled. + bool IsOutOfProcess = false; + + /// Path to the out-of-process JIT executor. + std::string OOPExecutor; + + std::string OOPExecutorConnect; + + /// Indicates whether to use shared memory for communication. + bool UseSharedMemory; + + /// String representing the slab allocation size for memory management. + std::string SlabAllocateSizeString; + + /// Path to the ORC runtime library. + std::string OrcRuntimePath; +}; + +} // namespace clang + +#endif // LLVM_CLANG_INTERPRETER_OUTOFPROCESSJITCONFIG_H \ No newline at end of file diff --git a/clang/include/clang/Interpreter/RemoteJITUtils.h b/clang/include/clang/Interpreter/RemoteJITUtils.h index 8705a3b1f669d..bc71232a5cad8 100644 --- a/clang/include/clang/Interpreter/RemoteJITUtils.h +++ b/clang/include/clang/Interpreter/RemoteJITUtils.h @@ -26,7 +26,8 @@ llvm::Expected> launchExecutor(llvm::StringRef ExecutablePath, bool UseSharedMemory, - llvm::StringRef SlabAllocateSizeString); + 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, @@ -35,4 +36,13 @@ llvm::Expected> connectTCPSocket(llvm::StringRef NetworkAddress, bool UseSharedMemory, llvm::StringRef SlabAllocateSizeString); +#ifdef LLVM_ON_UNIX +/// Returns PID of last launched executor. +pid_t getLastLaunchedExecutorPID(); + +/// Returns PID of nth launched executor. +/// 1-based indexing. +pid_t getNthLaunchedExecutorPID(int n); +#endif + #endif // LLVM_CLANG_INTERPRETER_REMOTEJITUTILS_H diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 5e5ae81b9ba44..b9a81bb4ebae1 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -41,6 +41,8 @@ #include "clang/Frontend/TextDiagnosticBuffer.h" #include "clang/FrontendTool/Utils.h" #include "clang/Interpreter/Interpreter.h" +#include "clang/Interpreter/OutOfProcessJITConfig.h" +#include "clang/Interpreter/RemoteJITUtils.h" #include "clang/Interpreter/Value.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/Lookup.h" @@ -55,6 +57,8 @@ #include "llvm/TargetParser/Host.h" #include "llvm/Transforms/Utils/Cloning.h" // for CloneModule +#include + #define DEBUG_TYPE "clang-repl" using namespace clang; @@ -460,10 +464,99 @@ const char *const Runtimes = R"( EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...); )"; +llvm::ExitOnError ExitOnErr; + +std::unique_ptr +Interpreter::outOfProcessJITBuilder(OutOfProcessJITConfig OutOfProcessConfig) { + std::unique_ptr EPC; + if (OutOfProcessConfig.OOPExecutor != "") { + // Launch an out-of-process executor locally in a child process. + EPC = ExitOnErr(launchExecutor(OutOfProcessConfig.OOPExecutor, + OutOfProcessConfig.UseSharedMemory, + OutOfProcessConfig.SlabAllocateSizeString)); + } else if (OutOfProcessConfig.OOPExecutorConnect != "") { + EPC = + ExitOnErr(connectTCPSocket(OutOfProcessConfig.OOPExecutorConnect, + OutOfProcessConfig.UseSharedMemory, + OutOfProcessConfig.SlabAllocateSizeString)); + } + + std::unique_ptr JB; + if (EPC) { + JB = ExitOnErr(clang::Interpreter::createLLJITBuilder( + std::move(EPC), OutOfProcessConfig.OrcRuntimePath)); + } + + return JB; +} + +llvm::Expected +Interpreter::getOrcRuntimePath(const driver::ToolChain &TC) { + std::optional CompilerRTPath = TC.getCompilerRTPath(); + if (!CompilerRTPath) { + return llvm::make_error("CompilerRT path not found", + std::error_code()); + } + llvm::SmallString<256> BasePath(llvm::sys::fs::getMainExecutable( + "clang-repl", reinterpret_cast(&getOrcRuntimePath))); + // Remove paths until you find /build at last + while (!(llvm::sys::path::filename(BasePath) == "build") && + !BasePath.empty()) { + llvm::sys::path::remove_filename(BasePath); + } + + llvm::sys::path::append(BasePath, *CompilerRTPath); + + if (llvm::sys::fs::exists(BasePath.str().str() + "/liborc_rt.a")) { + llvm::sys::path::append(BasePath, "liborc_rt.a"); + } else if (llvm::sys::fs::exists(BasePath.str().str() + "/liborc_rt_osx.a")) { + llvm::sys::path::append(BasePath, "liborc_rt_osx.a"); + } else if (llvm::sys::fs::exists(BasePath.str().str() + + "/liborc_rt-x86_64.a")) { + llvm::sys::path::append(BasePath, "liborc_rt-x86_64.a"); + } else { + return llvm::make_error("OrcRuntime library not found", + std::error_code()); + } + + return BasePath.str().str(); +} + llvm::Expected> Interpreter::create(std::unique_ptr CI, - std::unique_ptr JB) { + std::optional OutOfProcessConfig) { llvm::Error Err = llvm::Error::success(); + + std::unique_ptr JB; + + if (OutOfProcessConfig != std::nullopt && + OutOfProcessConfig->IsOutOfProcess) { + const TargetInfo &TI = CI->getTarget(); + const llvm::Triple &Triple = TI.getTriple(); + + DiagnosticsEngine &Diags = CI->getDiagnostics(); + driver::Driver Driver("clang", Triple.str(), Diags); + + std::vector Args = {"clang", "--version"}; + std::unique_ptr C( + Driver.BuildCompilation(Args)); + if (!C) { + return llvm::make_error( + "Failed to create driver compilation for out-of-process JIT", + std::error_code()); + } + + const clang::driver::ToolChain &TC = C->getDefaultToolChain(); + + auto OrcRuntimePathOrErr = getOrcRuntimePath(TC); + if (!OrcRuntimePathOrErr) { + return OrcRuntimePathOrErr.takeError(); + } + + OutOfProcessConfig->OrcRuntimePath = *OrcRuntimePathOrErr; + JB = outOfProcessJITBuilder(*OutOfProcessConfig); + } + auto Interp = std::unique_ptr( new Interpreter(std::move(CI), Err, JB ? std::move(JB) : nullptr)); if (Err) diff --git a/clang/lib/Interpreter/RemoteJITUtils.cpp b/clang/lib/Interpreter/RemoteJITUtils.cpp index c0e663b764785..c100f46931b6d 100644 --- a/clang/lib/Interpreter/RemoteJITUtils.cpp +++ b/clang/lib/Interpreter/RemoteJITUtils.cpp @@ -33,6 +33,10 @@ using namespace llvm; using namespace llvm::orc; +#if LLVM_ON_UNIX +static std::vector LaunchedExecutorPID; +#endif + Expected getSlabAllocSize(StringRef SizeString) { SizeString = SizeString.trim(); @@ -89,9 +93,14 @@ createSharedMemoryManager(SimpleRemoteEPC &SREPC, SlabSize, SREPC, SAs); } +// Launches an out-of-process executor for remote JIT. The calling program can +// provide a CustomizeFork callback, which allows it to run custom code in the +// child process before exec. This enables sending custom setup or code to be +// executed in the child (out-of-process) executor. Expected> launchExecutor(StringRef ExecutablePath, bool UseSharedMemory, - llvm::StringRef SlabAllocateSizeString) { + llvm::StringRef SlabAllocateSizeString, + std::function CustomizeFork) { #ifndef LLVM_ON_UNIX // FIXME: Add support for Windows. return make_error("-" + ExecutablePath + @@ -134,6 +143,9 @@ launchExecutor(StringRef ExecutablePath, bool UseSharedMemory, close(ToExecutor[WriteEnd]); close(FromExecutor[ReadEnd]); + if (CustomizeFork) + CustomizeFork(); + // Execute the child process. std::unique_ptr ExecutorPath, FDSpecifier; { @@ -158,6 +170,8 @@ launchExecutor(StringRef ExecutablePath, bool UseSharedMemory, } // else we're the parent... + LaunchedExecutorPID.push_back(ChildPID); + // Close the child ends of the pipes close(ToExecutor[ReadEnd]); close(FromExecutor[WriteEnd]); @@ -265,3 +279,18 @@ connectTCPSocket(StringRef NetworkAddress, bool UseSharedMemory, std::move(S), *SockFD, *SockFD); #endif } + +#if LLVM_ON_UNIX + +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); +} +#endif \ No newline at end of file diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index b666959c33b73..29939610ec6f6 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -19,21 +19,32 @@ #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Interpreter/CodeCompletion.h" #include "clang/Interpreter/Interpreter.h" +#include "clang/Interpreter/OutOfProcessJITConfig.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Sema.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include "llvm/LineEditor/LineEditor.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" // llvm_shutdown +#include "llvm/Support/Path.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" +#include "llvm/TargetParser/Triple.h" #include +#include +#include +#include + #include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h" +using namespace clang; + // 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. @@ -114,25 +125,8 @@ static llvm::Error sanitizeOopArguments(const char *ArgV0) { 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()); - } + // Out-of-process executors require the ORC runtime. ORC Runtime Path + // resolution is done in Interpreter.cpp. // If -oop-executor was used but no value was specified then use a sensible // default. @@ -186,7 +180,7 @@ struct ReplListCompleter { clang::Interpreter &MainInterp; ReplListCompleter(clang::IncrementalCompilerBuilder &CB, clang::Interpreter &Interp) - : CB(CB), MainInterp(Interp){}; + : CB(CB), MainInterp(Interp) {}; std::vector operator()(llvm::StringRef Buffer, size_t Pos) const; @@ -275,32 +269,22 @@ int main(int argc, const char **argv) { if (!CudaPath.empty()) CB.SetCudaSDK(CudaPath); - if (OffloadArch.empty()) { - OffloadArch = "sm_35"; + if (::OffloadArch.empty()) { + ::OffloadArch = "sm_35"; } - CB.SetOffloadArch(OffloadArch); + CB.SetOffloadArch(::OffloadArch); 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)); - } + clang::OutOfProcessJITConfig OutOfProcessConfig; + OutOfProcessConfig.IsOutOfProcess = !OOPExecutor.getNumOccurrences() || + !OOPExecutorConnect.getNumOccurrences(); + OutOfProcessConfig.OOPExecutor = OOPExecutor; + OutOfProcessConfig.SlabAllocateSizeString = SlabAllocateSizeString; + OutOfProcessConfig.UseSharedMemory = UseSharedMemory; // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It // can replace the boilerplate code for creation of the compiler instance. @@ -333,11 +317,10 @@ 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))); + } else { + Interp = ExitOnErr( + clang::Interpreter::create(std::move(CI), OutOfProcessConfig)); + } bool HasError = false; diff --git a/clang/unittests/Interpreter/CMakeLists.txt b/clang/unittests/Interpreter/CMakeLists.txt index db9f80d9f53fe..7e44b5a7f954d 100644 --- a/clang/unittests/Interpreter/CMakeLists.txt +++ b/clang/unittests/Interpreter/CMakeLists.txt @@ -29,13 +29,25 @@ set(CLANG_LIBS_TO_LINK ) endif() -add_distinct_clang_unittest(ClangReplInterpreterTests +set(CLANG_REPL_TEST_SOURCES IncrementalCompilerBuilderTest.cpp IncrementalProcessingTest.cpp InterpreterTest.cpp InterpreterExtensionsTest.cpp CodeCompletionTest.cpp +) + +if(TARGET compiler-rt) + list(APPEND CLANG_REPL_TEST_SOURCES + OutOfProcessInterpreterTests.cpp + ) + message(STATUS "Compiler-RT found, enabling out of process JIT tests") +endif() + +add_distinct_clang_unittest(ClangReplInterpreterTests + ${CLANG_REPL_TEST_SOURCES} + PARTIAL_SOURCES_INTENDED EXPORT_SYMBOLS CLANG_LIBS @@ -48,6 +60,14 @@ add_distinct_clang_unittest(ClangReplInterpreterTests ${LLVM_COMPONENTS_TO_LINK} ) +if(TARGET compiler-rt) + add_dependencies(ClangReplInterpreterTests + llvm-jitlink-executor + compiler-rt + ) + message(STATUS "Adding dependency on compiler-rt for out of process JIT tests") +endif() + if(EMSCRIPTEN) # Without the above you try to link to LLVMSupport twice, and end # up with a duplicate symbol error when creating the main module @@ -71,6 +91,14 @@ set_target_properties(ClangReplInterpreterTests PROPERTIES ) endif() +if(TARGET compiler-rt) + add_dependencies(ClangReplInterpreterTests + llvm-jitlink-executor + compiler-rt + ) + message(STATUS "Adding dependency on compiler-rt for out of process JIT tests") +endif() + # Exceptions on Windows are not yet supported. if(NOT WIN32) add_subdirectory(ExceptionTests) diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp index 9ff9092524d21..79c8c63105a76 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -15,12 +15,17 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/Mangle.h" +#include "clang/Basic/Version.h" +#include "clang/Config/config.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Interpreter/Interpreter.h" +#include "clang/Interpreter/RemoteJITUtils.h" #include "clang/Interpreter/Value.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/Sema.h" +#include "llvm/Support/Error.h" +#include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/Host.h" @@ -34,6 +39,12 @@ int Global = 42; REPL_EXTERNAL_VISIBILITY int getGlobal() { return Global; } REPL_EXTERNAL_VISIBILITY void setGlobal(int val) { Global = val; } +#ifdef _WIN32 +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#endif + namespace { class InterpreterTest : public InterpreterTestBase { diff --git a/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp b/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp new file mode 100644 index 0000000000000..bf1c9f355578b --- /dev/null +++ b/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp @@ -0,0 +1,118 @@ +//===- unittests/Interpreter/OutOfProcessInterpreterTest.cpp --- Interpreter +// tests when Out-of-Process ----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Unit tests for Clang's Interpreter library. +// +//===----------------------------------------------------------------------===// + +#include "InterpreterTestFixture.h" + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/Mangle.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/Version.h" +#include "clang/Config/config.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/ToolChain.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Interpreter/Interpreter.h" +#include "clang/Interpreter/OutOfProcessJITConfig.h" +#include "clang/Interpreter/RemoteJITUtils.h" +#include "clang/Interpreter/Value.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/TargetParser/Host.h" +#include "llvm/TargetParser/Triple.h" + +#include "llvm/TargetParser/Host.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace clang; + +llvm::ExitOnError ExitOnError; + +#ifdef _WIN32 +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#endif + +namespace { + +using Args = std::vector; + +static void removePathComponent(unsigned N, llvm::SmallString<256> &Path) { + for (unsigned i = 0; i < N; ++i) + llvm::sys::path::remove_filename(Path); +} + +static std::string getExecutorPath() { + llvm::SmallString<256> ExecutorPath(llvm::sys::fs::getMainExecutable( + nullptr, reinterpret_cast(&getExecutorPath))); + removePathComponent(5, ExecutorPath); + llvm::sys::path::append(ExecutorPath, "bin", "llvm-jitlink-executor"); + return ExecutorPath.str().str(); +} + +static std::unique_ptr +createInterpreterWithRemoteExecution(const Args &ExtraArgs = {}, + DiagnosticConsumer *Client = nullptr) { + Args ClangArgs = {"-Xclang", "-emit-llvm-only"}; + llvm::append_range(ClangArgs, ExtraArgs); + auto CB = clang::IncrementalCompilerBuilder(); + CB.SetCompilerArgs(ClangArgs); + auto CI = cantFail(CB.CreateCpp()); + if (Client) + CI->getDiagnostics().setClient(Client, /*ShouldOwnClient=*/false); + + OutOfProcessJITConfig OutOfProcessConfig; + OutOfProcessConfig.OOPExecutor = getExecutorPath(); + OutOfProcessConfig.UseSharedMemory = false; + OutOfProcessConfig.SlabAllocateSizeString = ""; + OutOfProcessConfig.IsOutOfProcess = true; + + std::unique_ptr JB; + + return cantFail( + clang::Interpreter::create(std::move(CI), OutOfProcessConfig)); +} + +static size_t DeclsSize(TranslationUnitDecl *PTUDecl) { + return std::distance(PTUDecl->decls().begin(), PTUDecl->decls().end()); +} + +TEST_F(InterpreterTestBase, SanityWithRemoteExecution) { + if (!HostSupportsJIT()) + GTEST_SKIP(); + + std::unique_ptr Interp = createInterpreterWithRemoteExecution(); + + using PTU = PartialTranslationUnit; + + PTU &R1(cantFail(Interp->Parse("void g(); void g() {}"))); + EXPECT_EQ(2U, DeclsSize(R1.TUPart)); + + PTU &R2(cantFail(Interp->Parse("int i;"))); + EXPECT_EQ(1U, DeclsSize(R2.TUPart)); +} + +} // end anonymous namespace