https://github.com/vgvassilev created https://github.com/llvm/llvm-project/pull/175322
cc: @aaronj0, @Vipul-Cariappa >From 051a276b02020b3213e35f9e85b55a4a2199aaf5 Mon Sep 17 00:00:00 2001 From: Vassil Vassilev <[email protected]> Date: Sat, 10 Jan 2026 15:17:04 +0000 Subject: [PATCH] [clang-repl] Simplify the logic around out of process execution. NFC --- clang/include/clang/Interpreter/Interpreter.h | 91 +++++--- clang/lib/Interpreter/IncrementalExecutor.cpp | 3 +- clang/lib/Interpreter/IncrementalExecutor.h | 6 +- clang/lib/Interpreter/Interpreter.cpp | 215 +++++++++--------- clang/tools/clang-repl/ClangRepl.cpp | 31 +-- .../Interpreter/InterpreterExtensionsTest.cpp | 39 ---- .../OutOfProcessInterpreterTests.cpp | 98 ++++---- 7 files changed, 240 insertions(+), 243 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 078d70b3b1749..4a69b37338349 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -38,7 +38,7 @@ class ThreadSafeContext; namespace clang { namespace driver { -class ToolChain; +class Compilation; } // namespace driver class CompilerInstance; @@ -50,6 +50,8 @@ class IncrementalCUDADeviceParser; /// Create a pre-configured \c CompilerInstance for incremental processing. class IncrementalCompilerBuilder { + using DriverCompilationFn = llvm::Error(const driver::Compilation &); + public: IncrementalCompilerBuilder() {} @@ -68,11 +70,16 @@ class IncrementalCompilerBuilder { // CUDA specific void SetCudaSDK(llvm::StringRef path) { CudaSDKPath = path; }; + // Hand over the compilation. + void SetDriverCompilationCallback(std::function<DriverCompilationFn> C) { + CompilationCB = C; + } + llvm::Expected<std::unique_ptr<CompilerInstance>> CreateCudaHost(); llvm::Expected<std::unique_ptr<CompilerInstance>> CreateCudaDevice(); private: - static llvm::Expected<std::unique_ptr<CompilerInstance>> + llvm::Expected<std::unique_ptr<CompilerInstance>> create(std::string TT, std::vector<const char *> &ClangArgv); llvm::Expected<std::unique_ptr<CompilerInstance>> createCuda(bool device); @@ -82,6 +89,46 @@ class IncrementalCompilerBuilder { llvm::StringRef OffloadArch; llvm::StringRef CudaSDKPath; + + std::optional<std::function<DriverCompilationFn>> CompilationCB; +}; + +// FIXME: Consider deriving from the LLJITBuilder into a common interpreter +// creation configuraion class. +class IncrementalExecutorBuilder { +public: + /// 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 = false; + /// Representing the slab allocation size for memory management in kb. + unsigned SlabAllocateSize = 0; + /// Path to the ORC runtime library. + std::string OrcRuntimePath = ""; + /// PID of the out-of-process JIT executor. + uint32_t ExecutorPID = 0; + /// Custom lambda to be executed inside child process/executor + std::function<void()> CustomizeFork = nullptr; + /// An optional code model to provide to the JITTargetMachineBuilder + std::optional<llvm::CodeModel::Model> CM = std::nullopt; + /// An optional external IncrementalExecutor + // std::unique_ptr<IncrementalExecutor> IE; + std::function<llvm::Error(const driver::Compilation &)> + UpdateOrcRuntimePathCB = [this](const driver::Compilation &C) { + return UpdateOrcRuntimePath(C); + }; + + ~IncrementalExecutorBuilder(); + + llvm::Expected<std::unique_ptr<IncrementalExecutor>> + create(llvm::orc::ThreadSafeContext &TSC, + llvm::orc::LLJITBuilder &JITBuilder); + +private: + llvm::Error UpdateOrcRuntimePath(const driver::Compilation &C); }; class IncrementalAction; @@ -120,42 +167,16 @@ class Interpreter { /// An optional compiler instance for CUDA offloading std::unique_ptr<CompilerInstance> DeviceCI; -public: - struct JITConfig { - /// 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 = false; - /// Representing the slab allocation size for memory management in kb. - unsigned SlabAllocateSize = 0; - /// Path to the ORC runtime library. - std::string OrcRuntimePath = ""; - /// PID of the out-of-process JIT executor. - uint32_t ExecutorPID = 0; - /// Custom lambda to be executed inside child process/executor - std::function<void()> CustomizeFork = nullptr; - /// An optional code model to provide to the JITTargetMachineBuilder - std::optional<llvm::CodeModel::Model> CM = std::nullopt; - - JITConfig() - : IsOutOfProcess(false), OOPExecutor(""), OOPExecutorConnect(""), - UseSharedMemory(false), SlabAllocateSize(0), OrcRuntimePath(""), - ExecutorPID(0), CustomizeFork(nullptr), CM(std::nullopt) {} - }; - protected: // Derived classes can use an extended interface of the Interpreter. Interpreter(std::unique_ptr<CompilerInstance> Instance, llvm::Error &Err, std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr, std::unique_ptr<clang::ASTConsumer> Consumer = nullptr, - JITConfig Config = JITConfig()); + std::unique_ptr<IncrementalExecutorBuilder> IEB = nullptr); // Create the internal IncrementalExecutor, or re-create it after calling // ResetExecutor(). - llvm::Error CreateExecutor(JITConfig Config = JITConfig()); + llvm::Error CreateExecutor(); // Delete the internal IncrementalExecutor. This causes a hard shutdown of the // JIT engine. In particular, it doesn't run cleanup or destructors. @@ -164,18 +185,14 @@ class Interpreter { public: virtual ~Interpreter(); static llvm::Expected<std::unique_ptr<Interpreter>> - create(std::unique_ptr<CompilerInstance> CI, JITConfig Config = {}); + create(std::unique_ptr<CompilerInstance> CI, + std::unique_ptr<IncrementalExecutorBuilder> IEB = nullptr); static llvm::Expected<std::unique_ptr<Interpreter>> createWithCUDA(std::unique_ptr<CompilerInstance> CI, std::unique_ptr<CompilerInstance> DCI); static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> createLLJITBuilder(std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC, llvm::StringRef OrcRuntimePath); - static llvm::Expected< - std::pair<std::unique_ptr<llvm::orc::LLJITBuilder>, uint32_t>> - outOfProcessJITBuilder(JITConfig Config); - static llvm::Expected<std::string> - getOrcRuntimePath(const driver::ToolChain &TC); const ASTContext &getASTContext() const; ASTContext &getASTContext(); @@ -221,6 +238,8 @@ class Interpreter { std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder; + std::unique_ptr<IncrementalExecutorBuilder> IncrExecutorBuilder; + /// @} /// @name Value and pretty printing support /// @{ diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp index 11ab2cfaac17a..f0069c4924f7a 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.cpp +++ b/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -69,9 +69,8 @@ IncrementalExecutor::createDefaultJITBuilder( IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, llvm::orc::LLJITBuilder &JITBuilder, - Interpreter::JITConfig Config, llvm::Error &Err) - : TSCtx(TSC), OutOfProcessChildPid(Config.ExecutorPID) { + : TSCtx(TSC) { using namespace llvm::orc; llvm::ErrorAsOutParameter EAO(&Err); diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h index bb1ec33452515..c11c99b9bff8d 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.h +++ b/clang/lib/Interpreter/IncrementalExecutor.h @@ -46,7 +46,6 @@ class IncrementalExecutor { using CtorDtorIterator = llvm::orc::CtorDtorIterator; std::unique_ptr<llvm::orc::LLJIT> Jit; llvm::orc::ThreadSafeContext &TSCtx; - uint32_t OutOfProcessChildPid = -1; llvm::DenseMap<const PartialTranslationUnit *, llvm::orc::ResourceTrackerSP> ResourceTrackers; @@ -58,8 +57,7 @@ class IncrementalExecutor { enum SymbolNameKind { IRName, LinkerName }; IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, - llvm::orc::LLJITBuilder &JITBuilder, - Interpreter::JITConfig Config, llvm::Error &Err); + llvm::orc::LLJITBuilder &JITBuilder, llvm::Error &Err); virtual ~IncrementalExecutor(); virtual llvm::Error addModule(PartialTranslationUnit &PTU); @@ -71,8 +69,6 @@ class IncrementalExecutor { llvm::orc::LLJIT &GetExecutionEngine() { return *Jit; } - uint32_t getOutOfProcessChildPid() const { return OutOfProcessChildPid; } - static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB); diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 1a6f6ea0813b7..01e534416152b 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -182,6 +182,10 @@ IncrementalCompilerBuilder::create(std::string TT, llvm::ArrayRef<const char *> RF = llvm::ArrayRef(ClangArgv); std::unique_ptr<driver::Compilation> Compilation(Driver.BuildCompilation(RF)); + if (CompilationCB) + if (auto Err = (*CompilationCB)(*Compilation.get())) + return std::move(Err); + if (Compilation->getArgs().hasArg(options::OPT_v)) Compilation->getJobs().Print(llvm::errs(), "\n", /*Quote=*/false); @@ -247,12 +251,83 @@ IncrementalCompilerBuilder::CreateCudaHost() { return IncrementalCompilerBuilder::createCuda(false); } +IncrementalExecutorBuilder::~IncrementalExecutorBuilder() = default; + +llvm::Expected<std::unique_ptr<IncrementalExecutor>> +IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC, + llvm::orc::LLJITBuilder &JITBuilder) { + // if (IE) + // return std::move(IE); + + llvm::Error Err = llvm::Error::success(); + std::unique_ptr<IncrementalExecutor> Executor; +#ifdef __EMSCRIPTEN__ + Executor = std::make_unique<WasmIncrementalExecutor>(TSC); +#else + Executor = std::make_unique<IncrementalExecutor>(TSC, JITBuilder, Err); +#endif + + if (Err) + return std::move(Err); + + return std::move(Executor); +} + +llvm::Error +IncrementalExecutorBuilder::UpdateOrcRuntimePath(const driver::Compilation &C) { + if (!IsOutOfProcess) + return llvm::Error::success(); + + const std::array<const char *, 3> OrcRTLibNames = { + "liborc_rt.a", "liborc_rt_osx.a", "liborc_rt-x86_64.a"}; + + auto findInDir = [&](llvm::StringRef Base) -> std::optional<std::string> { + for (const char *LibName : OrcRTLibNames) { + llvm::SmallString<256> CandidatePath(Base); + llvm::sys::path::append(CandidatePath, LibName); + if (llvm::sys::fs::exists(CandidatePath)) + return std::string(CandidatePath.str()); + } + return std::nullopt; + }; + + const driver::ToolChain &TC = C.getDefaultToolChain(); + std::string SearchedPaths; + if (std::optional<std::string> CompilerRTPath = TC.getCompilerRTPath()) { + if (auto Found = findInDir(*CompilerRTPath)) { + OrcRuntimePath = *Found; + return llvm::Error::success(); + } + SearchedPaths += *CompilerRTPath; + } else { + return llvm::make_error<llvm::StringError>("CompilerRT path not found", + std::error_code()); + } + + if (std::optional<std::string> ResourceDir = TC.getRuntimePath()) { + if (auto Found = findInDir(*ResourceDir)) { + OrcRuntimePath = *Found; + return llvm::Error::success(); + } + if (!SearchedPaths.empty()) + SearchedPaths += "; "; + SearchedPaths += *ResourceDir; + } else { + return llvm::make_error<llvm::StringError>("ResourceDir path not found", + std::error_code()); + } + + return llvm::make_error<llvm::StringError>( + llvm::Twine("OrcRuntime library not found in: ") + SearchedPaths, + std::error_code()); +} + Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance, llvm::Error &ErrOut, std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder, std::unique_ptr<clang::ASTConsumer> Consumer, - JITConfig Config) - : JITBuilder(std::move(JITBuilder)) { + std::unique_ptr<IncrementalExecutorBuilder> IEB) + : JITBuilder(std::move(JITBuilder)), IncrExecutorBuilder(std::move(IEB)) { CI = std::move(Instance); llvm::ErrorAsOutParameter EAO(&ErrOut); auto LLVMCtx = std::make_unique<llvm::LLVMContext>(); @@ -286,7 +361,7 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance, ASTContext &C = CI->getASTContext(); IncrParser->RegisterPTU(C.getTranslationUnitDecl(), std::move(M)); } - if (llvm::Error Err = CreateExecutor(Config)) { + if (llvm::Error Err = CreateExecutor()) { ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); return; } @@ -348,25 +423,28 @@ const char *const Runtimes = R"( EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...); )"; -llvm::Expected<std::pair<std::unique_ptr<llvm::orc::LLJITBuilder>, uint32_t>> -Interpreter::outOfProcessJITBuilder(JITConfig Config) { +static llvm::Expected< + std::pair<std::unique_ptr<llvm::orc::LLJITBuilder>, uint32_t>> +outOfProcessJITBuilder(const IncrementalExecutorBuilder &IncrExecutorBuilder) { std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC; uint32_t childPid = -1; - if (!Config.OOPExecutor.empty()) { + if (!IncrExecutorBuilder.OOPExecutor.empty()) { // Launch an out-of-process executor locally in a child process. auto ResultOrErr = IncrementalExecutor::launchExecutor( - Config.OOPExecutor, Config.UseSharedMemory, Config.SlabAllocateSize, - Config.CustomizeFork); + IncrExecutorBuilder.OOPExecutor, IncrExecutorBuilder.UseSharedMemory, + IncrExecutorBuilder.SlabAllocateSize, + IncrExecutorBuilder.CustomizeFork); if (!ResultOrErr) return ResultOrErr.takeError(); childPid = ResultOrErr->second; auto EPCOrErr = std::move(ResultOrErr->first); EPC = std::move(EPCOrErr); - } else if (Config.OOPExecutorConnect != "") { + } else if (IncrExecutorBuilder.OOPExecutorConnect != "") { #if LLVM_ON_UNIX && LLVM_ENABLE_THREADS auto EPCOrErr = IncrementalExecutor::connectTCPSocket( - Config.OOPExecutorConnect, Config.UseSharedMemory, - Config.SlabAllocateSize); + IncrExecutorBuilder.OOPExecutorConnect, + IncrExecutorBuilder.UseSharedMemory, + IncrExecutorBuilder.SlabAllocateSize); if (!EPCOrErr) return EPCOrErr.takeError(); EPC = std::move(*EPCOrErr); @@ -380,7 +458,7 @@ Interpreter::outOfProcessJITBuilder(JITConfig Config) { std::unique_ptr<llvm::orc::LLJITBuilder> JB; if (EPC) { auto JBOrErr = clang::Interpreter::createLLJITBuilder( - std::move(EPC), Config.OrcRuntimePath); + std::move(EPC), IncrExecutorBuilder.OrcRuntimePath); if (!JBOrErr) return JBOrErr.takeError(); JB = std::move(*JBOrErr); @@ -389,84 +467,14 @@ Interpreter::outOfProcessJITBuilder(JITConfig Config) { return std::make_pair(std::move(JB), childPid); } -llvm::Expected<std::string> -Interpreter::getOrcRuntimePath(const driver::ToolChain &TC) { - const std::array<const char *, 3> OrcRTLibNames = { - "liborc_rt.a", "liborc_rt_osx.a", "liborc_rt-x86_64.a"}; - - auto findInDir = [&](llvm::StringRef Base) -> std::optional<std::string> { - for (const char *LibName : OrcRTLibNames) { - llvm::SmallString<256> CandidatePath(Base); - llvm::sys::path::append(CandidatePath, LibName); - if (llvm::sys::fs::exists(CandidatePath)) - return std::string(CandidatePath.str()); - } - return std::nullopt; - }; - - std::string SearchedPaths; - - if (std::optional<std::string> CompilerRTPath = TC.getCompilerRTPath()) { - if (auto Found = findInDir(*CompilerRTPath)) - return *Found; - SearchedPaths += *CompilerRTPath; - } else { - return llvm::make_error<llvm::StringError>("CompilerRT path not found", - std::error_code()); - } - - if (std::optional<std::string> ResourceDir = TC.getRuntimePath()) { - if (auto Found = findInDir(*ResourceDir)) - return *Found; - if (!SearchedPaths.empty()) - SearchedPaths += "; "; - SearchedPaths += *ResourceDir; - } else { - return llvm::make_error<llvm::StringError>("ResourceDir path not found", - std::error_code()); - } - - return llvm::make_error<llvm::StringError>( - llvm::Twine("OrcRuntime library not found in: ") + SearchedPaths, - std::error_code()); -} - -llvm::Expected<std::unique_ptr<Interpreter>> -Interpreter::create(std::unique_ptr<CompilerInstance> CI, JITConfig Config) { - - if (Config.IsOutOfProcess) { - const TargetInfo &TI = CI->getTarget(); - const llvm::Triple &Triple = TI.getTriple(); - - DiagnosticsEngine &Diags = CI->getDiagnostics(); - std::string BinaryName = llvm::sys::fs::getMainExecutable(nullptr, nullptr); - driver::Driver Driver(BinaryName, Triple.str(), Diags); - // Need fake args to get the driver to create a compilation. - std::vector<const char *> Args = {"clang", "--version"}; - std::unique_ptr<clang::driver::Compilation> C( - Driver.BuildCompilation(Args)); - if (!C) { - return llvm::make_error<llvm::StringError>( - "Failed to create driver compilation for out-of-process JIT", - std::error_code()); - } - if (Config.OrcRuntimePath == "") { - const clang::driver::ToolChain &TC = C->getDefaultToolChain(); - - auto OrcRuntimePathOrErr = getOrcRuntimePath(TC); - if (!OrcRuntimePathOrErr) { - return OrcRuntimePathOrErr.takeError(); - } - - Config.OrcRuntimePath = *OrcRuntimePathOrErr; - } - } - +llvm::Expected<std::unique_ptr<Interpreter>> Interpreter::create( + std::unique_ptr<CompilerInstance> CI, + std::unique_ptr<IncrementalExecutorBuilder> IEB /*=nullptr*/) { llvm::Error Err = llvm::Error::success(); std::unique_ptr<llvm::orc::LLJITBuilder> JB; auto Interp = std::unique_ptr<Interpreter>(new Interpreter( - std::move(CI), Err, std::move(JB), /*Consumer=*/nullptr, Config)); + std::move(CI), Err, std::move(JB), /*Consumer=*/nullptr, std::move(IEB))); if (auto E = std::move(Err)) return std::move(E); @@ -560,7 +568,7 @@ size_t Interpreter::getEffectivePTUSize() const { uint32_t Interpreter::getOutOfProcessExecutorPID() const { if (IncrExecutor) - return IncrExecutor->getOutOfProcessChildPid(); + return IncrExecutorBuilder->ExecutorPID; return -1; } @@ -627,7 +635,7 @@ Interpreter::createLLJITBuilder( return std::move(*JB); } -llvm::Error Interpreter::CreateExecutor(JITConfig Config) { +llvm::Error Interpreter::CreateExecutor() { if (IncrExecutor) return llvm::make_error<llvm::StringError>("Operation failed. " "Execution engine exists", @@ -641,13 +649,16 @@ llvm::Error Interpreter::CreateExecutor(JITConfig Config) { llvm::Triple TargetTriple(TT); bool IsWindowsTarget = TargetTriple.isOSWindows(); - if (!IsWindowsTarget && Config.IsOutOfProcess) { + if (!IncrExecutorBuilder) + IncrExecutorBuilder = std::make_unique<IncrementalExecutorBuilder>(); + + if (!IsWindowsTarget && IncrExecutorBuilder->IsOutOfProcess) { if (!JITBuilder) { - auto ResOrErr = outOfProcessJITBuilder(Config); + auto ResOrErr = outOfProcessJITBuilder(*IncrExecutorBuilder); if (!ResOrErr) return ResOrErr.takeError(); JITBuilder = std::move(ResOrErr->first); - Config.ExecutorPID = ResOrErr->second; + IncrExecutorBuilder->ExecutorPID = ResOrErr->second; } if (!JITBuilder) return llvm::make_error<llvm::StringError>( @@ -659,29 +670,19 @@ llvm::Error Interpreter::CreateExecutor(JITConfig Config) { auto JTMB = createJITTargetMachineBuilder(TT); if (!JTMB) return JTMB.takeError(); - if (Config.CM) - JTMB->setCodeModel(Config.CM); + if (IncrExecutorBuilder->CM) + JTMB->setCodeModel(IncrExecutorBuilder->CM); auto JB = IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB)); if (!JB) return JB.takeError(); JITBuilder = std::move(*JB); } - llvm::Error Err = llvm::Error::success(); - - // Fix: Declare Executor as the appropriate unique_ptr type - std::unique_ptr<IncrementalExecutor> Executor; - -#ifdef __EMSCRIPTEN__ - Executor = std::make_unique<WasmIncrementalExecutor>(*TSCtx); -#else - Executor = - std::make_unique<IncrementalExecutor>(*TSCtx, *JITBuilder, Config, Err); -#endif - if (!Err) - IncrExecutor = std::move(Executor); + auto ExecutorOrErr = IncrExecutorBuilder->create(*TSCtx, *JITBuilder); + if (ExecutorOrErr) + IncrExecutor = std::move(*ExecutorOrErr); - return Err; + return ExecutorOrErr.takeError(); } void Interpreter::ResetExecutor() { IncrExecutor.reset(); } diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index 066f526cba9ae..e94749555ad1a 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -291,6 +291,22 @@ int main(int argc, const char **argv) { clang::IncrementalCompilerBuilder CB; CB.SetCompilerArgs(ClangArgv); + auto IEB = std::make_unique<clang::IncrementalExecutorBuilder>(); + IEB->IsOutOfProcess = !OOPExecutor.empty() || !OOPExecutorConnect.empty(); + IEB->OOPExecutor = OOPExecutor; + if (!OrcRuntimePath.empty()) + IEB->OrcRuntimePath = OrcRuntimePath; + else + CB.SetDriverCompilationCallback(IEB->UpdateOrcRuntimePathCB); + + auto SizeOrErr = getSlabAllocSize(SlabAllocateSizeString); + if (!SizeOrErr) { + llvm::logAllUnhandledErrors(SizeOrErr.takeError(), llvm::errs(), "error: "); + return EXIT_FAILURE; + } + IEB->SlabAllocateSize = *SizeOrErr; + IEB->UseSharedMemory = UseSharedMemory; + std::unique_ptr<clang::CompilerInstance> DeviceCI; if (CudaEnabled) { if (!CudaPath.empty()) @@ -306,18 +322,6 @@ int main(int argc, const char **argv) { ExitOnErr(sanitizeOopArguments(argv[0])); - clang::Interpreter::JITConfig Config; - Config.IsOutOfProcess = !OOPExecutor.empty() || !OOPExecutorConnect.empty(); - Config.OOPExecutor = OOPExecutor; - Config.OrcRuntimePath = OrcRuntimePath; - auto SizeOrErr = getSlabAllocSize(SlabAllocateSizeString); - if (!SizeOrErr) { - llvm::logAllUnhandledErrors(SizeOrErr.takeError(), llvm::errs(), "error: "); - return EXIT_FAILURE; - } - Config.SlabAllocateSize = *SizeOrErr; - Config.UseSharedMemory = UseSharedMemory; - // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It // can replace the boilerplate code for creation of the compiler instance. std::unique_ptr<clang::CompilerInstance> CI; @@ -350,7 +354,8 @@ int main(int argc, const char **argv) { ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str())); } } else { - Interp = ExitOnErr(clang::Interpreter::create(std::move(CI), Config)); + Interp = + ExitOnErr(clang::Interpreter::create(std::move(CI), std::move(IEB))); } bool HasError = false; diff --git a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp index f50f6e320776d..07a4a224f16bc 100644 --- a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp +++ b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp @@ -65,45 +65,6 @@ class InterpreterExtensionsTest : public InterpreterTestBase { } }; -struct OutOfProcInterpreter : public Interpreter { - OutOfProcInterpreter( - std::unique_ptr<CompilerInstance> CI, llvm::Error &ErrOut, - std::unique_ptr<clang::ASTConsumer> Consumer, - std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr) - : Interpreter(std::move(CI), ErrOut, std::move(JITBuilder), - std::move(Consumer)) {} -}; - -TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) { -// FIXME : WebAssembly doesn't currently support Jit (see -// https: // github.com/llvm/llvm-project/pull/150977#discussion_r2237521095). -// so this check of HostSupportsJIT has been skipped -// over until support is added, and HostSupportsJIT can return true. -#ifndef __EMSCRIPTEN__ - if (!HostSupportsJIT()) - GTEST_SKIP(); -#endif - clang::IncrementalCompilerBuilder CB; - llvm::Error ErrOut = llvm::Error::success(); - auto CI = cantFail(CB.CreateCpp()); - // Do not attach the default consumer which is specialized for in-process. - class NoopConsumer : public ASTConsumer {}; - std::unique_ptr<ASTConsumer> C = std::make_unique<NoopConsumer>(); - OutOfProcInterpreter I(std::move(CI), ErrOut, std::move(C), - /*JITBuilder=*/nullptr); - cantFail(std::move(ErrOut)); - cantFail(I.Parse("int a = 1; a")); - cantFail(I.Parse("int b = 2; b")); - cantFail(I.Parse("int c = 3; c")); - - // Make sure no clang::Value logic is attached by the Interpreter. - Value V1; - llvm::cantFail(I.ParseAndExecute("int x = 42;")); - llvm::cantFail(I.ParseAndExecute("x", &V1)); - EXPECT_FALSE(V1.isValid()); - EXPECT_FALSE(V1.hasValue()); -} - class CustomJBInterpreter : public Interpreter { using CustomJITBuilderCreatorFunction = std::function<llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>()>; diff --git a/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp b/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp index 704ddc37e642e..d33005244d8da 100644 --- a/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp +++ b/clang/unittests/Interpreter/OutOfProcessInterpreterTests.cpp @@ -27,6 +27,7 @@ #include "llvm/TargetParser/Host.h" #include "gmock/gmock.h" #include "gtest/gtest.h" + #include <memory> #include <signal.h> #include <sstream> @@ -101,47 +102,34 @@ static std::string getExecutorPath() { return ExecutorPath.str().str(); } -static std::string getOrcRuntimePath() { - llvm::SmallString<256> RuntimePath(llvm::sys::fs::getMainExecutable( - nullptr, reinterpret_cast<void *>(&getOrcRuntimePath))); - removePathComponent(5, RuntimePath); - llvm::sys::path::append(RuntimePath, CLANG_INSTALL_LIBDIR_BASENAME, "clang", - CLANG_VERSION_MAJOR_STRING, "lib"); - - llvm::Triple SystemTriple(llvm::sys::getProcessTriple()); - if (SystemTriple.isOSBinFormatMachO()) { - llvm::sys::path::append(RuntimePath, "darwin", "liborc_rt_osx.a"); - } else if (SystemTriple.isOSBinFormatELF()) { - llvm::sys::path::append(RuntimePath, "x86_64-unknown-linux-gnu", - "liborc_rt.a"); - } - return RuntimePath.str().str(); -} +struct OutOfProcessInterpreterInfo { + std::string OrcRuntimePath; + std::unique_ptr<Interpreter> Interpreter; +}; -static std::unique_ptr<Interpreter> +static OutOfProcessInterpreterInfo createInterpreterWithRemoteExecution(std::shared_ptr<IOContext> io_ctx, const Args &ExtraArgs = {}) { Args ClangArgs = {"-Xclang", "-emit-llvm-only"}; llvm::append_range(ClangArgs, ExtraArgs); - auto CB = clang::IncrementalCompilerBuilder(); - CB.SetCompilerArgs(ClangArgs); - auto CI = cantFail(CB.CreateCpp()); - clang::Interpreter::JITConfig Config; + auto Config = std::make_unique<IncrementalExecutorBuilder>(); llvm::Triple SystemTriple(llvm::sys::getProcessTriple()); if (SystemTriple.isOSBinFormatELF() || SystemTriple.isOSBinFormatMachO()) { - Config.IsOutOfProcess = true; - Config.OOPExecutor = getExecutorPath(); - Config.UseSharedMemory = false; - Config.SlabAllocateSize = 0; - Config.OrcRuntimePath = getOrcRuntimePath(); - + Config->IsOutOfProcess = true; + Config->OOPExecutor = getExecutorPath(); + Config->UseSharedMemory = false; + Config->SlabAllocateSize = 0; + + // Capture the raw file descriptors by value explicitly. This lambda will + // be invoked in the child process after fork(), so capturing the fd ints is + // safe and avoids capturing FILE* pointers or outer 'this'. int stdin_fd = fileno(io_ctx->stdin_file.get()); int stdout_fd = fileno(io_ctx->stdout_file.get()); int stderr_fd = fileno(io_ctx->stderr_file.get()); - Config.CustomizeFork = [=] { + Config->CustomizeFork = [stdin_fd, stdout_fd, stderr_fd]() { auto redirect = [](int from, int to) { if (from != to) { dup2(from, to); @@ -153,15 +141,21 @@ createInterpreterWithRemoteExecution(std::shared_ptr<IOContext> io_ctx, redirect(stdout_fd, STDOUT_FILENO); redirect(stderr_fd, STDERR_FILENO); + // Unbuffer the stdio in the child; useful for deterministic tests. setvbuf(stdout, nullptr, _IONBF, 0); setvbuf(stderr, nullptr, _IONBF, 0); + // Helpful marker for the unit-test to assert that fork customization ran. printf("CustomizeFork executed\n"); fflush(stdout); }; } - - return cantFail(clang::Interpreter::create(std::move(CI), Config)); + auto CB = IncrementalCompilerBuilder(); + CB.SetCompilerArgs(ClangArgs); + CB.SetDriverCompilationCallback(Config->UpdateOrcRuntimePathCB); + auto CI = cantFail(CB.CreateCpp()); + return {Config->OrcRuntimePath, + cantFail(Interpreter::create(std::move(CI), std::move(Config)))}; } static size_t DeclsSize(TranslationUnitDecl *PTUDecl) { @@ -172,20 +166,19 @@ TEST_F(InterpreterTestBase, SanityWithRemoteExecution) { if (!HostSupportsJIT()) GTEST_SKIP(); - std::string OrcRuntimePath = getOrcRuntimePath(); - std::string ExecutorPath = getExecutorPath(); - - if (!llvm::sys::fs::exists(OrcRuntimePath) || - !llvm::sys::fs::exists(ExecutorPath)) - GTEST_SKIP(); - auto io_ctx = std::make_shared<IOContext>(); ASSERT_TRUE(io_ctx->initializeTempFiles()); - std::unique_ptr<Interpreter> Interp = + OutOfProcessInterpreterInfo Info = createInterpreterWithRemoteExecution(io_ctx); + Interpreter *Interp = Info.Interpreter.get(); ASSERT_TRUE(Interp); + std::string ExecutorPath = getExecutorPath(); + if (!llvm::sys::fs::exists(Info.OrcRuntimePath) || + !llvm::sys::fs::exists(ExecutorPath)) + GTEST_SKIP(); + using PTU = PartialTranslationUnit; PTU &R1(cantFail(Interp->Parse("void g(); void g() {}"))); EXPECT_EQ(2U, DeclsSize(R1.TUPart)); @@ -196,8 +189,31 @@ TEST_F(InterpreterTestBase, SanityWithRemoteExecution) { std::string captured_stdout = io_ctx->readStdoutContent(); std::string captured_stderr = io_ctx->readStderrContent(); - EXPECT_TRUE(captured_stdout.find("CustomizeFork executed") != - std::string::npos); + EXPECT_NE(std::string::npos, captured_stdout.find("CustomizeFork executed")); +} + +TEST_F(InterpreterTestBase, FindRuntimeInterface) { + if (!HostSupportsJIT()) + GTEST_SKIP(); + + // make a fresh io context for this test + auto io_ctx = std::make_shared<IOContext>(); + ASSERT_TRUE(io_ctx->initializeTempFiles()); + + OutOfProcessInterpreterInfo I = createInterpreterWithRemoteExecution(io_ctx); + ASSERT_TRUE(I.Interpreter); + + // FIXME: Not yet supported. + // cantFail(I->Parse("int a = 1; a")); + // cantFail(I->Parse("int b = 2; b")); + // cantFail(I->Parse("int c = 3; c")); + + // // Make sure no clang::Value logic is attached by the Interpreter. + // Value V1; + // llvm::cantFail(I->ParseAndExecute("int x = 42;")); + // llvm::cantFail(I->ParseAndExecute("x", &V1)); + // EXPECT_FALSE(V1.isValid()); + // EXPECT_FALSE(V1.hasValue()); } -} // end anonymous namespace \ No newline at end of file +} // end anonymous namespace _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
