Skip to content

Commit

Permalink
[clang][modules] Remove preloaded SLocEntries from PCM files (#66962)
Browse files Browse the repository at this point in the history
This commit removes the list of SLocEntry offsets to preload eagerly
from PCM files. Commit introducing this functionality (258ae54) doesn't
clarify why this would be more performant than the lazy approach used
regularly.

Currently, the only SLocEntry the reader is supposed to preload is the
predefines buffer, but in my experience, it's not actually referenced in
most modules, so the time spent deserializing its SLocEntry is wasted.
This is especially noticeable in the dependency scanner, where this
change brings 4.56% speedup on my benchmark.
  • Loading branch information
jansvoboda11 committed Oct 6, 2023
1 parent 4a16b51 commit 0dfb5da
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 108 deletions.
1 change: 1 addition & 0 deletions clang/include/clang/Basic/SourceLocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class FileID {
friend class ASTWriter;
friend class ASTReader;
friend class SourceManager;
friend class SourceManagerTestHelper;

static FileID get(int V) {
FileID F;
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/SourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,11 @@ class SourceManager : public RefCountedBase<SourceManager> {
/// use (-ID - 2).
llvm::PagedVector<SrcMgr::SLocEntry> LoadedSLocEntryTable;

/// For each allocation in LoadedSLocEntryTable, we keep the first FileID.
/// We assume exactly one allocation per AST file, and use that to determine
/// whether two FileIDs come from the same AST file.
SmallVector<FileID, 0> LoadedSLocEntryAllocBegin;

/// The starting offset of the next local SLocEntry.
///
/// This is LocalSLocEntryTable.back().Offset + the size of that entry.
Expand Down Expand Up @@ -1639,6 +1644,11 @@ class SourceManager : public RefCountedBase<SourceManager> {
isInTheSameTranslationUnit(std::pair<FileID, unsigned> &LOffs,
std::pair<FileID, unsigned> &ROffs) const;

/// Determines whether the two decomposed source location is in the same TU.
bool isInTheSameTranslationUnitImpl(
const std::pair<FileID, unsigned> &LOffs,
const std::pair<FileID, unsigned> &ROffs) const;

/// Determines the order of 2 source locations in the "source location
/// address space".
bool isBeforeInSLocAddrSpace(SourceLocation LHS, SourceLocation RHS) const {
Expand Down
10 changes: 2 additions & 8 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const unsigned VERSION_MAJOR = 29;
/// for the previous version could still support reading the new
/// version by ignoring new kinds of subblocks), this number
/// should be increased.
const unsigned VERSION_MINOR = 0;
const unsigned VERSION_MINOR = 1;

/// An ID number that refers to an identifier in an AST file.
///
Expand Down Expand Up @@ -524,13 +524,7 @@ enum ASTRecordTypes {
/// of source-location information.
SOURCE_LOCATION_OFFSETS = 14,

/// Record code for the set of source location entries
/// that need to be preloaded by the AST reader.
///
/// This set contains the source location entry for the
/// predefines buffer and for any file entries that need to be
/// preloaded.
SOURCE_LOCATION_PRELOADS = 15,
// ID 15 used to be for source location entry preloads.

/// Record code for the set of ext_vector type names.
EXT_VECTOR_DECLS = 16,
Expand Down
3 changes: 0 additions & 3 deletions clang/include/clang/Serialization/ModuleFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,6 @@ class ModuleFile {
/// AST file.
const uint32_t *SLocEntryOffsets = nullptr;

/// SLocEntries that we're going to preload.
SmallVector<uint64_t, 4> PreloadSLocEntries;

/// Remapping table for source locations in this module.
ContinuousRangeMap<SourceLocation::UIntTy, SourceLocation::IntTy, 2>
SLocRemap;
Expand Down
165 changes: 99 additions & 66 deletions clang/lib/Basic/SourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,8 +460,9 @@ SourceManager::AllocateLoadedSLocEntries(unsigned NumSLocEntries,
LoadedSLocEntryTable.resize(LoadedSLocEntryTable.size() + NumSLocEntries);
SLocEntryLoaded.resize(LoadedSLocEntryTable.size());
CurrentLoadedOffset -= TotalSize;
int ID = LoadedSLocEntryTable.size();
return std::make_pair(-ID - 1, CurrentLoadedOffset);
int BaseID = -int(LoadedSLocEntryTable.size()) - 1;
LoadedSLocEntryAllocBegin.push_back(FileID::get(BaseID));
return std::make_pair(BaseID, CurrentLoadedOffset);
}

/// As part of recovering from missing or changed content, produce a
Expand Down Expand Up @@ -1964,14 +1965,38 @@ SourceManager::getDecomposedIncludedLoc(FileID FID) const {
return DecompLoc;
}

bool SourceManager::isInTheSameTranslationUnitImpl(
const std::pair<FileID, unsigned> &LOffs,
const std::pair<FileID, unsigned> &ROffs) const {
// If one is local while the other is loaded.
if (isLoadedFileID(LOffs.first) != isLoadedFileID(ROffs.first))
return false;

if (isLoadedFileID(LOffs.first) && isLoadedFileID(ROffs.first)) {
auto FindSLocEntryAlloc = [this](FileID FID) {
// Loaded FileIDs are negative, we store the lowest FileID from each
// allocation, later allocations have lower FileIDs.
return llvm::lower_bound(LoadedSLocEntryAllocBegin, FID, std::greater{});
};

// If both are loaded from different AST files.
if (FindSLocEntryAlloc(LOffs.first) != FindSLocEntryAlloc(ROffs.first))
return false;
}

return true;
}

/// Given a decomposed source location, move it up the include/expansion stack
/// to the parent source location. If this is possible, return the decomposed
/// version of the parent in Loc and return false. If Loc is the top-level
/// entry, return true and don't modify it.
static bool MoveUpIncludeHierarchy(std::pair<FileID, unsigned> &Loc,
const SourceManager &SM) {
/// to the parent source location within the same translation unit. If this is
/// possible, return the decomposed version of the parent in Loc and return
/// false. If Loc is a top-level entry, return true and don't modify it.
static bool
MoveUpTranslationUnitIncludeHierarchy(std::pair<FileID, unsigned> &Loc,
const SourceManager &SM) {
std::pair<FileID, unsigned> UpperLoc = SM.getDecomposedIncludedLoc(Loc.first);
if (UpperLoc.first.isInvalid())
if (UpperLoc.first.isInvalid() ||
!SM.isInTheSameTranslationUnitImpl(UpperLoc, Loc))
return true; // We reached the top.

Loc = UpperLoc;
Expand Down Expand Up @@ -2027,45 +2052,18 @@ bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS,
std::pair<bool, bool> InSameTU = isInTheSameTranslationUnit(LOffs, ROffs);
if (InSameTU.first)
return InSameTU.second;

// If we arrived here, the location is either in a built-ins buffer or
// associated with global inline asm. PR5662 and PR22576 are examples.

StringRef LB = getBufferOrFake(LOffs.first).getBufferIdentifier();
StringRef RB = getBufferOrFake(ROffs.first).getBufferIdentifier();
bool LIsBuiltins = LB == "<built-in>";
bool RIsBuiltins = RB == "<built-in>";
// Sort built-in before non-built-in.
if (LIsBuiltins || RIsBuiltins) {
if (LIsBuiltins != RIsBuiltins)
return LIsBuiltins;
// Both are in built-in buffers, but from different files. We just claim that
// lower IDs come first.
return LOffs.first < ROffs.first;
}
bool LIsAsm = LB == "<inline asm>";
bool RIsAsm = RB == "<inline asm>";
// Sort assembler after built-ins, but before the rest.
if (LIsAsm || RIsAsm) {
if (LIsAsm != RIsAsm)
return RIsAsm;
assert(LOffs.first == ROffs.first);
return false;
}
bool LIsScratch = LB == "<scratch space>";
bool RIsScratch = RB == "<scratch space>";
// Sort scratch after inline asm, but before the rest.
if (LIsScratch || RIsScratch) {
if (LIsScratch != RIsScratch)
return LIsScratch;
return LOffs.second < ROffs.second;
}
llvm_unreachable("Unsortable locations found");
// TODO: This should be unreachable, but some clients are calling this
// function before making sure LHS and RHS are in the same TU.
return LOffs.first < ROffs.first;
}

std::pair<bool, bool> SourceManager::isInTheSameTranslationUnit(
std::pair<FileID, unsigned> &LOffs,
std::pair<FileID, unsigned> &ROffs) const {
// If the source locations are not in the same TU, return early.
if (!isInTheSameTranslationUnitImpl(LOffs, ROffs))
return std::make_pair(false, false);

// If the source locations are in the same file, just compare offsets.
if (LOffs.first == ROffs.first)
return std::make_pair(true, LOffs.second < ROffs.second);
Expand All @@ -2088,53 +2086,88 @@ std::pair<bool, bool> SourceManager::isInTheSameTranslationUnit(

// A location within a FileID on the path up from LOffs to the main file.
struct Entry {
unsigned Offset;
FileID ParentFID; // Used for breaking ties.
std::pair<FileID, unsigned> DecomposedLoc; // FileID redundant, but clearer.
FileID ChildFID; // Used for breaking ties. Invalid for the initial loc.
};
llvm::SmallDenseMap<FileID, Entry, 16> LChain;

FileID Parent;
FileID LChild;
do {
LChain.try_emplace(LOffs.first, Entry{LOffs.second, Parent});
LChain.try_emplace(LOffs.first, Entry{LOffs, LChild});
// We catch the case where LOffs is in a file included by ROffs and
// quit early. The other way round unfortunately remains suboptimal.
if (LOffs.first == ROffs.first)
break;
Parent = LOffs.first;
} while (!MoveUpIncludeHierarchy(LOffs, *this));
LChild = LOffs.first;
} while (!MoveUpTranslationUnitIncludeHierarchy(LOffs, *this));

Parent = FileID();
FileID RChild;
do {
auto I = LChain.find(ROffs.first);
if (I != LChain.end()) {
auto LIt = LChain.find(ROffs.first);
if (LIt != LChain.end()) {
// Compare the locations within the common file and cache them.
LOffs.first = I->first;
LOffs.second = I->second.Offset;
// The relative order of LParent and RParent is a tiebreaker when
LOffs = LIt->second.DecomposedLoc;
LChild = LIt->second.ChildFID;
// The relative order of LChild and RChild is a tiebreaker when
// - locs expand to the same location (occurs in macro arg expansion)
// - one loc is a parent of the other (we consider the parent as "first")
// For the parent to be first, the invalid file ID must compare smaller.
// For the parent entry to be first, its invalid child file ID must
// compare smaller to the valid child file ID of the other entry.
// However loaded FileIDs are <0, so we perform *unsigned* comparison!
// This changes the relative order of local vs loaded FileIDs, but it
// doesn't matter as these are never mixed in macro expansion.
unsigned LParent = I->second.ParentFID.ID;
unsigned RParent = Parent.ID;
unsigned LChildID = LChild.ID;
unsigned RChildID = RChild.ID;
assert(((LOffs.second != ROffs.second) ||
(LParent == 0 || RParent == 0) ||
isInSameSLocAddrSpace(getComposedLoc(I->second.ParentFID, 0),
getComposedLoc(Parent, 0), nullptr)) &&
(LChildID == 0 || RChildID == 0) ||
isInSameSLocAddrSpace(getComposedLoc(LChild, 0),
getComposedLoc(RChild, 0), nullptr)) &&
"Mixed local/loaded FileIDs with same include location?");
IsBeforeInTUCache.setCommonLoc(LOffs.first, LOffs.second, ROffs.second,
LParent < RParent);
LChildID < RChildID);
return std::make_pair(
true, IsBeforeInTUCache.getCachedResult(LOffs.second, ROffs.second));
}
Parent = ROffs.first;
} while (!MoveUpIncludeHierarchy(ROffs, *this));
RChild = ROffs.first;
} while (!MoveUpTranslationUnitIncludeHierarchy(ROffs, *this));

// If we found no match, the location is either in a built-ins buffer or
// associated with global inline asm. PR5662 and PR22576 are examples.

StringRef LB = getBufferOrFake(LOffs.first).getBufferIdentifier();
StringRef RB = getBufferOrFake(ROffs.first).getBufferIdentifier();

bool LIsBuiltins = LB == "<built-in>";
bool RIsBuiltins = RB == "<built-in>";
// Sort built-in before non-built-in.
if (LIsBuiltins || RIsBuiltins) {
if (LIsBuiltins != RIsBuiltins)
return std::make_pair(true, LIsBuiltins);
// Both are in built-in buffers, but from different files. We just claim
// that lower IDs come first.
return std::make_pair(true, LOffs.first < ROffs.first);
}

bool LIsAsm = LB == "<inline asm>";
bool RIsAsm = RB == "<inline asm>";
// Sort assembler after built-ins, but before the rest.
if (LIsAsm || RIsAsm) {
if (LIsAsm != RIsAsm)
return std::make_pair(true, RIsAsm);
assert(LOffs.first == ROffs.first);
return std::make_pair(true, false);
}

bool LIsScratch = LB == "<scratch space>";
bool RIsScratch = RB == "<scratch space>";
// Sort scratch after inline asm, but before the rest.
if (LIsScratch || RIsScratch) {
if (LIsScratch != RIsScratch)
return std::make_pair(true, LIsScratch);
return std::make_pair(true, LOffs.second < ROffs.second);
}

// If we found no match, we're not in the same TU.
// We don't cache this, but it is rare.
return std::make_pair(false, false);
llvm_unreachable("Unsortable locations found");
}

void SourceManager::PrintStats() const {
Expand Down
23 changes: 0 additions & 23 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3226,7 +3226,6 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
case SOURCE_LOCATION_OFFSETS:
case MODULE_OFFSET_MAP:
case SOURCE_MANAGER_LINE_TABLE:
case SOURCE_LOCATION_PRELOADS:
case PPD_ENTITIES_OFFSETS:
case HEADER_SEARCH_TABLE:
case IMPORTED_MODULES:
Expand Down Expand Up @@ -3576,18 +3575,6 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
ParseLineTable(F, Record);
break;

case SOURCE_LOCATION_PRELOADS: {
// Need to transform from the local view (1-based IDs) to the global view,
// which is based off F.SLocEntryBaseID.
if (!F.PreloadSLocEntries.empty())
return llvm::createStringError(
std::errc::illegal_byte_sequence,
"Multiple SOURCE_LOCATION_PRELOADS records in AST file");

F.PreloadSLocEntries.swap(Record);
break;
}

case EXT_VECTOR_DECLS:
for (unsigned I = 0, N = Record.size(); I != N; ++I)
ExtVectorDecls.push_back(getGlobalDeclID(F, Record[I]));
Expand Down Expand Up @@ -4417,16 +4404,6 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, ModuleKind Type,
for (ImportedModule &M : Loaded) {
ModuleFile &F = *M.Mod;

// Preload SLocEntries.
for (unsigned I = 0, N = F.PreloadSLocEntries.size(); I != N; ++I) {
int Index = int(F.PreloadSLocEntries[I] - 1) + F.SLocEntryBaseID;
// Load it through the SourceManager and don't call ReadSLocEntry()
// directly because the entry may have already been loaded in which case
// calling ReadSLocEntry() directly would trigger an assertion in
// SourceManager.
SourceMgr.getLoadedSLocEntryByID(Index);
}

// Map the original source file ID into the ID space of the current
// compilation.
if (F.OriginalSourceFileID.isValid())
Expand Down
8 changes: 0 additions & 8 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,6 @@ void ASTWriter::WriteBlockInfoBlock() {
RECORD(METHOD_POOL);
RECORD(PP_COUNTER_VALUE);
RECORD(SOURCE_LOCATION_OFFSETS);
RECORD(SOURCE_LOCATION_PRELOADS);
RECORD(EXT_VECTOR_DECLS);
RECORD(UNUSED_FILESCOPED_DECLS);
RECORD(PPD_ENTITIES_OFFSETS);
Expand Down Expand Up @@ -2138,7 +2137,6 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
// entry, which is always the same dummy entry.
std::vector<uint32_t> SLocEntryOffsets;
uint64_t SLocEntryOffsetsBase = Stream.GetCurrentBitNo();
RecordData PreloadSLocs;
SLocEntryOffsets.reserve(SourceMgr.local_sloc_entry_size() - 1);
for (unsigned I = 1, N = SourceMgr.local_sloc_entry_size();
I != N; ++I) {
Expand Down Expand Up @@ -2214,9 +2212,6 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
Stream.EmitRecordWithBlob(SLocBufferAbbrv, Record,
StringRef(Name.data(), Name.size() + 1));
EmitBlob = true;

if (Name == "<built-in>")
PreloadSLocs.push_back(SLocEntryOffsets.size());
}

if (EmitBlob) {
Expand Down Expand Up @@ -2278,9 +2273,6 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
Stream.EmitRecordWithBlob(SLocOffsetsAbbrev, Record,
bytes(SLocEntryOffsets));
}
// Write the source location entry preloads array, telling the AST
// reader which source locations entries it should load eagerly.
Stream.EmitRecord(SOURCE_LOCATION_PRELOADS, PreloadSLocs);

// Write the line table. It depends on remapping working, so it must come
// after the source location offsets.
Expand Down

0 comments on commit 0dfb5da

Please sign in to comment.