[llvm-branch-commits] [llvm] [BOLT] Key GlobalSymbols on MCContext-owned names to reduce memory (PR #214891)
Rafael Auler via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Aug 7 16:53:08 PDT 2026
https://github.com/rafaelauler created https://github.com/llvm/llvm-project/pull/214891
BinaryContext::registerNameAtAddress registers every symbol name twice. It first calls MCContext::getOrCreateSymbol(Name), which interns the name in MCContext's symbol table (the MCSymbol owns the string via its table entry). It then also stored the name in the GlobalSymbols map, which was a StringMap<BinaryData *>. StringMap owns its keys, so each global name was duplicated: one copy in MCContext and a second copy in GlobalSymbols. Both grow with the number of symbols and, for large binaries with long mangled names, this duplication is a meaningful source of memory use during file object discovery.
This change makes MCContext the single owner of these name strings and have GlobalSymbols merely reference them. GlobalSymbols becomes a DenseMap<StringRef, BinaryData *> keyed on the MCContext-owned name (MCSymbol::getName() of the symbol just created/looked up). No string is copied into the map: each entry is a fixed-size (StringRef, pointer) pair regardless of name length. Lookups (getBinaryDataByName, count) are unchanged because DenseMap<StringRef> hashes and compares by content, and the keys outlive the map since MCContext is owned by BinaryContext.
The name used as the key is the symbol's interned name, which is equal to the input Name for every registerNameAtAddress caller (real ELF symbol names and BOLT-generated data/jump-table/hashed names never take MCContext's internal-prefix temp-label rename path), so symbol resolution is behaviorally identical. On a large binary this saved 2175MB (1-2% of RSS) of anonymous memory.
>From 5a434623606dc2d58e7e85762d69d9dc97f77a2c Mon Sep 17 00:00:00 2001
From: Rafael Auler <rafaelauler at meta.com>
Date: Wed, 29 Jul 2026 15:30:40 -0700
Subject: [PATCH] [BOLT] Key GlobalSymbols on MCContext-owned names to reduce
memory
BinaryContext::registerNameAtAddress registers every symbol name
twice. It first calls MCContext::getOrCreateSymbol(Name), which
interns the name in MCContext's symbol table (the MCSymbol owns the
string via its table entry). It then also stored the name in the
GlobalSymbols map, which was a StringMap<BinaryData *>. StringMap owns
its keys, so each global name was duplicated: one copy in MCContext
and a second copy in GlobalSymbols. Both grow with the number of
symbols and, for large binaries with long mangled names, this
duplication is a meaningful source of memory use during file
object discovery.
This change makes MCContext the single owner of these name strings and
have GlobalSymbols merely reference them. GlobalSymbols becomes a
DenseMap<StringRef, BinaryData *> keyed on the MCContext-owned name
(MCSymbol::getName() of the symbol just created/looked up). No string
is copied into the map: each entry is a fixed-size (StringRef,
pointer) pair regardless of name length. Lookups (getBinaryDataByName,
count) are unchanged because DenseMap<StringRef> hashes and compares
by content, and the keys outlive the map since MCContext is owned by
BinaryContext.
The name used as the key is the symbol's interned name, which is equal
to the input Name for every registerNameAtAddress caller (real ELF
symbol names and BOLT-generated data/jump-table/hashed names never
take MCContext's internal-prefix temp-label rename path), so symbol
resolution is behaviorally identical. On a large binary this saved
2175MB (1-2% of RSS) of anonymous memory.
---
bolt/include/bolt/Core/BinaryContext.h | 11 ++++++++++-
bolt/lib/Core/BinaryContext.cpp | 11 +++++++----
2 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h
index 240e5a75d1de5..968a44443b86e 100644
--- a/bolt/include/bolt/Core/BinaryContext.h
+++ b/bolt/include/bolt/Core/BinaryContext.h
@@ -23,8 +23,10 @@
#include "bolt/RuntimeLibs/RuntimeLibrary.h"
#include "llvm/ADT/AddressRanges.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/EquivalenceClasses.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/BinaryFormat/MachO.h"
@@ -367,7 +369,14 @@ class BinaryContext {
std::vector<SegmentInfo> NewSegments;
/// [name] -> [BinaryData*] map used for global symbol resolution.
- using SymbolMapType = StringMap<BinaryData *>;
+ ///
+ /// The map keys are StringRefs pointing into the names owned by MCContext
+ /// (i.e. MCSymbol::getName()) rather than strings owned by this map. Every
+ /// registered name is already interned in MCContext, so keying on those
+ /// strings avoids duplicating potentially large (mangled) symbol names, which
+ /// is a significant source of memory use on large binaries. The referenced
+ /// names outlive this map, as MCContext is owned by BinaryContext.
+ using SymbolMapType = DenseMap<StringRef, BinaryData *>;
SymbolMapType GlobalSymbols;
/// [address] -> [BinaryData], ...
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index 13d7e4bc1a5d6..f3ea3049fc502 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -1197,12 +1197,13 @@ MCSymbol *BinaryContext::registerNameAtAddress(StringRef Name, uint64_t Address,
BD = new BinaryData(*Symbol, Address, Size, Alignment ? Alignment : 1,
Section, Flags);
GAI = BinaryDataMap.emplace(Address, BD).first;
- GlobalSymbols[Name] = BD;
+ // Key on the MCContext-owned name so the map does not duplicate the string.
+ GlobalSymbols[Symbol->getName()] = BD;
updateObjectNesting(GAI);
} else {
BD = GAI->second;
if (!BD->hasName(Name)) {
- GlobalSymbols[Name] = BD;
+ GlobalSymbols[Symbol->getName()] = BD;
BD->updateSize(Size);
BD->Symbols.push_back(Symbol);
}
@@ -1327,8 +1328,10 @@ void BinaryContext::generateSymbolHashes() {
}
continue;
}
- BD.Symbols.insert(BD.Symbols.begin(), Ctx->getOrCreateSymbol(NewName));
- GlobalSymbols[NewName] = &BD;
+ MCSymbol *NewSymbol = Ctx->getOrCreateSymbol(NewName);
+ BD.Symbols.insert(BD.Symbols.begin(), NewSymbol);
+ // Key on the MCContext-owned name so the map does not duplicate the string.
+ GlobalSymbols[NewSymbol->getName()] = &BD;
}
if (NumCollisions) {
this->errs() << "BOLT-WARNING: " << NumCollisions
More information about the llvm-branch-commits
mailing list