diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index eb8b99bf3421306c7317d95a24cf6f2c6f8873fe..46a3a4e11f1b33bfdb06f9078f96f11ee0f6b0a3 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -1530,4 +1530,23 @@ namespace clang { } } // end namespace clang +namespace llvm { + template <> struct DenseMapInfo<clang::serialization::DeclarationNameKey> { + static clang::serialization::DeclarationNameKey getEmptyKey() { + return clang::serialization::DeclarationNameKey(-1, 1); + } + static clang::serialization::DeclarationNameKey getTombstoneKey() { + return clang::serialization::DeclarationNameKey(-1, 2); + } + static unsigned + getHashValue(const clang::serialization::DeclarationNameKey &Key) { + return Key.getHash(); + } + static bool isEqual(const clang::serialization::DeclarationNameKey &L, + const clang::serialization::DeclarationNameKey &R) { + return L == R; + } + }; +} + #endif diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index dca2032655f915a7c0a20c5522d3cb5b38671ce6..84f1b6629111d8666ed6771940733bca8e19f782 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -282,9 +282,8 @@ class ReadMethodPoolVisitor; namespace reader { class ASTIdentifierLookupTrait; - /// \brief The on-disk hash table used for the DeclContext's Name lookup table. - typedef llvm::OnDiskIterableChainedHashTable<ASTDeclContextNameLookupTrait> - ASTDeclContextNameLookupTable; + /// \brief The on-disk hash table(s) used for DeclContext name lookup. + struct DeclContextLookupTable; } } // end namespace serialization @@ -507,6 +506,10 @@ private: /// \brief Map from the TU to its lexical contents from each module file. std::vector<std::pair<ModuleFile*, LexicalContents>> TULexicalDecls; + /// \brief Map from a DeclContext to its lookup tables. + llvm::DenseMap<const DeclContext *, + serialization::reader::DeclContextLookupTable> Lookups; + // Updates for visible decls can occur for other contexts than just the // TU, and when we read those update records, the actual context may not // be available yet, so have this pending map using the ID as a key. It @@ -514,7 +517,6 @@ private: struct PendingVisibleUpdate { ModuleFile *Mod; const unsigned char *Data; - unsigned BucketOffset; }; typedef SmallVector<PendingVisibleUpdate, 1> DeclContextVisibleUpdates; @@ -1089,6 +1091,10 @@ public: Visit(GetExistingDecl(ID)); } + /// \brief Get the loaded lookup tables for \p Primary, if any. + const serialization::reader::DeclContextLookupTable * + getLoadedLookupTables(DeclContext *Primary) const; + private: struct ImportedModule { ModuleFile *Mod; @@ -1870,6 +1876,13 @@ public: /// Note: overrides method in ExternalASTSource Module *getModule(unsigned ID) override; + /// \brief Retrieve the module file with a given local ID within the specified + /// ModuleFile. + ModuleFile *getLocalModuleFile(ModuleFile &M, unsigned ID); + + /// \brief Get an ID for the given module file. + unsigned getModuleFileID(ModuleFile *M); + /// \brief Return a descriptor for the corresponding module. llvm::Optional<ASTSourceDescriptor> getSourceDescriptor(unsigned ID) override; /// \brief Return a descriptor for the module. diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 3711e155f633dd85ffe22f8d4275f36928e6e982..1c045c26b7ea0572898b9e0aa7dcb82da337c7ab 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -529,8 +529,8 @@ private: bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC); bool isLookupResultEntirelyExternal(StoredDeclsList &Result, DeclContext *DC); - uint32_t GenerateNameLookupTable(const DeclContext *DC, - llvm::SmallVectorImpl<char> &LookupTable); + void GenerateNameLookupTable(const DeclContext *DC, + llvm::SmallVectorImpl<char> &LookupTable); uint64_t WriteDeclContextLexicalBlock(ASTContext &Context, DeclContext *DC); uint64_t WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC); void WriteTypeDeclOffsets(); @@ -849,6 +849,7 @@ public: unsigned getExprImplicitCastAbbrev() const { return ExprImplicitCastAbbrev; } bool hasChain() const { return Chain; } + ASTReader *getChain() const { return Chain; } // ASTDeserializationListener implementation void ReaderInitialized(ASTReader *Reader) override; diff --git a/include/clang/Serialization/Module.h b/include/clang/Serialization/Module.h index 93067031ae8ef4f3af2fbcd0ea8ba54f45fec710..1b9560188cb0f17d80a80b3b297b870c150d0bf7 100644 --- a/include/clang/Serialization/Module.h +++ b/include/clang/Serialization/Module.h @@ -50,14 +50,6 @@ enum ModuleKind { MK_MainFile ///< File is a PCH file treated as the actual main file. }; -/// \brief Information about the contents of a DeclContext. -struct DeclContextInfo { - DeclContextInfo() : NameLookupTableData() {} - - llvm::OnDiskIterableChainedHashTable<reader::ASTDeclContextNameLookupTrait> - *NameLookupTableData; // an ASTDeclContextNameLookupTable. -}; - /// \brief The input file that has been loaded from this AST file, along with /// bools indicating whether this was an overridden buffer or if it was /// out-of-date or not-found. @@ -416,13 +408,6 @@ public: /// indexed by the C++ ctor initializer list ID minus 1. const uint32_t *CXXCtorInitializersOffsets; - typedef llvm::DenseMap<const DeclContext *, DeclContextInfo> - DeclContextInfosMap; - - /// \brief Information about the lexical and visible declarations - /// for each DeclContext. - DeclContextInfosMap DeclContextInfos; - /// \brief Array of file-level DeclIDs sorted by file. const serialization::DeclID *FileSortedDecls; unsigned NumFileSortedDecls; diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index af1130df3ff7c7bba36c108d4a99df9d19779823..91a977bb6914eb0a6eba4cc09ab8d9655666bc48 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -20,6 +20,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/Frontend/PCHContainerOperations.h" +#include "clang/AST/ASTMutationListener.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLocVisitor.h" @@ -903,6 +904,13 @@ unsigned DeclarationNameKey::getHash() const { return ID.ComputeHash(); } +ModuleFile * +ASTDeclContextNameLookupTrait::ReadFileRef(const unsigned char *&d) { + using namespace llvm::support; + uint32_t ModuleFileID = endian::readNext<uint32_t, little, unaligned>(d); + return Reader.getLocalModuleFile(F, ModuleFileID); +} + std::pair<unsigned, unsigned> ASTDeclContextNameLookupTrait::ReadKeyDataLength(const unsigned char *&d) { using namespace llvm::support; @@ -948,15 +956,15 @@ ASTDeclContextNameLookupTrait::ReadKey(const unsigned char *d, unsigned) { return DeclarationNameKey(Kind, Data); } -ASTDeclContextNameLookupTrait::data_type -ASTDeclContextNameLookupTrait::ReadData(internal_key_type, - const unsigned char *d, - unsigned DataLen) { +void ASTDeclContextNameLookupTrait::ReadDataInto(internal_key_type, + const unsigned char *d, + unsigned DataLen, + data_type &Val) { using namespace llvm::support; - unsigned NumDecls = DataLen / 4; - LE32DeclID *Start = reinterpret_cast<LE32DeclID *>( - const_cast<unsigned char *>(d)); - return std::make_pair(Start, Start + NumDecls); + for (unsigned NumDecls = DataLen / 4; NumDecls; --NumDecls) { + uint32_t LocalID = endian::readNext<uint32_t, little, unaligned>(d); + Val.insert(Reader.getGlobalDeclID(F, LocalID)); + } } bool ASTReader::ReadLexicalDeclContextStorage(ModuleFile &M, @@ -1015,9 +1023,8 @@ bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M, // We can't safely determine the primary context yet, so delay attaching the // lookup table until we're done with recursive deserialization. - unsigned BucketOffset = Record[0]; - PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{ - &M, (const unsigned char *)Blob.data(), BucketOffset}); + auto *Data = (const unsigned char*)Blob.data(); + PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{&M, Data}); return false; } @@ -2551,9 +2558,7 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { unsigned Idx = 0; serialization::DeclID ID = ReadDeclID(F, Record, Idx); auto *Data = (const unsigned char*)Blob.data(); - unsigned BucketOffset = Record[Idx++]; - PendingVisibleUpdates[ID].push_back( - PendingVisibleUpdate{&F, Data, BucketOffset}); + PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{&F, Data}); // If we've already loaded the decl, perform the updates when we finish // loading this block. if (Decl *D = GetExistingDecl(ID)) @@ -6354,196 +6359,48 @@ void ASTReader::FindFileRegionDecls(FileID File, Decls.push_back(GetDecl(getGlobalDeclID(*DInfo.Mod, *DIt))); } -/// \brief Retrieve the "definitive" module file for the definition of the -/// given declaration context, if there is one. -/// -/// The "definitive" module file is the only place where we need to look to -/// find information about the declarations within the given declaration -/// context. For example, C++ and Objective-C classes, C structs/unions, and -/// Objective-C protocols, categories, and extensions are all defined in a -/// single place in the source code, so they have definitive module files -/// associated with them. C++ namespaces, on the other hand, can have -/// definitions in multiple different module files. -/// -/// Note: this needs to be kept in sync with ASTWriter::AddedVisibleDecl's -/// NDEBUG checking. -static ModuleFile *getDefinitiveModuleFileFor(const DeclContext *DC, - ASTReader &Reader) { - if (const DeclContext *DefDC = getDefinitiveDeclContext(DC)) - return Reader.getOwningModuleFile(cast<Decl>(DefDC)); - - return nullptr; -} - -namespace { - /// \brief ModuleFile visitor used to perform name lookup into a - /// declaration context. - class DeclContextNameLookupVisitor { - ASTReader &Reader; - const DeclContext *Context; - DeclarationName Name; - DeclarationNameKey NameKey; - unsigned NameHash; - SmallVectorImpl<NamedDecl *> &Decls; - llvm::SmallPtrSetImpl<NamedDecl *> &DeclSet; - - public: - DeclContextNameLookupVisitor(ASTReader &Reader, const DeclContext *Context, - DeclarationName Name, - SmallVectorImpl<NamedDecl *> &Decls, - llvm::SmallPtrSetImpl<NamedDecl *> &DeclSet) - : Reader(Reader), Context(Context), Name(Name), NameKey(Name), - NameHash(NameKey.getHash()), Decls(Decls), DeclSet(DeclSet) {} - - bool operator()(ModuleFile &M) { - // Check whether we have any visible declaration information for - // this context in this module. - auto Info = M.DeclContextInfos.find(Context); - if (Info == M.DeclContextInfos.end() || !Info->second.NameLookupTableData) - return false; - - // Look for this name within this module. - ASTDeclContextNameLookupTable *LookupTable = - Info->second.NameLookupTableData; - ASTDeclContextNameLookupTable::iterator Pos = - LookupTable->find_hashed(NameKey, NameHash); - if (Pos == LookupTable->end()) - return false; - - bool FoundAnything = false; - ASTDeclContextNameLookupTrait::data_type Data = *Pos; - for (; Data.first != Data.second; ++Data.first) { - NamedDecl *ND = Reader.GetLocalDeclAs<NamedDecl>(M, *Data.first); - if (!ND) - continue; - - if (ND->getDeclName() != Name) { - // Not all names map to a unique DeclarationNameKey. - assert(DeclarationNameKey(ND->getDeclName()) == NameKey && - "mismatched name for decl in decl context lookup table?"); - continue; - } - - // Record this declaration. - FoundAnything = true; - if (DeclSet.insert(ND).second) - Decls.push_back(ND); - } - - return FoundAnything; - } - }; -} - bool ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) { - assert(DC->hasExternalVisibleStorage() && + assert(DC->hasExternalVisibleStorage() && DC == DC->getPrimaryContext() && "DeclContext has no visible decls in storage"); if (!Name) return false; + auto It = Lookups.find(DC); + if (It == Lookups.end()) + return false; + Deserializing LookupResults(this); + // Load the list of declarations. SmallVector<NamedDecl *, 64> Decls; - llvm::SmallPtrSet<NamedDecl*, 64> DeclSet; - - DeclContextNameLookupVisitor Visitor(*this, DC, Name, Decls, DeclSet); - - // If we can definitively determine which module file to look into, - // only look there. Otherwise, look in all module files. - if (ModuleFile *Definitive = getDefinitiveModuleFileFor(DC, *this)) - Visitor(*Definitive); - else - ModuleMgr.visit(Visitor); + for (DeclID ID : It->second.Table.find(Name)) { + NamedDecl *ND = cast<NamedDecl>(GetDecl(ID)); + if (ND->getDeclName() == Name) + Decls.push_back(ND); + } ++NumVisibleDeclContextsRead; SetExternalVisibleDeclsForName(DC, Name, Decls); return !Decls.empty(); } -namespace { - /// \brief ModuleFile visitor used to retrieve all visible names in a - /// declaration context. - class DeclContextAllNamesVisitor { - ASTReader &Reader; - SmallVectorImpl<const DeclContext *> &Contexts; - DeclsMap &Decls; - llvm::SmallPtrSet<NamedDecl *, 256> DeclSet; - bool VisitAll; - - public: - DeclContextAllNamesVisitor(ASTReader &Reader, - SmallVectorImpl<const DeclContext *> &Contexts, - DeclsMap &Decls, bool VisitAll) - : Reader(Reader), Contexts(Contexts), Decls(Decls), VisitAll(VisitAll) { } - - bool operator()(ModuleFile &M) { - // Check whether we have any visible declaration information for - // this context in this module. - ModuleFile::DeclContextInfosMap::iterator Info; - bool FoundInfo = false; - for (unsigned I = 0, N = Contexts.size(); I != N; ++I) { - Info = M.DeclContextInfos.find(Contexts[I]); - if (Info != M.DeclContextInfos.end() && - Info->second.NameLookupTableData) { - FoundInfo = true; - break; - } - } - - if (!FoundInfo) - return false; - - ASTDeclContextNameLookupTable *LookupTable = - Info->second.NameLookupTableData; - bool FoundAnything = false; - for (ASTDeclContextNameLookupTable::data_iterator - I = LookupTable->data_begin(), E = LookupTable->data_end(); - I != E; - ++I) { - ASTDeclContextNameLookupTrait::data_type Data = *I; - for (; Data.first != Data.second; ++Data.first) { - NamedDecl *ND = Reader.GetLocalDeclAs<NamedDecl>(M, *Data.first); - if (!ND) - continue; - - // Record this declaration. - FoundAnything = true; - if (DeclSet.insert(ND).second) - Decls[ND->getDeclName()].push_back(ND); - } - } - - return FoundAnything && !VisitAll; - } - }; -} - void ASTReader::completeVisibleDeclsMap(const DeclContext *DC) { if (!DC->hasExternalVisibleStorage()) return; - DeclsMap Decls; - // Compute the declaration contexts we need to look into. Multiple such - // declaration contexts occur when two declaration contexts from disjoint - // modules get merged, e.g., when two namespaces with the same name are - // independently defined in separate modules. - SmallVector<const DeclContext *, 2> Contexts; - Contexts.push_back(DC); + auto It = Lookups.find(DC); + assert(It != Lookups.end() && + "have external visible storage but no lookup tables"); - if (DC->isNamespace()) { - KeyDeclsMap::iterator Key = - KeyDecls.find(const_cast<Decl *>(cast<Decl>(DC))); - if (Key != KeyDecls.end()) { - for (unsigned I = 0, N = Key->second.size(); I != N; ++I) - Contexts.push_back(cast<DeclContext>(GetDecl(Key->second[I]))); - } + DeclsMap Decls; + + for (DeclID ID : It->second.Table.findAll()) { + NamedDecl *ND = cast<NamedDecl>(GetDecl(ID)); + Decls[ND->getDeclName()].push_back(ND); } - DeclContextAllNamesVisitor Visitor(*this, Contexts, Decls, - /*VisitAll=*/DC->isFileContext()); - ModuleMgr.visit(Visitor); ++NumVisibleDeclContextsRead; for (DeclsMap::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) { @@ -6552,6 +6409,12 @@ void ASTReader::completeVisibleDeclsMap(const DeclContext *DC) { const_cast<DeclContext *>(DC)->setHasExternalVisibleStorage(false); } +const serialization::reader::DeclContextLookupTable * +ASTReader::getLoadedLookupTables(DeclContext *Primary) const { + auto I = Lookups.find(Primary); + return I == Lookups.end() ? nullptr : &I->second; +} + /// \brief Under non-PCH compilation the consumer receives the objc methods /// before receiving the implementation, and codegen depends on this. /// We simulate this by deserializing and passing to consumer the methods of the @@ -7383,6 +7246,37 @@ Module *ASTReader::getModule(unsigned ID) { return getSubmodule(ID); } +ModuleFile *ASTReader::getLocalModuleFile(ModuleFile &F, unsigned ID) { + if (ID & 1) { + // It's a module, look it up by submodule ID. + auto I = GlobalSubmoduleMap.find(getGlobalSubmoduleID(F, ID >> 1)); + return I == GlobalSubmoduleMap.end() ? nullptr : I->second; + } else { + // It's a prefix (preamble, PCH, ...). Look it up by index. + unsigned IndexFromEnd = ID >> 1; + assert(IndexFromEnd && "got reference to unknown module file"); + return getModuleManager().pch_modules().end()[-IndexFromEnd]; + } +} + +unsigned ASTReader::getModuleFileID(ModuleFile *F) { + if (!F) + return 1; + + // For a file representing a module, use the submodule ID of the top-level + // module as the file ID. For any other kind of file, the number of such + // files loaded beforehand will be the same on reload. + // FIXME: Is this true even if we have an explicit module file and a PCH? + if (F->isModule()) + // FIXME: BaseSubmoduleID appears to be off by one. + return ((F->BaseSubmoduleID + 1) << 1) | 1; + + auto PCHModules = getModuleManager().pch_modules(); + auto I = std::find(PCHModules.begin(), PCHModules.end(), F); + assert(I != PCHModules.end() && "emitting reference to unknown file"); + return (I - PCHModules.end()) << 1; +} + ExternalASTSource::ASTSourceDescriptor ASTReader::getSourceDescriptor(const Module &M) { StringRef Dir, Filename; @@ -8431,6 +8325,8 @@ void ASTReader::FinishedDeserializing() { for (auto Update : Updates) { auto *FPT = Update.second->getType()->castAs<FunctionProtoType>(); auto ESI = FPT->getExtProtoInfo().ExceptionSpec; + if (auto *Listener = Context.getASTMutationListener()) + Listener->ResolvedExceptionSpec(cast<FunctionDecl>(Update.second)); for (auto *Redecl : Update.second->redecls()) Context.adjustExceptionSpec(cast<FunctionDecl>(Redecl), ESI); } diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 25a684a531ef6e9b302a20e2e9a0d2adf87804fd..2677592465d98eac1c6886b3d3c2eb3916b3c77d 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1489,6 +1489,8 @@ void ASTDeclReader::MergeDefinitionData( Reader.PendingDefinitions.erase(MergeDD.Definition); MergeDD.Definition->IsCompleteDefinition = false; mergeDefinitionVisibility(DD.Definition, MergeDD.Definition); + assert(Reader.Lookups.find(MergeDD.Definition) == Reader.Lookups.end() && + "already loaded pending lookups for merged definition"); } auto PFDI = Reader.PendingFakeDefinitionData.find(&DD); @@ -3346,15 +3348,10 @@ void ASTReader::loadDeclUpdateRecords(serialization::DeclID ID, Decl *D) { PendingVisibleUpdates.erase(I); auto *DC = cast<DeclContext>(D)->getPrimaryContext(); - for (const PendingVisibleUpdate &Update : VisibleUpdates) { - auto *&LookupTable = Update.Mod->DeclContextInfos[DC].NameLookupTableData; - assert(!LookupTable && "multiple lookup tables for DC in module"); - LookupTable = reader::ASTDeclContextNameLookupTable::Create( - Update.Data + Update.BucketOffset, - Update.Data + sizeof(uint32_t), - Update.Data, + for (const PendingVisibleUpdate &Update : VisibleUpdates) + Lookups[DC].Table.add( + Update.Mod, Update.Data, reader::ASTDeclContextNameLookupTrait(*this, *Update.Mod)); - } DC->setHasExternalVisibleStorage(true); } diff --git a/lib/Serialization/ASTReaderInternals.h b/lib/Serialization/ASTReaderInternals.h index 9e097000f25307f166190bf66f99c0722b607e76..5c239e3a0213301f6f714c0df4d3a6b1345ac1ec 100644 --- a/lib/Serialization/ASTReaderInternals.h +++ b/lib/Serialization/ASTReaderInternals.h @@ -15,8 +15,12 @@ #include "clang/AST/DeclarationName.h" #include "clang/Serialization/ASTBitCodes.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/Endian.h" #include "llvm/Support/OnDiskHashTable.h" +#include "MultiOnDiskHashTable.h" #include <utility> namespace clang { @@ -39,14 +43,15 @@ class ASTDeclContextNameLookupTrait { ModuleFile &F; public: - /// \brief Pair of begin/end iterators for DeclIDs. - /// - /// Note that these declaration IDs are local to the module that contains this - /// particular lookup t - typedef llvm::support::ulittle32_t LE32DeclID; - typedef std::pair<LE32DeclID *, LE32DeclID *> data_type; + // Maximum number of lookup tables we allow before condensing the tables. + static const int MaxTables = 4; + + /// The lookup result is a list of global declaration IDs. + // FIXME: LLVM doesn't really have a good data structure for this. + typedef llvm::DenseSet<DeclID> data_type; typedef unsigned hash_value_type; typedef unsigned offset_type; + typedef ModuleFile *file_type; typedef DeclarationName external_key_type; typedef DeclarationNameKey internal_key_type; @@ -54,8 +59,7 @@ public: explicit ASTDeclContextNameLookupTrait(ASTReader &Reader, ModuleFile &F) : Reader(Reader), F(F) { } - static bool EqualKey(const internal_key_type& a, - const internal_key_type& b) { + static bool EqualKey(const internal_key_type &a, const internal_key_type &b) { return a == b; } @@ -71,8 +75,18 @@ public: internal_key_type ReadKey(const unsigned char *d, unsigned); - data_type ReadData(internal_key_type, const unsigned char *d, - unsigned DataLen); + void ReadDataInto(internal_key_type, const unsigned char *d, + unsigned DataLen, data_type &Val); + + static void MergeDataInto(const data_type &From, data_type &To) { + To.insert(From.begin(), From.end()); + } + + file_type ReadFileRef(const unsigned char *&d); +}; + +struct DeclContextLookupTable { + MultiOnDiskHashTable<ASTDeclContextNameLookupTrait> Table; }; /// \brief Base class for the trait describing the on-disk hash table for the diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index b5340dc41d230e2bc27c6cb2abd40569cd03e80c..51839afce1a5cffefc33f2ab285774dbfd3bbf2c 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -13,6 +13,8 @@ #include "clang/Serialization/ASTWriter.h" #include "ASTCommon.h" +#include "ASTReaderInternals.h" +#include "MultiOnDiskHashTable.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclContextInternals.h" @@ -3338,12 +3340,14 @@ namespace { // Trait used for the on-disk hash table used in the method pool. class ASTDeclContextNameLookupTrait { ASTWriter &Writer; + llvm::SmallVector<DeclID, 64> DeclIDs; public: typedef DeclarationNameKey key_type; typedef key_type key_type_ref; - typedef DeclContext::lookup_result data_type; + /// A start and end index into DeclIDs, representing a sequence of decls. + typedef std::pair<unsigned, unsigned> data_type; typedef const data_type& data_type_ref; typedef unsigned hash_value_type; @@ -3351,10 +3355,40 @@ public: explicit ASTDeclContextNameLookupTrait(ASTWriter &Writer) : Writer(Writer) { } + template<typename Coll> + data_type getData(const Coll &Decls) { + unsigned Start = DeclIDs.size(); + for (NamedDecl *D : Decls) { + DeclIDs.push_back( + Writer.GetDeclRef(getDeclForLocalLookup(Writer.getLangOpts(), D))); + } + return std::make_pair(Start, DeclIDs.size()); + } + + data_type ImportData(const reader::ASTDeclContextNameLookupTrait::data_type &FromReader) { + unsigned Start = DeclIDs.size(); + for (auto ID : FromReader) + DeclIDs.push_back(ID); + return std::make_pair(Start, DeclIDs.size()); + } + + static bool EqualKey(key_type_ref a, key_type_ref b) { + return a == b; + } + hash_value_type ComputeHash(DeclarationNameKey Name) { return Name.getHash(); } + void EmitFileRef(raw_ostream &Out, ModuleFile *F) const { + assert(Writer.hasChain() && + "have reference to loaded module file but no chain?"); + + using namespace llvm::support; + endian::Writer<little>(Out) + .write<uint32_t>(Writer.getChain()->getModuleFileID(F)); + } + std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &Out, DeclarationNameKey Name, data_type_ref Lookup) { @@ -3381,7 +3415,9 @@ public: LE.write<uint16_t>(KeyLen); // 4 bytes for each DeclID. - unsigned DataLen = 4 * Lookup.size(); + unsigned DataLen = 4 * (Lookup.second - Lookup.first); + assert(uint16_t(DataLen) == DataLen && + "too many decls for serialized lookup result"); LE.write<uint16_t>(DataLen); return std::make_pair(KeyLen, DataLen); @@ -3421,11 +3457,8 @@ public: using namespace llvm::support; endian::Writer<little> LE(Out); uint64_t Start = Out.tell(); (void)Start; - for (DeclContext::lookup_iterator I = Lookup.begin(), E = Lookup.end(); - I != E; ++I) - LE.write<uint32_t>( - Writer.GetDeclRef(getDeclForLocalLookup(Writer.getLangOpts(), *I))); - + for (unsigned I = Lookup.first, N = Lookup.second; I != N; ++I) + LE.write<uint32_t>(DeclIDs[I]); assert(Out.tell() - Start == DataLen && "Data length is wrong"); } }; @@ -3445,7 +3478,7 @@ bool ASTWriter::isLookupResultEntirelyExternal(StoredDeclsList &Result, return true; } -uint32_t +void ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC, llvm::SmallVectorImpl<char> &LookupTable) { assert(!ConstDC->HasLazyLocalLexicalLookups && @@ -3457,8 +3490,8 @@ ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC, assert(DC == DC->getPrimaryContext() && "only primary DC has lookup table"); // Create the on-disk hash table representation. - llvm::OnDiskChainedHashTableGenerator<ASTDeclContextNameLookupTrait> - Generator; + MultiOnDiskHashTableGenerator<reader::ASTDeclContextNameLookupTrait, + ASTDeclContextNameLookupTrait> Generator; ASTDeclContextNameLookupTrait Trait(*this); // The first step is to collect the declaration names which we need to @@ -3593,7 +3626,7 @@ ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC, switch (Name.getNameKind()) { default: - Generator.insert(Name, Result, Trait); + Generator.insert(Name, Trait.getData(Result), Trait); break; case DeclarationName::CXXConstructorName: @@ -3611,17 +3644,15 @@ ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC, // the key, only the kind of name is used. if (!ConstructorDecls.empty()) Generator.insert(ConstructorDecls.front()->getDeclName(), - DeclContext::lookup_result(ConstructorDecls), Trait); + Trait.getData(ConstructorDecls), Trait); if (!ConversionDecls.empty()) Generator.insert(ConversionDecls.front()->getDeclName(), - DeclContext::lookup_result(ConversionDecls), Trait); + Trait.getData(ConversionDecls), Trait); - // Create the on-disk hash table in a buffer. - llvm::raw_svector_ostream Out(LookupTable); - // Make sure that no bucket is at offset 0 - using namespace llvm::support; - endian::Writer<little>(Out).write<uint32_t>(0); - return Generator.Emit(Out, Trait); + // Create the on-disk hash table. Also emit the existing imported and + // merged table if there is one. + auto *Lookups = Chain ? Chain->getLoadedLookupTables(DC) : nullptr; + Generator.emit(LookupTable, Trait, Lookups ? &Lookups->Table : nullptr); } /// \brief Write the block containing all of the declaration IDs @@ -3704,12 +3735,11 @@ uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context, // Create the on-disk hash table in a buffer. SmallString<4096> LookupTable; - uint32_t BucketOffset = GenerateNameLookupTable(DC, LookupTable); + GenerateNameLookupTable(DC, LookupTable); // Write the lookup table RecordData Record; Record.push_back(DECL_CONTEXT_VISIBLE); - Record.push_back(BucketOffset); Stream.EmitRecordWithBlob(DeclContextVisibleLookupAbbrev, Record, LookupTable); ++NumVisibleDeclContexts; @@ -3732,7 +3762,7 @@ void ASTWriter::WriteDeclContextVisibleUpdate(const DeclContext *DC) { // Create the on-disk hash table in a buffer. SmallString<4096> LookupTable; - uint32_t BucketOffset = GenerateNameLookupTable(DC, LookupTable); + GenerateNameLookupTable(DC, LookupTable); // If we're updating a namespace, select a key declaration as the key for the // update record; those are the only ones that will be checked on reload. @@ -3743,7 +3773,6 @@ void ASTWriter::WriteDeclContextVisibleUpdate(const DeclContext *DC) { RecordData Record; Record.push_back(UPDATE_VISIBLE); Record.push_back(getDeclID(cast<Decl>(DC))); - Record.push_back(BucketOffset); Stream.EmitRecordWithBlob(UpdateVisibleAbbrev, Record, LookupTable); } @@ -4207,7 +4236,6 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, Abv = new llvm::BitCodeAbbrev(); Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_VISIBLE)); Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6)); - Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed, 32)); Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)); UpdateVisibleAbbrev = Stream.EmitAbbrev(Abv); WriteDeclContextVisibleUpdate(TU); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 24e07c42a41ede0a315efbc959e51100221a1353..1095a4b4774e7ac0d7c805f70890a56f3db73059 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -2049,7 +2049,6 @@ void ASTWriter::WriteDeclAbbrevs() { Abv = new BitCodeAbbrev(); Abv->Add(BitCodeAbbrevOp(serialization::DECL_CONTEXT_VISIBLE)); - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); DeclContextVisibleLookupAbbrev = Stream.EmitAbbrev(Abv); } diff --git a/lib/Serialization/Module.cpp b/lib/Serialization/Module.cpp index 9111fc40a0270357ea9aa145ab739a16be2434c1..4884f0b0948062f58bb946a36a34285a2d334460 100644 --- a/lib/Serialization/Module.cpp +++ b/lib/Serialization/Module.cpp @@ -45,13 +45,6 @@ ModuleFile::ModuleFile(ModuleKind Kind, unsigned Generation) {} ModuleFile::~ModuleFile() { - for (DeclContextInfosMap::iterator I = DeclContextInfos.begin(), - E = DeclContextInfos.end(); - I != E; ++I) { - if (I->second.NameLookupTableData) - delete I->second.NameLookupTableData; - } - delete static_cast<ASTIdentifierLookupTable *>(IdentifierLookupTable); delete static_cast<HeaderFileInfoLookupTable *>(HeaderFileInfoTable); delete static_cast<ASTSelectorLookupTable *>(SelectorLookupTable); diff --git a/lib/Serialization/MultiOnDiskHashTable.h b/lib/Serialization/MultiOnDiskHashTable.h new file mode 100644 index 0000000000000000000000000000000000000000..bf06a77912f70f818010b497b3065fc8e35d752a --- /dev/null +++ b/lib/Serialization/MultiOnDiskHashTable.h @@ -0,0 +1,318 @@ +//===--- MultiOnDiskHashTable.h - Merged set of hash tables -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides a hash table data structure suitable for incremental and +// distributed storage across a set of files. +// +// Multiple hash tables from different files are implicitly merged to improve +// performance, and on reload the merged table will override those from other +// files. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_LIB_SERIALIZATION_MULTIONDISKHASHTABLE_H +#define LLVM_CLANG_LIB_SERIALIZATION_MULTIONDISKHASHTABLE_H + +#include "llvm/ADT/PointerUnion.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/OnDiskHashTable.h" + +namespace clang { +namespace serialization { + +class ModuleFile; + +/// \brief A collection of on-disk hash tables, merged when relevant for performance. +template<typename Info> class MultiOnDiskHashTable { +public: + /// A handle to a file, used when overriding tables. + typedef typename Info::file_type file_type; + /// A pointer to an on-disk representation of the hash table. + typedef const unsigned char *storage_type; + + typedef typename Info::external_key_type external_key_type; + typedef typename Info::internal_key_type internal_key_type; + typedef typename Info::data_type data_type; + typedef unsigned hash_value_type; + +private: + /// \brief A hash table stored on disk. + struct OnDiskTable { + typedef llvm::OnDiskIterableChainedHashTable<Info> HashTable; + + file_type File; + HashTable Table; + + OnDiskTable(file_type File, unsigned NumBuckets, unsigned NumEntries, + storage_type Buckets, storage_type Payload, storage_type Base, + const Info &InfoObj) + : File(File), + Table(NumBuckets, NumEntries, Buckets, Payload, Base, InfoObj) {} + }; + + struct MergedTable { + std::vector<file_type> Files; + llvm::DenseMap<internal_key_type, data_type> Data; + }; + + typedef llvm::PointerUnion<OnDiskTable*, MergedTable*> Table; + typedef llvm::TinyPtrVector<void*> TableVector; + + /// \brief The current set of on-disk and merged tables. + /// We manually store the opaque value of the Table because TinyPtrVector + /// can't cope with holding a PointerUnion directly. + /// There can be at most one MergedTable in this vector, and if present, + /// it is the first table. + TableVector Tables; + + /// \brief Files corresponding to overridden tables that we've not yet + /// discarded. + llvm::TinyPtrVector<file_type> PendingOverrides; + + struct AsOnDiskTable { + typedef OnDiskTable *result_type; + result_type operator()(void *P) const { + return Table::getFromOpaqueValue(P).template get<OnDiskTable *>(); + } + }; + typedef llvm::mapped_iterator<TableVector::iterator, AsOnDiskTable> + table_iterator; + typedef llvm::iterator_range<table_iterator> table_range; + + /// \brief The current set of on-disk tables. + table_range tables() { + auto Begin = Tables.begin(), End = Tables.end(); + if (getMergedTable()) + ++Begin; + return llvm::make_range(llvm::map_iterator(Begin, AsOnDiskTable()), + llvm::map_iterator(End, AsOnDiskTable())); + } + + MergedTable *getMergedTable() const { + // If we already have a merged table, it's the first one. + return Tables.empty() ? nullptr : Table::getFromOpaqueValue(*Tables.begin()) + .template dyn_cast<MergedTable*>(); + } + + /// \brief Delete all our current on-disk tables. + void clear() { + if (auto *M = getMergedTable()) + delete M; + for (auto *T : tables()) + delete T; + } + + void removeOverriddenTables() { + llvm::DenseSet<file_type> Files; + Files.insert(PendingOverrides.begin(), PendingOverrides.end()); + Tables.erase( + std::remove_if(tables().begin().getCurrent(), Tables.end(), [&](void *T) -> bool { + auto *ODT = Table::getFromOpaqueValue(T).template get<OnDiskTable*>(); + return Files.count(ODT->File); + }), Tables.end()); + PendingOverrides.clear(); + } + + void condense() { + MergedTable *Merged = getMergedTable(); + if (!Merged) + Merged = new MergedTable; + + // Read in all the tables and merge them together. + // FIXME: Be smarter about which tables we merge. + for (auto *ODT : tables()) { + auto &HT = ODT->Table; + Info &InfoObj = HT.getInfoObj(); + + for (auto I = HT.data_begin(), E = HT.data_end(); I != E; ++I) { + auto *LocalPtr = I.getItem(); + + // FIXME: Don't rely on the OnDiskHashTable format here. + auto L = InfoObj.ReadKeyDataLength(LocalPtr); + const internal_key_type &Key = InfoObj.ReadKey(LocalPtr, L.first); + InfoObj.ReadDataInto(Key, LocalPtr + L.first, L.second, + Merged->Data[Key]); + } + + Merged->Files.push_back(ODT->File); + delete ODT; + } + + Tables.clear(); + Tables.push_back(Table(Merged).getOpaqueValue()); + } + + /// The generator is permitted to read our merged table. + template<typename ReaderInfo, typename WriterInfo> + friend class MultiOnDiskHashTableGenerator; + +public: + MultiOnDiskHashTable() {} + MultiOnDiskHashTable(MultiOnDiskHashTable &&O) + : Tables(std::move(O.Tables)), + PendingOverrides(std::move(O.PendingOverrides)) { + O.Tables.clear(); + } + MultiOnDiskHashTable &operator=(MultiOnDiskHashTable &&O) { + if (&O == this) + return *this; + clear(); + Tables = std::move(O.Tables); + O.Tables.clear(); + PendingOverrides = std::move(O.PendingOverrides); + return *this; + } + ~MultiOnDiskHashTable() { clear(); } + + /// \brief Add the table \p Data loaded from file \p File. + void add(file_type File, storage_type Data, Info InfoObj = Info()) { + using namespace llvm::support; + storage_type Ptr = Data; + + uint32_t BucketOffset = endian::readNext<uint32_t, little, unaligned>(Ptr); + + // Read the list of overridden files. + uint32_t NumFiles = endian::readNext<uint32_t, little, unaligned>(Ptr); + // FIXME: Add a reserve() to TinyPtrVector so that we don't need to make + // an additional copy. + llvm::SmallVector<file_type, 16> OverriddenFiles; + OverriddenFiles.reserve(NumFiles); + for (/**/; NumFiles != 0; --NumFiles) + OverriddenFiles.push_back(InfoObj.ReadFileRef(Ptr)); + PendingOverrides.insert(PendingOverrides.end(), OverriddenFiles.begin(), + OverriddenFiles.end()); + + // Read the OnDiskChainedHashTable header. + storage_type Buckets = Data + BucketOffset; + auto NumBucketsAndEntries = + OnDiskTable::HashTable::readNumBucketsAndEntries(Buckets); + + // Register the table. + Table NewTable = new OnDiskTable(File, NumBucketsAndEntries.first, + NumBucketsAndEntries.second, + Buckets, Ptr, Data, std::move(InfoObj)); + Tables.push_back(NewTable.getOpaqueValue()); + } + + /// \brief Find and read the lookup results for \p EKey. + data_type find(const external_key_type &EKey) { + data_type Result; + + if (!PendingOverrides.empty()) + removeOverriddenTables(); + + if (Tables.size() > Info::MaxTables) + condense(); + + internal_key_type Key = Info::GetInternalKey(EKey); + auto KeyHash = Info::ComputeHash(Key); + + if (MergedTable *M = getMergedTable()) { + auto It = M->Data.find(Key); + if (It != M->Data.end()) + Result = It->second; + } + + for (auto *ODT : tables()) { + auto &HT = ODT->Table; + auto It = HT.find_hashed(Key, KeyHash); + if (It != HT.end()) + HT.getInfoObj().ReadDataInto(Key, It.getDataPtr(), It.getDataLen(), + Result); + } + + return Result; + } + + /// \brief Read all the lookup results into a single value. This only makes + /// sense if merging values across keys is meaningful. + data_type findAll() { + data_type Result; + + if (!PendingOverrides.empty()) + removeOverriddenTables(); + + if (MergedTable *M = getMergedTable()) { + for (auto &KV : M->Data) + Info::MergeDataInto(KV.second, Result); + } + + for (auto *ODT : tables()) { + auto &HT = ODT->Table; + Info &InfoObj = HT.getInfoObj(); + for (auto I = HT.data_begin(), E = HT.data_end(); I != E; ++I) { + auto *LocalPtr = I.getItem(); + + // FIXME: Don't rely on the OnDiskHashTable format here. + auto L = InfoObj.ReadKeyDataLength(LocalPtr); + const internal_key_type &Key = InfoObj.ReadKey(LocalPtr, L.first); + InfoObj.ReadDataInto(Key, LocalPtr + L.first, L.second, Result); + } + } + + return Result; + } +}; + +/// \brief Writer for the on-disk hash table. +template<typename ReaderInfo, typename WriterInfo> +class MultiOnDiskHashTableGenerator { + typedef MultiOnDiskHashTable<ReaderInfo> BaseTable; + typedef llvm::OnDiskChainedHashTableGenerator<WriterInfo> Generator; + + Generator Gen; + +public: + MultiOnDiskHashTableGenerator() : Gen() {} + + void insert(typename WriterInfo::key_type_ref Key, + typename WriterInfo::data_type_ref Data, WriterInfo &Info) { + Gen.insert(Key, Data, Info); + } + + void emit(llvm::SmallVectorImpl<char> &Out, WriterInfo &Info, + const BaseTable *Base) { + using namespace llvm::support; + llvm::raw_svector_ostream OutStream(Out); + + // Write our header information. + { + endian::Writer<little> Writer(OutStream); + + // Reserve four bytes for the bucket offset. + Writer.write<uint32_t>(0); + + if (auto *Merged = Base ? Base->getMergedTable() : nullptr) { + // Write list of overridden files. + Writer.write<uint32_t>(Merged->Files.size()); + for (const auto &F : Merged->Files) + Info.EmitFileRef(OutStream, F); + + // Add all merged entries from Base to the generator. + for (auto &KV : Merged->Data) { + if (!Gen.contains(KV.first, Info)) + Gen.insert(KV.first, Info.ImportData(KV.second), Info); + } + } else { + Writer.write<uint32_t>(0); + } + } + + // Write the table itself. + uint32_t BucketOffset = Gen.Emit(OutStream, Info); + + // Replace the first four bytes with the bucket offset. + endian::write32le(Out.data(), BucketOffset); + } +}; + +} // end namespace clang::serialization +} // end namespace clang + + +#endif diff --git a/test/Modules/cxx-templates.cpp b/test/Modules/cxx-templates.cpp index 8e91b8247f2101a8e521fd9a83c298747710a18a..ea6f05d3318629ddccfa527642136a403d2d32aa 100644 --- a/test/Modules/cxx-templates.cpp +++ b/test/Modules/cxx-templates.cpp @@ -29,14 +29,14 @@ void g() { f<int>(); f(); // expected-error {{no matching function}} // expected-note@Inputs/cxx-templates-b.h:3 {{couldn't infer template argument}} - // expected-note@Inputs/cxx-templates-b.h:4 {{requires single argument}} + // expected-note-re@Inputs/cxx-templates-a.h:4 {{requires {{single|1}} argument}} N::f(0); N::f<double>(1.0); N::f<int>(); N::f(); // expected-error {{no matching function}} // expected-note@Inputs/cxx-templates-b.h:6 {{couldn't infer template argument}} - // expected-note@Inputs/cxx-templates-b.h:7 {{requires single argument}} + // expected-note-re@Inputs/cxx-templates-a.h:7 {{requires {{single|1}} argument}} template_param_kinds_1<0>(); // ok, from cxx-templates-a.h template_param_kinds_1<int>(); // ok, from cxx-templates-b.h @@ -179,10 +179,14 @@ namespace Std { // CHECK-GLOBAL: DeclarationName 'f' // CHECK-GLOBAL-NEXT: |-FunctionTemplate {{.*}} 'f' +// CHECK-GLOBAL-NEXT: |-FunctionTemplate {{.*}} 'f' +// CHECK-GLOBAL-NEXT: |-FunctionTemplate {{.*}} 'f' // CHECK-GLOBAL-NEXT: `-FunctionTemplate {{.*}} 'f' // CHECK-NAMESPACE-N: DeclarationName 'f' // CHECK-NAMESPACE-N-NEXT: |-FunctionTemplate {{.*}} 'f' +// CHECK-NAMESPACE-N-NEXT: |-FunctionTemplate {{.*}} 'f' +// CHECK-NAMESPACE-N-NEXT: |-FunctionTemplate {{.*}} 'f' // CHECK-NAMESPACE-N-NEXT: `-FunctionTemplate {{.*}} 'f' // CHECK-DUMP: ClassTemplateDecl {{.*}} <{{.*[/\\]}}cxx-templates-common.h:1:1, {{.*}}> col:{{.*}} in cxx_templates_common SomeTemplate diff --git a/test/Modules/merge-using-decls.cpp b/test/Modules/merge-using-decls.cpp index 789f75b574003fd52d776a4098b240b4c192bca1..15d4af4837a869e21f2c4f23ef1955702db19f82 100644 --- a/test/Modules/merge-using-decls.cpp +++ b/test/Modules/merge-using-decls.cpp @@ -31,7 +31,9 @@ template int UseAll<YA>(); template int UseAll<YB>(); template int UseAll<Y>(); -#if ORDER == 1 +// Which of these two sets of diagnostics is chosen is not important. It's OK +// if this varies with ORDER, but it must be consistent across runs. +#if 1 // Here, we're instantiating the definition from 'A' and merging the definition // from 'B' into it.