[clang][lex] Store HeaderFileInfo in a DenseMap (#200968)
Calling `FileManager::GetUniqueIDMapping()` during modular builds gets very expensive if the `FileManager` has seen lots of files. This function is used in two places in the `ASTWriter` to look up `HeaderFileInfo` in `HeaderSearch`. This PR changes the storage of `HeaderFileInfo` from `FileEntry::getUID()`-indexed `std::vector<T>` to `llvm::DenseMap<FileEntryRef, T>`, improving scanning performance by ~2.5%.
This commit is contained in:
1 parent
2851820759
commit
f357a470de
5 files changed
+54
-113
No files matched your search
@@ -306,11 +306,6 @@ public:
|
||||
bool makeAbsolutePath(SmallVectorImpl<char> &Path,
|
||||
bool Canonicalize = false) const;
|
||||
|
||||
/// Produce an array mapping from the unique IDs assigned to each
|
||||
/// file to the corresponding FileEntryRef.
|
||||
void
|
||||
GetUniqueIDMapping(SmallVectorImpl<OptionalFileEntryRef> &UIDToFiles) const;
|
||||
|
||||
/// Retrieve the canonical name for a given directory.
|
||||
///
|
||||
/// This is a very expensive operation, despite its results being cached,
|
||||
|
||||
@@ -286,9 +286,8 @@ class HeaderSearch {
|
||||
/// SpecificModuleCachePath.
|
||||
size_t NormalizedModuleCachePathLen = 0;
|
||||
|
||||
/// All of the preprocessor-specific data about files that are
|
||||
/// included, indexed by the FileEntry's UID.
|
||||
mutable std::vector<HeaderFileInfo> FileInfo;
|
||||
/// All the preprocessor-specific data about files that are included.
|
||||
mutable llvm::DenseMap<FileEntryRef, HeaderFileInfo> FileInfo;
|
||||
|
||||
/// Keeps track of each lookup performed by LookupFile.
|
||||
struct LookupFileCacheInfo {
|
||||
@@ -899,8 +898,6 @@ public:
|
||||
/// Retrieve the module map.
|
||||
const ModuleMap &getModuleMap() const { return ModMap; }
|
||||
|
||||
unsigned header_file_size() const { return FileInfo.size(); }
|
||||
|
||||
/// Return the HeaderFileInfo structure for the specified FileEntry, in
|
||||
/// preparation for updating it in some way.
|
||||
HeaderFileInfo &getFileInfo(FileEntryRef FE);
|
||||
@@ -909,9 +906,10 @@ public:
|
||||
/// ever been filled in (either locally or externally).
|
||||
const HeaderFileInfo *getExistingFileInfo(FileEntryRef FE) const;
|
||||
|
||||
/// Return the headerFileInfo structure for the specified FileEntry, if it has
|
||||
/// ever been filled in locally.
|
||||
const HeaderFileInfo *getExistingLocalFileInfo(FileEntryRef FE) const;
|
||||
/// Iterate HeaderFileInfo structures and their corresponding FileEntryRef, if
|
||||
/// they have ever been filled in locally.
|
||||
void forEachExistingLocalFileInfo(
|
||||
llvm::function_ref<void(FileEntryRef, const HeaderFileInfo &)> Fn) const;
|
||||
|
||||
SearchDirIterator search_dir_begin() { return {*this, 0}; }
|
||||
SearchDirIterator search_dir_end() { return {*this, SearchDirs.size()}; }
|
||||
|
||||
@@ -642,24 +642,6 @@ std::error_code FileManager::getStatValue(StringRef Path,
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
void FileManager::GetUniqueIDMapping(
|
||||
SmallVectorImpl<OptionalFileEntryRef> &UIDToFiles) const {
|
||||
UIDToFiles.clear();
|
||||
UIDToFiles.resize(NextFileUID);
|
||||
|
||||
for (const auto &Entry : SeenFileEntries) {
|
||||
// Only return files that exist and are not redirected.
|
||||
if (!Entry.getValue() || !isa<FileEntry *>(Entry.getValue()->V))
|
||||
continue;
|
||||
FileEntryRef FE(Entry);
|
||||
// Add this file if it's the first one with the UID, or if its name is
|
||||
// better than the existing one.
|
||||
OptionalFileEntryRef &ExistingFE = UIDToFiles[FE.getUID()];
|
||||
if (!ExistingFE || FE.getName() < ExistingFE->getName())
|
||||
ExistingFE = FE;
|
||||
}
|
||||
}
|
||||
|
||||
StringRef FileManager::getCanonicalName(DirectoryEntryRef Dir) {
|
||||
return getCanonicalName(Dir, Dir.getName());
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ void HeaderSearch::PrintStats() {
|
||||
llvm::errs() << "\n*** HeaderSearch Stats:\n"
|
||||
<< FileInfo.size() << " files tracked.\n";
|
||||
unsigned NumOnceOnlyFiles = 0;
|
||||
for (unsigned i = 0, e = FileInfo.size(); i != e; ++i)
|
||||
NumOnceOnlyFiles += (FileInfo[i].isPragmaOnce || FileInfo[i].isImport);
|
||||
for (const auto &[FE, HFI] : FileInfo)
|
||||
NumOnceOnlyFiles += (HFI.isPragmaOnce || HFI.isImport);
|
||||
llvm::errs() << " " << NumOnceOnlyFiles << " #import/#pragma once files.\n";
|
||||
|
||||
llvm::errs() << " " << NumIncluded << " #include/#include_next/#import.\n"
|
||||
@@ -1376,10 +1376,7 @@ static void mergeHeaderFileInfo(HeaderFileInfo &HFI,
|
||||
}
|
||||
|
||||
HeaderFileInfo &HeaderSearch::getFileInfo(FileEntryRef FE) {
|
||||
if (FE.getUID() >= FileInfo.size())
|
||||
FileInfo.resize(FE.getUID() + 1);
|
||||
|
||||
HeaderFileInfo *HFI = &FileInfo[FE.getUID()];
|
||||
HeaderFileInfo *HFI = &FileInfo[FE];
|
||||
// FIXME: Use a generation count to check whether this is really up to date.
|
||||
if (ExternalSource && !HFI->Resolved) {
|
||||
auto ExternalHFI = ExternalSource->GetHeaderFileInfo(FE);
|
||||
@@ -1400,10 +1397,7 @@ HeaderFileInfo &HeaderSearch::getFileInfo(FileEntryRef FE) {
|
||||
const HeaderFileInfo *HeaderSearch::getExistingFileInfo(FileEntryRef FE) const {
|
||||
HeaderFileInfo *HFI;
|
||||
if (ExternalSource) {
|
||||
if (FE.getUID() >= FileInfo.size())
|
||||
FileInfo.resize(FE.getUID() + 1);
|
||||
|
||||
HFI = &FileInfo[FE.getUID()];
|
||||
HFI = &FileInfo[FE];
|
||||
// FIXME: Use a generation count to check whether this is really up to date.
|
||||
if (!HFI->Resolved) {
|
||||
auto ExternalHFI = ExternalSource->GetHeaderFileInfo(FE);
|
||||
@@ -1413,8 +1407,8 @@ const HeaderFileInfo *HeaderSearch::getExistingFileInfo(FileEntryRef FE) const {
|
||||
mergeHeaderFileInfo(*HFI, ExternalHFI);
|
||||
}
|
||||
}
|
||||
} else if (FE.getUID() < FileInfo.size()) {
|
||||
HFI = &FileInfo[FE.getUID()];
|
||||
} else if (auto It = FileInfo.find(FE); It != FileInfo.end()) {
|
||||
HFI = &It->second;
|
||||
} else {
|
||||
HFI = nullptr;
|
||||
}
|
||||
@@ -1422,16 +1416,11 @@ const HeaderFileInfo *HeaderSearch::getExistingFileInfo(FileEntryRef FE) const {
|
||||
return (HFI && HFI->IsValid) ? HFI : nullptr;
|
||||
}
|
||||
|
||||
const HeaderFileInfo *
|
||||
HeaderSearch::getExistingLocalFileInfo(FileEntryRef FE) const {
|
||||
HeaderFileInfo *HFI;
|
||||
if (FE.getUID() < FileInfo.size()) {
|
||||
HFI = &FileInfo[FE.getUID()];
|
||||
} else {
|
||||
HFI = nullptr;
|
||||
}
|
||||
|
||||
return (HFI && HFI->IsValid && !HFI->External) ? HFI : nullptr;
|
||||
void HeaderSearch::forEachExistingLocalFileInfo(
|
||||
llvm::function_ref<void(FileEntryRef, const HeaderFileInfo &)> Fn) const {
|
||||
for (const auto &[FE, HFI] : FileInfo)
|
||||
if (HFI.IsValid && !HFI.External)
|
||||
Fn(FE, HFI);
|
||||
}
|
||||
|
||||
bool HeaderSearch::isFileMultipleIncludeGuarded(FileEntryRef File) const {
|
||||
|
||||
@@ -246,30 +246,17 @@ GetAffectingModuleMaps(const Preprocessor &PP, Module *RootModule) {
|
||||
}
|
||||
|
||||
// Handle textually-included headers that belong to other modules.
|
||||
HS.forEachExistingLocalFileInfo(
|
||||
[&](FileEntryRef File, const HeaderFileInfo &HFI) {
|
||||
if (!HFI.isCompilingModuleHeader && HFI.isModuleHeader)
|
||||
return; // Modular header, handled in the above module-based loop.
|
||||
if (!HFI.isCompilingModuleHeader && !HFI.IsLocallyIncluded)
|
||||
return; // Non-modular header not included locally is not affecting.
|
||||
|
||||
SmallVector<OptionalFileEntryRef, 16> FilesByUID;
|
||||
HS.getFileMgr().GetUniqueIDMapping(FilesByUID);
|
||||
|
||||
if (FilesByUID.size() > HS.header_file_size())
|
||||
FilesByUID.resize(HS.header_file_size());
|
||||
|
||||
for (unsigned UID = 0, LastUID = FilesByUID.size(); UID != LastUID; ++UID) {
|
||||
OptionalFileEntryRef File = FilesByUID[UID];
|
||||
if (!File)
|
||||
continue;
|
||||
|
||||
const HeaderFileInfo *HFI = HS.getExistingLocalFileInfo(*File);
|
||||
if (!HFI)
|
||||
continue; // We have no information on this being a header file.
|
||||
if (!HFI->isCompilingModuleHeader && HFI->isModuleHeader)
|
||||
continue; // Modular header, handled in the above module-based loop.
|
||||
if (!HFI->isCompilingModuleHeader && !HFI->IsLocallyIncluded)
|
||||
continue; // Non-modular header not included locally is not affecting.
|
||||
|
||||
for (const auto &KH : HS.findResolvedModulesForHeader(*File))
|
||||
if (const Module *M = KH.getModule())
|
||||
CollectModuleMapsForHierarchy(M, AR_TextualHeader);
|
||||
}
|
||||
for (const auto &KH : HS.findResolvedModulesForHeader(File))
|
||||
if (const Module *M = KH.getModule())
|
||||
CollectModuleMapsForHierarchy(M, AR_TextualHeader);
|
||||
});
|
||||
|
||||
// FIXME: This algorithm is not correct for module map hierarchies where
|
||||
// module map file defining a (sub)module of a top-level module X includes
|
||||
@@ -2252,46 +2239,36 @@ void ASTWriter::WriteHeaderSearch(const HeaderSearch &HS) {
|
||||
}
|
||||
}
|
||||
|
||||
SmallVector<OptionalFileEntryRef, 16> FilesByUID;
|
||||
HS.getFileMgr().GetUniqueIDMapping(FilesByUID);
|
||||
HS.forEachExistingLocalFileInfo(
|
||||
[&](FileEntryRef File, const HeaderFileInfo &HFI) {
|
||||
if (!HFI.isCompilingModuleHeader && HFI.isModuleHeader)
|
||||
return; // Header file info is tracked by the owning module file.
|
||||
if (!HFI.isCompilingModuleHeader && !HFI.IsLocallyIncluded)
|
||||
return; // Header file info is tracked by the including module file.
|
||||
|
||||
if (FilesByUID.size() > HS.header_file_size())
|
||||
FilesByUID.resize(HS.header_file_size());
|
||||
// Massage the file path into an appropriate form.
|
||||
StringRef Filename = File.getName();
|
||||
SmallString<128> FilenameTmp(Filename);
|
||||
if (PreparePathForOutput(FilenameTmp)) {
|
||||
// If we performed any translation on the file name at all, we need to
|
||||
// save this string, since the generator will refer to it later.
|
||||
Filename = StringRef(strdup(FilenameTmp.c_str()));
|
||||
SavedStrings.push_back(Filename.data());
|
||||
}
|
||||
|
||||
for (unsigned UID = 0, LastUID = FilesByUID.size(); UID != LastUID; ++UID) {
|
||||
OptionalFileEntryRef File = FilesByUID[UID];
|
||||
if (!File)
|
||||
continue;
|
||||
bool Included = HFI.IsLocallyIncluded || PP->alreadyIncluded(File);
|
||||
|
||||
const HeaderFileInfo *HFI = HS.getExistingLocalFileInfo(*File);
|
||||
if (!HFI)
|
||||
continue; // We have no information on this being a header file.
|
||||
if (!HFI->isCompilingModuleHeader && HFI->isModuleHeader)
|
||||
continue; // Header file info is tracked by the owning module file.
|
||||
if (!HFI->isCompilingModuleHeader && !HFI->IsLocallyIncluded)
|
||||
continue; // Header file info is tracked by the including module file.
|
||||
|
||||
// Massage the file path into an appropriate form.
|
||||
StringRef Filename = File->getName();
|
||||
SmallString<128> FilenameTmp(Filename);
|
||||
if (PreparePathForOutput(FilenameTmp)) {
|
||||
// If we performed any translation on the file name at all, we need to
|
||||
// save this string, since the generator will refer to it later.
|
||||
Filename = StringRef(strdup(FilenameTmp.c_str()));
|
||||
SavedStrings.push_back(Filename.data());
|
||||
}
|
||||
|
||||
bool Included = HFI->IsLocallyIncluded || PP->alreadyIncluded(*File);
|
||||
|
||||
HeaderFileInfoTrait::key_type Key = {
|
||||
Filename, File->getSize(),
|
||||
getTimestampForOutput(File->getModificationTime())};
|
||||
HeaderFileInfoTrait::data_type Data = {
|
||||
*HFI, Included, HS.getModuleMap().findResolvedModulesForHeader(*File), {}
|
||||
};
|
||||
Generator.insert(Key, Data, GeneratorTrait);
|
||||
++NumHeaderSearchEntries;
|
||||
}
|
||||
HeaderFileInfoTrait::key_type Key = {
|
||||
Filename, File.getSize(),
|
||||
getTimestampForOutput(File.getModificationTime())};
|
||||
HeaderFileInfoTrait::data_type Data = {
|
||||
HFI,
|
||||
Included,
|
||||
HS.getModuleMap().findResolvedModulesForHeader(File),
|
||||
{}};
|
||||
Generator.insert(Key, Data, GeneratorTrait);
|
||||
++NumHeaderSearchEntries;
|
||||
});
|
||||
|
||||
// Create the on-disk hash table in a buffer.
|
||||
SmallString<4096> TableData;
|
||||
|
||||
Reference in new issue
Block a user