diff --git a/src/clang_complete.cc b/src/clang_complete.cc index be8daf57..ea5ce4e5 100644 --- a/src/clang_complete.cc +++ b/src/clang_complete.cc @@ -25,7 +25,99 @@ using namespace llvm; #include namespace chrono = std::chrono; +#if LLVM_VERSION_MAJOR < 8 +namespace clang::vfs { +struct ProxyFileSystem : FileSystem { + explicit ProxyFileSystem(IntrusiveRefCntPtr FS) + : FS(std::move(FS)) {} + llvm::ErrorOr status(const Twine &Path) override { + return FS->status(Path); + } + llvm::ErrorOr> + openFileForRead(const Twine &Path) override { + return FS->openFileForRead(Path); + } + directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override { + return FS->dir_begin(Dir, EC); + } + llvm::ErrorOr getCurrentWorkingDirectory() const override { + return FS->getCurrentWorkingDirectory(); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + return FS->setCurrentWorkingDirectory(Path); + } +#if LLVM_VERSION_MAJOR == 7 + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl &Output) const override { + return FS->getRealPath(Path, Output); + } +#endif + FileSystem &getUnderlyingFS() { return *FS; } + IntrusiveRefCntPtr FS; +}; +} +#endif + namespace ccls { +struct PreambleStatCache { + llvm::StringMap> Cache; + + void Update(Twine Path, ErrorOr S) { + Cache.try_emplace(Path.str(), std::move(S)); + } + + IntrusiveRefCntPtr + Producer(IntrusiveRefCntPtr FS) { + struct VFS : vfs::ProxyFileSystem { + PreambleStatCache &Cache; + + VFS(IntrusiveRefCntPtr FS, PreambleStatCache &Cache) + : ProxyFileSystem(std::move(FS)), Cache(Cache) {} + llvm::ErrorOr> + openFileForRead(const Twine &Path) override { + auto File = getUnderlyingFS().openFileForRead(Path); + if (!File || !*File) + return File; + Cache.Update(Path, File->get()->status()); + return File; + } + llvm::ErrorOr status(const Twine &Path) override { + auto S = getUnderlyingFS().status(Path); + Cache.Update(Path, S); + return S; + } + }; + return new VFS(std::move(FS), *this); + } + + IntrusiveRefCntPtr + Consumer(IntrusiveRefCntPtr FS) { + struct VFS : vfs::ProxyFileSystem { + const PreambleStatCache &Cache; + VFS(IntrusiveRefCntPtr FS, + const PreambleStatCache &Cache) + : ProxyFileSystem(std::move(FS)), Cache(Cache) {} + llvm::ErrorOr status(const Twine &Path) override { + auto I = Cache.Cache.find(Path.str()); + if (I != Cache.Cache.end()) + return I->getValue(); + return getUnderlyingFS().status(Path); + } + }; + return new VFS(std::move(FS), *this); + } +}; + +struct PreambleData { + PreambleData(clang::PrecompiledPreamble P, std::vector diags, + std::unique_ptr stat_cache) + : Preamble(std::move(P)), diags(std::move(diags)), + stat_cache(std::move(stat_cache)) {} + clang::PrecompiledPreamble Preamble; + std::vector diags; + std::unique_ptr stat_cache; +}; + namespace { std::string StripFileType(const std::string &path) { @@ -160,31 +252,29 @@ public: std::unique_ptr BuildCompilerInstance( CompletionSession &session, std::unique_ptr CI, - DiagnosticConsumer &DC, const WorkingFiles::Snapshot &snapshot, + IntrusiveRefCntPtr FS, DiagnosticConsumer &DC, + const PreambleData *preamble, const WorkingFiles::Snapshot &snapshot, std::vector> &Bufs) { std::string main = ResolveIfRelative( session.file.directory, sys::path::convert_to_slash(CI->getFrontendOpts().Inputs[0].getFile())); for (auto &file : snapshot.files) { Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(file.content)); - if (file.filename == main) - if (auto Preamble = session.GetPreamble()) { + if (preamble && file.filename == main) { #if LLVM_VERSION_MAJOR >= 7 - Preamble->Preamble.OverridePreamble(*CI, session.FS, - Bufs.back().get()); + preamble->Preamble.OverridePreamble(*CI, FS, Bufs.back().get()); #else - Preamble->Preamble.AddImplicitPreamble(*CI, session.FS, - Bufs.back().get()); + preamble->Preamble.AddImplicitPreamble(*CI, FS, Bufs.back().get()); #endif - continue; - } + continue; + } CI->getPreprocessorOpts().addRemappedFile(file.filename, Bufs.back().get()); } auto Clang = std::make_unique(session.PCH); Clang->setInvocation(std::move(CI)); - Clang->setVirtualFileSystem(session.FS); + Clang->setVirtualFileSystem(FS); Clang->createDiagnostics(&DC, false); Clang->setTarget(TargetInfo::CreateTargetInfo( Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); @@ -209,6 +299,37 @@ bool Parse(CompilerInstance &Clang) { return true; } +void BuildPreamble(CompletionSession &session, CompilerInvocation &CI, + IntrusiveRefCntPtr FS, + const std::string &main, + std::unique_ptr stat_cache) { + std::shared_ptr OldP = session.GetPreamble(); + std::string content = session.wfiles->GetContent(main); + std::unique_ptr Buf = + llvm::MemoryBuffer::getMemBuffer(content); + auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0); + if (OldP && OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get())) + return; + CI.getDiagnosticOpts().IgnoreWarnings = false; + CI.getFrontendOpts().SkipFunctionBodies = true; + CI.getLangOpts()->CommentOpts.ParseAllComments = true; + CI.getLangOpts()->RetainCommentsFromSystemHeaders = true; +#if LLVM_VERSION_MAJOR >= 7 + CI.getPreprocessorOpts().WriteCommentListToPCH = false; +#endif + + StoreDiags DC(main); + IntrusiveRefCntPtr DE = + CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false); + PreambleCallbacks PP; + if (auto NewPreamble = PrecompiledPreamble::Build( + CI, Buf.get(), Bounds, *DE, FS, session.PCH, true, PP)) { + std::lock_guard lock(session.mutex); + session.preamble = std::make_shared( + std::move(*NewPreamble), DC.Take(), std::move(stat_cache)); + } +} + void *CompletionPreloadMain(void *manager_) { auto *manager = static_cast(manager_); set_thread_name("comp-preload"); @@ -224,9 +345,11 @@ void *CompletionPreloadMain(void *manager_) { const auto &args = session->file.args; WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot({StripFileType(session->file.filename)}); + auto stat_cache = std::make_unique(); + IntrusiveRefCntPtr FS = stat_cache->Producer(session->FS); if (std::unique_ptr CI = - BuildCompilerInvocation(args, session->FS)) - session->BuildPreamble(*CI, request.path); + BuildCompilerInvocation(args, FS)) + BuildPreamble(*session, *CI, FS, request.path, std::move(stat_cache)); int debounce = is_open ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave; @@ -260,9 +383,11 @@ void *CompletionMain(void *manager_) { std::shared_ptr session = manager->TryGetSession(path, false); - + std::shared_ptr preamble = session->GetPreamble(); + IntrusiveRefCntPtr FS = + preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS; std::unique_ptr CI = - BuildCompilerInvocation(session->file.args, session->FS); + BuildCompilerInvocation(session->file.args, FS); if (!CI) continue; auto &FOpts = CI->getFrontendOpts(); @@ -277,7 +402,8 @@ void *CompletionMain(void *manager_) { WorkingFiles::Snapshot snapshot = manager->working_files_->AsSnapshot({StripFileType(path)}); std::vector> Bufs; - auto Clang = BuildCompilerInstance(*session, std::move(CI), DC, snapshot, Bufs); + auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC, + preamble.get(), snapshot, Bufs); if (!Clang) continue; @@ -337,9 +463,11 @@ void *DiagnosticMain(void *manager_) { std::shared_ptr session = manager->TryGetSession(path, false); - + std::shared_ptr preamble = session->GetPreamble(); + IntrusiveRefCntPtr FS = + preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS; std::unique_ptr CI = - BuildCompilerInvocation(session->file.args, session->FS); + BuildCompilerInvocation(session->file.args, FS); if (!CI) continue; CI->getDiagnosticOpts().IgnoreWarnings = false; @@ -348,7 +476,8 @@ void *DiagnosticMain(void *manager_) { WorkingFiles::Snapshot snapshot = manager->working_files_->AsSnapshot({StripFileType(path)}); std::vector> Bufs; - auto Clang = BuildCompilerInstance(*session, std::move(CI), DC, snapshot, Bufs); + auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC, + preamble.get(), snapshot, Bufs); if (!Clang) continue; if (!Parse(*Clang)) @@ -428,35 +557,6 @@ std::shared_ptr CompletionSession::GetPreamble() { return preamble; } -void CompletionSession::BuildPreamble(CompilerInvocation &CI, - const std::string &main) { - std::shared_ptr OldP = GetPreamble(); - std::string content = wfiles->GetContent(main); - std::unique_ptr Buf = - llvm::MemoryBuffer::getMemBuffer(content); - auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0); - if (OldP && OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get())) - return; - CI.getDiagnosticOpts().IgnoreWarnings = false; - CI.getFrontendOpts().SkipFunctionBodies = true; - CI.getLangOpts()->RetainCommentsFromSystemHeaders = true; - CI.getLangOpts()->CommentOpts.ParseAllComments = true; -#if LLVM_VERSION_MAJOR >= 7 - CI.getPreprocessorOpts().WriteCommentListToPCH = false; -#endif - - StoreDiags DC(main); - IntrusiveRefCntPtr DE = - CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false); - PreambleCallbacks PP; - if (auto NewPreamble = PrecompiledPreamble::Build(CI, Buf.get(), Bounds, - *DE, FS, PCH, true, PP)) { - std::lock_guard lock(mutex); - preamble = - std::make_shared(std::move(*NewPreamble), DC.Take()); - } -} - } // namespace ccls CompletionManager::CompletionManager(Project *project, diff --git a/src/clang_complete.hh b/src/clang_complete.hh index eb749169..5a10419f 100644 --- a/src/clang_complete.hh +++ b/src/clang_complete.hh @@ -21,6 +21,8 @@ #include namespace ccls { +struct PreambleData; + struct DiagBase { Range range; std::string message; @@ -35,13 +37,6 @@ struct Diag : DiagBase { std::vector edits; }; -struct PreambleData { - PreambleData(clang::PrecompiledPreamble P, std::vector diags) - : Preamble(std::move(P)), diags(std::move(diags)) {} - clang::PrecompiledPreamble Preamble; - std::vector diags; -}; - struct CompletionSession : public std::enable_shared_from_this { std::mutex mutex; @@ -61,7 +56,6 @@ struct CompletionSession : file(file), wfiles(wfiles), PCH(PCH) {} std::shared_ptr GetPreamble(); - void BuildPreamble(clang::CompilerInvocation &CI, const std::string &main); }; }