[clang] [clang][DebugInfo] Cache repeated debug info lookups (PR #193785)
Clemens Wasser via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 23 09:00:57 PDT 2026
https://github.com/clemenswasser created https://github.com/llvm/llvm-project/pull/193785
Found while profiling clang compiling SLPVectorizer.cpp.
Avoiding repeated debug location, type identifier, and file lookup work yields small compile-time wins for debug-info builds, mostly noise in optimized non-debug configs, but consistent improvements even up to -3.72% for debug-info builds:
https://llvm-compile-time-tracker.com/compare.php?from=78820cb91605693b7d768be4ebc8b66181d3e9c3&to=d41b2f249e68dad472cbad01435c96991113db7d&stat=instructions:u
Stats of RelWithDebInfo SLPVectorizer.cpp build before and after:
```text
Benchmark 1 (3 runs): main -O2 -g compiling SLPVectorizer.cpp with pch
measurement mean ± σ min … max outliers delta
wall_time 32.3s ± 115ms 32.2s … 32.4s 0 ( 0%) 0%
peak_rss 1.07GB ± 567KB 1.07GB … 1.07GB 0 ( 0%) 0%
cpu_cycles 159G ± 488M 159G … 160G 0 ( 0%) 0%
instructions 214G ± 50.5M 214G … 214G 0 ( 0%) 0%
cache_references 3.99G ± 16.6M 3.98G … 4.01G 0 ( 0%) 0%
cache_misses 661M ± 4.18M 658M … 666M 0 ( 0%) 0%
branch_misses 1.44G ± 3.19M 1.43G … 1.44G 0 ( 0%) 0%
Benchmark 2 (3 runs): perf/cgdebuginfo-lookup-cache -O2 -g compiling SLPVectorizer.cpp with pch
measurement mean ± σ min … max outliers delta
wall_time 32.2s ± 139ms 32.1s … 32.3s 0 ( 0%) - 0.5% ± 0.9%
peak_rss 1.07GB ± 453KB 1.07GB … 1.07GB 0 ( 0%) - 0.2% ± 0.1%
cpu_cycles 158G ± 386M 158G … 159G 0 ( 0%) - 0.5% ± 0.6%
instructions 211G ± 87.2M 211G … 211G 0 ( 0%) ⚡- 1.4% ± 0.1%
cache_references 3.98G ± 9.84M 3.97G … 3.99G 0 ( 0%) - 0.3% ± 0.8%
cache_misses 656M ± 3.93M 652M … 660M 0 ( 0%) - 0.7% ± 1.4%
branch_misses 1.44G ± 2.29M 1.43G … 1.44G 0 ( 0%) + 0.0% ± 0.4%
```
>From d41b2f249e68dad472cbad01435c96991113db7d Mon Sep 17 00:00:00 2001
From: Clemens Wasser <clemens.wasser at gmail.com>
Date: Sat, 18 Apr 2026 12:56:58 +0200
Subject: [PATCH] [clang][DebugInfo] Cache repeated debug info lookups
---
clang/lib/Basic/SourceManager.cpp | 19 +++--
clang/lib/CodeGen/CGDebugInfo.cpp | 125 +++++++++++++++++++-----------
clang/lib/CodeGen/CGDebugInfo.h | 13 ++++
3 files changed, 107 insertions(+), 50 deletions(-)
diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp
index b6cc6ec9365f5..af89d1592d18c 100644
--- a/clang/lib/Basic/SourceManager.cpp
+++ b/clang/lib/Basic/SourceManager.cpp
@@ -812,7 +812,6 @@ FileID SourceManager::getFileIDLocal(SourceLocation::UIntTy SLocOffset) const {
assert(SLocOffset >= LocalSLocEntryTable[0].getOffset() && SLocOffset > 0 &&
"Invalid SLocOffset");
assert(LocalSLocEntryTable.size() == LocalLocOffsetTable.size());
- assert(LastFileIDLookup.ID >= 0 && "Only cache local file sloc entry");
// After the first and second level caches, I see two common sorts of
// behavior: 1) a lot of searched FileID's are "near" the cached file
@@ -833,10 +832,12 @@ FileID SourceManager::getFileIDLocal(SourceLocation::UIntTy SLocOffset) const {
// upper bound of the search range.
unsigned GreaterIndex = LocalLocOffsetTable.size();
// Use the LastFileIDLookup to prune the search space.
- if (LastLookupStartOffset < SLocOffset)
- LessIndex = LastFileIDLookup.ID;
- else
- GreaterIndex = LastFileIDLookup.ID;
+ if (LastFileIDLookup.ID >= 0) {
+ if (LastLookupStartOffset < SLocOffset)
+ LessIndex = LastFileIDLookup.ID;
+ else
+ GreaterIndex = LastFileIDLookup.ID;
+ }
// Find the FileID that contains this.
unsigned NumProbes = 0;
@@ -887,7 +888,13 @@ FileID SourceManager::getFileIDLoaded(SourceLocation::UIntTy SLocOffset) const {
return FileID();
}
- return FileID::get(ExternalSLocEntries->getSLocEntryID(SLocOffset));
+ FileID FID = FileID::get(ExternalSLocEntries->getSLocEntryID(SLocOffset));
+ LastFileIDLookup = FID;
+ LastLookupStartOffset = getSLocEntry(FID).getOffset();
+ FileID NextFID = getNextFileID(FID);
+ LastLookupEndOffset =
+ NextFID.isValid() ? getSLocEntry(NextFID).getOffset() : MaxLoadedOffset;
+ return FID;
}
SourceLocation SourceManager::
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index c5a92a8e7ceb0..12119cc6678b1 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -471,58 +471,71 @@ StringRef CGDebugInfo::getSelectorName(Selector S) {
StringRef CGDebugInfo::getClassName(const RecordDecl *RD,
bool *NameIsSimplified) {
- if (isa<ClassTemplateSpecializationDecl>(RD)) {
- // Copy this name on the side and use its reference.
- return internString(GetName(RD, false, NameIsSimplified));
- }
-
// quick optimization to avoid having to intern strings that are already
// stored reliably elsewhere
if (const IdentifierInfo *II = RD->getIdentifier())
return II->getName();
- // The CodeView printer in LLVM wants to see the names of unnamed types
- // because they need to have a unique identifier.
- // These names are used to reconstruct the fully qualified type names.
- if (CGM.getCodeGenOpts().EmitCodeView) {
+ const auto *CanonicalRD = cast<RecordDecl>(RD->getCanonicalDecl());
+ if (auto It = ClassNameCache.find(CanonicalRD); It != ClassNameCache.end()) {
+ if (NameIsSimplified)
+ *NameIsSimplified = It->second.NameIsSimplified;
+ return It->second.Name;
+ }
+
+ StringRef Name;
+ bool Simplified = false;
+ if (isa<ClassTemplateSpecializationDecl>(RD)) {
+ // Copy this name on the side and use its reference.
+ Name = internString(GetName(RD, false, &Simplified));
+ } else if (CGM.getCodeGenOpts().EmitCodeView) {
+ // The CodeView printer in LLVM wants to see the names of unnamed types
+ // because they need to have a unique identifier.
+ // These names are used to reconstruct the fully qualified type names.
if (const TypedefNameDecl *D = RD->getTypedefNameForAnonDecl()) {
assert(RD->getDeclContext() == D->getDeclContext() &&
"Typedef should not be in another decl context!");
assert(D->getDeclName().getAsIdentifierInfo() &&
"Typedef was not named!");
- return D->getDeclName().getAsIdentifierInfo()->getName();
- }
-
- if (CGM.getLangOpts().CPlusPlus) {
- StringRef Name;
+ Name = D->getDeclName().getAsIdentifierInfo()->getName();
+ } else if (CGM.getLangOpts().CPlusPlus) {
+ StringRef UnnamedName;
ASTContext &Context = CGM.getContext();
if (const DeclaratorDecl *DD = Context.getDeclaratorForUnnamedTagDecl(RD))
// Anonymous types without a name for linkage purposes have their
// declarator mangled in if they have one.
- Name = DD->getName();
+ UnnamedName = DD->getName();
else if (const TypedefNameDecl *TND =
Context.getTypedefNameForUnnamedTagDecl(RD))
// Anonymous types without a name for linkage purposes have their
// associate typedef mangled in if they have one.
- Name = TND->getName();
+ UnnamedName = TND->getName();
// Give lambdas a display name based on their name mangling.
- if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD))
- if (CXXRD->isLambda())
- return internString(
+ if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ if (CXXRD->isLambda()) {
+ Name = internString(
CGM.getCXXABI().getMangleContext().getLambdaString(CXXRD));
-
- if (!Name.empty()) {
+ } else if (!UnnamedName.empty()) {
+ SmallString<256> UnnamedType("<unnamed-type-");
+ UnnamedType += UnnamedName;
+ UnnamedType += '>';
+ Name = internString(UnnamedType);
+ }
+ } else if (!UnnamedName.empty()) {
SmallString<256> UnnamedType("<unnamed-type-");
- UnnamedType += Name;
+ UnnamedType += UnnamedName;
UnnamedType += '>';
- return internString(UnnamedType);
+ Name = internString(UnnamedType);
}
}
}
- return StringRef();
+ ClassNameCache[CanonicalRD] = {Name, Simplified};
+ if (NameIsSimplified)
+ *NameIsSimplified = Simplified;
+ return Name;
}
std::optional<llvm::DIFile::ChecksumKind>
@@ -661,11 +674,30 @@ std::string CGDebugInfo::remapDIPath(StringRef Path) const {
return P.str().str();
}
+void CGDebugInfo::cacheLineAndColumn(SourceLocation Loc) const {
+ if (Loc == CachedLineColLoc)
+ return;
+
+ CachedLineColLoc = Loc;
+ CachedLine = 0;
+ CachedColumn = 0;
+ if (Loc.isInvalid())
+ return;
+
+ SourceManager &SM = CGM.getContext().getSourceManager();
+ PresumedLoc PLoc = SM.getPresumedLoc(Loc);
+ if (!PLoc.isValid())
+ return;
+
+ CachedLine = PLoc.getLine();
+ CachedColumn = PLoc.getColumn();
+}
+
unsigned CGDebugInfo::getLineNumber(SourceLocation Loc) {
if (Loc.isInvalid())
return 0;
- SourceManager &SM = CGM.getContext().getSourceManager();
- return SM.getPresumedLoc(getMacroDebugLoc(CGM, Loc)).getLine();
+ cacheLineAndColumn(getMacroDebugLoc(CGM, Loc));
+ return CachedLine;
}
unsigned CGDebugInfo::getColumnNumber(SourceLocation Loc, bool Force) {
@@ -676,10 +708,8 @@ unsigned CGDebugInfo::getColumnNumber(SourceLocation Loc, bool Force) {
// If the location is invalid then use the current column.
if (Loc.isInvalid() && CurLoc.isInvalid())
return 0;
- SourceManager &SM = CGM.getContext().getSourceManager();
- PresumedLoc PLoc =
- SM.getPresumedLoc(Loc.isValid() ? getMacroDebugLoc(CGM, Loc) : CurLoc);
- return PLoc.isValid() ? PLoc.getColumn() : 0;
+ cacheLineAndColumn(Loc.isValid() ? getMacroDebugLoc(CGM, Loc) : CurLoc);
+ return CachedColumn;
}
StringRef CGDebugInfo::getCurrentDirname() {
@@ -1413,23 +1443,29 @@ static bool needsTypeIdentifier(const TagDecl *TD, CodeGenModule &CGM,
}
// Returns a unique type identifier string if one exists, or an empty string.
-static SmallString<256> getTypeIdentifier(const TagType *Ty, CodeGenModule &CGM,
- llvm::DICompileUnit *TheCU) {
- SmallString<256> Identifier;
+StringRef CGDebugInfo::getTypeIdentifier(const TagType *Ty) {
const TagDecl *TD = Ty->getDecl()->getDefinitionOrSelf();
+ const auto *CanonicalTD = cast<TagDecl>(TD->getCanonicalDecl());
+ if (auto It = TypeIdentifierCache.find(CanonicalTD);
+ It != TypeIdentifierCache.end())
+ return It->second;
+ StringRef Identifier;
if (!needsTypeIdentifier(TD, CGM, TheCU))
- return Identifier;
+ return TypeIdentifierCache[CanonicalTD] = Identifier;
if (const auto *RD = dyn_cast<CXXRecordDecl>(TD))
if (RD->getDefinition())
if (RD->isDynamicClass() &&
CGM.getVTableLinkage(RD) == llvm::GlobalValue::ExternalLinkage)
- return Identifier;
+ return TypeIdentifierCache[CanonicalTD] = Identifier;
// TODO: This is using the RTTI name. Is there a better way to get
// a unique string for a type?
- llvm::raw_svector_ostream Out(Identifier);
+ SmallString<256> IdentifierStorage;
+ llvm::raw_svector_ostream Out(IdentifierStorage);
CGM.getCXXABI().getMangleContext().mangleCXXRTTIName(QualType(Ty, 0), Out);
+ Identifier = internString(IdentifierStorage);
+ TypeIdentifierCache[CanonicalTD] = Identifier;
return Identifier;
}
@@ -1478,10 +1514,10 @@ CGDebugInfo::getOrCreateRecordFwdDecl(const RecordType *Ty,
Flags |= llvm::DINode::FlagNonTrivial;
// Create the type.
- SmallString<256> Identifier;
+ StringRef Identifier;
// Don't include a linkage name in line tables only.
if (CGM.getCodeGenOpts().hasReducedDebugInfo())
- Identifier = getTypeIdentifier(Ty, CGM, TheCU);
+ Identifier = getTypeIdentifier(Ty);
llvm::DICompositeType *RetTy = DBuilder.createReplaceableCompositeType(
getTagForRecord(RD), RDName, Ctx, DefUnit, Line, 0, Size, Align, Flags,
Identifier);
@@ -3936,8 +3972,7 @@ llvm::DIType *CGDebugInfo::CreateType(const HLSLInlineSpirvType *Ty,
return nullptr;
}
-static auto getEnumInfo(CodeGenModule &CGM, llvm::DICompileUnit *TheCU,
- const EnumType *Ty) {
+static auto getEnumInfo(CodeGenModule &CGM, const EnumType *Ty) {
const EnumDecl *ED = Ty->getDecl()->getDefinitionOrSelf();
uint64_t Size = 0;
@@ -3946,11 +3981,12 @@ static auto getEnumInfo(CodeGenModule &CGM, llvm::DICompileUnit *TheCU,
Size = CGM.getContext().getTypeSize(QualType(Ty, 0));
Align = getDeclAlignIfRequired(ED, CGM.getContext());
}
- return std::make_tuple(ED, Size, Align, getTypeIdentifier(Ty, CGM, TheCU));
+ return std::make_tuple(ED, Size, Align);
}
llvm::DIType *CGDebugInfo::CreateEnumType(const EnumType *Ty) {
- auto [ED, Size, Align, Identifier] = getEnumInfo(CGM, TheCU, Ty);
+ auto [ED, Size, Align] = getEnumInfo(CGM, Ty);
+ StringRef Identifier = getTypeIdentifier(Ty);
bool isImportedFromModule =
DebugTypeExtRefs && ED->isFromASTFile() && ED->getDefinition();
@@ -3985,7 +4021,8 @@ llvm::DIType *CGDebugInfo::CreateEnumType(const EnumType *Ty) {
}
llvm::DIType *CGDebugInfo::CreateTypeDefinition(const EnumType *Ty) {
- auto [ED, Size, Align, Identifier] = getEnumInfo(CGM, TheCU, Ty);
+ auto [ED, Size, Align] = getEnumInfo(CGM, Ty);
+ StringRef Identifier = getTypeIdentifier(Ty);
SmallVector<llvm::Metadata *, 16> Enumerators;
ED = ED->getDefinition();
@@ -4378,7 +4415,7 @@ llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) {
// to be used.
auto Align = getTypeAlignIfRequired(Ty, CGM.getContext());
- SmallString<256> Identifier = getTypeIdentifier(Ty, CGM, TheCU);
+ StringRef Identifier = getTypeIdentifier(Ty);
// Explicitly record the calling convention and export symbols for C++
// records.
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 8e1eda2d93ac0..88b0ed1e2d27f 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -69,6 +69,9 @@ class CGDebugInfo {
ModuleMap *ClangModuleMap = nullptr;
ASTSourceDescriptor PCHDescriptor;
SourceLocation CurLoc;
+ mutable SourceLocation CachedLineColLoc;
+ mutable unsigned CachedLine = 0;
+ mutable unsigned CachedColumn = 0;
llvm::MDNode *CurInlinedAt = nullptr;
llvm::DIType *VTablePtrType = nullptr;
llvm::DIType *ClassTy = nullptr;
@@ -165,6 +168,12 @@ class CGDebugInfo {
/// using declarations and global alias variables) that aren't covered
/// by other more specific caches.
llvm::DenseMap<const Decl *, llvm::TrackingMDRef> DeclCache;
+ struct ClassNameCacheEntry {
+ StringRef Name;
+ bool NameIsSimplified;
+ };
+ llvm::DenseMap<const RecordDecl *, ClassNameCacheEntry> ClassNameCache;
+ llvm::DenseMap<const TagDecl *, StringRef> TypeIdentifierCache;
llvm::DenseMap<const Decl *, llvm::TrackingMDRef> ImportedDeclCache;
llvm::DenseMap<const NamespaceDecl *, llvm::TrackingMDRef> NamespaceCache;
llvm::DenseMap<const NamespaceAliasDecl *, llvm::TrackingMDRef>
@@ -740,6 +749,8 @@ class CGDebugInfo {
/// Return current directory name.
StringRef getCurrentDirname();
+ void cacheLineAndColumn(SourceLocation Loc) const;
+
/// Create new compile unit.
void CreateCompileUnit();
@@ -857,6 +868,8 @@ class CGDebugInfo {
StringRef getClassName(const RecordDecl *RD,
bool *NameIsSimplified = nullptr);
+ StringRef getTypeIdentifier(const TagType *Ty);
+
/// Get the vtable name for the given class.
StringRef getVTableName(const CXXRecordDecl *Decl);
More information about the cfe-commits
mailing list