/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Interpreter/IncrementalParser.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--------- IncrementalParser.cpp - Incremental Compilation -----------===// |
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 | | // This file implements the class which performs incremental code compilation. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "IncrementalParser.h" |
14 | | |
15 | | #include "clang/AST/DeclContextInternals.h" |
16 | | #include "clang/CodeGen/BackendUtil.h" |
17 | | #include "clang/CodeGen/CodeGenAction.h" |
18 | | #include "clang/CodeGen/ModuleBuilder.h" |
19 | | #include "clang/Frontend/CompilerInstance.h" |
20 | | #include "clang/Frontend/FrontendAction.h" |
21 | | #include "clang/FrontendTool/Utils.h" |
22 | | #include "clang/Interpreter/Interpreter.h" |
23 | | #include "clang/Parse/Parser.h" |
24 | | #include "clang/Sema/Sema.h" |
25 | | #include "llvm/Option/ArgList.h" |
26 | | #include "llvm/Support/CrashRecoveryContext.h" |
27 | | #include "llvm/Support/Error.h" |
28 | | #include "llvm/Support/Timer.h" |
29 | | |
30 | | #include <sstream> |
31 | | |
32 | | namespace clang { |
33 | | |
34 | | class IncrementalASTConsumer final : public ASTConsumer { |
35 | | Interpreter &Interp; |
36 | | std::unique_ptr<ASTConsumer> Consumer; |
37 | | |
38 | | public: |
39 | | IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr<ASTConsumer> C) |
40 | 50 | : Interp(InterpRef), Consumer(std::move(C)) {} |
41 | | |
42 | 583 | bool HandleTopLevelDecl(DeclGroupRef DGR) override final { |
43 | 583 | if (DGR.isNull()) |
44 | 0 | return true; |
45 | 583 | if (!Consumer) |
46 | 0 | return true; |
47 | | |
48 | 583 | for (Decl *D : DGR) |
49 | 588 | if (auto *TSD = llvm::dyn_cast<TopLevelStmtDecl>(D); |
50 | 588 | TSD && TSD->isSemiMissing()27 ) |
51 | 9 | TSD->setStmt(Interp.SynthesizeExpr(cast<Expr>(TSD->getStmt()))); |
52 | | |
53 | 583 | return Consumer->HandleTopLevelDecl(DGR); |
54 | 583 | } |
55 | 412 | void HandleTranslationUnit(ASTContext &Ctx) override final { |
56 | 412 | Consumer->HandleTranslationUnit(Ctx); |
57 | 412 | } |
58 | 0 | void HandleInlineFunctionDefinition(FunctionDecl *D) override final { |
59 | 0 | Consumer->HandleInlineFunctionDefinition(D); |
60 | 0 | } |
61 | 0 | void HandleInterestingDecl(DeclGroupRef D) override final { |
62 | 0 | Consumer->HandleInterestingDecl(D); |
63 | 0 | } |
64 | 0 | void HandleTagDeclDefinition(TagDecl *D) override final { |
65 | 0 | Consumer->HandleTagDeclDefinition(D); |
66 | 0 | } |
67 | 0 | void HandleTagDeclRequiredDefinition(const TagDecl *D) override final { |
68 | 0 | Consumer->HandleTagDeclRequiredDefinition(D); |
69 | 0 | } |
70 | 0 | void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final { |
71 | 0 | Consumer->HandleCXXImplicitFunctionInstantiation(D); |
72 | 0 | } |
73 | 0 | void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final { |
74 | 0 | Consumer->HandleTopLevelDeclInObjCContainer(D); |
75 | 0 | } |
76 | 0 | void HandleImplicitImportDecl(ImportDecl *D) override final { |
77 | 0 | Consumer->HandleImplicitImportDecl(D); |
78 | 0 | } |
79 | 0 | void CompleteTentativeDefinition(VarDecl *D) override final { |
80 | 0 | Consumer->CompleteTentativeDefinition(D); |
81 | 0 | } |
82 | 0 | void CompleteExternalDeclaration(VarDecl *D) override final { |
83 | 0 | Consumer->CompleteExternalDeclaration(D); |
84 | 0 | } |
85 | 0 | void AssignInheritanceModel(CXXRecordDecl *RD) override final { |
86 | 0 | Consumer->AssignInheritanceModel(RD); |
87 | 0 | } |
88 | 0 | void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final { |
89 | 0 | Consumer->HandleCXXStaticMemberVarInstantiation(D); |
90 | 0 | } |
91 | 0 | void HandleVTable(CXXRecordDecl *RD) override final { |
92 | 0 | Consumer->HandleVTable(RD); |
93 | 0 | } |
94 | 0 | ASTMutationListener *GetASTMutationListener() override final { |
95 | 0 | return Consumer->GetASTMutationListener(); |
96 | 0 | } |
97 | 0 | ASTDeserializationListener *GetASTDeserializationListener() override final { |
98 | 0 | return Consumer->GetASTDeserializationListener(); |
99 | 0 | } |
100 | 0 | void PrintStats() override final { Consumer->PrintStats(); } |
101 | 0 | bool shouldSkipFunctionBody(Decl *D) override final { |
102 | 0 | return Consumer->shouldSkipFunctionBody(D); |
103 | 0 | } |
104 | 0 | static bool classof(const clang::ASTConsumer *) { return true; } |
105 | | }; |
106 | | |
107 | | /// A custom action enabling the incremental processing functionality. |
108 | | /// |
109 | | /// The usual \p FrontendAction expects one call to ExecuteAction and once it |
110 | | /// sees a call to \p EndSourceFile it deletes some of the important objects |
111 | | /// such as \p Preprocessor and \p Sema assuming no further input will come. |
112 | | /// |
113 | | /// \p IncrementalAction ensures it keep its underlying action's objects alive |
114 | | /// as long as the \p IncrementalParser needs them. |
115 | | /// |
116 | | class IncrementalAction : public WrapperFrontendAction { |
117 | | private: |
118 | | bool IsTerminating = false; |
119 | | |
120 | | public: |
121 | | IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx, |
122 | | llvm::Error &Err) |
123 | 50 | : WrapperFrontendAction([&]() { |
124 | 50 | llvm::ErrorAsOutParameter EAO(&Err); |
125 | 50 | std::unique_ptr<FrontendAction> Act; |
126 | 50 | switch (CI.getFrontendOpts().ProgramAction) { |
127 | 0 | default: |
128 | 0 | Err = llvm::createStringError( |
129 | 0 | std::errc::state_not_recoverable, |
130 | 0 | "Driver initialization failed. " |
131 | 0 | "Incremental mode for action %d is not supported", |
132 | 0 | CI.getFrontendOpts().ProgramAction); |
133 | 0 | return Act; |
134 | 1 | case frontend::ASTDump: |
135 | 1 | [[fallthrough]]; |
136 | 1 | case frontend::ASTPrint: |
137 | 1 | [[fallthrough]]; |
138 | 1 | case frontend::ParseSyntaxOnly: |
139 | 1 | Act = CreateFrontendAction(CI); |
140 | 1 | break; |
141 | 0 | case frontend::PluginAction: |
142 | 0 | [[fallthrough]]; |
143 | 7 | case frontend::EmitAssembly: |
144 | 7 | [[fallthrough]]; |
145 | 8 | case frontend::EmitBC: |
146 | 8 | [[fallthrough]]; |
147 | 38 | case frontend::EmitObj: |
148 | 38 | [[fallthrough]]; |
149 | 39 | case frontend::PrintPreprocessedInput: |
150 | 39 | [[fallthrough]]; |
151 | 49 | case frontend::EmitLLVMOnly: |
152 | 49 | Act.reset(new EmitLLVMOnlyAction(&LLVMCtx)); |
153 | 49 | break; |
154 | 50 | } |
155 | 50 | return Act; |
156 | 50 | }()) {} |
157 | 450 | FrontendAction *getWrapped() const { return WrappedAction.get(); } |
158 | 100 | TranslationUnitKind getTranslationUnitKind() override { |
159 | 100 | return TU_Incremental; |
160 | 100 | } |
161 | | |
162 | 50 | void ExecuteAction() override { |
163 | 50 | CompilerInstance &CI = getCompilerInstance(); |
164 | 50 | assert(CI.hasPreprocessor() && "No PP!"); |
165 | | |
166 | | // Use a code completion consumer? |
167 | 50 | CodeCompleteConsumer *CompletionConsumer = nullptr; |
168 | 50 | if (CI.hasCodeCompletionConsumer()) |
169 | 0 | CompletionConsumer = &CI.getCodeCompletionConsumer(); |
170 | | |
171 | 50 | Preprocessor &PP = CI.getPreprocessor(); |
172 | 50 | PP.EnterMainSourceFile(); |
173 | | |
174 | 50 | if (!CI.hasSema()) |
175 | 50 | CI.createSema(getTranslationUnitKind(), CompletionConsumer); |
176 | 50 | } |
177 | | |
178 | | // Do not terminate after processing the input. This allows us to keep various |
179 | | // clang objects alive and to incrementally grow the current TU. |
180 | 86 | void EndSourceFile() override { |
181 | | // The WrappedAction can be nullptr if we issued an error in the ctor. |
182 | 86 | if (IsTerminating && getWrapped()36 ) |
183 | 36 | WrapperFrontendAction::EndSourceFile(); |
184 | 86 | } |
185 | | |
186 | 36 | void FinalizeAction() { |
187 | 36 | assert(!IsTerminating && "Already finalized!"); |
188 | 36 | IsTerminating = true; |
189 | 36 | EndSourceFile(); |
190 | 36 | } |
191 | | }; |
192 | | |
193 | 414 | CodeGenerator *IncrementalParser::getCodeGen() const { |
194 | 414 | FrontendAction *WrappedAct = Act->getWrapped(); |
195 | 414 | if (!WrappedAct->hasIRSupport()) |
196 | 16 | return nullptr; |
197 | 398 | return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator(); |
198 | 414 | } |
199 | | |
200 | 0 | IncrementalParser::IncrementalParser() {} |
201 | | |
202 | | IncrementalParser::IncrementalParser(Interpreter &Interp, |
203 | | std::unique_ptr<CompilerInstance> Instance, |
204 | | llvm::LLVMContext &LLVMCtx, |
205 | | llvm::Error &Err) |
206 | 50 | : CI(std::move(Instance)) { |
207 | 50 | llvm::ErrorAsOutParameter EAO(&Err); |
208 | 50 | Act = std::make_unique<IncrementalAction>(*CI, LLVMCtx, Err); |
209 | 50 | if (Err) |
210 | 0 | return; |
211 | 50 | CI->ExecuteAction(*Act); |
212 | 50 | std::unique_ptr<ASTConsumer> IncrConsumer = |
213 | 50 | std::make_unique<IncrementalASTConsumer>(Interp, CI->takeASTConsumer()); |
214 | 50 | CI->setASTConsumer(std::move(IncrConsumer)); |
215 | 50 | Consumer = &CI->getASTConsumer(); |
216 | 50 | P.reset( |
217 | 50 | new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false)); |
218 | 50 | P->Initialize(); |
219 | | |
220 | | // An initial PTU is needed as CUDA includes some headers automatically |
221 | 50 | auto PTU = ParseOrWrapTopLevelDecl(); |
222 | 50 | if (auto E = PTU.takeError()) { |
223 | 0 | consumeError(std::move(E)); // FIXME |
224 | 0 | return; // PTU.takeError(); |
225 | 0 | } |
226 | | |
227 | 50 | if (CodeGenerator *CG = getCodeGen()) { |
228 | 49 | std::unique_ptr<llvm::Module> M(CG->ReleaseModule()); |
229 | 49 | CG->StartModule("incr_module_" + std::to_string(PTUs.size()), |
230 | 49 | M->getContext()); |
231 | 49 | PTU->TheModule = std::move(M); |
232 | 49 | assert(PTU->TheModule && "Failed to create initial PTU"); |
233 | 49 | } |
234 | 50 | } |
235 | | |
236 | 36 | IncrementalParser::~IncrementalParser() { |
237 | 36 | P.reset(); |
238 | 36 | Act->FinalizeAction(); |
239 | 36 | } |
240 | | |
241 | | llvm::Expected<PartialTranslationUnit &> |
242 | 416 | IncrementalParser::ParseOrWrapTopLevelDecl() { |
243 | | // Recover resources if we crash before exiting this method. |
244 | 416 | Sema &S = CI->getSema(); |
245 | 416 | llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S); |
246 | 416 | Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true); |
247 | 416 | Sema::LocalEagerInstantiationScope LocalInstantiations(S); |
248 | | |
249 | 416 | PTUs.emplace_back(PartialTranslationUnit()); |
250 | 416 | PartialTranslationUnit &LastPTU = PTUs.back(); |
251 | | // Add a new PTU. |
252 | 416 | ASTContext &C = S.getASTContext(); |
253 | 416 | C.addTranslationUnitDecl(); |
254 | 416 | LastPTU.TUPart = C.getTranslationUnitDecl(); |
255 | | |
256 | | // Skip previous eof due to last incremental input. |
257 | 416 | if (P->getCurToken().is(tok::annot_repl_input_end)) { |
258 | 416 | P->ConsumeAnyToken(); |
259 | | // FIXME: Clang does not call ExitScope on finalizing the regular TU, we |
260 | | // might want to do that around HandleEndOfTranslationUnit. |
261 | 416 | P->ExitScope(); |
262 | 416 | S.CurContext = nullptr; |
263 | | // Start a new PTU. |
264 | 416 | P->EnterScope(Scope::DeclScope); |
265 | 416 | S.ActOnTranslationUnitScope(P->getCurScope()); |
266 | 416 | } |
267 | | |
268 | 416 | Parser::DeclGroupPtrTy ADecl; |
269 | 416 | Sema::ModuleImportState ImportState; |
270 | 1.00k | for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; |
271 | 584 | AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) { |
272 | 584 | if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())582 ) |
273 | 0 | return llvm::make_error<llvm::StringError>("Parsing failed. " |
274 | 0 | "The consumer rejected a decl", |
275 | 0 | std::error_code()); |
276 | 584 | } |
277 | | |
278 | 416 | DiagnosticsEngine &Diags = getCI()->getDiagnostics(); |
279 | 416 | if (Diags.hasErrorOccurred()) { |
280 | 4 | PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(), |
281 | 4 | nullptr}; |
282 | 4 | CleanUpPTU(MostRecentPTU); |
283 | | |
284 | 4 | Diags.Reset(/*soft=*/true); |
285 | 4 | Diags.getClient()->clear(); |
286 | 4 | return llvm::make_error<llvm::StringError>("Parsing failed.", |
287 | 4 | std::error_code()); |
288 | 4 | } |
289 | | |
290 | | // Process any TopLevelDecls generated by #pragma weak. |
291 | 412 | for (Decl *D : S.WeakTopLevelDecls()) { |
292 | 0 | DeclGroupRef DGR(D); |
293 | 0 | Consumer->HandleTopLevelDecl(DGR); |
294 | 0 | } |
295 | | |
296 | 412 | LocalInstantiations.perform(); |
297 | 412 | GlobalInstantiations.perform(); |
298 | | |
299 | 412 | Consumer->HandleTranslationUnit(C); |
300 | | |
301 | 412 | return LastPTU; |
302 | 416 | } |
303 | | |
304 | | llvm::Expected<PartialTranslationUnit &> |
305 | 366 | IncrementalParser::Parse(llvm::StringRef input) { |
306 | 366 | Preprocessor &PP = CI->getPreprocessor(); |
307 | 366 | assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?"); |
308 | | |
309 | 366 | std::ostringstream SourceName; |
310 | 366 | SourceName << "input_line_" << InputCount++; |
311 | | |
312 | | // Create an uninitialized memory buffer, copy code in and append "\n" |
313 | 366 | size_t InputSize = input.size(); // don't include trailing 0 |
314 | | // MemBuffer size should *not* include terminating zero |
315 | 366 | std::unique_ptr<llvm::MemoryBuffer> MB( |
316 | 366 | llvm::WritableMemoryBuffer::getNewUninitMemBuffer(InputSize + 1, |
317 | 366 | SourceName.str())); |
318 | 366 | char *MBStart = const_cast<char *>(MB->getBufferStart()); |
319 | 366 | memcpy(MBStart, input.data(), InputSize); |
320 | 366 | MBStart[InputSize] = '\n'; |
321 | | |
322 | 366 | SourceManager &SM = CI->getSourceManager(); |
323 | | |
324 | | // FIXME: Create SourceLocation, which will allow clang to order the overload |
325 | | // candidates for example |
326 | 366 | SourceLocation NewLoc = SM.getLocForStartOfFile(SM.getMainFileID()); |
327 | | |
328 | | // Create FileID for the current buffer. |
329 | 366 | FileID FID = SM.createFileID(std::move(MB), SrcMgr::C_User, /*LoadedID=*/0, |
330 | 366 | /*LoadedOffset=*/0, NewLoc); |
331 | | |
332 | | // NewLoc only used for diags. |
333 | 366 | if (PP.EnterSourceFile(FID, /*DirLookup=*/nullptr, NewLoc)) |
334 | 0 | return llvm::make_error<llvm::StringError>("Parsing failed. " |
335 | 0 | "Cannot enter source file.", |
336 | 0 | std::error_code()); |
337 | | |
338 | 366 | auto PTU = ParseOrWrapTopLevelDecl(); |
339 | 366 | if (!PTU) |
340 | 4 | return PTU.takeError(); |
341 | | |
342 | 362 | if (PP.getLangOpts().DelayedTemplateParsing) { |
343 | | // Microsoft-specific: |
344 | | // Late parsed templates can leave unswallowed "macro"-like tokens. |
345 | | // They will seriously confuse the Parser when entering the next |
346 | | // source file. So lex until we are EOF. |
347 | 0 | Token Tok; |
348 | 0 | do { |
349 | 0 | PP.Lex(Tok); |
350 | 0 | } while (Tok.isNot(tok::annot_repl_input_end)); |
351 | 362 | } else { |
352 | 362 | Token AssertTok; |
353 | 362 | PP.Lex(AssertTok); |
354 | 362 | assert(AssertTok.is(tok::annot_repl_input_end) && |
355 | 362 | "Lexer must be EOF when starting incremental parse!"); |
356 | 362 | } |
357 | | |
358 | 362 | if (std::unique_ptr<llvm::Module> M = GenModule()) |
359 | 347 | PTU->TheModule = std::move(M); |
360 | | |
361 | 362 | return PTU; |
362 | 362 | } |
363 | | |
364 | 362 | std::unique_ptr<llvm::Module> IncrementalParser::GenModule() { |
365 | 362 | static unsigned ID = 0; |
366 | 362 | if (CodeGenerator *CG = getCodeGen()) { |
367 | 347 | std::unique_ptr<llvm::Module> M(CG->ReleaseModule()); |
368 | 347 | CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); |
369 | 347 | return M; |
370 | 347 | } |
371 | 15 | return nullptr; |
372 | 362 | } |
373 | | |
374 | 10 | void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { |
375 | 10 | TranslationUnitDecl *MostRecentTU = PTU.TUPart; |
376 | 10 | TranslationUnitDecl *FirstTU = MostRecentTU->getFirstDecl(); |
377 | 10 | if (StoredDeclsMap *Map = FirstTU->getPrimaryContext()->getLookupPtr()) { |
378 | 171 | for (auto I = Map->begin(); I != Map->end(); ++I161 ) { |
379 | 161 | StoredDeclsList &List = I->second; |
380 | 161 | DeclContextLookupResult R = List.getLookupResult(); |
381 | 271 | for (NamedDecl *D : R) { |
382 | 271 | if (D->getTranslationUnitDecl() == MostRecentTU) { |
383 | 8 | List.remove(D); |
384 | 8 | } |
385 | 271 | } |
386 | 161 | if (List.isNull()) |
387 | 8 | Map->erase(I); |
388 | 161 | } |
389 | 10 | } |
390 | 10 | } |
391 | | |
392 | 2 | llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const { |
393 | 2 | CodeGenerator *CG = getCodeGen(); |
394 | 2 | assert(CG); |
395 | 2 | return CG->GetMangledName(GD); |
396 | 2 | } |
397 | | } // end namespace clang |