Skip to content

[IncludeTree] IncludeTreeFileList optimizations #10535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: stable/20240723
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions clang/include/clang/CAS/IncludeTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ class IncludeTree::FileList : public IncludeTreeBase<FileList> {
FileSizeTy getFileSize(size_t I) const;

llvm::Error
forEachFileImpl(llvm::DenseSet<ObjectRef> &Seen,
forEachFileImpl(llvm::DenseMap<ObjectRef, ObjectRef> &Seen,
llvm::function_ref<llvm::Error(File, FileSizeTy)> Callback);

static bool isValid(const ObjectProxy &Node);
Expand Down Expand Up @@ -864,16 +864,16 @@ class IncludeTreeRoot : public IncludeTreeBase<IncludeTreeRoot> {

/// An implementation of a \p vfs::FileSystem that supports the simple queries
/// of the preprocessor, for creating \p FileEntries using a file path, while
/// "replaying" an \p IncludeTreeRoot. It is not intended to be a complete
/// "replaying" an \p IncludeTree::FileList. It is not intended to be a complete
/// implementation of a file system.
Expected<IntrusiveRefCntPtr<llvm::vfs::FileSystem>>
createIncludeTreeFileSystem(IncludeTreeRoot &Root);
createIncludeTreeFileSystem(IncludeTree::FileList &List);

/// Create the same IncludeTreeFileSystem but from
/// ArrayRef<IncludeTree::FileEntry>.
Expected<IntrusiveRefCntPtr<llvm::vfs::FileSystem>> createIncludeTreeFileSystem(
llvm::cas::ObjectStore &CAS,
llvm::ArrayRef<IncludeTree::FileList::FileEntry> List);
llvm::ArrayRef<IncludeTree::FileList::FileEntry> Files);

} // namespace cas
} // namespace clang
Expand Down
124 changes: 72 additions & 52 deletions clang/lib/CAS/IncludeTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,24 +246,49 @@ IncludeTree::FileList::getFileSize(size_t I) const {
Data.data() + I * sizeof(FileSizeTy));
}

static llvm::Error diagnoseFileChange(IncludeTree::File F, ObjectRef Content) {
auto FilenameBlob = F.getFilename();
if (!FilenameBlob)
return FilenameBlob.takeError();
cas::ObjectStore &DB = F.getCAS();
std::string Filename(FilenameBlob->getData());
std::string OldID = DB.getID(Content).toString();
std::string NewID = DB.getID(F.getContentsRef()).toString();
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"file '%s' changed during build; include-tree "
"contents changed from %s to %s",
Filename.c_str(), OldID.c_str(),
NewID.c_str());
}

