Skip to content

Commit 0dfb5da

Browse files
authored
[clang][modules] Remove preloaded SLocEntries from PCM files (#66962)
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.
1 parent 4a16b51 commit 0dfb5da

File tree

8 files changed

+165
-108
lines changed

8 files changed

+165
-108
lines changed

clang/include/clang/Basic/SourceLocation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class FileID {
5959
friend class ASTWriter;
6060
friend class ASTReader;
6161
friend class SourceManager;
62+
friend class SourceManagerTestHelper;
6263

6364
static FileID get(int V) {
6465
FileID F;

clang/include/clang/Basic/SourceManager.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,11 @@ class SourceManager : public RefCountedBase<SourceManager> {
702702
/// use (-ID - 2).
703703
llvm::PagedVector<SrcMgr::SLocEntry> LoadedSLocEntryTable;
704704

705+
/// For each allocation in LoadedSLocEntryTable, we keep the first FileID.
706+
/// We assume exactly one allocation per AST file, and use that to determine
707+
/// whether two FileIDs come from the same AST file.
708+
SmallVector<FileID, 0> LoadedSLocEntryAllocBegin;
709+
705710
/// The starting offset of the next local SLocEntry.
706711
///
707712
/// This is LocalSLocEntryTable.back().Offset + the size of that entry.
@@ -1639,6 +1644,11 @@ class SourceManager : public RefCountedBase<SourceManager> {
16391644
isInTheSameTranslationUnit(std::pair<FileID, unsigned> &LOffs,
16401645
std::pair<FileID, unsigned> &ROffs) const;
16411646

1647+
/// Determines whether the two decomposed source location is in the same TU.
1648+
bool isInTheSameTranslationUnitImpl(
1649+
const std::pair<FileID, unsigned> &LOffs,
1650+
const std::pair<FileID, unsigned> &ROffs) const;
1651+
16421652
/// Determines the order of 2 source locations in the "source location
16431653
/// address space".
16441654
bool isBeforeInSLocAddrSpace(SourceLocation LHS, SourceLocation RHS) const {

clang/include/clang/Serialization/ASTBitCodes.h

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const unsigned VERSION_MAJOR = 29;
5151
/// for the previous version could still support reading the new
5252
/// version by ignoring new kinds of subblocks), this number
5353
/// should be increased.
54-
const unsigned VERSION_MINOR = 0;
54+
const unsigned VERSION_MINOR = 1;
5555

5656
/// An ID number that refers to an identifier in an AST file.
5757
///
@@ -524,13 +524,7 @@ enum ASTRecordTypes {
524524
/// of source-location information.
525525
SOURCE_LOCATION_OFFSETS = 14,
526526

527-
/// Record code for the set of source location entries
528-
/// that need to be preloaded by the AST reader.
529-
///
530-
/// This set contains the source location entry for the
531-
/// predefines buffer and for any file entries that need to be
532-
/// preloaded.
533-
SOURCE_LOCATION_PRELOADS = 15,
527+
// ID 15 used to be for source location entry preloads.
534528

535529
/// Record code for the set of ext_vector type names.
536530
EXT_VECTOR_DECLS = 16,

clang/include/clang/Serialization/ModuleFile.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,6 @@ class ModuleFile {
292292
/// AST file.
293293
const uint32_t *SLocEntryOffsets = nullptr;
294294

295-
/// SLocEntries that we're going to preload.
296-
SmallVector<uint64_t, 4> PreloadSLocEntries;
297-
298295
/// Remapping table for source locations in this module.
299296
ContinuousRangeMap<SourceLocation::UIntTy, SourceLocation::IntTy, 2>
300297
SLocRemap;

clang/lib/Basic/SourceManager.cpp

Lines changed: 99 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,9 @@ SourceManager::AllocateLoadedSLocEntries(unsigned NumSLocEntries,
460460
LoadedSLocEntryTable.resize(LoadedSLocEntryTable.size() + NumSLocEntries);
461461
SLocEntryLoaded.resize(LoadedSLocEntryTable.size());
462462
CurrentLoadedOffset -= TotalSize;
463-
int ID = LoadedSLocEntryTable.size();
464-
return std::make_pair(-ID - 1, CurrentLoadedOffset);
463+
int BaseID = -int(LoadedSLocEntryTable.size()) - 1;
464+
LoadedSLocEntryAllocBegin.push_back(FileID::get(BaseID));
465+
return std::make_pair(BaseID, CurrentLoadedOffset);
465466
}
466467

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

1968+
bool SourceManager::isInTheSameTranslationUnitImpl(
1969+
const std::pair<FileID, unsigned> &LOffs,
1970+
const std::pair<FileID, unsigned> &ROffs) const {
1971+
// If one is local while the other is loaded.
1972+
if (isLoadedFileID(LOffs.first) != isLoadedFileID(ROffs.first))
1973+
return false;
1974+
1975+
if (isLoadedFileID(LOffs.first) && isLoadedFileID(ROffs.first)) {
1976+
auto FindSLocEntryAlloc = [this](FileID FID) {
1977+
// Loaded FileIDs are negative, we store the lowest FileID from each
1978+
// allocation, later allocations have lower FileIDs.
1979+
return llvm::lower_bound(LoadedSLocEntryAllocBegin, FID, std::greater{});
1980+
};
1981+
1982+
// If both are loaded from different AST files.
1983+
if (FindSLocEntryAlloc(LOffs.first) != FindSLocEntryAlloc(ROffs.first))
1984+
return false;
1985+
}
1986+
1987+
return true;
1988+
}
1989+
19671990
/// Given a decomposed source location, move it up the include/expansion stack
1968-
/// to the parent source location. If this is possible, return the decomposed
1969-
/// version of the parent in Loc and return false. If Loc is the top-level
1970-
/// entry, return true and don't modify it.
1971-
static bool MoveUpIncludeHierarchy(std::pair<FileID, unsigned> &Loc,
1972-
const SourceManager &SM) {
1991+
/// to the parent source location within the same translation unit. If this is
1992+
/// possible, return the decomposed version of the parent in Loc and return
1993+
/// false. If Loc is a top-level entry, return true and don't modify it.
1994+
static bool
1995+
MoveUpTranslationUnitIncludeHierarchy(std::pair<FileID, unsigned> &Loc,
1996+
const SourceManager &SM) {
19731997
std::pair<FileID, unsigned> UpperLoc = SM.getDecomposedIncludedLoc(Loc.first);
1974-
if (UpperLoc.first.isInvalid())
1998+
if (UpperLoc.first.isInvalid() ||
1999+
!SM.isInTheSameTranslationUnitImpl(UpperLoc, Loc))
19752000
return true; // We reached the top.
19762001

19772002
Loc = UpperLoc;
@@ -2027,45 +2052,18 @@ bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS,
20272052
std::pair<bool, bool> InSameTU = isInTheSameTranslationUnit(LOffs, ROffs);
20282053
if (InSameTU.first)
20292054
return InSameTU.second;
2030-
2031-
// If we arrived here, the location is either in a built-ins buffer or
2032-
// associated with global inline asm. PR5662 and PR22576 are examples.
2033-
2034-
StringRef LB = getBufferOrFake(LOffs.first).getBufferIdentifier();
2035-
StringRef RB = getBufferOrFake(ROffs.first).getBufferIdentifier();
2036-
bool LIsBuiltins = LB == "<built-in>";
2037-
bool RIsBuiltins = RB == "<built-in>";
2038-
// Sort built-in before non-built-in.
2039-
if (LIsBuiltins || RIsBuiltins) {
2040-
if (LIsBuiltins != RIsBuiltins)
2041-
return LIsBuiltins;
2042-
// Both are in built-in buffers, but from different files. We just claim that
2043-
// lower IDs come first.
2044-
return LOffs.first < ROffs.first;
2045-
}
2046-
bool LIsAsm = LB == "<inline asm>";
2047-
bool RIsAsm = RB == "<inline asm>";
2048-
// Sort assembler after built-ins, but before the rest.
2049-
if (LIsAsm || RIsAsm) {
2050-
if (LIsAsm != RIsAsm)
2051-
return RIsAsm;
2052-
assert(LOffs.first == ROffs.first);
2053-
return false;
2054-
}
2055-
bool LIsScratch = LB == "<scratch space>";
2056-
bool RIsScratch = RB == "<scratch space>";
2057-
// Sort scratch after inline asm, but before the rest.
2058-
if (LIsScratch || RIsScratch) {
2059-
if (LIsScratch != RIsScratch)
2060-
return LIsScratch;
2061-
return LOffs.second < ROffs.second;
2062-
}
2063-
llvm_unreachable("Unsortable locations found");
2055+
// TODO: This should be unreachable, but some clients are calling this
2056+
// function before making sure LHS and RHS are in the same TU.
2057+
return LOffs.first < ROffs.first;
20642058
}
20652059

20662060
std::pair<bool, bool> SourceManager::isInTheSameTranslationUnit(
20672061
std::pair<FileID, unsigned> &LOffs,
20682062
std::pair<FileID, unsigned> &ROffs) const {
2063+
// If the source locations are not in the same TU, return early.
2064+
if (!isInTheSameTranslationUnitImpl(LOffs, ROffs))
2065+
return std::make_pair(false, false);
2066+
20692067
// If the source locations are in the same file, just compare offsets.
20702068
if (LOffs.first == ROffs.first)
20712069
return std::make_pair(true, LOffs.second < ROffs.second);
@@ -2088,53 +2086,88 @@ std::pair<bool, bool> SourceManager::isInTheSameTranslationUnit(
20882086

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

2096-
FileID Parent;
2094+
FileID LChild;
20972095
do {
2098-
LChain.try_emplace(LOffs.first, Entry{LOffs.second, Parent});
2096+
LChain.try_emplace(LOffs.first, Entry{LOffs, LChild});
20992097
// We catch the case where LOffs is in a file included by ROffs and
21002098
// quit early. The other way round unfortunately remains suboptimal.
21012099
if (LOffs.first == ROffs.first)
21022100
break;
2103-
Parent = LOffs.first;
2104-
} while (!MoveUpIncludeHierarchy(LOffs, *this));
2101+
LChild = LOffs.first;
2102+
} while (!MoveUpTranslationUnitIncludeHierarchy(LOffs, *this));
21052103

2106-
Parent = FileID();
2104+
FileID RChild;
21072105
do {
2108-
auto I = LChain.find(ROffs.first);
2109-
if (I != LChain.end()) {
2106+
auto LIt = LChain.find(ROffs.first);
2107+
if (LIt != LChain.end()) {
21102108
// Compare the locations within the common file and cache them.
2111-
LOffs.first = I->first;
2112-
LOffs.second = I->second.Offset;
2113-
// The relative order of LParent and RParent is a tiebreaker when
2109+
LOffs = LIt->second.DecomposedLoc;
2110+
LChild = LIt->second.ChildFID;
2111+
// The relative order of LChild and RChild is a tiebreaker when
21142112
// - locs expand to the same location (occurs in macro arg expansion)
21152113
// - one loc is a parent of the other (we consider the parent as "first")
2116-
// For the parent to be first, the invalid file ID must compare smaller.
2114+
// For the parent entry to be first, its invalid child file ID must
2115+
// compare smaller to the valid child file ID of the other entry.
21172116
// However loaded FileIDs are <0, so we perform *unsigned* comparison!
21182117
// This changes the relative order of local vs loaded FileIDs, but it
21192118
// doesn't matter as these are never mixed in macro expansion.
2120-
unsigned LParent = I->second.ParentFID.ID;
2121-
unsigned RParent = Parent.ID;
2119+
unsigned LChildID = LChild.ID;
2120+
unsigned RChildID = RChild.ID;
21222121
assert(((LOffs.second != ROffs.second) ||
2123-
(LParent == 0 || RParent == 0) ||
2124-
isInSameSLocAddrSpace(getComposedLoc(I->second.ParentFID, 0),
2125-
getComposedLoc(Parent, 0), nullptr)) &&
2122+
(LChildID == 0 || RChildID == 0) ||
2123+
isInSameSLocAddrSpace(getComposedLoc(LChild, 0),
2124+
getComposedLoc(RChild, 0), nullptr)) &&
21262125
"Mixed local/loaded FileIDs with same include location?");
21272126
IsBeforeInTUCache.setCommonLoc(LOffs.first, LOffs.second, ROffs.second,
2128-
LParent < RParent);
2127+
LChildID < RChildID);
21292128
return std::make_pair(
21302129
true, IsBeforeInTUCache.getCachedResult(LOffs.second, ROffs.second));
21312130
}
2132-
Parent = ROffs.first;
2133-
} while (!MoveUpIncludeHierarchy(ROffs, *this));
2131+
RChild = ROffs.first;
2132+
} while (!MoveUpTranslationUnitIncludeHierarchy(ROffs, *this));
2133+
2134+
// If we found no match, the location is either in a built-ins buffer or
2135+
// associated with global inline asm. PR5662 and PR22576 are examples.
2136+
2137+
StringRef LB = getBufferOrFake(LOffs.first).getBufferIdentifier();
2138+
StringRef RB = getBufferOrFake(ROffs.first).getBufferIdentifier();
2139+
2140+
bool LIsBuiltins = LB == "<built-in>";
2141+
bool RIsBuiltins = RB == "<built-in>";
2142+
// Sort built-in before non-built-in.
2143+
if (LIsBuiltins || RIsBuiltins) {
2144+
if (LIsBuiltins != RIsBuiltins)
2145+
return std::make_pair(true, LIsBuiltins);
2146+
// Both are in built-in buffers, but from different files. We just claim
2147+
// that lower IDs come first.
2148+
return std::make_pair(true, LOffs.first < ROffs.first);
2149+
}
2150+
2151+
bool LIsAsm = LB == "<inline asm>";
2152+
bool RIsAsm = RB == "<inline asm>";
2153+
// Sort assembler after built-ins, but before the rest.
2154+
if (LIsAsm || RIsAsm) {
2155+
if (LIsAsm != RIsAsm)
2156+
return std::make_pair(true, RIsAsm);
2157+
assert(LOffs.first == ROffs.first);
2158+
return std::make_pair(true, false);
2159+
}
2160+
2161+
bool LIsScratch = LB == "<scratch space>";
2162+
bool RIsScratch = RB == "<scratch space>";
2163+
// Sort scratch after inline asm, but before the rest.
2164+
if (LIsScratch || RIsScratch) {
2165+
if (LIsScratch != RIsScratch)
2166+
return std::make_pair(true, LIsScratch);
2167+
return std::make_pair(true, LOffs.second < ROffs.second);
2168+
}
21342169

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

21402173
void SourceManager::PrintStats() const {

clang/lib/Serialization/ASTReader.cpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3226,7 +3226,6 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
32263226
case SOURCE_LOCATION_OFFSETS:
32273227
case MODULE_OFFSET_MAP:
32283228
case SOURCE_MANAGER_LINE_TABLE:
3229-
case SOURCE_LOCATION_PRELOADS:
32303229
case PPD_ENTITIES_OFFSETS:
32313230
case HEADER_SEARCH_TABLE:
32323231
case IMPORTED_MODULES:
@@ -3576,18 +3575,6 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
35763575
ParseLineTable(F, Record);
35773576
break;
35783577

3579-
case SOURCE_LOCATION_PRELOADS: {
3580-
// Need to transform from the local view (1-based IDs) to the global view,
3581-
// which is based off F.SLocEntryBaseID.
3582-
if (!F.PreloadSLocEntries.empty())
3583-
return llvm::createStringError(
3584-
std::errc::illegal_byte_sequence,
3585-
"Multiple SOURCE_LOCATION_PRELOADS records in AST file");
3586-
3587-
F.PreloadSLocEntries.swap(Record);
3588-
break;
3589-
}
3590-
35913578
case EXT_VECTOR_DECLS:
35923579
for (unsigned I = 0, N = Record.size(); I != N; ++I)
35933580
ExtVectorDecls.push_back(getGlobalDeclID(F, Record[I]));
@@ -4417,16 +4404,6 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, ModuleKind Type,
44174404
for (ImportedModule &M : Loaded) {
44184405
ModuleFile &F = *M.Mod;
44194406

4420-
// Preload SLocEntries.
4421-
for (unsigned I = 0, N = F.PreloadSLocEntries.size(); I != N; ++I) {
4422-
int Index = int(F.PreloadSLocEntries[I] - 1) + F.SLocEntryBaseID;
4423-
// Load it through the SourceManager and don't call ReadSLocEntry()
4424-
// directly because the entry may have already been loaded in which case
4425-
// calling ReadSLocEntry() directly would trigger an assertion in
4426-
// SourceManager.
4427-
SourceMgr.getLoadedSLocEntryByID(Index);
4428-
}
4429-
44304407
// Map the original source file ID into the ID space of the current
44314408
// compilation.
44324409
if (F.OriginalSourceFileID.isValid())

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,6 @@ void ASTWriter::WriteBlockInfoBlock() {
839839
RECORD(METHOD_POOL);
840840
RECORD(PP_COUNTER_VALUE);
841841
RECORD(SOURCE_LOCATION_OFFSETS);
842-
RECORD(SOURCE_LOCATION_PRELOADS);
843842
RECORD(EXT_VECTOR_DECLS);
844843
RECORD(UNUSED_FILESCOPED_DECLS);
845844
RECORD(PPD_ENTITIES_OFFSETS);
@@ -2138,7 +2137,6 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
21382137
// entry, which is always the same dummy entry.
21392138
std::vector<uint32_t> SLocEntryOffsets;
21402139
uint64_t SLocEntryOffsetsBase = Stream.GetCurrentBitNo();
2141-
RecordData PreloadSLocs;
21422140
SLocEntryOffsets.reserve(SourceMgr.local_sloc_entry_size() - 1);
21432141
for (unsigned I = 1, N = SourceMgr.local_sloc_entry_size();
21442142
I != N; ++I) {
@@ -2214,9 +2212,6 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
22142212
Stream.EmitRecordWithBlob(SLocBufferAbbrv, Record,
22152213
StringRef(Name.data(), Name.size() + 1));
22162214
EmitBlob = true;
2217-
2218-
if (Name == "<built-in>")
2219-
PreloadSLocs.push_back(SLocEntryOffsets.size());
22202215
}
22212216

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

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

0 commit comments

Comments
 (0)