From: Douglas Gregor Date: Wed, 16 Feb 2011 18:16:54 +0000 (+0000) Subject: Improve the invalidation logic for the cache of global code X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9b7db6200d366e4964d63ae1f33c7b9d7b9831cb;p=clang Improve the invalidation logic for the cache of global code completions. We now compute a hash of the names of all top-level declarations and macro definitions, and invalidate the cache when the hash value changes. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@125670 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index b1eb0da248..0841480cf5 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -298,20 +298,24 @@ private: /// type, which is used for type equality comparisons. llvm::StringMap CachedCompletionTypes; - /// \brief The number of top-level declarations present the last time we - /// cached code-completion results. + /// \brief A string hash of the top-level declaration and macro definition + /// names processed the last time that we reparsed the file. /// - /// The value is used to help detect when we should repopulate the global - /// completion cache. - unsigned NumTopLevelDeclsAtLastCompletionCache; + /// This hash value is used to determine when we need to refresh the + /// global code-completion cache. + unsigned CompletionCacheTopLevelHashValue; - /// \brief The number of reparses left until we'll consider updating the - /// code-completion cache. + /// \brief A string hash of the top-level declaration and macro definition + /// names processed the last time that we reparsed the precompiled preamble. /// - /// This is meant to avoid thrashing during reparsing, by not allowing the - /// code-completion cache to be updated on every reparse. - unsigned CacheCodeCompletionCoolDown; + /// This hash value is used to determine when we need to refresh the + /// global code-completion cache after a rebuild of the precompiled preamble. + unsigned PreambleTopLevelHashValue; + /// \brief The current hash value for the top-level declaration and macro + /// definition names + unsigned CurrentTopLevelHashValue; + /// \brief Bit used by CIndex to mark when a translation unit may be in an /// inconsistent state, and is not safe to free. unsigned UnsafeToFree : 1; @@ -450,6 +454,11 @@ public: TopLevelDeclsInPreamble.push_back(D); } + /// \brief Retrieve a reference to the current top-level name hash value. + /// + /// Note: This is used internally by the top-level tracking action + unsigned &getCurrentTopLevelHashValue() { return CurrentTopLevelHashValue; } + typedef std::vector::iterator pp_entity_iterator; pp_entity_iterator pp_entity_begin(); diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 0370c2e125..a56fefc69f 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -34,6 +34,7 @@ #include "clang/Basic/TargetOptions.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Atomic.h" #include "llvm/Support/MemoryBuffer.h" @@ -95,8 +96,9 @@ ASTUnit::ASTUnit(bool _MainFileIsAST) ConcurrencyCheckValue(CheckUnlocked), PreambleRebuildCounter(0), SavedMainFileBuffer(0), PreambleBuffer(0), ShouldCacheCodeCompletionResults(false), - NumTopLevelDeclsAtLastCompletionCache(0), - CacheCodeCompletionCoolDown(0), + CompletionCacheTopLevelHashValue(0), + PreambleTopLevelHashValue(0), + CurrentTopLevelHashValue(0), UnsafeToFree(false) { if (getenv("LIBCLANG_OBJTRACKING")) { llvm::sys::AtomicIncrement(&ActiveASTUnitObjects); @@ -343,9 +345,9 @@ void ASTUnit::CacheCodeCompletionResults() { } } } - - // Make a note of the state when we performed this caching. - NumTopLevelDeclsAtLastCompletionCache = top_level_size(); + + // Save the current top-level hash value. + CompletionCacheTopLevelHashValue = CurrentTopLevelHashValue; } void ASTUnit::ClearCachedCompletionResults() { @@ -603,12 +605,69 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, namespace { +/// \brief Preprocessor callback class that updates a hash value with the names +/// of all macros that have been defined by the translation unit. +class MacroDefinitionTrackerPPCallbacks : public PPCallbacks { + unsigned &Hash; + +public: + explicit MacroDefinitionTrackerPPCallbacks(unsigned &Hash) : Hash(Hash) { } + + virtual void MacroDefined(const Token &MacroNameTok, const MacroInfo *MI) { + Hash = llvm::HashString(MacroNameTok.getIdentifierInfo()->getName(), Hash); + } +}; + +/// \brief Add the given declaration to the hash of all top-level entities. +void AddTopLevelDeclarationToHash(Decl *D, unsigned &Hash) { + if (!D) + return; + + DeclContext *DC = D->getDeclContext(); + if (!DC) + return; + + if (!(DC->isTranslationUnit() || DC->getLookupParent()->isTranslationUnit())) + return; + + if (NamedDecl *ND = dyn_cast(D)) { + if (ND->getIdentifier()) + Hash = llvm::HashString(ND->getIdentifier()->getName(), Hash); + else if (DeclarationName Name = ND->getDeclName()) { + std::string NameStr = Name.getAsString(); + Hash = llvm::HashString(NameStr, Hash); + } + return; + } + + if (ObjCForwardProtocolDecl *Forward + = dyn_cast(D)) { + for (ObjCForwardProtocolDecl::protocol_iterator + P = Forward->protocol_begin(), + PEnd = Forward->protocol_end(); + P != PEnd; ++P) + AddTopLevelDeclarationToHash(*P, Hash); + return; + } + + if (ObjCClassDecl *Class = llvm::dyn_cast(D)) { + for (ObjCClassDecl::iterator I = Class->begin(), IEnd = Class->end(); + I != IEnd; ++I) + AddTopLevelDeclarationToHash(I->getInterface(), Hash); + return; + } +} + class TopLevelDeclTrackerConsumer : public ASTConsumer { ASTUnit &Unit; - + unsigned &Hash; + public: - TopLevelDeclTrackerConsumer(ASTUnit &_Unit) : Unit(_Unit) {} - + TopLevelDeclTrackerConsumer(ASTUnit &_Unit, unsigned &Hash) + : Unit(_Unit), Hash(Hash) { + Hash = 0; + } + void HandleTopLevelDecl(DeclGroupRef D) { for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) { Decl *D = *it; @@ -618,6 +677,8 @@ public: // fundamental problem in the parser right now. if (isa(D)) continue; + + AddTopLevelDeclarationToHash(D, Hash); Unit.addTopLevelDecl(D); } } @@ -632,7 +693,10 @@ public: virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) { - return new TopLevelDeclTrackerConsumer(Unit); + CI.getPreprocessor().addPPCallbacks( + new MacroDefinitionTrackerPPCallbacks(Unit.getCurrentTopLevelHashValue())); + return new TopLevelDeclTrackerConsumer(Unit, + Unit.getCurrentTopLevelHashValue()); } public: @@ -647,13 +711,17 @@ public: class PrecompilePreambleConsumer : public PCHGenerator, public ASTSerializationListener { ASTUnit &Unit; + unsigned &Hash; std::vector TopLevelDecls; public: PrecompilePreambleConsumer(ASTUnit &Unit, const Preprocessor &PP, bool Chaining, const char *isysroot, llvm::raw_ostream *Out) - : PCHGenerator(PP, "", Chaining, isysroot, Out), Unit(Unit) { } + : PCHGenerator(PP, "", Chaining, isysroot, Out), Unit(Unit), + Hash(Unit.getCurrentTopLevelHashValue()) { + Hash = 0; + } virtual void HandleTopLevelDecl(DeclGroupRef D) { for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) { @@ -664,6 +732,7 @@ public: // fundamental problem in the parser right now. if (isa(D)) continue; + AddTopLevelDeclarationToHash(D, Hash); TopLevelDecls.push_back(D); } } @@ -710,6 +779,8 @@ public: const char *isysroot = CI.getFrontendOpts().RelocatablePCH ? Sysroot.c_str() : 0; + CI.getPreprocessor().addPPCallbacks( + new MacroDefinitionTrackerPPCallbacks(Unit.getCurrentTopLevelHashValue())); return new PrecompilePreambleConsumer(Unit, CI.getPreprocessor(), Chaining, isysroot, OS); } @@ -856,14 +927,6 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { } Invocation.reset(Clang.takeInvocation()); - - if (ShouldCacheCodeCompletionResults) { - if (CacheCodeCompletionCoolDown > 0) - --CacheCodeCompletionCoolDown; - else if (top_level_size() != NumTopLevelDeclsAtLastCompletionCache) - CacheCodeCompletionResults(); - } - return false; error: @@ -1336,6 +1399,15 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( PreambleRebuildCounter = 1; PreprocessorOpts.eraseRemappedFile( PreprocessorOpts.remapped_file_buffer_end() - 1); + + // If the hash of top-level entities differs from the hash of the top-level + // entities the last time we rebuilt the preamble, clear out the completion + // cache. + if (CurrentTopLevelHashValue != PreambleTopLevelHashValue) { + CompletionCacheTopLevelHashValue = 0; + PreambleTopLevelHashValue = CurrentTopLevelHashValue; + } + return CreatePaddedMainFileBuffer(NewPreamble.first, PreambleReservedSize, FrontendOpts.Inputs[0].second); @@ -1458,7 +1530,6 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, AST->CaptureDiagnostics = CaptureDiagnostics; AST->CompleteTranslationUnit = CompleteTranslationUnit; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; - AST->CacheCodeCompletionCoolDown = 1; AST->Invocation.reset(CI); return AST->LoadFromCompilerInvocation(PrecompilePreamble)? 0 : AST.take(); @@ -1566,7 +1637,6 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, AST->CaptureDiagnostics = CaptureDiagnostics; AST->CompleteTranslationUnit = CompleteTranslationUnit; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; - AST->CacheCodeCompletionCoolDown = 1; AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size(); AST->NumStoredDiagnosticsInPreamble = StoredDiagnostics.size(); AST->StoredDiagnostics.swap(StoredDiagnostics); @@ -1609,7 +1679,14 @@ bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { } // Parse the sources - bool Result = Parse(OverrideMainBuffer); + bool Result = Parse(OverrideMainBuffer); + + // If we're caching global code-completion results, and the top-level + // declarations have changed, clear out the code-completion cache. + if (!Result && ShouldCacheCodeCompletionResults && + CurrentTopLevelHashValue != CompletionCacheTopLevelHashValue) + CacheCodeCompletionResults(); + return Result; }