Skip to content

Commit 0cf4a51

Browse files
authored
Merge pull request #1718 from CodaFi/i-node-nothing
Perform an Extra Consistency Check When Searching The ModuleManager's Cache For Implicit Modules
2 parents 87701de + 01595d7 commit 0cf4a51

File tree

2 files changed

+38
-57
lines changed

2 files changed

+38
-57
lines changed

clang/include/clang/Serialization/ModuleManager.h

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -59,50 +59,8 @@ class ModuleManager {
5959
// to implement short-circuiting logic when running DFS over the dependencies.
6060
SmallVector<ModuleFile *, 2> Roots;
6161

62-
/// An \c EntryKey is a thin wrapper around a \c FileEntry that implements
63-
/// a richer notion of identity.
64-
///
65-
/// A plain \c FileEntry has its identity tied to inode numbers. When the
66-
/// module cache regenerates a PCM, some filesystem allocators may reuse
67-
/// inode numbers for distinct modules, which can cause the cache to return
68-
/// mismatched entries. An \c EntryKey ensures that the size and modification
69-
/// time are taken into account when determining the identity of a key, which
70-
/// significantly decreases - but does not eliminate - the chance of
71-
/// a collision.
72-
struct EntryKey {
73-
const FileEntry *Entry;
74-
75-
struct Info {
76-
static inline EntryKey getEmptyKey() {
77-
return EntryKey{llvm::DenseMapInfo<const FileEntry *>::getEmptyKey()};
78-
}
79-
static inline EntryKey getTombstoneKey() {
80-
return EntryKey{
81-
llvm::DenseMapInfo<const FileEntry *>::getTombstoneKey()};
82-
}
83-
static unsigned getHashValue(const EntryKey &Val) {
84-
return llvm::DenseMapInfo<const FileEntry *>::getHashValue(Val.Entry);
85-
}
86-
static bool isEqual(const EntryKey &LHS, const EntryKey &RHS) {
87-
if (LHS.Entry == getEmptyKey().Entry ||
88-
LHS.Entry == getTombstoneKey().Entry ||
89-
RHS.Entry == getEmptyKey().Entry ||
90-
RHS.Entry == getTombstoneKey().Entry) {
91-
return LHS.Entry == RHS.Entry;
92-
}
93-
if (LHS.Entry == nullptr || RHS.Entry == nullptr) {
94-
return LHS.Entry == RHS.Entry;
95-
}
96-
return LHS.Entry == RHS.Entry &&
97-
LHS.Entry->getSize() == RHS.Entry->getSize() &&
98-
LHS.Entry->getModificationTime() ==
99-
RHS.Entry->getModificationTime();
100-
}
101-
};
102-
};
103-
10462
/// All loaded modules, indexed by name.
105-
llvm::DenseMap<EntryKey, ModuleFile *, EntryKey::Info> Modules;
63+
llvm::DenseMap<const FileEntry *, ModuleFile *> Modules;
10664

10765
/// FileManager that handles translating between filenames and
10866
/// FileEntry *.
@@ -118,7 +76,7 @@ class ModuleManager {
11876
const HeaderSearch &HeaderSearchInfo;
11977

12078
/// A lookup of in-memory (virtual file) buffers
121-
llvm::DenseMap<EntryKey, std::unique_ptr<llvm::MemoryBuffer>, EntryKey::Info>
79+
llvm::DenseMap<const FileEntry *, std::unique_ptr<llvm::MemoryBuffer>>
12280
InMemoryBuffers;
12381

12482
/// The visitation order.

clang/lib/Serialization/ModuleManager.cpp

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ ModuleFile *ModuleManager::lookupByModuleName(StringRef Name) const {
5959
}
6060

6161
ModuleFile *ModuleManager::lookup(const FileEntry *File) const {
62-
auto Known = Modules.find(EntryKey{File});
62+
auto Known = Modules.find(File);
6363
if (Known == Modules.end())
6464
return nullptr;
6565

@@ -72,7 +72,7 @@ ModuleManager::lookupBuffer(StringRef Name) {
7272
/*CacheFailure=*/false);
7373
if (!Entry)
7474
return nullptr;
75-
return std::move(InMemoryBuffers[EntryKey{*Entry}]);
75+
return std::move(InMemoryBuffers[*Entry]);
7676
}
7777

7878
static bool checkSignature(ASTFileSignature Signature,
@@ -132,15 +132,38 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type,
132132
return Missing;
133133
}
134134

135+
// The ModuleManager's use of FileEntry nodes as the keys for its map of
136+
// loaded modules is less than ideal. Uniqueness for FileEntry nodes is
137+
// maintained by FileManager, which in turn uses inode numbers on hosts
138+
// that support that. When coupled with the module cache's proclivity for
139+
// turning over and deleting stale PCMs, this means entries for different
140+
// module files can wind up reusing the same underlying inode. When this
141+
// happens, subsequent accesses to the Modules map will disagree on the
142+
// ModuleFile associated with a given file. In general, it is not sufficient
143+
// to resolve this conundrum with a type like FileEntryRef that stores the
144+
// name of the FileEntry node on first access because of path canonicalization
145+
// issues. However, the paths constructed for implicit module builds are
146+
// fully under Clang's control. We *can*, therefore, rely on their structure
147+
// being consistent across operating systems and across subsequent accesses
148+
// to the Modules map.
149+
auto implicitModuleNamesMatch = [](ModuleKind Kind, const ModuleFile *MF,
150+
const FileEntry *Entry) -> bool {
151+
if (Kind != MK_ImplicitModule)
152+
return true;
153+
return Entry->getName() == MF->FileName;
154+
};
155+
135156
// Check whether we already loaded this module, before
136-
if (ModuleFile *ModuleEntry = Modules.lookup(EntryKey{Entry})) {
137-
// Check the stored signature.
138-
if (checkSignature(ModuleEntry->Signature, ExpectedSignature, ErrorStr))
139-
return OutOfDate;
140-
141-
Module = ModuleEntry;
142-
updateModuleImports(*ModuleEntry, ImportedBy, ImportLoc);
143-
return AlreadyLoaded;
157+
if (ModuleFile *ModuleEntry = Modules.lookup(Entry)) {
158+
if (implicitModuleNamesMatch(Type, ModuleEntry, Entry)) {
159+
// Check the stored signature.
160+
if (checkSignature(ModuleEntry->Signature, ExpectedSignature, ErrorStr))
161+
return OutOfDate;
162+
163+
Module = ModuleEntry;
164+
updateModuleImports(*ModuleEntry, ImportedBy, ImportLoc);
165+
return AlreadyLoaded;
166+
}
144167
}
145168

146169
// Allocate a new module.
@@ -208,7 +231,7 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type,
208231
return OutOfDate;
209232

210233
// We're keeping this module. Store it everywhere.
211-
Module = Modules[EntryKey{Entry}] = NewModule.get();
234+
Module = Modules[Entry] = NewModule.get();
212235

213236
updateModuleImports(*NewModule, ImportedBy, ImportLoc);
214237

@@ -255,7 +278,7 @@ void ModuleManager::removeModules(ModuleIterator First, ModuleMap *modMap) {
255278

256279
// Delete the modules and erase them from the various structures.
257280
for (ModuleIterator victim = First; victim != Last; ++victim) {
258-
Modules.erase(EntryKey{victim->File});
281+
Modules.erase(victim->File);
259282

260283
if (modMap) {
261284
StringRef ModuleName = victim->ModuleName;
@@ -274,7 +297,7 @@ ModuleManager::addInMemoryBuffer(StringRef FileName,
274297
std::unique_ptr<llvm::MemoryBuffer> Buffer) {
275298
const FileEntry *Entry =
276299
FileMgr.getVirtualFile(FileName, Buffer->getBufferSize(), 0);
277-
InMemoryBuffers[EntryKey{Entry}] = std::move(Buffer);
300+
InMemoryBuffers[Entry] = std::move(Buffer);
278301
}
279302

280303
ModuleManager::VisitState *ModuleManager::allocateVisitState() {

0 commit comments

Comments
 (0)