[llvm] [BOLT] Compute local code section names lazily to reduce memory (PR #214890)
Rafael Auler via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 7 16:47:17 PDT 2026
https://github.com/rafaelauler created https://github.com/llvm/llvm-project/pull/214890
Every BinaryFunction eagerly materialized two std::string members at construction: CodeSectionName (".local.text.<name>") and ColdCodeSectionName (".local.cold.text.<name>"), built by appending the full function name to a fixed prefix. On ELF the name is the (often large, mangled) symbol name, which is already owned by the function's MCSymbol. So every function stored two extra heap copies of its name -- for all functions, whether or not they were ever emitted into a per-function section. During discoverFileObjects on a large binary, this eager construction (buildSectionName -> Twine::str) accounted for 1-2% of RSS (1770MB).
Here we make both members std::optional<std::string>, left empty by default, and recompute the default name on demand in getCodeSectionName() from the function's name. Only functions with an explicitly assigned section name (via
setCodeSectionName/setColdCodeSectionName -- mostly short constants such as ".text"/".text.cold" set during reordering, plus the injected/patch cases) store a string. The common, unoptimized function stores nothing.
>From 0ff88e2ec986ebc8182c695dc1e855fc03e82894 Mon Sep 17 00:00:00 2001
From: Rafael Auler <rafaelauler at meta.com>
Date: Wed, 29 Jul 2026 14:50:04 -0700
Subject: [PATCH] [BOLT] Compute local code section names lazily to reduce
memory
Every BinaryFunction eagerly materialized two std::string members at
construction: CodeSectionName (".local.text.<name>") and
ColdCodeSectionName (".local.cold.text.<name>"), built by appending
the full function name to a fixed prefix. On ELF the name is the
(often large, mangled) symbol name, which is already owned by the
function's MCSymbol. So every function stored two extra heap copies of
its name -- for all functions, whether or not they were ever emitted
into a per-function section. During discoverFileObjects on a large
binary, this eager construction (buildSectionName -> Twine::str)
accounted for 1-2% of RSS (1770MB).
Here we make both members std::optional<std::string>, left empty by
default, and recompute the default name on demand in
getCodeSectionName() from the function's name. Only functions with an
explicitly assigned section name (via
setCodeSectionName/setColdCodeSectionName -- mostly short constants
such as ".text"/".text.cold" set during reordering, plus the
injected/patch cases) store a string. The common, unoptimized function
stores nothing.
---
bolt/include/bolt/Core/BinaryFunction.h | 35 ++++++++++++++++++-------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 2846b562a1c1a..14d7f9b5b5359 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -54,6 +54,7 @@
#include <algorithm>
#include <iterator>
#include <limits>
+#include <optional>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -405,11 +406,15 @@ class BinaryFunction {
/// to avoid redundant processing.
bool NeedBranchValidation{true};
- /// Name for the section this function code should reside in.
- std::string CodeSectionName;
+ /// Name for the section this function code should reside in. When unset, the
+ /// default name is derived on demand from the function's name (see
+ /// getMainSectionName()). Deferring this avoids eagerly storing a copy of the
+ /// (potentially large, mangled) function name for every function, which is a
+ /// significant source of memory use on large binaries.
+ std::optional<std::string> CodeSectionName;
- /// Name for the corresponding cold code section.
- std::string ColdCodeSectionName;
+ /// Name for the corresponding cold code section. See CodeSectionName.
+ std::optional<std::string> ColdCodeSectionName;
/// Parent function fragment for split function fragments.
using FragmentsSetTy = SmallPtrSet<BinaryFunction *, 1>;
@@ -749,12 +754,24 @@ class BinaryFunction {
static std::string buildColdCodeSectionName(StringRef Name,
const BinaryContext &BC);
+ /// Return the name of the main code section, using the default derived from
+ /// the function's name when no name has been explicitly assigned.
+ std::string getMainSectionName() const {
+ return CodeSectionName ? *CodeSectionName
+ : buildCodeSectionName(getOneName(), BC);
+ }
+
+ /// Return the name of the cold code section, using the default derived from
+ /// the function's name when no name has been explicitly assigned.
+ std::string getColdSectionName() const {
+ return ColdCodeSectionName ? *ColdCodeSectionName
+ : buildColdCodeSectionName(getOneName(), BC);
+ }
+
/// Creation should be handled by RewriteInstance or BinaryContext
BinaryFunction(const std::string &Name, BinarySection &Section,
uint64_t Address, uint64_t Size, BinaryContext &BC)
: OriginSection(&Section), Address(Address), Size(Size), BC(BC),
- CodeSectionName(buildCodeSectionName(Name, BC)),
- ColdCodeSectionName(buildColdCodeSectionName(Name, BC)),
FunctionNumber(++Count) {
Symbols.push_back(BC.Ctx->getOrCreateSymbol(Name));
}
@@ -1400,12 +1417,12 @@ class BinaryFunction {
SmallString<32>
getCodeSectionName(const FragmentNum Fragment = FragmentNum::main()) const {
if (Fragment == FragmentNum::main())
- return SmallString<32>(CodeSectionName);
+ return SmallString<32>(getMainSectionName());
if (Fragment == FragmentNum::cold())
- return SmallString<32>(ColdCodeSectionName);
+ return SmallString<32>(getColdSectionName());
if (BC.HasWarmSection && Fragment == FragmentNum::warm())
return SmallString<32>(BC.getWarmCodeSectionName());
- return formatv("{0}.{1}", ColdCodeSectionName, Fragment.get() - 1);
+ return formatv("{0}.{1}", getColdSectionName(), Fragment.get() - 1);
}
/// Assign a code section name to the function.
More information about the llvm-commits
mailing list