https://github.com/kastiglione created https://github.com/llvm/llvm-project/pull/200495
Change `m_demangled_info` from `std::optional<DemangledNameInfo>` to `std::shared_ptr<DemangledNameInfo>`, which is a change from the unavoidable fixed costs of inline storage, to lazy (dynamic cost) heap storage. `DemangledNameInfo` is 128 bytes (8 pairs of size_t). After `std::optional` overhead, the total is 136 bytes inline — regardless of whether the info is populated. This dominates the size of `Mangled`, without it `Mangled` is just 16 bytes for two `ConstString` instances. By replacing optional with shared_ptr, the inline cost drops to 16 bytes, reducing `Mangled` from 152 to 32 bytes — netting 120 bytes per `Symbol`. The `DemangledNameInfo` is only applicable to Itanium-mangled C++ symbols, and is produced on demand. Depending on the binary and the use, many (if not most) symbols will have null for `DemangledNameInfo`. shared_ptr is used instead of unique_ptr to preserve copyability of `Mangled`. >From 31debc181566aa67d92828fcd6200036e8e5ce8b Mon Sep 17 00:00:00 2001 From: Dave Lee <[email protected]> Date: Fri, 29 May 2026 13:20:56 -0700 Subject: [PATCH] [lldb] Reduce symbol table memory by storing `DemangledNameInfo` on heap (NFC) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change `m_demangled_info` from `std::optional<DemangledNameInfo>` to `std::shared_ptr<DemangledNameInfo>`, which is a change from the unavoidable fixed costs of inline storage, to lazy (dynamic cost) heap storage. `DemangledNameInfo` is 128 bytes (8 pairs of size_t). After `std::optional` overhead, the total is 136 bytes inline — regardless of whether the info is populated. This dominates the size of `Mangled`, without it `Mangled` is just 16 bytes for two `ConstString` instances. By replacing optional with shared_ptr, the inline cost drops to 16 bytes, reducing `Mangled` from 152 to 32 bytes — netting 120 bytes per `Symbol`. The `DemangledNameInfo` is only applicable to Itanium-mangled C++ symbols, and is produced on demand. Depending on the binary and the use, many (if not most) symbols will have null for `DemangledNameInfo`. shared_ptr is used instead of unique_ptr to preserve copyability of `Mangled`. --- lldb/include/lldb/Core/Mangled.h | 4 +-- lldb/source/Core/Mangled.cpp | 12 ++++--- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 2 +- lldb/unittests/Core/MangledTest.cpp | 34 +++++++++---------- 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index 546d7a9b409ed..bf4a68370d5a1 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -279,7 +279,7 @@ class Mangled { void Encode(DataEncoder &encoder, ConstStringTable &strtab) const; /// Retrieve \c DemangledNameInfo of the demangled name held by this object. - const std::optional<DemangledNameInfo> &GetDemangledInfo() const; + const DemangledNameInfo *GetDemangledInfo() const; /// Compute the base name (without namespace/class qualifiers) from the /// demangled name. @@ -308,7 +308,7 @@ class Mangled { /// If available, holds information about where in \c m_demangled certain /// parts of the name (e.g., basename, arguments, etc.) begin and end. - mutable std::optional<DemangledNameInfo> m_demangled_info = std::nullopt; + mutable std::shared_ptr<DemangledNameInfo> m_demangled_info; }; Stream &operator<<(Stream &s, const Mangled &obj); diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 66c785d5871cc..47770649a90ea 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -25,6 +25,7 @@ #include "llvm/Demangle/Demangle.h" #include "llvm/Support/Compiler.h" +#include <memory> #include <mutex> #include <string> #include <string_view> @@ -285,11 +286,11 @@ ConstString Mangled::GetDemangledName() const { return GetDemangledNameImpl(/*force=*/false); } -std::optional<DemangledNameInfo> const &Mangled::GetDemangledInfo() const { +const DemangledNameInfo *Mangled::GetDemangledInfo() const { if (!m_demangled_info) GetDemangledNameImpl(/*force=*/true); - return m_demangled_info; + return m_demangled_info.get(); } // Generate the demangled name on demand using this accessor. Code in this @@ -319,7 +320,8 @@ ConstString Mangled::GetDemangledNameImpl(bool force) const { std::pair<char *, DemangledNameInfo> demangled = GetItaniumDemangledStr(m_mangled.GetCString()); demangled_name = demangled.first; - m_demangled_info.emplace(std::move(demangled.second)); + if (demangled_name) + m_demangled_info = std::make_shared<DemangledNameInfo>(demangled.second); break; } case eManglingSchemeRustV0: @@ -556,8 +558,8 @@ void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const { } ConstString Mangled::GetBaseName() const { - const auto &demangled_info = GetDemangledInfo(); - if (!demangled_info.has_value()) + const auto *demangled_info = GetDemangledInfo(); + if (!demangled_info) return {}; ConstString demangled_name = GetDemangledName(); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 7e68d25c0110f..74a5a414de67b 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -269,7 +269,7 @@ GetAndValidateInfo(const SymbolContext &sc) { "function '{0}' does not have a demangled name", mangled.GetMangledName()); - const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo(); + const DemangledNameInfo *info = mangled.GetDemangledInfo(); if (!info) return llvm::createStringErrorV( "function '{0}' does not have demangled info", demangled_name); diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp index 706e67801e01a..97bd239c544b9 100644 --- a/lldb/unittests/Core/MangledTest.cpp +++ b/lldb/unittests/Core/MangledTest.cpp @@ -355,23 +355,24 @@ static bool NameInfoEquals(const DemangledNameInfo &lhs, TEST(MangledTest, DemangledNameInfo_SetMangledResets) { Mangled mangled; - EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt); + EXPECT_EQ(mangled.GetDemangledInfo(), nullptr); mangled.SetMangledName(ConstString("_Z3foov")); ASSERT_TRUE(mangled); - auto info1 = mangled.GetDemangledInfo(); - EXPECT_NE(info1, std::nullopt); + auto *info1 = mangled.GetDemangledInfo(); + EXPECT_NE(info1, nullptr); EXPECT_TRUE(info1->hasBasename()); + DemangledNameInfo prev_info = *info1; mangled.SetMangledName(ConstString("_Z4funcv")); // Should have re-calculated demangled-info since mangled name changed. - auto info2 = mangled.GetDemangledInfo(); - ASSERT_NE(info2, std::nullopt); + auto *info2 = mangled.GetDemangledInfo(); + ASSERT_NE(info2, nullptr); EXPECT_TRUE(info2->hasBasename()); - EXPECT_FALSE(NameInfoEquals(info1.value(), info2.value())); + EXPECT_FALSE(NameInfoEquals(prev_info, *info2)); EXPECT_EQ(mangled.GetDemangledName(), "func()"); } @@ -383,45 +384,44 @@ TEST(MangledTest, DemangledNameInfo_SetDemangledResets) { // Mangled name hasn't changed, so GetDemangledInfo causes re-demangling // of previously set mangled name. - EXPECT_NE(mangled.GetDemangledInfo(), std::nullopt); + EXPECT_NE(mangled.GetDemangledInfo(), nullptr); EXPECT_EQ(mangled.GetDemangledName(), "foo()"); } TEST(MangledTest, DemangledNameInfo_Clear) { Mangled mangled("_Z3foov"); ASSERT_TRUE(mangled); - EXPECT_NE(mangled.GetDemangledInfo(), std::nullopt); + EXPECT_NE(mangled.GetDemangledInfo(), nullptr); mangled.Clear(); - EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt); + EXPECT_EQ(mangled.GetDemangledInfo(), nullptr); } TEST(MangledTest, DemangledNameInfo_SetValue) { Mangled mangled("_Z4funcv"); ASSERT_TRUE(mangled); - auto demangled_func = mangled.GetDemangledInfo(); + DemangledNameInfo demangled_func = *mangled.GetDemangledInfo(); // SetValue(mangled) resets demangled-info mangled.SetValue(ConstString("_Z3foov")); - auto demangled_foo = mangled.GetDemangledInfo(); - EXPECT_NE(demangled_foo, std::nullopt); - EXPECT_FALSE(NameInfoEquals(demangled_foo.value(), demangled_func.value())); + auto *demangled_foo = mangled.GetDemangledInfo(); + EXPECT_NE(demangled_foo, nullptr); + EXPECT_FALSE(NameInfoEquals(*demangled_foo, demangled_func)); // SetValue(demangled) resets demangled-info mangled.SetValue(ConstString("_Z4funcv")); - EXPECT_TRUE(NameInfoEquals(mangled.GetDemangledInfo().value(), - demangled_func.value())); + EXPECT_TRUE(NameInfoEquals(*mangled.GetDemangledInfo(), demangled_func)); // SetValue(empty) resets demangled-info mangled.SetValue(ConstString()); - EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt); + EXPECT_EQ(mangled.GetDemangledInfo(), nullptr); // Demangling invalid mangled name will set demangled-info // (without a valid basename). mangled.SetValue(ConstString("_Zinvalid")); - ASSERT_NE(mangled.GetDemangledInfo(), std::nullopt); + ASSERT_NE(mangled.GetDemangledInfo(), nullptr); EXPECT_FALSE(mangled.GetDemangledInfo()->hasBasename()); } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
