diff options
Diffstat (limited to 'clang/lib/Interpreter/Interpreter.cpp')
| -rw-r--r-- | clang/lib/Interpreter/Interpreter.cpp | 352 |
1 files changed, 153 insertions, 199 deletions
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 5e5ae81b9ba4..043e0c1e5754 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "DeviceOffload.h" +#include "IncrementalAction.h" #include "IncrementalExecutor.h" #include "IncrementalParser.h" #include "InterpreterUtils.h" @@ -28,7 +29,6 @@ #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CodeGenAction.h" -#include "clang/CodeGen/ModuleBuilder.h" #include "clang/CodeGen/ObjectFilePCHContainerWriter.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" @@ -248,124 +248,11 @@ IncrementalCompilerBuilder::CreateCudaHost() { return IncrementalCompilerBuilder::createCuda(false); } -class InProcessPrintingASTConsumer final : public MultiplexConsumer { - Interpreter &Interp; - -public: - InProcessPrintingASTConsumer(std::unique_ptr<ASTConsumer> C, Interpreter &I) - : MultiplexConsumer(std::move(C)), Interp(I) {} - bool HandleTopLevelDecl(DeclGroupRef DGR) override final { - if (DGR.isNull()) - return true; - - for (Decl *D : DGR) - if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(D)) - if (TLSD && TLSD->isSemiMissing()) { - auto ExprOrErr = - Interp.convertExprToValue(cast<Expr>(TLSD->getStmt())); - if (llvm::Error E = ExprOrErr.takeError()) { - llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), - "Value printing failed: "); - return false; // abort parsing - } - TLSD->setStmt(*ExprOrErr); - } - - return MultiplexConsumer::HandleTopLevelDecl(DGR); - } -}; - -/// A custom action enabling the incremental processing functionality. -/// -/// The usual \p FrontendAction expects one call to ExecuteAction and once it -/// sees a call to \p EndSourceFile it deletes some of the important objects -/// such as \p Preprocessor and \p Sema assuming no further input will come. -/// -/// \p IncrementalAction ensures it keep its underlying action's objects alive -/// as long as the \p IncrementalParser needs them. -/// -class IncrementalAction : public WrapperFrontendAction { -private: - bool IsTerminating = false; - Interpreter &Interp; - std::unique_ptr<ASTConsumer> Consumer; - -public: - IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx, - llvm::Error &Err, Interpreter &I, - std::unique_ptr<ASTConsumer> Consumer = nullptr) - : WrapperFrontendAction([&]() { - llvm::ErrorAsOutParameter EAO(&Err); - std::unique_ptr<FrontendAction> Act; - switch (CI.getFrontendOpts().ProgramAction) { - default: - Err = llvm::createStringError( - std::errc::state_not_recoverable, - "Driver initialization failed. " - "Incremental mode for action %d is not supported", - CI.getFrontendOpts().ProgramAction); - return Act; - case frontend::ASTDump: - case frontend::ASTPrint: - case frontend::ParseSyntaxOnly: - Act = CreateFrontendAction(CI); - break; - case frontend::PluginAction: - case frontend::EmitAssembly: - case frontend::EmitBC: - case frontend::EmitObj: - case frontend::PrintPreprocessedInput: - case frontend::EmitLLVMOnly: - Act.reset(new EmitLLVMOnlyAction(&LLVMCtx)); - break; - } - return Act; - }()), - Interp(I), Consumer(std::move(Consumer)) {} - FrontendAction *getWrapped() const { return WrappedAction.get(); } - TranslationUnitKind getTranslationUnitKind() override { - return TU_Incremental; - } - - std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) override { - std::unique_ptr<ASTConsumer> C = - WrapperFrontendAction::CreateASTConsumer(CI, InFile); - - if (Consumer) { - std::vector<std::unique_ptr<ASTConsumer>> Cs; - Cs.push_back(std::move(Consumer)); - Cs.push_back(std::move(C)); - return std::make_unique<MultiplexConsumer>(std::move(Cs)); - } - - return std::make_unique<InProcessPrintingASTConsumer>(std::move(C), Interp); - } - - void ExecuteAction() override { - WrapperFrontendAction::ExecuteAction(); - getCompilerInstance().getSema().CurContext = nullptr; - } - - // Do not terminate after processing the input. This allows us to keep various - // clang objects alive and to incrementally grow the current TU. - void EndSourceFile() override { - // The WrappedAction can be nullptr if we issued an error in the ctor. - if (IsTerminating && getWrapped()) - WrapperFrontendAction::EndSourceFile(); - } - - void FinalizeAction() { - assert(!IsTerminating && "Already finalized!"); - IsTerminating = true; - EndSourceFile(); - } -}; - Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance, llvm::Error &ErrOut, std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder, - std::unique_ptr<clang::ASTConsumer> Consumer) + std::unique_ptr<clang::ASTConsumer> Consumer, + JITConfig Config) : JITBuilder(std::move(JITBuilder)) { CI = std::move(Instance); llvm::ErrorAsOutParameter EAO(&ErrOut); @@ -381,31 +268,32 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance, return; CI->ExecuteAction(*Act); - IncrParser = std::make_unique<IncrementalParser>(*CI, ErrOut); + IncrParser = + std::make_unique<IncrementalParser>(*CI, Act.get(), ErrOut, PTUs); if (ErrOut) return; - if (getCodeGen()) { - CachedInCodeGenModule = GenModule(); + if (Act->getCodeGen()) { + Act->CacheCodeGenModule(); // The initial PTU is filled by `-include` or by CUDA includes // automatically. if (!CI->getPreprocessorOpts().Includes.empty()) { // We can't really directly pass the CachedInCodeGenModule to the Jit // because it will steal it, causing dangling references as explained in // Interpreter::Execute - auto M = llvm::CloneModule(*CachedInCodeGenModule); + auto M = llvm::CloneModule(*Act->getCachedCodeGenModule()); ASTContext &C = CI->getASTContext(); - RegisterPTU(C.getTranslationUnitDecl(), std::move(M)); + IncrParser->RegisterPTU(C.getTranslationUnitDecl(), std::move(M)); } - if (llvm::Error Err = CreateExecutor()) { + if (llvm::Error Err = CreateExecutor(Config)) { ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); return; } } // Not all frontends support code-generation, e.g. ast-dump actions don't - if (getCodeGen()) { + if (Act->getCodeGen()) { // Process the PTUs that came from initialization. For example -include will // give us a header that's processed at initialization of the preprocessor. for (PartialTranslationUnit &PTU : PTUs) @@ -460,20 +348,116 @@ 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) { + std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC; + uint32_t childPid = -1; + if (!Config.OOPExecutor.empty()) { + // Launch an out-of-process executor locally in a child process. + auto ResultOrErr = IncrementalExecutor::launchExecutor( + Config.OOPExecutor, Config.UseSharedMemory, Config.SlabAllocateSize); + if (!ResultOrErr) + return ResultOrErr.takeError(); + childPid = ResultOrErr->second; + auto EPCOrErr = std::move(ResultOrErr->first); + EPC = std::move(EPCOrErr); + } else if (Config.OOPExecutorConnect != "") { +#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS + auto EPCOrErr = IncrementalExecutor::connectTCPSocket( + Config.OOPExecutorConnect, Config.UseSharedMemory, + Config.SlabAllocateSize); + if (!EPCOrErr) + return EPCOrErr.takeError(); + EPC = std::move(*EPCOrErr); +#else + return llvm::make_error<llvm::StringError>( + "Out-of-process JIT over TCP is not supported on this platform", + std::error_code()); +#endif + } + + std::unique_ptr<llvm::orc::LLJITBuilder> JB; + if (EPC) { + auto JBOrErr = clang::Interpreter::createLLJITBuilder( + std::move(EPC), Config.OrcRuntimePath); + if (!JBOrErr) + return JBOrErr.takeError(); + JB = std::move(*JBOrErr); + } + + return std::make_pair(std::move(JB), childPid); +} + +llvm::Expected<std::string> +Interpreter::getOrcRuntimePath(const driver::ToolChain &TC) { + std::optional<std::string> CompilerRTPath = TC.getCompilerRTPath(); + std::optional<std::string> ResourceDir = TC.getRuntimePath(); + + if (!CompilerRTPath) { + return llvm::make_error<llvm::StringError>("CompilerRT path not found", + std::error_code()); + } + + const std::array<const char *, 3> OrcRTLibNames = { + "liborc_rt.a", "liborc_rt_osx.a", "liborc_rt-x86_64.a"}; + + for (const char *LibName : OrcRTLibNames) { + llvm::SmallString<256> CandidatePath((*CompilerRTPath).c_str()); + llvm::sys::path::append(CandidatePath, LibName); + + if (llvm::sys::fs::exists(CandidatePath)) { + return CandidatePath.str().str(); + } + } + + return llvm::make_error<llvm::StringError>( + llvm::Twine("OrcRuntime library not found in: ") + (*CompilerRTPath), + std::error_code()); +} + llvm::Expected<std::unique_ptr<Interpreter>> -Interpreter::create(std::unique_ptr<CompilerInstance> CI, - std::unique_ptr<llvm::orc::LLJITBuilder> JB) { +Interpreter::create(std::unique_ptr<CompilerInstance> CI, JITConfig Config) { llvm::Error Err = llvm::Error::success(); - auto Interp = std::unique_ptr<Interpreter>( - new Interpreter(std::move(CI), Err, JB ? std::move(JB) : nullptr)); - if (Err) - return std::move(Err); + + std::unique_ptr<llvm::orc::LLJITBuilder> JB; + + 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; + } + } + + auto Interp = std::unique_ptr<Interpreter>(new Interpreter( + std::move(CI), Err, std::move(JB), /*Consumer=*/nullptr, Config)); + if (auto E = std::move(Err)) + return std::move(E); // Add runtime code and set a marker to hide it from user code. Undo will not // go through that. - Err = Interp->ParseAndExecute(Runtimes); - if (Err) - return std::move(Err); + if (auto E = Interp->ParseAndExecute(Runtimes)) + return std::move(E); Interp->markUserCodeStart(); @@ -515,8 +499,8 @@ Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI, Interp->DeviceCI = std::move(DCI); auto DeviceParser = std::make_unique<IncrementalCUDADeviceParser>( - *Interp->DeviceCI, *Interp->getCompilerInstance(), IMVFS, Err, - Interp->PTUs); + *Interp->DeviceCI, *Interp->getCompilerInstance(), + Interp->DeviceAct.get(), IMVFS, Err, Interp->PTUs); if (Err) return std::move(Err); @@ -557,28 +541,10 @@ size_t Interpreter::getEffectivePTUSize() const { return PTUs.size() - InitPTUSize; } -PartialTranslationUnit & -Interpreter::RegisterPTU(TranslationUnitDecl *TU, - std::unique_ptr<llvm::Module> M /*={}*/, - IncrementalAction *Action) { - PTUs.emplace_back(PartialTranslationUnit()); - PartialTranslationUnit &LastPTU = PTUs.back(); - LastPTU.TUPart = TU; - - if (!M) - M = GenModule(Action); - - assert((!getCodeGen(Action) || M) && - "Must have a llvm::Module at this point"); - - LastPTU.TheModule = std::move(M); - LLVM_DEBUG(llvm::dbgs() << "compile-ptu " << PTUs.size() - 1 - << ": [TU=" << LastPTU.TUPart); - if (LastPTU.TheModule) - LLVM_DEBUG(llvm::dbgs() << ", M=" << LastPTU.TheModule.get() << " (" - << LastPTU.TheModule->getName() << ")"); - LLVM_DEBUG(llvm::dbgs() << "]\n"); - return LastPTU; +uint32_t Interpreter::getOutOfProcessExecutorPID() const { + if (IncrExecutor) + return IncrExecutor->getOutOfProcessChildPid(); + return -1; } llvm::Expected<PartialTranslationUnit &> @@ -590,7 +556,7 @@ Interpreter::Parse(llvm::StringRef Code) { if (auto E = DeviceTU.takeError()) return std::move(E); - RegisterPTU(*DeviceTU, nullptr, DeviceAct.get()); + DeviceParser->RegisterPTU(*DeviceTU); llvm::Expected<llvm::StringRef> PTX = DeviceParser->GeneratePTX(); if (!PTX) @@ -614,7 +580,7 @@ Interpreter::Parse(llvm::StringRef Code) { PartialTranslationUnit &LastPTU = PTUs.back(); LastPTU.TUPart = *TuOrErr; - if (std::unique_ptr<llvm::Module> M = GenModule()) + if (std::unique_ptr<llvm::Module> M = Act->GenModule()) LastPTU.TheModule = std::move(M); return LastPTU; @@ -649,17 +615,35 @@ Interpreter::createLLJITBuilder( return std::move(*JB); } -llvm::Error Interpreter::CreateExecutor() { +llvm::Error Interpreter::CreateExecutor(JITConfig Config) { if (IncrExecutor) return llvm::make_error<llvm::StringError>("Operation failed. " "Execution engine exists", std::error_code()); - if (!getCodeGen()) + if (!Act->getCodeGen()) return llvm::make_error<llvm::StringError>("Operation failed. " "No code generator available", std::error_code()); + + const std::string &TT = getCompilerInstance()->getTargetOpts().Triple; + llvm::Triple TargetTriple(TT); + bool IsWindowsTarget = TargetTriple.isOSWindows(); + + if (!IsWindowsTarget && Config.IsOutOfProcess) { + if (!JITBuilder) { + auto ResOrErr = outOfProcessJITBuilder(Config); + if (!ResOrErr) + return ResOrErr.takeError(); + JITBuilder = std::move(ResOrErr->first); + Config.ExecutorPID = ResOrErr->second; + } + if (!JITBuilder) + return llvm::make_error<llvm::StringError>( + "Operation failed. No LLJITBuilder for out-of-process JIT", + std::error_code()); + } + if (!JITBuilder) { - const std::string &TT = getCompilerInstance()->getTargetOpts().Triple; auto JTMB = createJITTargetMachineBuilder(TT); if (!JTMB) return JTMB.takeError(); @@ -670,11 +654,15 @@ llvm::Error Interpreter::CreateExecutor() { } llvm::Error Err = llvm::Error::success(); + + // Fix: Declare Executor as the appropriate unique_ptr type + std::unique_ptr<IncrementalExecutor> Executor; + #ifdef __EMSCRIPTEN__ - auto Executor = std::make_unique<WasmIncrementalExecutor>(*TSCtx); + Executor = std::make_unique<WasmIncrementalExecutor>(*TSCtx); #else - auto Executor = - std::make_unique<IncrementalExecutor>(*TSCtx, *JITBuilder, Err); + Executor = + std::make_unique<IncrementalExecutor>(*TSCtx, *JITBuilder, Config, Err); #endif if (!Err) IncrExecutor = std::move(Executor); @@ -733,7 +721,7 @@ Interpreter::getSymbolAddress(GlobalDecl GD) const { return llvm::make_error<llvm::StringError>("Operation failed. " "No execution engine", std::error_code()); - llvm::StringRef MangledName = getCodeGen()->GetMangledName(GD); + llvm::StringRef MangledName = Act->getCodeGen()->GetMangledName(GD); return getSymbolAddress(MangledName); } @@ -809,38 +797,4 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { return llvm::Error::success(); } - -std::unique_ptr<llvm::Module> -Interpreter::GenModule(IncrementalAction *Action) { - static unsigned ID = 0; - if (CodeGenerator *CG = getCodeGen(Action)) { - // Clang's CodeGen is designed to work with a single llvm::Module. In many - // cases for convenience various CodeGen parts have a reference to the - // llvm::Module (TheModule or Module) which does not change when a new - // module is pushed. However, the execution engine wants to take ownership - // of the module which does not map well to CodeGen's design. To work this - // around we created an empty module to make CodeGen happy. We should make - // sure it always stays empty. - assert(((!CachedInCodeGenModule || - !getCompilerInstance()->getPreprocessorOpts().Includes.empty()) || - ((CachedInCodeGenModule->empty() && - CachedInCodeGenModule->global_empty() && - CachedInCodeGenModule->alias_empty() && - CachedInCodeGenModule->ifunc_empty()))) && - "CodeGen wrote to a readonly module"); - std::unique_ptr<llvm::Module> M(CG->ReleaseModule()); - CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); - return M; - } - return nullptr; -} - -CodeGenerator *Interpreter::getCodeGen(IncrementalAction *Action) const { - if (!Action) - Action = Act.get(); - FrontendAction *WrappedAct = Action->getWrapped(); - if (!WrappedAct->hasIRSupport()) - return nullptr; - return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator(); -} } // end namespace clang |
