1//===--- IncrementalAction.h - Incremental Frontend Action -*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "IncrementalAction.h"
10
11#include "clang/AST/ASTConsumer.h"
12#include "clang/CodeGen/CodeGenAction.h"
13#include "clang/CodeGen/ModuleBuilder.h"
14#include "clang/Frontend/CompilerInstance.h"
15#include "clang/Frontend/FrontendOptions.h"
16#include "clang/FrontendTool/Utils.h"
17#include "clang/Interpreter/Interpreter.h"
18#include "clang/Lex/PreprocessorOptions.h"
19#include "clang/Sema/Sema.h"
20#include "llvm/IR/Module.h"
21#include "llvm/Support/Error.h"
22#include "llvm/Support/ErrorHandling.h"
23
24namespace clang {
25IncrementalAction::IncrementalAction(CompilerInstance &Instance,
26 llvm::LLVMContext &LLVMCtx,
27 llvm::Error &Err, Interpreter &I,
28 std::unique_ptr<ASTConsumer> Consumer)
29 : WrapperFrontendAction([&]() {
30 llvm::ErrorAsOutParameter EAO(&Err);
31 std::unique_ptr<FrontendAction> Act;
32 switch (Instance.getFrontendOpts().ProgramAction) {
33 default:
34 Err = llvm::createStringError(
35 EC: std::errc::state_not_recoverable,
36 Fmt: "Driver initialization failed. "
37 "Incremental mode for action %d is not supported",
38 Vals: Instance.getFrontendOpts().ProgramAction);
39 return Act;
40 case frontend::ASTDump:
41 case frontend::ASTPrint:
42 case frontend::ParseSyntaxOnly:
43 Act = CreateFrontendAction(CI&: Instance);
44 break;
45 case frontend::PluginAction:
46 case frontend::EmitAssembly:
47 case frontend::EmitBC:
48 case frontend::EmitObj:
49 case frontend::PrintPreprocessedInput:
50 case frontend::EmitLLVM:
51 case frontend::EmitLLVMOnly:
52 Act.reset(p: new EmitLLVMOnlyAction(&LLVMCtx));
53 break;
54 }
55 return Act;
56 }()),
57 Interp(I), CI(Instance), Consumer(std::move(Consumer)) {}
58
59std::unique_ptr<ASTConsumer>
60IncrementalAction::CreateASTConsumer(CompilerInstance & /*CI*/,
61 StringRef InFile) {
62 std::unique_ptr<ASTConsumer> C =
63 WrapperFrontendAction::CreateASTConsumer(CI&: this->CI, InFile);
64
65 if (Consumer) {
66 std::vector<std::unique_ptr<ASTConsumer>> Cs;
67 Cs.push_back(x: std::move(Consumer));
68 Cs.push_back(x: std::move(C));
69 return std::make_unique<MultiplexConsumer>(args: std::move(Cs));
70 }
71
72 return std::make_unique<InProcessPrintingASTConsumer>(args: std::move(C), args&: Interp);
73}
74
75void IncrementalAction::ExecuteAction() {
76 WrapperFrontendAction::ExecuteAction();
77 getCompilerInstance().getSema().CurContext = nullptr;
78}
79
80void IncrementalAction::EndSourceFile() {
81 if (IsTerminating && getWrapped())
82 WrapperFrontendAction::EndSourceFile();
83}
84
85void IncrementalAction::FinalizeAction() {
86 assert(!IsTerminating && "Already finalized!");
87 IsTerminating = true;
88 EndSourceFile();
89}
90
91void IncrementalAction::CacheCodeGenModule() {
92 CachedInCodeGenModule = GenModule();
93}
94
95llvm::Module *IncrementalAction::getCachedCodeGenModule() const {
96 return CachedInCodeGenModule.get();
97}
98
99std::unique_ptr<llvm::Module> IncrementalAction::GenModule() {
100 static unsigned ID = 0;
101 if (CodeGenerator *CG = getCodeGen()) {
102 // Clang's CodeGen is designed to work with a single llvm::Module. In many
103 // cases for convenience various CodeGen parts have a reference to the
104 // llvm::Module (TheModule or Module) which does not change when a new
105 // module is pushed. However, the execution engine wants to take ownership
106 // of the module which does not map well to CodeGen's design. To work this
107 // around we created an empty module to make CodeGen happy. We should make
108 // sure it always stays empty.
109 assert(((!CachedInCodeGenModule ||
110 !CI.getPreprocessorOpts().Includes.empty() ||
111 !CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) ||
112 (CachedInCodeGenModule->empty() &&
113 CachedInCodeGenModule->global_empty() &&
114 CachedInCodeGenModule->alias_empty() &&
115 CachedInCodeGenModule->ifunc_empty())) &&
116 "CodeGen wrote to a readonly module");
117 std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
118 CG->StartModule(ModuleName: "incr_module_" + std::to_string(val: ID++), C&: M->getContext());
119 return M;
120 }
121 return nullptr;
122}
123
124CodeGenerator *IncrementalAction::getCodeGen() const {
125 FrontendAction *WrappedAct = getWrapped();
126 if (!WrappedAct || !WrappedAct->hasIRSupport())
127 return nullptr;
128 return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
129}
130
131InProcessPrintingASTConsumer::InProcessPrintingASTConsumer(
132 std::unique_ptr<ASTConsumer> C, Interpreter &I)
133 : MultiplexConsumer(std::move(C)), Interp(I) {}
134
135bool InProcessPrintingASTConsumer::HandleTopLevelDecl(DeclGroupRef DGR) {
136 if (DGR.isNull())
137 return true;
138
139 CompilerInstance *CI = Interp.getCompilerInstance();
140 DiagnosticsEngine &Diags = CI->getDiagnostics();
141 if (Diags.hasErrorOccurred())
142 return true;
143
144 for (Decl *D : DGR)
145 if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(Val: D))
146 if (TLSD && TLSD->isSemiMissing()) {
147 auto ExprOrErr = Interp.convertExprToValue(E: cast<Expr>(Val: TLSD->getStmt()));
148 if (llvm::Error E = ExprOrErr.takeError()) {
149 llvm::logAllUnhandledErrors(E: std::move(E), OS&: llvm::errs(),
150 ErrorBanner: "Value printing failed: ");
151 return false; // abort parsing
152 }
153 TLSD->setStmt(*ExprOrErr);
154 }
155
156 return MultiplexConsumer::HandleTopLevelDecl(D: DGR);
157}
158
159} // namespace clang
160