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::EmitLLVMOnly:
51 Act.reset(p: new EmitLLVMOnlyAction(&LLVMCtx));
52 break;
53 }
54 return Act;
55 }()),
56 Interp(I), CI(Instance), Consumer(std::move(Consumer)) {}
57
58std::unique_ptr<ASTConsumer>
59IncrementalAction::CreateASTConsumer(CompilerInstance & /*CI*/,
60 StringRef InFile) {
61 std::unique_ptr<ASTConsumer> C =
62 WrapperFrontendAction::CreateASTConsumer(CI&: this->CI, InFile);
63
64 if (Consumer) {
65 std::vector<std::unique_ptr<ASTConsumer>> Cs;
66 Cs.push_back(x: std::move(Consumer));
67 Cs.push_back(x: std::move(C));
68 return std::make_unique<MultiplexConsumer>(args: std::move(Cs));
69 }
70
71 return std::make_unique<InProcessPrintingASTConsumer>(args: std::move(C), args&: Interp);
72}
73
74void IncrementalAction::ExecuteAction() {
75 WrapperFrontendAction::ExecuteAction();
76 getCompilerInstance().getSema().CurContext = nullptr;
77}
78
79void IncrementalAction::EndSourceFile() {
80 if (IsTerminating && getWrapped())
81 WrapperFrontendAction::EndSourceFile();
82}
83
84void IncrementalAction::FinalizeAction() {
85 assert(!IsTerminating && "Already finalized!");
86 IsTerminating = true;
87 EndSourceFile();
88}
89
90void IncrementalAction::CacheCodeGenModule() {
91 CachedInCodeGenModule = GenModule();
92}
93
94llvm::Module *IncrementalAction::getCachedCodeGenModule() const {
95 return CachedInCodeGenModule.get();
96}
97
98std::unique_ptr<llvm::Module> IncrementalAction::GenModule() {
99 static unsigned ID = 0;
100 if (CodeGenerator *CG = getCodeGen()) {
101 // Clang's CodeGen is designed to work with a single llvm::Module. In many
102 // cases for convenience various CodeGen parts have a reference to the
103 // llvm::Module (TheModule or Module) which does not change when a new
104 // module is pushed. However, the execution engine wants to take ownership
105 // of the module which does not map well to CodeGen's design. To work this
106 // around we created an empty module to make CodeGen happy. We should make
107 // sure it always stays empty.
108 assert(((!CachedInCodeGenModule ||
109 !CI.getPreprocessorOpts().Includes.empty() ||
110 !CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) ||
111 (CachedInCodeGenModule->empty() &&
112 CachedInCodeGenModule->global_empty() &&
113 CachedInCodeGenModule->alias_empty() &&
114 CachedInCodeGenModule->ifunc_empty())) &&
115 "CodeGen wrote to a readonly module");
116 std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
117 CG->StartModule(ModuleName: "incr_module_" + std::to_string(val: ID++), C&: M->getContext());
118 return M;
119 }
120 return nullptr;
121}
122
123CodeGenerator *IncrementalAction::getCodeGen() const {
124 FrontendAction *WrappedAct = getWrapped();
125 if (!WrappedAct || !WrappedAct->hasIRSupport())
126 return nullptr;
127 return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
128}
129
130InProcessPrintingASTConsumer::InProcessPrintingASTConsumer(
131 std::unique_ptr<ASTConsumer> C, Interpreter &I)
132 : MultiplexConsumer(std::move(C)), Interp(I) {}
133
134bool InProcessPrintingASTConsumer::HandleTopLevelDecl(DeclGroupRef DGR) {
135 if (DGR.isNull())
136 return true;
137
138 CompilerInstance *CI = Interp.getCompilerInstance();
139 DiagnosticsEngine &Diags = CI->getDiagnostics();
140 if (Diags.hasErrorOccurred())
141 return true;
142
143 for (Decl *D : DGR)
144 if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(Val: D))
145 if (TLSD && TLSD->isSemiMissing()) {
146 auto ExprOrErr = Interp.convertExprToValue(E: cast<Expr>(Val: TLSD->getStmt()));
147 if (llvm::Error E = ExprOrErr.takeError()) {
148 llvm::logAllUnhandledErrors(E: std::move(E), OS&: llvm::errs(),
149 ErrorBanner: "Value printing failed: ");
150 return false; // abort parsing
151 }
152 TLSD->setStmt(*ExprOrErr);
153 }
154
155 return MultiplexConsumer::HandleTopLevelDecl(D: DGR);
156}
157
158} // namespace clang
159