Skip to content

[clang][modules] Avoid allocations when reading blob paths #113984

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

Merged
merged 7 commits into from
Oct 31, 2024
Merged
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
44 changes: 42 additions & 2 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "llvm/ADT/iterator_range.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/VersionTuple.h"
#include <cassert>
Expand Down Expand Up @@ -1341,9 +1342,48 @@ class ASTReader
serialization::InputFile getInputFile(ModuleFile &F, unsigned ID,
bool Complain = true);

/// The buffer used as the temporary backing storage for resolved paths.
SmallString<0> PathBuf;

/// A wrapper around StringRef that temporarily borrows the underlying buffer.
class TemporarilyOwnedStringRef {
StringRef String;
llvm::SaveAndRestore<SmallString<0>> UnderlyingBuffer;

public:
TemporarilyOwnedStringRef(StringRef S, SmallString<0> &UnderlyingBuffer)
: String(S), UnderlyingBuffer(UnderlyingBuffer, {}) {}

/// Return the wrapped \c StringRef that must be outlived by \c this.
const StringRef *operator->() const & { return &String; }
const StringRef &operator*() const & { return String; }

/// Make it harder to get a \c StringRef that outlives \c this.
const StringRef *operator->() && = delete;
const StringRef &operator*() && = delete;
};

public:
void ResolveImportedPath(ModuleFile &M, std::string &Filename);
static void ResolveImportedPath(std::string &Filename, StringRef Prefix);
/// Get the buffer for resolving paths.
SmallString<0> &getPathBuf() { return PathBuf; }

/// Resolve \c Path in the context of module file \c M. The return value
/// must go out of scope before the next call to \c ResolveImportedPath.
static TemporarilyOwnedStringRef
ResolveImportedPath(SmallString<0> &Buf, StringRef Path, ModuleFile &ModF);
/// Resolve \c Path in the context of the \c Prefix directory. The return
/// value must go out of scope before the next call to \c ResolveImportedPath.
static TemporarilyOwnedStringRef
ResolveImportedPath(SmallString<0> &Buf, StringRef Path, StringRef Prefix);

/// Resolve \c Path in the context of module file \c M.
static std::string ResolveImportedPathAndAllocate(SmallString<0> &Buf,
StringRef Path,
ModuleFile &ModF);
/// Resolve \c Path in the context of the \c Prefix directory.
static std::string ResolveImportedPathAndAllocate(SmallString<0> &Buf,
StringRef Path,
StringRef Prefix);

/// Returns the first key declaration for the given declaration. This
/// is one that is formerly-canonical (or still canonical) and whose module
Expand Down
106 changes: 59 additions & 47 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2048,9 +2048,9 @@ HeaderFileInfoTrait::getFile(const internal_key_type &Key) {
if (!Key.Imported)
return FileMgr.getOptionalFileRef(Key.Filename);

std::string Resolved = std::string(Key.Filename);
Reader.ResolveImportedPath(M, Resolved);
return FileMgr.getOptionalFileRef(Resolved);
auto Resolved =
ASTReader::ResolveImportedPath(Reader.getPathBuf(), Key.Filename, M);
return FileMgr.getOptionalFileRef(*Resolved);
}

unsigned HeaderFileInfoTrait::ComputeHash(internal_key_ref ikey) {
Expand Down Expand Up @@ -2513,11 +2513,12 @@ InputFileInfo ASTReader::getInputFileInfo(ModuleFile &F, unsigned ID) {
std::tie(R.FilenameAsRequested, R.Filename) = [&]() {
uint16_t AsRequestedLength = Record[7];

std::string NameAsRequested = Blob.substr(0, AsRequestedLength).str();
std::string Name = Blob.substr(AsRequestedLength).str();
StringRef NameAsRequestedRef = Blob.substr(0, AsRequestedLength);
StringRef NameRef = Blob.substr(AsRequestedLength);

ResolveImportedPath(F, NameAsRequested);
ResolveImportedPath(F, Name);
std::string NameAsRequested =
ResolveImportedPathAndAllocate(PathBuf, NameAsRequestedRef, F);
std::string Name = ResolveImportedPathAndAllocate(PathBuf, NameRef, F);

if (Name.empty())
Name = NameAsRequested;
Expand Down Expand Up @@ -2743,23 +2744,38 @@ InputFile ASTReader::getInputFile(ModuleFile &F, unsigned ID, bool Complain) {
return IF;
}

/// If we are loading a relocatable PCH or module file, and the filename
/// is not an absolute path, add the system or module root to the beginning of
/// the file name.
void ASTReader::ResolveImportedPath(ModuleFile &M, std::string &Filename) {
// Resolve relative to the base directory, if we have one.
if (!M.BaseDirectory.empty())
return ResolveImportedPath(Filename, M.BaseDirectory);
ASTReader::TemporarilyOwnedStringRef
ASTReader::ResolveImportedPath(SmallString<0> &Buf, StringRef Path,
ModuleFile &ModF) {
return ResolveImportedPath(Buf, Path, ModF.BaseDirectory);
}

void ASTReader::ResolveImportedPath(std::string &Filename, StringRef Prefix) {
if (Filename.empty() || llvm::sys::path::is_absolute(Filename) ||
Filename == "<built-in>" || Filename == "<command line>")
return;
ASTReader::TemporarilyOwnedStringRef
ASTReader::ResolveImportedPath(SmallString<0> &Buf, StringRef Path,
StringRef Prefix) {
assert(Buf.capacity() != 0 && "Overlapping ResolveImportedPath calls");

if (Prefix.empty() || Path.empty() || llvm::sys::path::is_absolute(Path) ||
Path == "<built-in>" || Path == "<command line>")
return {Path, Buf};

Buf.clear();
llvm::sys::path::append(Buf, Prefix, Path);
StringRef ResolvedPath{Buf.data(), Buf.size()};
return {ResolvedPath, Buf};
}

SmallString<128> Buffer;
llvm::sys::path::append(Buffer, Prefix, Filename);
Filename.assign(Buffer.begin(), Buffer.end());
std::string ASTReader::ResolveImportedPathAndAllocate(SmallString<0> &Buf,
StringRef P,
ModuleFile &ModF) {
return ResolveImportedPathAndAllocate(Buf, P, ModF.BaseDirectory);
}

std::string ASTReader::ResolveImportedPathAndAllocate(SmallString<0> &Buf,
StringRef P,
StringRef Prefix) {
auto ResolvedPath = ResolveImportedPath(Buf, P, Prefix);
return ResolvedPath->str();
}

static bool isDiagnosedResult(ASTReader::ASTReadResult ARR, unsigned Caps) {
Expand Down Expand Up @@ -3187,8 +3203,8 @@ ASTReader::ReadControlBlock(ModuleFile &F,
case ORIGINAL_FILE:
F.OriginalSourceFileID = FileID::get(Record[0]);
F.ActualOriginalSourceFileName = std::string(Blob);
F.OriginalSourceFileName = F.ActualOriginalSourceFileName;
ResolveImportedPath(F, F.OriginalSourceFileName);
F.OriginalSourceFileName = ResolveImportedPathAndAllocate(
PathBuf, F.ActualOriginalSourceFileName, F);
break;

case ORIGINAL_FILE_ID:
Expand Down Expand Up @@ -5477,6 +5493,8 @@ bool ASTReader::readASTFileControlBlock(
RecordData Record;
std::string ModuleDir;
bool DoneWithControlBlock = false;
SmallString<0> PathBuf;
PathBuf.reserve(256);
while (!DoneWithControlBlock) {
Expected<llvm::BitstreamEntry> MaybeEntry = Stream.advance();
if (!MaybeEntry) {
Expand Down Expand Up @@ -5559,9 +5577,9 @@ bool ASTReader::readASTFileControlBlock(
break;
case MODULE_MAP_FILE: {
unsigned Idx = 0;
auto Path = ReadString(Record, Idx);
ResolveImportedPath(Path, ModuleDir);
Listener.ReadModuleMapFile(Path);
std::string PathStr = ReadString(Record, Idx);
auto Path = ResolveImportedPath(PathBuf, PathStr, ModuleDir);
Listener.ReadModuleMapFile(*Path);
break;
}
case INPUT_FILE_OFFSETS: {
Expand Down Expand Up @@ -5608,10 +5626,9 @@ bool ASTReader::readASTFileControlBlock(
break;
case INPUT_FILE:
bool Overridden = static_cast<bool>(Record[3]);
std::string Filename = std::string(Blob);
ResolveImportedPath(Filename, ModuleDir);
auto Filename = ResolveImportedPath(PathBuf, Blob, ModuleDir);
shouldContinue = Listener.visitInputFile(
Filename, isSystemFile, Overridden, /*IsExplicitModule*/false);
*Filename, isSystemFile, Overridden, /*IsExplicitModule=*/false);
break;
}
if (!shouldContinue)
Expand Down Expand Up @@ -5646,9 +5663,9 @@ bool ASTReader::readASTFileControlBlock(
// Skip Size, ModTime and Signature
Idx += 1 + 1 + ASTFileSignature::size;
std::string ModuleName = ReadString(Record, Idx);
std::string Filename = ReadString(Record, Idx);
ResolveImportedPath(Filename, ModuleDir);
Listener.visitImport(ModuleName, Filename);
std::string FilenameStr = ReadString(Record, Idx);
auto Filename = ResolveImportedPath(PathBuf, FilenameStr, ModuleDir);
Listener.visitImport(ModuleName, *Filename);
}
break;
}
Expand Down Expand Up @@ -5901,9 +5918,8 @@ llvm::Error ASTReader::ReadSubmoduleBlock(ModuleFile &F,
// FIXME: This doesn't work for framework modules as `Filename` is the
// name as written in the module file and does not include
// `Headers/`, so this path will never exist.
std::string Filename = std::string(Blob);
ResolveImportedPath(F, Filename);
if (auto Umbrella = PP.getFileManager().getOptionalFileRef(Filename)) {
auto Filename = ResolveImportedPath(PathBuf, Blob, F);
if (auto Umbrella = PP.getFileManager().getOptionalFileRef(*Filename)) {
if (!CurrentModule->getUmbrellaHeaderAsWritten()) {
// FIXME: NameAsWritten
ModMap.setUmbrellaHeaderAsWritten(CurrentModule, *Umbrella, Blob, "");
Expand Down Expand Up @@ -5931,18 +5947,16 @@ llvm::Error ASTReader::ReadSubmoduleBlock(ModuleFile &F,
break;

case SUBMODULE_TOPHEADER: {
std::string HeaderName(Blob);
ResolveImportedPath(F, HeaderName);
CurrentModule->addTopHeaderFilename(HeaderName);
auto HeaderName = ResolveImportedPath(PathBuf, Blob, F);
CurrentModule->addTopHeaderFilename(*HeaderName);
break;
}

case SUBMODULE_UMBRELLA_DIR: {
// See comments in SUBMODULE_UMBRELLA_HEADER
std::string Dirname = std::string(Blob);
ResolveImportedPath(F, Dirname);
auto Dirname = ResolveImportedPath(PathBuf, Blob, F);
if (auto Umbrella =
PP.getFileManager().getOptionalDirectoryRef(Dirname)) {
PP.getFileManager().getOptionalDirectoryRef(*Dirname)) {
if (!CurrentModule->getUmbrellaDirAsWritten()) {
// FIXME: NameAsWritten
ModMap.setUmbrellaDirAsWritten(CurrentModule, *Umbrella, Blob, "");
Expand Down Expand Up @@ -9597,17 +9611,13 @@ std::string ASTReader::ReadString(const RecordDataImpl &Record, unsigned &Idx) {

std::string ASTReader::ReadPath(ModuleFile &F, const RecordData &Record,
unsigned &Idx) {
std::string Filename = ReadString(Record, Idx);
ResolveImportedPath(F, Filename);
return Filename;
return ReadPath(F.BaseDirectory, Record, Idx);
}

std::string ASTReader::ReadPath(StringRef BaseDirectory,
const RecordData &Record, unsigned &Idx) {
std::string Filename = ReadString(Record, Idx);
if (!BaseDirectory.empty())
ResolveImportedPath(Filename, BaseDirectory);
return Filename;
return ResolveImportedPathAndAllocate(PathBuf, Filename, BaseDirectory);
}

VersionTuple ASTReader::ReadVersionTuple(const RecordData &Record,
Expand Down Expand Up @@ -10512,6 +10522,8 @@ ASTReader::ASTReader(Preprocessor &PP, InMemoryModuleCache &ModuleCache,
UseGlobalIndex(UseGlobalIndex), CurrSwitchCaseStmts(&SwitchCaseStmts) {
SourceMgr.setExternalSLocEntrySource(this);

PathBuf.reserve(256);

for (const auto &Ext : Extensions) {
auto BlockName = Ext->getExtensionMetadata().BlockName;
auto Known = ModuleFileExtensions.find(BlockName);
Expand Down
Loading