Skip to content

Commit 9ba53c8

Browse files
committed
[clang][modules] Remove preloaded SLocEntries from PCM files (llvm#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 f051029 commit 9ba53c8

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
@@ -58,6 +58,7 @@ class FileID {
5858
friend class ASTWriter;
5959
friend class ASTReader;
6060
friend class SourceManager;
61+
friend class SourceManagerTestHelper;
6162

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

clang/include/clang/Basic/SourceManager.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,11 @@ class SourceManager : public RefCountedBase<SourceManager> {
706706
/// use (-ID - 2).
707707
SmallVector<SrcMgr::SLocEntry, 0> LoadedSLocEntryTable;
708708

709+
/// For each allocation in LoadedSLocEntryTable, we keep the first FileID.
710+
/// We assume exactly one allocation per AST file, and use that to determine
711+
/// whether two FileIDs come from the same AST file.
712+
SmallVector<FileID, 0> LoadedSLocEntryAllocBegin;
713+
709714
/// The starting offset of the next local SLocEntry.
710715
///
711716
/// This is LocalSLocEntryTable.back().Offset + the size of that entry.
@@ -1654,6 +1659,11 @@ class SourceManager : public RefCountedBase<SourceManager> {
16541659
isInTheSameTranslationUnit(std::pair<FileID, unsigned> &LOffs,
16551660
std::pair<FileID, unsigned> &ROffs) const;
16561661

1662+
/// Determines whether the two decomposed source location is in the same TU.
1663+
bool isInTheSameTranslationUnitImpl(
1664+
const std::pair<FileID, unsigned> &LOffs,
1665+
const std::pair<FileID, unsigned> &ROffs) const;
1666+
16571667
/// Determines the order of 2 source locations in the "source location
16581668
/// address space".
16591669
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
///
@@ -535,13 +535,7 @@ enum ASTRecordTypes {
535535
/// of source-location information.
536536
SOURCE_LOCATION_OFFSETS = 14,
537537

538-
/// Record code for the set of source location entries
539-
/// that need to be preloaded by the AST reader.
540-
///
541-
/// This set contains the source location entry for the
542-
/// predefines buffer and for any file entries that need to be
543-
/// preloaded.
544-
SOURCE_LOCATION_PRELOADS = 15,
538+
// ID 15 used to be for source location entry preloads.
545539

546540
/// Record code for the set of ext_vector type names.
547541
EXT_VECTOR_DECLS = 16,

clang/include/clang/Serialization/ModuleFile.h

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

306-
/// SLocEntries that we're going to preload.
307-
SmallVector<uint64_t, 4> PreloadSLocEntries;
308-
309306
/// Remapping table for source locations in this module.
310307
ContinuousRangeMap<SourceLocation::UIntTy, SourceLocation::IntTy, 2>
311308
SLocRemap;

clang/lib/Basic/SourceManager.cpp

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

468469
/// As part of recovering from missing or changed content, produce a
@@ -1976,14 +1977,38 @@ SourceManager::getDecomposedIncludedLoc(FileID FID) const {
19761977
return DecompLoc;
19771978
}
19781979

1980+
bool SourceManager::isInTheSameTranslationUnitImpl(
1981+
const std::pair<FileID, unsigned> &LOffs,
1982+
const std::pair<FileID, unsigned> &ROffs) const {
1983+
// If one is local while the other is loaded.
1984+
if (isLoadedFileID(LOffs.first) != isLoadedFileID(ROffs.first))
1985+
return false;
1986+
1987+
if (isLoadedFileID(LOffs.first) && isLoadedFileID(ROffs.first)) {
1988+
auto FindSLocEntryAlloc = [this](FileID FID) {
1989+
// Loaded FileIDs are negative, we store the lowest FileID from each
1990+
// allocation, later allocations have lower FileIDs.
1991+
return llvm::lower_bound(LoadedSLocEntryAllocBegin, FID, std::greater{});
1992+
};
1993+
1994+
// If both are loaded from different AST files.
1995+
if (FindSLocEntryAlloc(LOffs.first) != FindSLocEntryAlloc(ROffs.first))
1996+
return false;
1997+
}
1998+
1999+
return true;
2000+
}
2001+
19792002
/// Given a decomposed source location, move it up the include/expansion stack
1980-
/// to the parent source location. If this is possible, return the decomposed
1981-
/// version of the parent in Loc and return false. If Loc is the top-level
1982-
/// entry, return true and don't modify it.
1983-
static bool MoveUpIncludeHierarchy(std::pair<FileID, unsigned> &Loc,
1984-
const SourceManager &SM) {
2003+
/// to the parent source location within the same translation unit. If this is
2004+
/// possible, return the decomposed version of the parent in Loc and return
2005+
/// false. If Loc is a top-level entry, return true and don't modify it.
2006+
static bool
2007+
MoveUpTranslationUnitIncludeHierarchy(std::pair<FileID, unsigned> &Loc,
2008+
const SourceManager &SM) {
19852009
std::pair<FileID, unsigned> UpperLoc = SM.getDecomposedIncludedLoc(Loc.first);
1986-
if (UpperLoc.first.isInvalid())
2010+
if (UpperLoc.first.isInvalid() ||
2011+
!SM.isInTheSameTranslationUnitImpl(UpperLoc, Loc))
19872012
return true; // We reached the top.
19882013

19892014
Loc = UpperLoc;
@@ -2039,45 +2064,18 @@ bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS,
20392064
std::pair<bool, bool> InSameTU = isInTheSameTranslationUnit(LOffs, ROffs);
20402065
if (InSameTU.first)
20412066
return InSameTU.second;
2042-
2043-
// If we arrived here, the location is either in a built-ins buffer or
2044-
// associated with global inline asm. PR5662 and PR22576 are examples.
2045-
2046-
StringRef LB = getBufferOrFake(LOffs.first).getBufferIdentifier();
2047-
StringRef RB = getBufferOrFake(ROffs.first).getBufferIdentifier();
2048-
bool LIsBuiltins = LB == "<built-in>";
2049-
bool RIsBuiltins = RB == "<built-in>";
2050-
// Sort built-in before non-built-in.
2051-
if (LIsBuiltins || RIsBuiltins) {
2052-
if (LIsBuiltins != RIsBuiltins)
2053-
return LIsBuiltins;
2054-
// Both are in built-in buffers, but from different files. We just claim that
2055-
// lower IDs come first.
2056-
return LOffs.first < ROffs.first;
2057-
}
2058-
bool LIsAsm = LB == "<inline asm>";
2059-
bool RIsAsm = RB == "<inline asm>";
2060-
// Sort assembler after built-ins, but before the rest.
2061-
if (LIsAsm || RIsAsm) {
2062-
if (LIsAsm != RIsAsm)
2063-
return RIsAsm;
2064-
assert(LOffs.first == ROffs.first);
2065-
return false;
2066-
}
2067-
bool LIsScratch = LB == "<scratch space>";
2068-
bool RIsScratch = RB == "<scratch space>";
2069-
// Sort scratch after inline asm, but before the rest.
2070-
if (LIsScratch || RIsScratch) {
2071-
if (LIsScratch != RIsScratch)
2072-
return LIsScratch;
2073-
return LOffs.second < ROffs.second;
2074-
}
2075-
llvm_unreachable("Unsortable locations found");
2067+
// TODO: This should be unreachable, but some clients are calling this
2068+
// function before making sure LHS and RHS are in the same TU.
2069+
return LOffs.first < ROffs.first;
20762070
}
20772071

20782072
std::pair<bool, bool> SourceManager::isInTheSameTranslationUnit(
20792073
std::pair<FileID, unsigned> &LOffs,
20802074
std::pair<FileID, unsigned> &ROffs) const {
2075+
// If the source locations are not in the same TU, return early.
2076+
if (!isInTheSameTranslationUnitImpl(LOffs, ROffs))
2077+
return std::make_pair(false, false);
2078+
20812079
// If the source locations are in the same file, just compare offsets.
20822080
if (LOffs.first == ROffs.first)
20832081
return std::make_pair(true, LOffs.second < ROffs.second);
@@ -2100,53 +2098,88 @@ std::pair<bool, bool> SourceManager::isInTheSameTranslationUnit(
21002098

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

2108-
FileID Parent;
2106+
FileID LChild;
21092107
do {
2110-
LChain.try_emplace(LOffs.first, Entry{LOffs.second, Parent});
2108+
LChain.try_emplace(LOffs.first, Entry{LOffs, LChild});
21112109
// We catch the case where LOffs is in a file included by ROffs and
21122110
// quit early. The other way round unfortunately remains suboptimal.
21132111
if (LOffs.first == ROffs.first)
21142112
break;
2115-
Parent = LOffs.first;
2116-
} while (!MoveUpIncludeHierarchy(LOffs, *this));
2113+
LChild = LOffs.first;
2114+
} while (!MoveUpTranslationUnitIncludeHierarchy(LOffs, *this));
21172115

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

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

21522185
void SourceManager::PrintStats() const {

clang/lib/Serialization/ASTReader.cpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3248,7 +3248,6 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
32483248
case SOURCE_LOCATION_OFFSETS:
32493249
case MODULE_OFFSET_MAP:
32503250
case SOURCE_MANAGER_LINE_TABLE:
3251-
case SOURCE_LOCATION_PRELOADS:
32523251
case PPD_ENTITIES_OFFSETS:
32533252
case HEADER_SEARCH_TABLE:
32543253
case IMPORTED_MODULES:
@@ -3598,18 +3597,6 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
35983597
ParseLineTable(F, Record);
35993598
break;
36003599

3601-
case SOURCE_LOCATION_PRELOADS: {
3602-
// Need to transform from the local view (1-based IDs) to the global view,
3603-
// which is based off F.SLocEntryBaseID.
3604-
if (!F.PreloadSLocEntries.empty())
3605-
return llvm::createStringError(
3606-
std::errc::illegal_byte_sequence,
3607-
"Multiple SOURCE_LOCATION_PRELOADS records in AST file");
3608-
3609-
F.PreloadSLocEntries.swap(Record);
3610-
break;
3611-
}
3612-
36133600
case EXT_VECTOR_DECLS:
36143601
for (unsigned I = 0, N = Record.size(); I != N; ++I)
36153602
ExtVectorDecls.push_back(getGlobalDeclID(F, Record[I]));
@@ -4419,16 +4406,6 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName,
44194406
for (ImportedModule &M : Loaded) {
44204407
ModuleFile &F = *M.Mod;
44214408

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

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,6 @@ void ASTWriter::WriteBlockInfoBlock() {
837837
RECORD(METHOD_POOL);
838838
RECORD(PP_COUNTER_VALUE);
839839
RECORD(SOURCE_LOCATION_OFFSETS);
840-
RECORD(SOURCE_LOCATION_PRELOADS);
841840
RECORD(EXT_VECTOR_DECLS);
842841
RECORD(UNUSED_FILESCOPED_DECLS);
843842
RECORD(PPD_ENTITIES_OFFSETS);
@@ -2171,7 +2170,6 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
21712170
// entry, which is always the same dummy entry.
21722171
std::vector<uint32_t> SLocEntryOffsets;
21732172
uint64_t SLocEntryOffsetsBase = Stream.GetCurrentBitNo();
2174-
RecordData PreloadSLocs;
21752173
SLocEntryOffsets.reserve(SourceMgr.local_sloc_entry_size() - 1);
21762174
for (unsigned I = 1, N = SourceMgr.local_sloc_entry_size();
21772175
I != N; ++I) {
@@ -2247,9 +2245,6 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
22472245
Stream.EmitRecordWithBlob(SLocBufferAbbrv, Record,
22482246
StringRef(Name.data(), Name.size() + 1));
22492247
EmitBlob = true;
2250-
2251-
if (Name == "<built-in>")
2252-
PreloadSLocs.push_back(SLocEntryOffsets.size());
22532248
}
22542249

22552250
if (EmitBlob) {
@@ -2311,9 +2306,6 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr,
23112306
Stream.EmitRecordWithBlob(SLocOffsetsAbbrev, Record,
23122307
bytes(SLocEntryOffsets));
23132308
}
2314-
// Write the source location entry preloads array, telling the AST
2315-
// reader which source locations entries it should load eagerly.
2316-
Stream.EmitRecord(SOURCE_LOCATION_PRELOADS, PreloadSLocs);
23172309

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

0 commit comments

Comments
 (0)