ioeric updated this revision to Diff 166667. ioeric added a comment. - update comment
Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D52419 Files: clangd/ClangdServer.cpp clangd/ClangdUnit.cpp clangd/ClangdUnit.h clangd/CodeComplete.cpp clangd/CodeComplete.h unittests/clangd/ClangdTests.cpp
Index: unittests/clangd/ClangdTests.cpp =================================================================== --- unittests/clangd/ClangdTests.cpp +++ unittests/clangd/ClangdTests.cpp @@ -963,6 +963,89 @@ Field(&CodeCompletion::Name, "baz"))); } +TEST(ClangdTests, PreambleVFSStatCache) { + class ListenStatsFSProvider : public FileSystemProvider { + public: + ListenStatsFSProvider(llvm::StringMap<unsigned> &CountStats) + : CountStats(CountStats) {} + + IntrusiveRefCntPtr<vfs::FileSystem> getFileSystem() override { + class ListenStatVFS : public vfs::FileSystem { + public: + ListenStatVFS(IntrusiveRefCntPtr<vfs::FileSystem> FS, + llvm::StringMap<unsigned> &CountStats) + : FS(std::move(FS)), CountStats(CountStats) {} + + vfs::directory_iterator dir_begin(const Twine &Dir, + std::error_code &EC) override { + return FS->dir_begin(Dir, EC); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + return FS->setCurrentWorkingDirectory(Path); + } + llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { + return FS->getCurrentWorkingDirectory(); + } + std::error_code + getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const override { + return FS->getRealPath(Path, Output); + } + + llvm::ErrorOr<std::unique_ptr<vfs::File>> + openFileForRead(const Twine &Path) override { + return FS->openFileForRead(Path); + } + + llvm::ErrorOr<vfs::Status> status(const Twine &Path) override { + auto I = + CountStats.try_emplace(llvm::sys::path::filename(Path.str()), 1); + if (!I.second) + I.first->second += 1; + return FS->status(Path); + } + + private: + IntrusiveRefCntPtr<vfs::FileSystem> FS; + llvm::StringMap<unsigned> &CountStats; + }; + + return IntrusiveRefCntPtr<ListenStatVFS>( + new ListenStatVFS(buildTestFS(Files), CountStats)); + } + + // If relative paths are used, they are resolved with testPath(). + llvm::StringMap<std::string> Files; + llvm::StringMap<unsigned> &CountStats; + }; + + llvm::StringMap<unsigned> CountStats; + ListenStatsFSProvider FS(CountStats); + ErrorCheckingDiagConsumer DiagConsumer; + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + auto SourcePath = testPath("foo.cpp"); + auto HeaderPath = testPath("foo.h"); + FS.Files[HeaderPath] = "struct TestSym {};"; + Annotations Code(R"cpp( + #include "foo.h" + + int main() { + TestSy^ + })cpp"); + + runAddDocument(Server, SourcePath, Code.code()); + + unsigned Before = CountStats["foo.h"]; + auto Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(), + clangd::CodeCompleteOptions())) + .Completions; + EXPECT_EQ(CountStats["foo.h"], Before); + EXPECT_THAT(Completions, + ElementsAre(Field(&CodeCompletion::Name, "TestSym"))); +} + } // namespace } // namespace clangd } // namespace clang Index: clangd/CodeComplete.h =================================================================== --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -20,6 +20,7 @@ #include "Logger.h" #include "Path.h" #include "Protocol.h" +#include "TUScheduler.h" #include "index/Index.h" #include "clang/Frontend/PrecompiledPreamble.h" #include "clang/Sema/CodeCompleteConsumer.h" @@ -212,11 +213,8 @@ /// the speculative result is used by code completion (e.g. speculation failed), /// the speculative result is not consumed, and `SpecFuzzyFind` is only /// destroyed when the async request finishes. -CodeCompleteResult codeComplete(PathRef FileName, - const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, - const IncludeStructure &PreambleInclusions, - StringRef Contents, Position Pos, +CodeCompleteResult codeComplete(PathRef FileName, Position Pos, + const InputsAndPreamble &Input, IntrusiveRefCntPtr<vfs::FileSystem> VFS, std::shared_ptr<PCHContainerOperations> PCHs, CodeCompleteOptions Opts, Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -1000,7 +1000,8 @@ struct SemaCompleteInput { PathRef FileName; const tooling::CompileCommand &Command; - PrecompiledPreamble const *Preamble; + const PrecompiledPreamble *Preamble; + const PreambleFileStatusCache *PreambleStatCache; StringRef Contents; Position Pos; IntrusiveRefCntPtr<vfs::FileSystem> VFS; @@ -1024,12 +1025,16 @@ // working dirs. } + IntrusiveRefCntPtr<vfs::FileSystem> VFS = Input.VFS; + if (Input.PreambleStatCache) + VFS = createVFSWithPreambleStatCache(std::move(VFS), + *Input.PreambleStatCache); IgnoreDiagnostics DummyDiagsConsumer; auto CI = createInvocationFromCommandLine( ArgStrs, CompilerInstance::createDiagnostics(new DiagnosticOptions, &DummyDiagsConsumer, false), - Input.VFS); + VFS); if (!CI) { elog("Couldn't create CompilerInvocation"); return false; @@ -1069,7 +1074,7 @@ // the remapped buffers do not get freed. auto Clang = prepareCompilerInstance( std::move(CI), CompletingInPreamble ? nullptr : Input.Preamble, - std::move(ContentsBuffer), std::move(Input.PCHs), std::move(Input.VFS), + std::move(ContentsBuffer), std::move(Input.PCHs), std::move(VFS), DummyDiagsConsumer); Clang->getPreprocessorOpts().SingleFileParseMode = CompletingInPreamble; Clang->setCodeCompletionConsumer(Consumer.release()); @@ -1586,15 +1591,20 @@ return Content.substr(St, Len); } -CodeCompleteResult -codeComplete(PathRef FileName, const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, - const IncludeStructure &PreambleInclusions, StringRef Contents, - Position Pos, IntrusiveRefCntPtr<vfs::FileSystem> VFS, - std::shared_ptr<PCHContainerOperations> PCHs, - CodeCompleteOptions Opts, SpeculativeFuzzyFind *SpecFuzzyFind) { - return CodeCompleteFlow(FileName, PreambleInclusions, SpecFuzzyFind, Opts) - .run({FileName, Command, Preamble, Contents, Pos, VFS, PCHs}); +CodeCompleteResult codeComplete(PathRef FileName, Position Pos, + const InputsAndPreamble &Input, + IntrusiveRefCntPtr<vfs::FileSystem> VFS, + std::shared_ptr<PCHContainerOperations> PCHs, + CodeCompleteOptions Opts, + SpeculativeFuzzyFind *SpecFuzzyFind) { + return CodeCompleteFlow(FileName, + Input.Preamble ? Input.Preamble->Includes + : IncludeStructure(), + SpecFuzzyFind, Opts) + .run({FileName, Input.Command, + Input.Preamble ? &Input.Preamble->Preamble : nullptr, + Input.Preamble ? &Input.Preamble->StatCache : nullptr, + Input.Contents, Pos, VFS, PCHs}); } SignatureHelp signatureHelp(PathRef FileName, @@ -1614,8 +1624,8 @@ semaCodeComplete( llvm::make_unique<SignatureHelpCollector>(Options, Index, Result), Options, - {FileName, Command, Preamble, Contents, Pos, std::move(VFS), - std::move(PCHs)}); + {FileName, Command, Preamble, /*PreambleStatCache=*/nullptr, Contents, + Pos, std::move(VFS), std::move(PCHs)}); return Result; } Index: clangd/ClangdUnit.h =================================================================== --- clangd/ClangdUnit.h +++ clangd/ClangdUnit.h @@ -15,12 +15,16 @@ #include "Headers.h" #include "Path.h" #include "Protocol.h" +#include "clang/Basic/VirtualFileSystem.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/PrecompiledPreamble.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTBitCodes.h" #include "clang/Tooling/CompilationDatabase.h" #include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" #include <memory> #include <string> #include <vector> @@ -42,17 +46,32 @@ namespace clangd { +/// Cached `stat()` calls during preamble build. The map keys are absolute paths +/// of the file path in the cached status, which are the paths stored in +/// preamble. +using PreambleFileStatusCache = llvm::StringMap<llvm::ErrorOr<vfs::Status>>; + +/// Wraps \p FS with the \p StatCache. This should only be used when preamble +/// is reused (e.g. code completion) and the cached status is valid. +IntrusiveRefCntPtr<vfs::FileSystem> +createVFSWithPreambleStatCache(IntrusiveRefCntPtr<vfs::FileSystem> FS, + const PreambleFileStatusCache &StatCache); + // Stores Preamble and associated data. struct PreambleData { PreambleData(PrecompiledPreamble Preamble, std::vector<Diag> Diags, - IncludeStructure Includes); + IncludeStructure Includes, PreambleFileStatusCache StatCache); tooling::CompileCommand CompileCommand; PrecompiledPreamble Preamble; std::vector<Diag> Diags; // Processes like code completions and go-to-definitions will need #include // information, and their compile action skips preamble range. IncludeStructure Includes; + // Cache of FS `stat()` results when building preamble. This can be used to + // avoid re-`stat`s header files when preamble is re-used (e.g. in code + // completion). Note that the cache is only valid if preamble is reused. + const PreambleFileStatusCache StatCache; }; /// Information required to run clang, e.g. to parse AST or do code completion. Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -15,6 +15,7 @@ #include "Trace.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/VirtualFileSystem.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" @@ -29,6 +30,7 @@ #include "clang/Serialization/ASTWriter.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/raw_ostream.h" @@ -114,6 +116,59 @@ SourceManager *SourceMgr = nullptr; }; +/// Collect and cache all file status from the underlying file system. +class CollectStatCacheVFS : public vfs::FileSystem { +public: + CollectStatCacheVFS(IntrusiveRefCntPtr<vfs::FileSystem> FS, + PreambleFileStatusCache &StatCache) + : FS(std::move(FS)), StatCache(StatCache) {} + + vfs::directory_iterator dir_begin(const Twine &Dir, + std::error_code &EC) override { + return FS->dir_begin(Dir, EC); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + return FS->setCurrentWorkingDirectory(Path); + } + llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { + return FS->getCurrentWorkingDirectory(); + } + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const override { + return FS->getRealPath(Path, Output); + } + + llvm::ErrorOr<std::unique_ptr<vfs::File>> + openFileForRead(const Twine &Path) override { + auto File = FS->openFileForRead(Path); + if (!File || !*File) + return File; + // Only cache stats for files that exist because, unlike building preamble, + // we only care about existing files when reusing preamble. + if (auto S = File->get()->status()) + cacheStatus(std::move(*S)); + return File; + } + + llvm::ErrorOr<vfs::Status> status(const Twine &Path) override { + auto S = FS->status(Path); + if (S) + cacheStatus(*S); + return S; + } + +private: + void cacheStatus(vfs::Status S) { + SmallString<32> PathStore(S.getName()); + if (auto Err = makeAbsolute(PathStore)) + return; + // Stores the latest status in cache as it can change in a preamble build. + StatCache.insert({PathStore, std::move(S)}); + } + IntrusiveRefCntPtr<vfs::FileSystem> FS; + PreambleFileStatusCache &StatCache; +}; + } // namespace void clangd::dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) { @@ -246,9 +301,51 @@ } PreambleData::PreambleData(PrecompiledPreamble Preamble, - std::vector<Diag> Diags, IncludeStructure Includes) + std::vector<Diag> Diags, IncludeStructure Includes, + PreambleFileStatusCache StatCache) : Preamble(std::move(Preamble)), Diags(std::move(Diags)), - Includes(std::move(Includes)) {} + Includes(std::move(Includes)), StatCache(std::move(StatCache)) {} + +IntrusiveRefCntPtr<vfs::FileSystem> clangd::createVFSWithPreambleStatCache( + IntrusiveRefCntPtr<vfs::FileSystem> FS, + const PreambleFileStatusCache &StatCache) { + class CacheVFS : public vfs::FileSystem { + public: + CacheVFS(IntrusiveRefCntPtr<vfs::FileSystem> FS, + const PreambleFileStatusCache &StatCache) + : FS(std::move(FS)), StatCache(StatCache) {} + + vfs::directory_iterator dir_begin(const Twine &Dir, + std::error_code &EC) override { + return FS->dir_begin(Dir, EC); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + return FS->setCurrentWorkingDirectory(Path); + } + llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { + return FS->getCurrentWorkingDirectory(); + } + + llvm::ErrorOr<std::unique_ptr<vfs::File>> + openFileForRead(const Twine &Path) override { + return FS->openFileForRead(Path); + } + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const override { + return FS->getRealPath(Path, Output); + } + + llvm::ErrorOr<vfs::Status> status(const Twine &Path) override { + auto I = StatCache.find(Path.str()); + return (I != StatCache.end()) ? I->getValue() : FS->status(Path); + } + + private: + IntrusiveRefCntPtr<vfs::FileSystem> FS; + const PreambleFileStatusCache &StatCache; + }; + return IntrusiveRefCntPtr<CacheVFS>(new CacheVFS(std::move(FS), StatCache)); +} ParsedAST::ParsedAST(std::shared_ptr<const PreambleData> Preamble, std::unique_ptr<CompilerInstance> Clang, @@ -334,8 +431,12 @@ // We proceed anyway, our lit-tests rely on results for non-existing working // dirs. } + + PreambleFileStatusCache StatCache; + llvm::IntrusiveRefCntPtr<vfs::FileSystem> CacheVFS( + new CollectStatCacheVFS(Inputs.FS, StatCache)); auto BuiltPreamble = PrecompiledPreamble::Build( - CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, Inputs.FS, PCHs, + CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, CacheVFS, PCHs, StoreInMemory, SerializedDeclsCollector); // When building the AST for the main file, we do want the function @@ -347,7 +448,7 @@ FileName); return std::make_shared<PreambleData>( std::move(*BuiltPreamble), PreambleDiagnostics.take(), - SerializedDeclsCollector.takeIncludes()); + SerializedDeclsCollector.takeIncludes(), std::move(StatCache)); } else { elog("Could not build a preamble for file {0}", FileName); return nullptr; Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -181,8 +181,6 @@ if (isCancelled()) return CB(llvm::make_error<CancelledError>()); - auto PreambleData = IP->Preamble; - llvm::Optional<SpeculativeFuzzyFind> SpecFuzzyFind; if (CodeCompleteOpts.Index && CodeCompleteOpts.SpeculativeIndexRequest) { SpecFuzzyFind.emplace(); @@ -195,9 +193,7 @@ // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. CodeCompleteResult Result = clangd::codeComplete( - File, IP->Command, PreambleData ? &PreambleData->Preamble : nullptr, - PreambleData ? PreambleData->Includes : IncludeStructure(), - IP->Contents, Pos, FS, PCHs, CodeCompleteOpts, + File, Pos, *IP, FS, PCHs, CodeCompleteOpts, SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr); { clang::clangd::trace::Span Tracer("Completion results callback");
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits