Path: blob/main/contrib/llvm-project/clang/lib/Interpreter/IncrementalParser.cpp
35234 views
//===--------- IncrementalParser.cpp - Incremental Compilation -----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the class which performs incremental code compilation.9//10//===----------------------------------------------------------------------===//1112#include "IncrementalParser.h"1314#include "clang/AST/DeclContextInternals.h"15#include "clang/CodeGen/BackendUtil.h"16#include "clang/CodeGen/CodeGenAction.h"17#include "clang/CodeGen/ModuleBuilder.h"18#include "clang/Frontend/CompilerInstance.h"19#include "clang/Frontend/FrontendAction.h"20#include "clang/FrontendTool/Utils.h"21#include "clang/Interpreter/Interpreter.h"22#include "clang/Parse/Parser.h"23#include "clang/Sema/Sema.h"24#include "llvm/Option/ArgList.h"25#include "llvm/Support/CrashRecoveryContext.h"26#include "llvm/Support/Error.h"27#include "llvm/Support/Timer.h"2829#include <sstream>3031namespace clang {3233class IncrementalASTConsumer final : public ASTConsumer {34Interpreter &Interp;35std::unique_ptr<ASTConsumer> Consumer;3637public:38IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr<ASTConsumer> C)39: Interp(InterpRef), Consumer(std::move(C)) {}4041bool HandleTopLevelDecl(DeclGroupRef DGR) override final {42if (DGR.isNull())43return true;44if (!Consumer)45return true;4647for (Decl *D : DGR)48if (auto *TSD = llvm::dyn_cast<TopLevelStmtDecl>(D);49TSD && TSD->isSemiMissing())50TSD->setStmt(Interp.SynthesizeExpr(cast<Expr>(TSD->getStmt())));5152return Consumer->HandleTopLevelDecl(DGR);53}54void HandleTranslationUnit(ASTContext &Ctx) override final {55Consumer->HandleTranslationUnit(Ctx);56}57void HandleInlineFunctionDefinition(FunctionDecl *D) override final {58Consumer->HandleInlineFunctionDefinition(D);59}60void HandleInterestingDecl(DeclGroupRef D) override final {61Consumer->HandleInterestingDecl(D);62}63void HandleTagDeclDefinition(TagDecl *D) override final {64Consumer->HandleTagDeclDefinition(D);65}66void HandleTagDeclRequiredDefinition(const TagDecl *D) override final {67Consumer->HandleTagDeclRequiredDefinition(D);68}69void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final {70Consumer->HandleCXXImplicitFunctionInstantiation(D);71}72void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final {73Consumer->HandleTopLevelDeclInObjCContainer(D);74}75void HandleImplicitImportDecl(ImportDecl *D) override final {76Consumer->HandleImplicitImportDecl(D);77}78void CompleteTentativeDefinition(VarDecl *D) override final {79Consumer->CompleteTentativeDefinition(D);80}81void CompleteExternalDeclaration(DeclaratorDecl *D) override final {82Consumer->CompleteExternalDeclaration(D);83}84void AssignInheritanceModel(CXXRecordDecl *RD) override final {85Consumer->AssignInheritanceModel(RD);86}87void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final {88Consumer->HandleCXXStaticMemberVarInstantiation(D);89}90void HandleVTable(CXXRecordDecl *RD) override final {91Consumer->HandleVTable(RD);92}93ASTMutationListener *GetASTMutationListener() override final {94return Consumer->GetASTMutationListener();95}96ASTDeserializationListener *GetASTDeserializationListener() override final {97return Consumer->GetASTDeserializationListener();98}99void PrintStats() override final { Consumer->PrintStats(); }100bool shouldSkipFunctionBody(Decl *D) override final {101return Consumer->shouldSkipFunctionBody(D);102}103static bool classof(const clang::ASTConsumer *) { return true; }104};105106/// A custom action enabling the incremental processing functionality.107///108/// The usual \p FrontendAction expects one call to ExecuteAction and once it109/// sees a call to \p EndSourceFile it deletes some of the important objects110/// such as \p Preprocessor and \p Sema assuming no further input will come.111///112/// \p IncrementalAction ensures it keep its underlying action's objects alive113/// as long as the \p IncrementalParser needs them.114///115class IncrementalAction : public WrapperFrontendAction {116private:117bool IsTerminating = false;118119public:120IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,121llvm::Error &Err)122: WrapperFrontendAction([&]() {123llvm::ErrorAsOutParameter EAO(&Err);124std::unique_ptr<FrontendAction> Act;125switch (CI.getFrontendOpts().ProgramAction) {126default:127Err = llvm::createStringError(128std::errc::state_not_recoverable,129"Driver initialization failed. "130"Incremental mode for action %d is not supported",131CI.getFrontendOpts().ProgramAction);132return Act;133case frontend::ASTDump:134[[fallthrough]];135case frontend::ASTPrint:136[[fallthrough]];137case frontend::ParseSyntaxOnly:138Act = CreateFrontendAction(CI);139break;140case frontend::PluginAction:141[[fallthrough]];142case frontend::EmitAssembly:143[[fallthrough]];144case frontend::EmitBC:145[[fallthrough]];146case frontend::EmitObj:147[[fallthrough]];148case frontend::PrintPreprocessedInput:149[[fallthrough]];150case frontend::EmitLLVMOnly:151Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));152break;153}154return Act;155}()) {}156FrontendAction *getWrapped() const { return WrappedAction.get(); }157TranslationUnitKind getTranslationUnitKind() override {158return TU_Incremental;159}160161void ExecuteAction() override {162CompilerInstance &CI = getCompilerInstance();163assert(CI.hasPreprocessor() && "No PP!");164165// Use a code completion consumer?166CodeCompleteConsumer *CompletionConsumer = nullptr;167if (CI.hasCodeCompletionConsumer())168CompletionConsumer = &CI.getCodeCompletionConsumer();169170Preprocessor &PP = CI.getPreprocessor();171PP.EnterMainSourceFile();172173if (!CI.hasSema())174CI.createSema(getTranslationUnitKind(), CompletionConsumer);175}176177// Do not terminate after processing the input. This allows us to keep various178// clang objects alive and to incrementally grow the current TU.179void EndSourceFile() override {180// The WrappedAction can be nullptr if we issued an error in the ctor.181if (IsTerminating && getWrapped())182WrapperFrontendAction::EndSourceFile();183}184185void FinalizeAction() {186assert(!IsTerminating && "Already finalized!");187IsTerminating = true;188EndSourceFile();189}190};191192CodeGenerator *IncrementalParser::getCodeGen() const {193FrontendAction *WrappedAct = Act->getWrapped();194if (!WrappedAct->hasIRSupport())195return nullptr;196return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();197}198199IncrementalParser::IncrementalParser() {}200201IncrementalParser::IncrementalParser(Interpreter &Interp,202std::unique_ptr<CompilerInstance> Instance,203llvm::LLVMContext &LLVMCtx,204llvm::Error &Err)205: CI(std::move(Instance)) {206llvm::ErrorAsOutParameter EAO(&Err);207Act = std::make_unique<IncrementalAction>(*CI, LLVMCtx, Err);208if (Err)209return;210CI->ExecuteAction(*Act);211212if (getCodeGen())213CachedInCodeGenModule = GenModule();214215std::unique_ptr<ASTConsumer> IncrConsumer =216std::make_unique<IncrementalASTConsumer>(Interp, CI->takeASTConsumer());217CI->setASTConsumer(std::move(IncrConsumer));218Consumer = &CI->getASTConsumer();219P.reset(220new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false));221P->Initialize();222223// An initial PTU is needed as CUDA includes some headers automatically224auto PTU = ParseOrWrapTopLevelDecl();225if (auto E = PTU.takeError()) {226consumeError(std::move(E)); // FIXME227return; // PTU.takeError();228}229230if (getCodeGen()) {231PTU->TheModule = GenModule();232assert(PTU->TheModule && "Failed to create initial PTU");233}234}235236IncrementalParser::~IncrementalParser() {237P.reset();238Act->FinalizeAction();239}240241llvm::Expected<PartialTranslationUnit &>242IncrementalParser::ParseOrWrapTopLevelDecl() {243// Recover resources if we crash before exiting this method.244Sema &S = CI->getSema();245llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);246Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true);247Sema::LocalEagerInstantiationScope LocalInstantiations(S);248249PTUs.emplace_back(PartialTranslationUnit());250PartialTranslationUnit &LastPTU = PTUs.back();251// Add a new PTU.252ASTContext &C = S.getASTContext();253C.addTranslationUnitDecl();254LastPTU.TUPart = C.getTranslationUnitDecl();255256// Skip previous eof due to last incremental input.257if (P->getCurToken().is(tok::annot_repl_input_end)) {258P->ConsumeAnyToken();259// FIXME: Clang does not call ExitScope on finalizing the regular TU, we260// might want to do that around HandleEndOfTranslationUnit.261P->ExitScope();262S.CurContext = nullptr;263// Start a new PTU.264P->EnterScope(Scope::DeclScope);265S.ActOnTranslationUnitScope(P->getCurScope());266}267268Parser::DeclGroupPtrTy ADecl;269Sema::ModuleImportState ImportState;270for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF;271AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) {272if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))273return llvm::make_error<llvm::StringError>("Parsing failed. "274"The consumer rejected a decl",275std::error_code());276}277278DiagnosticsEngine &Diags = getCI()->getDiagnostics();279if (Diags.hasErrorOccurred()) {280PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(),281nullptr};282CleanUpPTU(MostRecentPTU);283284Diags.Reset(/*soft=*/true);285Diags.getClient()->clear();286return llvm::make_error<llvm::StringError>("Parsing failed.",287std::error_code());288}289290// Process any TopLevelDecls generated by #pragma weak.291for (Decl *D : S.WeakTopLevelDecls()) {292DeclGroupRef DGR(D);293Consumer->HandleTopLevelDecl(DGR);294}295296LocalInstantiations.perform();297GlobalInstantiations.perform();298299Consumer->HandleTranslationUnit(C);300301return LastPTU;302}303304llvm::Expected<PartialTranslationUnit &>305IncrementalParser::Parse(llvm::StringRef input) {306Preprocessor &PP = CI->getPreprocessor();307assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");308309std::ostringstream SourceName;310SourceName << "input_line_" << InputCount++;311312// Create an uninitialized memory buffer, copy code in and append "\n"313size_t InputSize = input.size(); // don't include trailing 0314// MemBuffer size should *not* include terminating zero315std::unique_ptr<llvm::MemoryBuffer> MB(316llvm::WritableMemoryBuffer::getNewUninitMemBuffer(InputSize + 1,317SourceName.str()));318char *MBStart = const_cast<char *>(MB->getBufferStart());319memcpy(MBStart, input.data(), InputSize);320MBStart[InputSize] = '\n';321322SourceManager &SM = CI->getSourceManager();323324// FIXME: Create SourceLocation, which will allow clang to order the overload325// candidates for example326SourceLocation NewLoc = SM.getLocForStartOfFile(SM.getMainFileID());327328// Create FileID for the current buffer.329FileID FID = SM.createFileID(std::move(MB), SrcMgr::C_User, /*LoadedID=*/0,330/*LoadedOffset=*/0, NewLoc);331332// NewLoc only used for diags.333if (PP.EnterSourceFile(FID, /*DirLookup=*/nullptr, NewLoc))334return llvm::make_error<llvm::StringError>("Parsing failed. "335"Cannot enter source file.",336std::error_code());337338auto PTU = ParseOrWrapTopLevelDecl();339if (!PTU)340return PTU.takeError();341342if (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 next346// source file. So lex until we are EOF.347Token Tok;348do {349PP.Lex(Tok);350} while (Tok.isNot(tok::annot_repl_input_end));351} else {352Token AssertTok;353PP.Lex(AssertTok);354assert(AssertTok.is(tok::annot_repl_input_end) &&355"Lexer must be EOF when starting incremental parse!");356}357358if (std::unique_ptr<llvm::Module> M = GenModule())359PTU->TheModule = std::move(M);360361return PTU;362}363364std::unique_ptr<llvm::Module> IncrementalParser::GenModule() {365static unsigned ID = 0;366if (CodeGenerator *CG = getCodeGen()) {367// Clang's CodeGen is designed to work with a single llvm::Module. In many368// cases for convenience various CodeGen parts have a reference to the369// llvm::Module (TheModule or Module) which does not change when a new370// module is pushed. However, the execution engine wants to take ownership371// of the module which does not map well to CodeGen's design. To work this372// around we created an empty module to make CodeGen happy. We should make373// sure it always stays empty.374assert((!CachedInCodeGenModule ||375(CachedInCodeGenModule->empty() &&376CachedInCodeGenModule->global_empty() &&377CachedInCodeGenModule->alias_empty() &&378CachedInCodeGenModule->ifunc_empty())) &&379"CodeGen wrote to a readonly module");380std::unique_ptr<llvm::Module> M(CG->ReleaseModule());381CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());382return M;383}384return nullptr;385}386387void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {388TranslationUnitDecl *MostRecentTU = PTU.TUPart;389if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) {390for (auto &&[Key, List] : *Map) {391DeclContextLookupResult R = List.getLookupResult();392std::vector<NamedDecl *> NamedDeclsToRemove;393bool RemoveAll = true;394for (NamedDecl *D : R) {395if (D->getTranslationUnitDecl() == MostRecentTU)396NamedDeclsToRemove.push_back(D);397else398RemoveAll = false;399}400if (LLVM_LIKELY(RemoveAll)) {401Map->erase(Key);402} else {403for (NamedDecl *D : NamedDeclsToRemove)404List.remove(D);405}406}407}408409// FIXME: We should de-allocate MostRecentTU410for (Decl *D : MostRecentTU->decls()) {411auto *ND = dyn_cast<NamedDecl>(D);412if (!ND)413continue;414// Check if we need to clean up the IdResolver chain.415if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC &&416!D->getLangOpts().CPlusPlus)417getCI()->getSema().IdResolver.RemoveDecl(ND);418}419}420421llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const {422CodeGenerator *CG = getCodeGen();423assert(CG);424return CG->GetMangledName(GD);425}426} // end namespace clang427428429