diff --git a/clang/include/clang/Serialization/ModuleManager.h b/clang/include/clang/Serialization/ModuleManager.h --- a/clang/include/clang/Serialization/ModuleManager.h +++ b/clang/include/clang/Serialization/ModuleManager.h @@ -59,8 +59,67 @@ // to implement short-circuiting logic when running DFS over the dependencies. SmallVector Roots; + /// An \c EntryKey is a thin wrapper around a \c FileEntry that implements + /// a richer notion of identity. + /// + /// A plain \c FileEntry has its identity tied to inode numbers. When the + /// module cache regenerates a PCM, some filesystem allocators may reuse + /// inode numbers for distinct modules, which can cause the cache to return + /// mismatched entries. An \c EntryKey ensures that the size and modification + /// time are taken into account when determining the identity of a key, which + /// significantly decreases - but does not eliminate - the chance of + /// a collision. + struct EntryKey { + const FileEntry *Entry; + off_t Size; + time_t ModTime; + + EntryKey(const FileEntry *Entry) : Entry(Entry), Size(0), ModTime(0) { + if (Entry) { + Size = Entry->getSize(); + ModTime = Entry->getModificationTime(); + } + } + + EntryKey(const FileEntry *Entry, off_t Size, time_t ModTime) + : Entry(Entry), Size(Size), ModTime(ModTime) {} + + struct Info { + static inline EntryKey getEmptyKey() { + return EntryKey{ + llvm::DenseMapInfo::getEmptyKey(), + llvm::DenseMapInfo::getEmptyKey(), + llvm::DenseMapInfo::getEmptyKey(), + }; + } + static inline EntryKey getTombstoneKey() { + return EntryKey{ + llvm::DenseMapInfo::getTombstoneKey(), + llvm::DenseMapInfo::getTombstoneKey(), + llvm::DenseMapInfo::getTombstoneKey(), + }; + } + static unsigned getHashValue(const EntryKey &Val) { + return llvm::DenseMapInfo::getHashValue(Val.Entry); + } + static bool isEqual(const EntryKey &LHS, const EntryKey &RHS) { + if (LHS.Entry == getEmptyKey().Entry || + LHS.Entry == getTombstoneKey().Entry || + RHS.Entry == getEmptyKey().Entry || + RHS.Entry == getTombstoneKey().Entry) { + return LHS.Entry == RHS.Entry; + } + if (LHS.Entry == nullptr || RHS.Entry == nullptr) { + return LHS.Entry == RHS.Entry; + } + return LHS.Entry == RHS.Entry && LHS.Size == RHS.Size && + LHS.ModTime == RHS.ModTime; + } + }; + }; + /// All loaded modules, indexed by name. - llvm::DenseMap Modules; + llvm::DenseMap Modules; /// FileManager that handles translating between filenames and /// FileEntry *. @@ -76,7 +135,7 @@ const HeaderSearch &HeaderSearchInfo; /// A lookup of in-memory (virtual file) buffers - llvm::DenseMap> + llvm::DenseMap, EntryKey::Info> InMemoryBuffers; /// The visitation order. diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp --- a/clang/lib/Serialization/ModuleManager.cpp +++ b/clang/lib/Serialization/ModuleManager.cpp @@ -59,7 +59,7 @@ } ModuleFile *ModuleManager::lookup(const FileEntry *File) const { - auto Known = Modules.find(File); + auto Known = Modules.find(EntryKey{File}); if (Known == Modules.end()) return nullptr; @@ -72,7 +72,7 @@ /*CacheFailure=*/false); if (!Entry) return nullptr; - return std::move(InMemoryBuffers[*Entry]); + return std::move(InMemoryBuffers[EntryKey{*Entry}]); } static bool checkSignature(ASTFileSignature Signature, @@ -133,7 +133,7 @@ } // Check whether we already loaded this module, before - if (ModuleFile *ModuleEntry = Modules.lookup(Entry)) { + if (ModuleFile *ModuleEntry = Modules.lookup(EntryKey{Entry})) { // Check the stored signature. if (checkSignature(ModuleEntry->Signature, ExpectedSignature, ErrorStr)) return OutOfDate; @@ -208,7 +208,7 @@ return OutOfDate; // We're keeping this module. Store it everywhere. - Module = Modules[Entry] = NewModule.get(); + Module = Modules[EntryKey{Entry}] = NewModule.get(); updateModuleImports(*NewModule, ImportedBy, ImportLoc); @@ -255,7 +255,7 @@ // Delete the modules and erase them from the various structures. for (ModuleIterator victim = First; victim != Last; ++victim) { - Modules.erase(victim->File); + Modules.erase(EntryKey{victim->File}); if (modMap) { StringRef ModuleName = victim->ModuleName; @@ -274,7 +274,7 @@ std::unique_ptr Buffer) { const FileEntry *Entry = FileMgr.getVirtualFile(FileName, Buffer->getBufferSize(), 0); - InMemoryBuffers[Entry] = std::move(Buffer); + InMemoryBuffers[EntryKey{Entry}] = std::move(Buffer); } ModuleManager::VisitState *ModuleManager::allocateVisitState() {