diff options
Diffstat (limited to 'lib/Serialization/GlobalModuleIndex.cpp')
-rw-r--r-- | lib/Serialization/GlobalModuleIndex.cpp | 404 |
1 files changed, 387 insertions, 17 deletions
diff --git a/lib/Serialization/GlobalModuleIndex.cpp b/lib/Serialization/GlobalModuleIndex.cpp index 1600e357dd..bdd33642eb 100644 --- a/lib/Serialization/GlobalModuleIndex.cpp +++ b/lib/Serialization/GlobalModuleIndex.cpp @@ -42,7 +42,7 @@ namespace { enum IndexRecordTypes { /// \brief Contains version information and potentially other metadata, /// used to determine if we can read this global index file. - METADATA, + INDEX_METADATA, /// \brief Describes a module, including its file name and dependencies. MODULE, /// \brief The index for identifiers. @@ -57,6 +57,378 @@ static const char * const IndexFileName = "modules.idx"; static const unsigned CurrentVersion = 1; //----------------------------------------------------------------------------// +// Global module index reader. +//----------------------------------------------------------------------------// + +namespace { + +/// \brief Trait used to read the identifier index from the on-disk hash +/// table. +class IdentifierIndexReaderTrait { +public: + typedef StringRef external_key_type; + typedef StringRef internal_key_type; + typedef SmallVector<unsigned, 2> data_type; + + static bool EqualKey(const internal_key_type& a, const internal_key_type& b) { + return a == b; + } + + static unsigned ComputeHash(const internal_key_type& a) { + return llvm::HashString(a); + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const unsigned char*& d) { + using namespace clang::io; + unsigned KeyLen = ReadUnalignedLE16(d); + unsigned DataLen = ReadUnalignedLE16(d); + return std::make_pair(KeyLen, DataLen); + } + + static const internal_key_type& + GetInternalKey(const external_key_type& x) { return x; } + + static const external_key_type& + GetExternalKey(const internal_key_type& x) { return x; } + + static internal_key_type ReadKey(const unsigned char* d, unsigned n) { + return StringRef((const char *)d, n); + } + + static data_type ReadData(const internal_key_type& k, + const unsigned char* d, + unsigned DataLen) { + using namespace clang::io; + + data_type Result; + while (DataLen > 0) { + unsigned ID = ReadUnalignedLE32(d); + Result.push_back(ID); + DataLen -= 4; + } + + return Result; + } +}; + +typedef OnDiskChainedHashTable<IdentifierIndexReaderTrait> IdentifierIndexTable; + +/// \brief Module information as it was loaded from the index file. +struct LoadedModuleInfo { + const FileEntry *File; + SmallVector<unsigned, 2> Dependencies; + SmallVector<unsigned, 2> ImportedBy; +}; + +} + +GlobalModuleIndex::GlobalModuleIndex(FileManager &FileMgr, + llvm::MemoryBuffer *Buffer, + llvm::BitstreamCursor Cursor) + : Buffer(Buffer), IdentifierIndex() +{ + typedef llvm::DenseMap<unsigned, LoadedModuleInfo> LoadedModulesMap; + LoadedModulesMap LoadedModules; + + // Read the global index. + unsigned LargestID = 0; + bool InGlobalIndexBlock = false; + bool Done = false; + bool AnyOutOfDate = false; + while (!Done) { + llvm::BitstreamEntry Entry = Cursor.advance(); + + switch (Entry.Kind) { + case llvm::BitstreamEntry::Error: + return; + + case llvm::BitstreamEntry::EndBlock: + if (InGlobalIndexBlock) { + InGlobalIndexBlock = false; + Done = true; + continue; + } + return; + + + case llvm::BitstreamEntry::Record: + // Entries in the global index block are handled below. + if (InGlobalIndexBlock) + break; + + return; + + case llvm::BitstreamEntry::SubBlock: + if (!InGlobalIndexBlock && Entry.ID == GLOBAL_INDEX_BLOCK_ID) { + if (Cursor.EnterSubBlock(GLOBAL_INDEX_BLOCK_ID)) + return; + + InGlobalIndexBlock = true; + } else if (Cursor.SkipBlock()) { + return; + } + continue; + } + + SmallVector<uint64_t, 64> Record; + StringRef Blob; + switch ((IndexRecordTypes)Cursor.readRecord(Entry.ID, Record, &Blob)) { + case INDEX_METADATA: + // Make sure that the version matches. + if (Record.size() < 1 || Record[0] != CurrentVersion) + return; + break; + + case MODULE: { + unsigned Idx = 0; + unsigned ID = Record[Idx++]; + if (ID > LargestID) + LargestID = ID; + + off_t Size = Record[Idx++]; + time_t ModTime = Record[Idx++]; + + // File name. + unsigned NameLen = Record[Idx++]; + llvm::SmallString<64> FileName(Record.begin() + Idx, + Record.begin() + Idx + NameLen); + Idx += NameLen; + + // Dependencies + unsigned NumDeps = Record[Idx++]; + llvm::SmallVector<unsigned, 2> + Dependencies(Record.begin() + Idx, Record.begin() + Idx + NumDeps); + + // Find the file. If we can't find it, ignore it. + const FileEntry *File = FileMgr.getFile(FileName); + if (!File) { + AnyOutOfDate = true; + break; + } + + // If the module file is newer than the index, ignore it. + if (File->getSize() != Size || File->getModificationTime() != ModTime) { + AnyOutOfDate = true; + break; + } + + // Record this module. The dependencies will be resolved later. + LoadedModuleInfo &Info = LoadedModules[ID]; + Info.File = File; + Info.Dependencies.swap(Dependencies); + break; + } + + case IDENTIFIER_INDEX: + // Wire up the identifier index. + if (Record[0]) { + IdentifierIndex = IdentifierIndexTable::Create( + (const unsigned char *)Blob.data() + Record[0], + (const unsigned char *)Blob.data(), + IdentifierIndexReaderTrait()); + } + break; + } + } + + // If there are any modules that have gone out-of-date, prune out any modules + // that depend on them. + if (AnyOutOfDate) { + // First, build back links in the module dependency graph. + SmallVector<unsigned, 4> Stack; + for (LoadedModulesMap::iterator LM = LoadedModules.begin(), + LMEnd = LoadedModules.end(); + LM != LMEnd; ++LM) { + unsigned ID = LM->first; + + // If this module is out-of-date, push it onto the stack. + if (LM->second.File == 0) + Stack.push_back(ID); + + for (unsigned I = 0, N = LM->second.Dependencies.size(); I != N; ++I) { + unsigned DepID = LM->second.Dependencies[I]; + LoadedModulesMap::iterator Known = LoadedModules.find(DepID); + if (Known == LoadedModules.end() || !Known->second.File) { + // The dependency was out-of-date, so mark us as out of date. + // This is just an optimization. + if (LM->second.File) + Stack.push_back(ID); + + LM->second.File = 0; + continue; + } + + // Record this reverse dependency. + Known->second.ImportedBy.push_back(ID); + } + } + + // Second, walk the back links from out-of-date modules to those modules + // that depend on them, making those modules out-of-date as well. + while (!Stack.empty()) { + unsigned ID = Stack.back(); + Stack.pop_back(); + + LoadedModuleInfo &Info = LoadedModules[ID]; + for (unsigned I = 0, N = Info.ImportedBy.size(); I != N; ++I) { + unsigned FromID = Info.ImportedBy[I]; + if (LoadedModules[FromID].File) { + LoadedModules[FromID].File = 0; + Stack.push_back(FromID); + } + } + } + } + + // Allocate the vector containing information about all of the modules. + Modules.resize(LargestID + 1); + for (LoadedModulesMap::iterator LM = LoadedModules.begin(), + LMEnd = LoadedModules.end(); + LM != LMEnd; ++LM) { + if (!LM->second.File) + continue; + + Modules[LM->first].File = LM->second.File; + + // Resolve dependencies. Drop any we can't resolve due to out-of-date + // module files. + for (unsigned I = 0, N = LM->second.Dependencies.size(); I != N; ++I) { + unsigned DepID = LM->second.Dependencies[I]; + LoadedModulesMap::iterator Known = LoadedModules.find(DepID); + if (Known == LoadedModules.end() || !Known->second.File) + continue; + + Modules[LM->first].Dependencies.push_back(Known->second.File); + } + } +} + +GlobalModuleIndex::~GlobalModuleIndex() { } + +std::pair<GlobalModuleIndex *, GlobalModuleIndex::ErrorCode> +GlobalModuleIndex::readIndex(FileManager &FileMgr, StringRef Path) { + // Load the index file, if it's there. + llvm::SmallString<128> IndexPath; + IndexPath += Path; + llvm::sys::path::append(IndexPath, IndexFileName); + + llvm::OwningPtr<llvm::MemoryBuffer> Buffer( + FileMgr.getBufferForFile(IndexPath)); + if (!Buffer) + return std::make_pair((GlobalModuleIndex *)0, EC_NotFound); + + /// \brief The bitstream reader from which we'll read the AST file. + llvm::BitstreamReader Reader((const unsigned char *)Buffer->getBufferStart(), + (const unsigned char *)Buffer->getBufferEnd()); + + /// \brief The main bitstream cursor for the main block. + llvm::BitstreamCursor Cursor(Reader); + + // Sniff for the signature. + if (Cursor.Read(8) != 'B' || + Cursor.Read(8) != 'C' || + Cursor.Read(8) != 'G' || + Cursor.Read(8) != 'I') { + return std::make_pair((GlobalModuleIndex *)0, EC_IOError); + } + + return std::make_pair(new GlobalModuleIndex(FileMgr, Buffer.take(), Cursor), + EC_None); +} + +void GlobalModuleIndex::getKnownModules( + SmallVectorImpl<const FileEntry *> &ModuleFiles) { + ModuleFiles.clear(); + for (unsigned I = 0, N = Modules.size(); I != N; ++I) { + if (Modules[I].File) + ModuleFiles.push_back(Modules[I].File); + } +} + +void GlobalModuleIndex::getModuleDependencies( + const clang::FileEntry *ModuleFile, + SmallVectorImpl<const clang::FileEntry *> &Dependencies) { + // If the file -> index mapping is empty, populate it now. + if (ModulesByFile.empty()) { + for (unsigned I = 0, N = Modules.size(); I != N; ++I) { + if (Modules[I].File) + ModulesByFile[Modules[I].File] = I; + } + } + + // Look for information about this module file. + llvm::DenseMap<const FileEntry *, unsigned>::iterator Known + = ModulesByFile.find(ModuleFile); + if (Known == ModulesByFile.end()) + return; + + // Record dependencies. + Dependencies = Modules[Known->second].Dependencies; +} + +bool GlobalModuleIndex::lookupIdentifier( + StringRef Name, + SmallVectorImpl<const FileEntry *> &ModuleFiles) { + ModuleFiles.clear(); + + // If there's no identifier index, there is nothing we can do. + if (!IdentifierIndex) + return false; + + // Look into the identifier index. + ++NumIdentifierLookups; + IdentifierIndexTable &Table + = *static_cast<IdentifierIndexTable *>(IdentifierIndex); + IdentifierIndexTable::iterator Known = Table.find(Name); + if (Known == Table.end()) { + return true; + } + + SmallVector<unsigned, 2> ModuleIDs = *Known; + for (unsigned I = 0, N = ModuleIDs.size(); I != N; ++I) { + unsigned ID = ModuleIDs[I]; + if (ID >= Modules.size() || !Modules[ID].File) + continue; + + ModuleFiles.push_back(Modules[ID].File); + } + + ++NumIdentifierLookupHits; + return true; +} + +GlobalModuleIndex::SkipSet +GlobalModuleIndex::computeSkipSet( + const SmallVectorImpl<const FileEntry *> &ModuleFiles) { + llvm::SmallPtrSet<const FileEntry *, 8> Found(ModuleFiles.begin(), + ModuleFiles.end()); + + SkipSet Result; + for (unsigned I = 0, N = Modules.size(); I != N; ++I) { + if (Modules[I].File && !Found.count(Modules[I].File)) + Result.insert(Modules[I].File); + } + + NumIdentifierModulesSkipped += Result.size(); + return Result; +} + +void GlobalModuleIndex::printStats() { + std::fprintf(stderr, "*** Global Module Index Statistics:\n"); + if (NumIdentifierLookups) { + fprintf(stderr, " %u / %u identifier lookups succeeded (%f%%)\n", + NumIdentifierLookupHits, NumIdentifierLookups, + (double)NumIdentifierLookupHits*100.0/NumIdentifierLookups); + } + if (NumIdentifierLookups && NumIdentifierModulesSkipped) { + fprintf(stderr, " %f modules skipped per lookup (on average)\n", + (double)NumIdentifierModulesSkipped/NumIdentifierLookups); + } + std::fprintf(stderr, "\n"); +} + +//----------------------------------------------------------------------------// // Global module index writer. //----------------------------------------------------------------------------// @@ -151,7 +523,7 @@ GlobalModuleIndexBuilder::emitBlockInfoBlock(llvm::BitstreamWriter &Stream) { #define BLOCK(X) emitBlockID(X ## _ID, #X, Stream, Record) #define RECORD(X) emitRecordID(X, #X, Stream, Record) BLOCK(GLOBAL_INDEX_BLOCK); - RECORD(METADATA); + RECORD(INDEX_METADATA); RECORD(MODULE); RECORD(IDENTIFIER_INDEX); #undef RECORD @@ -160,7 +532,7 @@ GlobalModuleIndexBuilder::emitBlockInfoBlock(llvm::BitstreamWriter &Stream) { Stream.ExitBlock(); } -namespace clang { +namespace { class InterestingASTIdentifierLookupTrait : public serialization::reader::ASTIdentifierLookupTraitBase { @@ -209,18 +581,18 @@ bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) { unsigned ID = getModuleFileInfo(File).ID; // Search for the blocks and records we care about. - enum { Outer, ControlBlock, ASTBlock } State = Outer; + enum { Other, ControlBlock, ASTBlock } State = Other; bool Done = false; while (!Done) { - const unsigned Flags = llvm::BitstreamCursor::AF_DontPopBlockAtEnd; - llvm::BitstreamEntry Entry = InStream.advance(Flags); + llvm::BitstreamEntry Entry = InStream.advance(); switch (Entry.Kind) { case llvm::BitstreamEntry::Error: - return true; + Done = true; + continue; case llvm::BitstreamEntry::Record: - // In the outer state, just skip the record. We don't care. - if (State == Outer) { + // In the 'other' state, just skip the record. We don't care. + if (State == Other) { InStream.skipRecord(Entry.ID); continue; } @@ -229,7 +601,7 @@ bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) { break; case llvm::BitstreamEntry::SubBlock: - if (State == Outer && Entry.ID == CONTROL_BLOCK_ID) { + if (Entry.ID == CONTROL_BLOCK_ID) { if (InStream.EnterSubBlock(CONTROL_BLOCK_ID)) return true; @@ -238,14 +610,13 @@ bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) { continue; } - if (State == Outer && Entry.ID == AST_BLOCK_ID) { + if (Entry.ID == AST_BLOCK_ID) { if (InStream.EnterSubBlock(AST_BLOCK_ID)) return true; // Found the AST block. State = ASTBlock; continue; - } if (InStream.SkipBlock()) @@ -254,10 +625,7 @@ bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) { continue; case llvm::BitstreamEntry::EndBlock: - if (State == Outer) { - Done = true; - } - State = Outer; + State = Other; continue; } @@ -312,6 +680,8 @@ bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) { std::pair<StringRef, bool> Ident = *D; if (Ident.second) InterestingIdentifiers[Ident.first].push_back(ID); + else + (void)InterestingIdentifiers[Ident.first]; } } @@ -378,7 +748,7 @@ void GlobalModuleIndexBuilder::writeIndex(llvm::BitstreamWriter &Stream) { // Write the metadata. SmallVector<uint64_t, 2> Record; Record.push_back(CurrentVersion); - Stream.EmitRecord(METADATA, Record); + Stream.EmitRecord(INDEX_METADATA, Record); // Write the set of known module files. for (ModuleFilesMap::iterator M = ModuleFiles.begin(), |