FileManager: std::map => BumpPtrAllocator + DenseMap of pointers. NFC

This is both smaller and faster.

Differential Revision: https://reviews.llvm.org/D123144
This commit is contained in:
Sam McCall 2022-04-05 18:56:05 +02:00
parent 79ad5fb295
commit cf1c5507b7
2 changed files with 74 additions and 65 deletions

View File

@ -53,24 +53,26 @@ class FileSystemStatCache;
class FileManager : public RefCountedBase<FileManager> { class FileManager : public RefCountedBase<FileManager> {
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS; IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
FileSystemOptions FileSystemOpts; FileSystemOptions FileSystemOpts;
llvm::SpecificBumpPtrAllocator<FileEntry> FilesAlloc;
llvm::SpecificBumpPtrAllocator<DirectoryEntry> DirsAlloc;
/// Cache for existing real directories. /// Cache for existing real directories.
std::map<llvm::sys::fs::UniqueID, DirectoryEntry> UniqueRealDirs; llvm::DenseMap<llvm::sys::fs::UniqueID, DirectoryEntry *> UniqueRealDirs;
/// Cache for existing real files. /// Cache for existing real files.
std::map<llvm::sys::fs::UniqueID, FileEntry> UniqueRealFiles; llvm::DenseMap<llvm::sys::fs::UniqueID, FileEntry *> UniqueRealFiles;
/// The virtual directories that we have allocated. /// The virtual directories that we have allocated.
/// ///
/// For each virtual file (e.g. foo/bar/baz.cpp), we add all of its parent /// For each virtual file (e.g. foo/bar/baz.cpp), we add all of its parent
/// directories (foo/ and foo/bar/) here. /// directories (foo/ and foo/bar/) here.
SmallVector<std::unique_ptr<DirectoryEntry>, 4> VirtualDirectoryEntries; SmallVector<DirectoryEntry *, 4> VirtualDirectoryEntries;
/// The virtual files that we have allocated. /// The virtual files that we have allocated.
SmallVector<std::unique_ptr<FileEntry>, 4> VirtualFileEntries; SmallVector<FileEntry *, 4> VirtualFileEntries;
/// A set of files that bypass the maps and uniquing. They can have /// A set of files that bypass the maps and uniquing. They can have
/// conflicting filenames. /// conflicting filenames.
SmallVector<std::unique_ptr<FileEntry>, 0> BypassFileEntries; SmallVector<FileEntry *, 0> BypassFileEntries;
/// A cache that maps paths to directory entries (either real or /// A cache that maps paths to directory entries (either real or
/// virtual) we have looked up, or an error that occurred when we looked up /// virtual) we have looked up, or an error that occurred when we looked up

View File

@ -105,10 +105,10 @@ void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
return; return;
// Add the virtual directory to the cache. // Add the virtual directory to the cache.
auto UDE = std::make_unique<DirectoryEntry>(); auto *UDE = new (DirsAlloc.Allocate()) DirectoryEntry();
UDE->Name = NamedDirEnt.first(); UDE->Name = NamedDirEnt.first();
NamedDirEnt.second = *UDE.get(); NamedDirEnt.second = *UDE;
VirtualDirectoryEntries.push_back(std::move(UDE)); VirtualDirectoryEntries.push_back(UDE);
// Recursively add the other ancestors. // Recursively add the other ancestors.
addAncestorsAsVirtualDirs(DirName); addAncestorsAsVirtualDirs(DirName);
@ -172,14 +172,15 @@ FileManager::getDirectoryRef(StringRef DirName, bool CacheFailure) {
// same inode (this occurs on Unix-like systems when one dir is // same inode (this occurs on Unix-like systems when one dir is
// symlinked to another, for example) or the same path (on // symlinked to another, for example) or the same path (on
// Windows). // Windows).
DirectoryEntry &UDE = UniqueRealDirs[Status.getUniqueID()]; DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()];
NamedDirEnt.second = UDE; if (!UDE) {
if (UDE.getName().empty()) {
// We don't have this directory yet, add it. We use the string // We don't have this directory yet, add it. We use the string
// key from the SeenDirEntries map as the string. // key from the SeenDirEntries map as the string.
UDE.Name = InterndDirName; UDE = new (DirsAlloc.Allocate()) DirectoryEntry();
UDE->Name = InterndDirName;
} }
NamedDirEnt.second = *UDE;
return DirectoryEntryRef(NamedDirEnt); return DirectoryEntryRef(NamedDirEnt);
} }
@ -268,7 +269,10 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
// It exists. See if we have already opened a file with the same inode. // It exists. See if we have already opened a file with the same inode.
// This occurs when one dir is symlinked to another, for example. // This occurs when one dir is symlinked to another, for example.
FileEntry &UFE = UniqueRealFiles[Status.getUniqueID()]; FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
bool ReusingEntry = UFE != nullptr;
if (!UFE)
UFE = new (FilesAlloc.Allocate()) FileEntry();
// FIXME: This should just check `!Status.ExposesExternalVFSPath`, but the // FIXME: This should just check `!Status.ExposesExternalVFSPath`, but the
// else branch also ends up fixing up relative paths to be the actually // else branch also ends up fixing up relative paths to be the actually
@ -276,7 +280,7 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
// be relied on in some clients. // be relied on in some clients.
if (Status.getName() == Filename) { if (Status.getName() == Filename) {
// The name matches. Set the FileEntry. // The name matches. Set the FileEntry.
NamedFileEnt->second = FileEntryRef::MapValue(UFE, DirInfo); NamedFileEnt->second = FileEntryRef::MapValue(*UFE, DirInfo);
} else { } else {
// We need a redirect. First grab the actual entry we want to return. // We need a redirect. First grab the actual entry we want to return.
// //
@ -295,11 +299,11 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
// API. // API.
auto &Redirection = auto &Redirection =
*SeenFileEntries *SeenFileEntries
.insert({Status.getName(), FileEntryRef::MapValue(UFE, DirInfo)}) .insert({Status.getName(), FileEntryRef::MapValue(*UFE, DirInfo)})
.first; .first;
assert(Redirection.second->V.is<FileEntry *>() && assert(Redirection.second->V.is<FileEntry *>() &&
"filename redirected to a non-canonical filename?"); "filename redirected to a non-canonical filename?");
assert(Redirection.second->V.get<FileEntry *>() == &UFE && assert(Redirection.second->V.get<FileEntry *>() == UFE &&
"filename from getStatValue() refers to wrong file"); "filename from getStatValue() refers to wrong file");
// Cache the redirection in the previously-inserted entry, still available // Cache the redirection in the previously-inserted entry, still available
@ -311,7 +315,7 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
} }
FileEntryRef ReturnedRef(*NamedFileEnt); FileEntryRef ReturnedRef(*NamedFileEnt);
if (UFE.isValid()) { // Already have an entry with this inode, return it. if (ReusingEntry) { // Already have an entry with this inode, return it.
// FIXME: This hack ensures that `getDir()` will use the path that was // FIXME: This hack ensures that `getDir()` will use the path that was
// used to lookup this file, even if we found a file by different path // used to lookup this file, even if we found a file by different path
@ -322,8 +326,8 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
// *and* the redirection hack above is removed. The removal of the latter // *and* the redirection hack above is removed. The removal of the latter
// is required since otherwise the ref will have the exposed external VFS // is required since otherwise the ref will have the exposed external VFS
// path still. // path still.
if (&DirInfo.getDirEntry() != UFE.Dir && Status.ExposesExternalVFSPath) if (&DirInfo.getDirEntry() != UFE->Dir && Status.ExposesExternalVFSPath)
UFE.Dir = &DirInfo.getDirEntry(); UFE->Dir = &DirInfo.getDirEntry();
// Always update LastRef to the last name by which a file was accessed. // Always update LastRef to the last name by which a file was accessed.
// FIXME: Neither this nor always using the first reference is correct; we // FIXME: Neither this nor always using the first reference is correct; we
@ -332,28 +336,28 @@ FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) {
// corresponding FileEntry. // corresponding FileEntry.
// FIXME: LastRef should be removed from FileEntry once all clients adopt // FIXME: LastRef should be removed from FileEntry once all clients adopt
// FileEntryRef. // FileEntryRef.
UFE.LastRef = ReturnedRef; UFE->LastRef = ReturnedRef;
return ReturnedRef; return ReturnedRef;
} }
// Otherwise, we don't have this file yet, add it. // Otherwise, we don't have this file yet, add it.
UFE.LastRef = ReturnedRef; UFE->LastRef = ReturnedRef;
UFE.Size = Status.getSize(); UFE->Size = Status.getSize();
UFE.ModTime = llvm::sys::toTimeT(Status.getLastModificationTime()); UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
UFE.Dir = &DirInfo.getDirEntry(); UFE->Dir = &DirInfo.getDirEntry();
UFE.UID = NextFileUID++; UFE->UID = NextFileUID++;
UFE.UniqueID = Status.getUniqueID(); UFE->UniqueID = Status.getUniqueID();
UFE.IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file; UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
UFE.File = std::move(F); UFE->File = std::move(F);
UFE.IsValid = true; UFE->IsValid = true;
if (UFE.File) { if (UFE->File) {
if (auto PathName = UFE.File->getName()) if (auto PathName = UFE->File->getName())
fillRealPathName(&UFE, *PathName); fillRealPathName(UFE, *PathName);
} else if (!openFile) { } else if (!openFile) {
// We should still fill the path even if we aren't opening the file. // We should still fill the path even if we aren't opening the file.
fillRealPathName(&UFE, InterndFileName); fillRealPathName(UFE, InterndFileName);
} }
return ReturnedRef; return ReturnedRef;
} }
@ -417,37 +421,41 @@ FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size,
llvm::vfs::Status Status; llvm::vfs::Status Status;
const char *InterndFileName = NamedFileEnt.first().data(); const char *InterndFileName = NamedFileEnt.first().data();
if (!getStatValue(InterndFileName, Status, true, nullptr)) { if (!getStatValue(InterndFileName, Status, true, nullptr)) {
UFE = &UniqueRealFiles[Status.getUniqueID()];
Status = llvm::vfs::Status( Status = llvm::vfs::Status(
Status.getName(), Status.getUniqueID(), Status.getName(), Status.getUniqueID(),
llvm::sys::toTimePoint(ModificationTime), llvm::sys::toTimePoint(ModificationTime),
Status.getUser(), Status.getGroup(), Size, Status.getUser(), Status.getGroup(), Size,
Status.getType(), Status.getPermissions()); Status.getType(), Status.getPermissions());
NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo); auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
if (RealFE) {
// If we had already opened this file, close it now so we don't // If we had already opened this file, close it now so we don't
// leak the descriptor. We're not going to use the file // leak the descriptor. We're not going to use the file
// descriptor anyway, since this is a virtual file. // descriptor anyway, since this is a virtual file.
if (UFE->File) if (RealFE->File)
UFE->closeFile(); RealFE->closeFile();
// If we already have an entry with this inode, return it.
// If we already have an entry with this inode, return it. //
// // FIXME: Surely this should add a reference by the new name, and return
// FIXME: Surely this should add a reference by the new name, and return // it instead...
// it instead... NamedFileEnt.second = FileEntryRef::MapValue(*RealFE, *DirInfo);
if (UFE->isValid())
return FileEntryRef(NamedFileEnt); return FileEntryRef(NamedFileEnt);
}
// File exists, but no entry - create it.
RealFE = new (FilesAlloc.Allocate()) FileEntry();
RealFE->UniqueID = Status.getUniqueID();
RealFE->IsNamedPipe =
Status.getType() == llvm::sys::fs::file_type::fifo_file;
fillRealPathName(RealFE, Status.getName());
UFE->UniqueID = Status.getUniqueID(); UFE = RealFE;
UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
fillRealPathName(UFE, Status.getName());
} else { } else {
VirtualFileEntries.push_back(std::make_unique<FileEntry>()); // File does not exist, create a virtual entry.
UFE = VirtualFileEntries.back().get(); UFE = new (FilesAlloc.Allocate()) FileEntry();
NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo); VirtualFileEntries.push_back(UFE);
} }
NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo);
UFE->LastRef = FileEntryRef(NamedFileEnt); UFE->LastRef = FileEntryRef(NamedFileEnt);
UFE->Size = Size; UFE->Size = Size;
UFE->ModTime = ModificationTime; UFE->ModTime = ModificationTime;
@ -475,16 +483,15 @@ llvm::Optional<FileEntryRef> FileManager::getBypassFile(FileEntryRef VF) {
return FileEntryRef(*Insertion.first); return FileEntryRef(*Insertion.first);
// Fill in the new entry from the stat. // Fill in the new entry from the stat.
BypassFileEntries.push_back(std::make_unique<FileEntry>()); FileEntry *BFE = new (FilesAlloc.Allocate()) FileEntry();
const FileEntry &VFE = VF.getFileEntry(); BypassFileEntries.push_back(BFE);
FileEntry &BFE = *BypassFileEntries.back(); Insertion.first->second = FileEntryRef::MapValue(*BFE, VF.getDir());
Insertion.first->second = FileEntryRef::MapValue(BFE, VF.getDir()); BFE->LastRef = FileEntryRef(*Insertion.first);
BFE.LastRef = FileEntryRef(*Insertion.first); BFE->Size = Status.getSize();
BFE.Size = Status.getSize(); BFE->Dir = VF.getFileEntry().Dir;
BFE.Dir = VFE.Dir; BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
BFE.ModTime = llvm::sys::toTimeT(Status.getLastModificationTime()); BFE->UID = NextFileUID++;
BFE.UID = NextFileUID++; BFE->IsValid = true;
BFE.IsValid = true;
// Save the entry in the bypass table and return. // Save the entry in the bypass table and return.
return FileEntryRef(*Insertion.first); return FileEntryRef(*Insertion.first);
@ -601,7 +608,7 @@ FileManager::getNoncachedStatValue(StringRef Path,
} }
void FileManager::GetUniqueIDMapping( void FileManager::GetUniqueIDMapping(
SmallVectorImpl<const FileEntry *> &UIDToFiles) const { SmallVectorImpl<const FileEntry *> &UIDToFiles) const {
UIDToFiles.clear(); UIDToFiles.clear();
UIDToFiles.resize(NextFileUID); UIDToFiles.resize(NextFileUID);
@ -618,7 +625,7 @@ void FileManager::GetUniqueIDMapping(
// Map virtual file entries // Map virtual file entries
for (const auto &VFE : VirtualFileEntries) for (const auto &VFE : VirtualFileEntries)
UIDToFiles[VFE->getUID()] = VFE.get(); UIDToFiles[VFE->getUID()] = VFE;
} }
StringRef FileManager::getCanonicalName(const DirectoryEntry *Dir) { StringRef FileManager::getCanonicalName(const DirectoryEntry *Dir) {