[Lldb-commits] [lldb] [lldb] Reduce size of Mangled class (PR #200181)

Raphael Isemann via lldb-commits lldb-commits at lists.llvm.org
Mon Jun 1 03:11:51 PDT 2026


https://github.com/Teemperor updated https://github.com/llvm/llvm-project/pull/200181

>From 574751b32ead97104e10b7bb3828dfc604dddfc2 Mon Sep 17 00:00:00 2001
From: Raphael Isemann <rise at apple.com>
Date: Thu, 28 May 2026 14:22:54 +0100
Subject: [PATCH] [lldb] Reduce size of Mangled class

The Mangled class is used in several places in LLDB, most notably as a
direct member of Symbol. This makes this class one of the most
frequently long-lived allocations in LLDB.

In commit a2672250be871bdac18c1a955265a98704434218 , this class got a
(large) cache that stores information about demangled data. This cache
is stored in a std::optional member, which means the memory for the
class is allocated within our Mangled object. It should be noted that
this cache is only used when we actually demangle the name, which
doesn't happen for every mangled name we encounter.

The additional cache member caused that the size of Mangled went from
16B to 152B by default (that is, even if the Mangled name was never
demangled).

This patch replaces the std::optional with a unique_ptr which stores
the cache on first use in a separate heap allocation. This changes
decreases the amount of allocated memory when debugging a relatively
small Objective-C project from 1.57GiB to 1.18GiB (-400MiB).
---
 lldb/include/lldb/Core/Mangled.h              | 26 +++++++++++-
 lldb/source/Core/Mangled.cpp                  | 12 +++---
 .../Language/CPlusPlus/CPlusPlusLanguage.cpp  |  2 +-
 lldb/unittests/Core/MangledTest.cpp           | 40 ++++++++++---------
 4 files changed, 52 insertions(+), 28 deletions(-)

diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h
index 546d7a9b409ed..e1ac0e8761f70 100644
--- a/lldb/include/lldb/Core/Mangled.h
+++ b/lldb/include/lldb/Core/Mangled.h
@@ -53,6 +53,28 @@ class Mangled {
   /// Initialize with both mangled and demangled names empty.
   Mangled() = default;
 
+  Mangled(const Mangled &other)
+      : m_mangled(other.m_mangled), m_demangled(other.m_demangled),
+        m_demangled_info(
+            other.m_demangled_info
+                ? std::make_unique<DemangledNameInfo>(*other.m_demangled_info)
+                : nullptr) {}
+
+  Mangled &operator=(const Mangled &other) {
+    if (this != &other) {
+      m_mangled = other.m_mangled;
+      m_demangled = other.m_demangled;
+      m_demangled_info =
+          other.m_demangled_info
+              ? std::make_unique<DemangledNameInfo>(*other.m_demangled_info)
+              : nullptr;
+    }
+    return *this;
+  }
+
+  Mangled(Mangled &&) = default;
+  Mangled &operator=(Mangled &&) = default;
+
   /// Construct with name.
   ///
   /// Constructor with an optional string and auto-detect if \a name is
@@ -279,7 +301,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 +330,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::unique_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..3f72571cee9ca 100644
--- a/lldb/source/Core/Mangled.cpp
+++ b/lldb/source/Core/Mangled.cpp
@@ -285,11 +285,10 @@ 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 +318,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));
+    m_demangled_info =
+        std::make_unique<DemangledNameInfo>(std::move(demangled.second));
     break;
   }
   case eManglingSchemeRustV0:
@@ -556,8 +556,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 == nullptr)
     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..530bc1950a2c8 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);
-  EXPECT_TRUE(info1->hasBasename());
+  ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
+  // Keep a copy of the original demangled info.
+  DemangledNameInfo info1 = *mangled.GetDemangledInfo();
+  EXPECT_TRUE(info1.hasBasename());
 
   mangled.SetMangledName(ConstString("_Z4funcv"));
 
   // Should have re-calculated demangled-info since mangled name changed.
-  auto info2 = mangled.GetDemangledInfo();
-  ASSERT_NE(info2, std::nullopt);
-  EXPECT_TRUE(info2->hasBasename());
+  ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
+  DemangledNameInfo info2 = *mangled.GetDemangledInfo();
+  EXPECT_TRUE(info2.hasBasename());
 
-  EXPECT_FALSE(NameInfoEquals(info1.value(), info2.value()));
+  EXPECT_FALSE(NameInfoEquals(info1, info2));
   EXPECT_EQ(mangled.GetDemangledName(), "func()");
 }
 
@@ -383,45 +384,46 @@ 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();
+  ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
+  // Keep a copy of the original demangled info.
+  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()));
+  ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
+  DemangledNameInfo demangled_foo = *mangled.GetDemangledInfo();
+  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());
 }
 



More information about the lldb-commits mailing list