llvm::Error IncludeTree::FileList::forEachFileImpl(
llvm::DenseSet<ObjectRef> &Seen,
llvm::DenseMap<ObjectRef, ObjectRef> &Seen,
llvm::function_ref<llvm::Error(File, FileSizeTy)> Callback) {
size_t Next = 0;
size_t FileCount = getNumFilesCurrentList();

return forEachReference([&](ObjectRef Ref) -> llvm::Error {
size_t Index = Next++;
if (!Seen.insert(Ref).second)
return llvm::Error::success();

if (Index < FileCount) {
auto Include = File::get(getCAS(), Ref);
if (!Include)
return Include.takeError();
auto Inserted = Seen.try_emplace(Ref, Include->getContentsRef());
if (!Inserted.second) {
if (Inserted.first->second != Include->getContentsRef())
return diagnoseFileChange(*Include, Inserted.first->second);
return llvm::Error::success();
}
return Callback(std::move(*Include), getFileSize(Index));
}

// Otherwise, it's a chained FileList.
// Use a value to itself to indicate if the filelist node has been
// visited or not.
auto Inserted = Seen.try_emplace(Ref, Ref);
if (!Inserted.second)
return llvm::Error::success();

auto Proxy = getCAS().getProxy(Ref);
if (!Proxy)
return Proxy.takeError();
Expand All @@ -274,7 +299,7 @@ llvm::Error IncludeTree::FileList::forEachFileImpl(

llvm::Error IncludeTree::FileList::forEachFile(
llvm::function_ref<llvm::Error(File, FileSizeTy)> Callback) {
llvm::DenseSet<ObjectRef> Seen;
llvm::DenseMap<ObjectRef, ObjectRef> Seen;
return forEachFileImpl(Seen, Callback);
}

Expand Down Expand Up @@ -321,7 +346,7 @@ bool IncludeTree::FileList::isValid(const ObjectProxy &Node) {
return false;
unsigned NumFiles =
llvm::support::endian::read<uint32_t, llvm::endianness::little>(Data.data());
return NumFiles != 0 && NumFiles <= Base.getNumReferences() &&
return NumFiles <= Base.getNumReferences() &&
Data.size() == sizeof(uint32_t) + NumFiles * sizeof(FileSizeTy);
}

Expand Down Expand Up @@ -1015,38 +1040,19 @@ class IncludeTreeFileSystem : public llvm::vfs::FileSystem {
};
} // namespace

static llvm::Error diagnoseFileChange(IncludeTree::File F, ObjectRef Content) {
auto FilenameBlob = F.getFilename();
if (!FilenameBlob)
return FilenameBlob.takeError();
cas::ObjectStore &DB = F.getCAS();
std::string Filename(FilenameBlob->getData());
std::string OldID = DB.getID(Content).toString();
std::string NewID = DB.getID(F.getContentsRef()).toString();
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"file '%s' changed during build; include-tree "
"contents changed from %s to %s",
Filename.c_str(), OldID.c_str(),
NewID.c_str());
}

Expected<IntrusiveRefCntPtr<llvm::vfs::FileSystem>>
cas::createIncludeTreeFileSystem(IncludeTreeRoot &Root) {
auto FileList = Root.getFileList();
if (!FileList)
return FileList.takeError();

std::vector<IncludeTree::FileList::FileEntry> Files;
Files.reserve(FileList->getNumReferences());

if (auto Err = FileList->forEachFile(
[&](IncludeTree::File File, IncludeTree::FileList::FileSizeTy Size) {
Files.push_back({File.getRef(), Size});
return llvm::Error::success();
}))
return std::move(Err);

return createIncludeTreeFileSystem(Root.getCAS(), Files);
static std::string computeFilename(StringRef Name, IncludeTreeFileSystem &FS) {
SmallString<128> Filename(Name);
assert(Filename != ".");
llvm::sys::path::remove_dots(Filename);

StringRef DirName = llvm::sys::path::parent_path(Filename);
if (DirName.empty())
DirName = ".";
auto &DirEntry = FS.Directories[DirName];
if (DirEntry == llvm::sys::fs::UniqueID()) {
DirEntry = llvm::vfs::getNextVirtualUniqueID();
}
return Filename.str().str();
}

Expected<IntrusiveRefCntPtr<llvm::vfs::FileSystem>>
Expand Down Expand Up @@ -1077,20 +1083,7 @@ cas::createIncludeTreeFileSystem(
if (!FilenameBlob)
return FilenameBlob.takeError();

SmallString<128> Filename(FilenameBlob->getData());
// Strip './' in the filename to match the behaviour of ASTWriter; we
// also strip './' in IncludeTreeFileSystem::getPath.
assert(Filename != ".");
llvm::sys::path::remove_dots(Filename);

StringRef DirName = llvm::sys::path::parent_path(Filename);
if (DirName.empty())
DirName = ".";
auto &DirEntry = IncludeTreeFS->Directories[DirName];
if (DirEntry == llvm::sys::fs::UniqueID()) {
DirEntry = llvm::vfs::getNextVirtualUniqueID();
}

auto Filename = computeFilename(FilenameBlob->getData(), *IncludeTreeFS);
IncludeTreeFS->Files.insert(std::make_pair(
Filename,
IncludeTreeFileSystem::FileEntry{File->getContentsRef(), Entry.Size,
Expand All @@ -1099,3 +1092,30 @@ cas::createIncludeTreeFileSystem(

return IncludeTreeFS;
}

Expected<IntrusiveRefCntPtr<llvm::vfs::FileSystem>>
cas::createIncludeTreeFileSystem(IncludeTree::FileList &List) {
auto &CAS = List.getCAS();
IntrusiveRefCntPtr<IncludeTreeFileSystem> IncludeTreeFS =
new IncludeTreeFileSystem(CAS);

auto E = List.forEachFile(
[&](IncludeTree::File File,
IncludeTree::FileList::FileSizeTy Size) -> llvm::Error {
auto FilenameBlob = File.getFilename();
if (!FilenameBlob)
return FilenameBlob.takeError();
auto Filename =
computeFilename(FilenameBlob->getData(), *IncludeTreeFS);
IncludeTreeFS->Files.insert(
std::make_pair(Filename, IncludeTreeFileSystem::FileEntry{
File.getContentsRef(), Size,
llvm::vfs::getNextVirtualUniqueID()}));
return llvm::Error::success();
});

if (E)
return std::move(E);

return IncludeTreeFS;
}
6 changes: 5 additions & 1 deletion clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1559,7 +1559,11 @@ createBaseFS(const FileSystemOptions &FSOpts, const FrontendOptions &FEOpts,
auto Root = cas::IncludeTreeRoot::get(*CAS, *Ref);
if (!Root)
return Root.takeError();
return cas::createIncludeTreeFileSystem(*Root);
auto FileList = Root->getFileList();
if (!FileList)
return FileList.takeError();

return cas::createIncludeTreeFileSystem(*FileList);
};
auto makeCASFS = [&](std::shared_ptr<llvm::cas::ObjectStore> CAS,
llvm::cas::CASID &ID)
Expand Down