[Lldb-commits] [lldb] [lldb] Move DemangledNameInfo cache out of Mangled class (PR #225332)

Raphael Isemann via lldb-commits lldb-commits at lists.llvm.org
Tue Sep 22 05:50:21 PDT 2026


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

>From b116ee448c9a52a4925af5993b05c6e6f75a3ca4 Mon Sep 17 00:00:00 2001
From: Raphael Isemann <rise at apple.com>
Date: Tue, 22 Sep 2026 09:44:30 +0100
Subject: [PATCH] [lldb] Move DemangledNameInfo cache out of Mangled class

This patch moves the per-Mangled DemangledInfoCache into a global
cache. The motivation for this is that is (A) makes Mangled a smaller
which saves memory due to fact its very frequently allocated and (B)
the current Mangled class is sometimes accesses via the SB API from
multiple threads, and the stateful cache in Symbol causes crashes.
---
 lldb/include/lldb/Core/DemangledNameInfo.h    |  35 ++++++
 lldb/include/lldb/Core/Mangled.h              |  58 +++-------
 lldb/include/lldb/Symbol/Symbol.h             |   2 +-
 lldb/source/Core/DemangledNameInfo.cpp        |  32 ++++++
 lldb/source/Core/Mangled.cpp                  |  71 ++++++------
 .../Language/CPlusPlus/CPlusPlusLanguage.cpp  |  10 +-
 lldb/source/Symbol/Symbol.cpp                 |   2 +-
 lldb/source/Symbol/Symtab.cpp                 |   2 +-
 lldb/unittests/Core/MangledTest.cpp           | 108 +++++++++++++-----
 9 files changed, 207 insertions(+), 113 deletions(-)

diff --git a/lldb/include/lldb/Core/DemangledNameInfo.h b/lldb/include/lldb/Core/DemangledNameInfo.h
index ac08e091d902b..ffc748c62eb77 100644
--- a/lldb/include/lldb/Core/DemangledNameInfo.h
+++ b/lldb/include/lldb/Core/DemangledNameInfo.h
@@ -9,14 +9,19 @@
 #ifndef LLDB_CORE_DEMANGLEDNAMEINFO_H
 #define LLDB_CORE_DEMANGLEDNAMEINFO_H
 
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/Demangle/ItaniumDemangle.h"
 #include "llvm/Demangle/Utility.h"
 
 #include <cstddef>
+#include <mutex>
+#include <optional>
 #include <utility>
 
 namespace lldb_private {
 
+class Mangled;
+
 /// Stores information about where certain portions of a demangled
 /// function name begin and end.
 struct DemangledNameInfo {
@@ -132,6 +137,36 @@ struct DemangledNameInfo {
   bool hasSuffix() const { return SuffixRange.second >= SuffixRange.first; }
 };
 
+/// A cached map from mangled names to DemangledNameInfo.
+///
+/// The cache holds at most \c GetMaxEntries entries and clears itself.
+class DemangledNameInfoCache {
+public:
+  static constexpr size_t DefaultMaxEntries = 16 * 1024;
+
+  explicit DemangledNameInfoCache(size_t max_entries = DefaultMaxEntries)
+      : m_max_entries(max_entries) {}
+
+  /// Calculates the DemangledNameInfo and caches the result.
+  ///
+  /// \return
+  ///     std::nullopt if no info could be computed for \c mangled.
+  std::optional<DemangledNameInfo> Get(const Mangled &mangled);
+
+  void Clear();
+
+  size_t GetMaxEntries() const { return m_max_entries; }
+
+private:
+  std::mutex m_mutex;
+
+  /// Maps a ConstString to its DemangledNameInfo.
+  //  Values can be empty if no name info could be computed for a name.
+  llvm::DenseMap<const char *, std::optional<DemangledNameInfo>> m_infos;
+
+  const size_t m_max_entries;
+};
+
 /// An OutputBuffer which keeps a record of where certain parts of a
 /// demangled name begin/end (e.g., basename, scope, argument list, etc.).
 /// The tracking occurs during printing of the Itanium demangle tree.
diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h
index 1c7bbeb05a37d..622c0f82f49f7 100644
--- a/lldb/include/lldb/Core/Mangled.h
+++ b/lldb/include/lldb/Core/Mangled.h
@@ -17,7 +17,7 @@
 #include "llvm/ADT/StringRef.h"
 
 #include <cstddef>
-#include <memory>
+#include <optional>
 
 namespace lldb_private {
 
@@ -53,28 +53,6 @@ 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
@@ -157,15 +135,9 @@ class Mangled {
   ///     A const reference to the display demangled name string object.
   ConstString GetDisplayDemangledName() const;
 
-  void SetDemangledName(ConstString name) {
-    m_demangled = name;
-    m_demangled_info.reset();
-  }
+  void SetDemangledName(ConstString name) { m_demangled = name; }
 
-  void SetMangledName(ConstString name) {
-    m_mangled = name;
-    m_demangled_info.reset();
-  }
+  void SetMangledName(ConstString name) { m_mangled = name; }
 
   /// Mangled name get accessor.
   ///
@@ -290,8 +262,15 @@ class Mangled {
   ///   table offsets in the cache data.
   void Encode(DataEncoder &encoder, ConstStringTable &strtab) const;
 
-  /// Retrieve \c DemangledNameInfo of the demangled name held by this object.
-  const DemangledNameInfo *GetDemangledInfo() const;
+  /// Compute the \c DemangledNameInfo of the demangled name.
+  ///
+  /// Note that this always re-runs the demangler. \see DemangledNameInfoCache
+  /// for caching the result.
+  ///
+  /// \return
+  ///     std::nullopt if no info could be computed (for example because the
+  ///     name is mangled with a scheme that doesn't provide any info).
+  std::optional<DemangledNameInfo> ComputeDemangledInfo() const;
 
   /// Compute the base name (without namespace/class qualifiers) from the
   /// demangled name.
@@ -305,25 +284,14 @@ class Mangled {
   ConstString GetBaseName() const;
 
 private:
-  /// If \c force is \c false, this function will re-use the previously
-  /// demangled name (if any). If \c force is \c true (or the mangled name
-  /// on this object was not previously demangled), demangle and cache the
-  /// name.
-  ConstString GetDemangledNameImpl(bool force) const;
-
   /// The mangled version of the name.
   ConstString m_mangled;
 
   /// Mutable so we can get it on demand with
   /// a const version of this object.
   mutable ConstString m_demangled;
-
-  /// 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::unique_ptr<DemangledNameInfo> m_demangled_info;
 };
-static_assert(sizeof(Mangled) <= 2 * sizeof(ConstString) +
-                                     sizeof(std::unique_ptr<DemangledNameInfo>),
+static_assert(sizeof(Mangled) <= 2 * sizeof(ConstString),
               "High-volume object, size of object must be increased with care");
 
 Stream &operator<<(Stream &s, const Mangled &obj);
diff --git a/lldb/include/lldb/Symbol/Symbol.h b/lldb/include/lldb/Symbol/Symbol.h
index 184e1ca9bdb63..700b29b23a747 100644
--- a/lldb/include/lldb/Symbol/Symbol.h
+++ b/lldb/include/lldb/Symbol/Symbol.h
@@ -458,7 +458,7 @@ class Symbol : public SymbolContextScope {
 
 #if __SIZEOF_POINTER__ == 8
 static_assert(
-    sizeof(lldb_private::Symbol) == 80,
+    sizeof(lldb_private::Symbol) == 72,
     "Symbol is a high volume data type, size must be increased with care");
 #endif
 
diff --git a/lldb/source/Core/DemangledNameInfo.cpp b/lldb/source/Core/DemangledNameInfo.cpp
index 16fbfda299b21..c48d838eb1108 100644
--- a/lldb/source/Core/DemangledNameInfo.cpp
+++ b/lldb/source/Core/DemangledNameInfo.cpp
@@ -7,11 +7,43 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/Core/DemangledNameInfo.h"
+#include "lldb/Core/Mangled.h"
 
 using namespace llvm::itanium_demangle;
 
 namespace lldb_private {
 
+std::optional<DemangledNameInfo>
+DemangledNameInfoCache::Get(const Mangled &mangled) {
+  ConstString mangled_name = mangled.GetMangledName();
+  if (!mangled_name)
+    return std::nullopt;
+
+  const char *key = mangled_name.GetCString();
+  {
+    std::lock_guard<std::mutex> guard(m_mutex);
+    auto it = m_infos.find(key);
+    if (it != m_infos.end())
+      return it->second;
+  }
+
+  // Demangle outside of the lock. Two threads asking for the same name at the
+  // same time just compute the same info twice, which is cheaper than making
+  // every other thread wait for one demangle to finish.
+  std::optional<DemangledNameInfo> info = mangled.ComputeDemangledInfo();
+
+  std::lock_guard<std::mutex> guard(m_mutex);
+  if (m_infos.size() >= m_max_entries)
+    m_infos.clear();
+  m_infos.insert({key, info});
+  return info;
+}
+
+void DemangledNameInfoCache::Clear() {
+  std::lock_guard<std::mutex> guard(m_mutex);
+  m_infos.clear();
+}
+
 bool TrackingOutputBuffer::shouldTrack() const {
   if (!isPrintingTopLevelFunctionType())
     return false;
diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp
index f58c8e63600c4..6ee2cb12b0551 100644
--- a/lldb/source/Core/Mangled.cpp
+++ b/lldb/source/Core/Mangled.cpp
@@ -26,6 +26,7 @@
 #include "llvm/Support/Compiler.h"
 
 #include <mutex>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -112,7 +113,6 @@ Mangled::operator bool() const { return m_mangled || m_demangled; }
 void Mangled::Clear() {
   m_mangled.Clear();
   m_demangled.Clear();
-  m_demangled_info.reset();
 }
 
 // Compare the string values.
@@ -126,16 +126,13 @@ void Mangled::SetValue(ConstString name) {
     if (IsMangledName(name.GetStringRef())) {
       m_demangled.Clear();
       m_mangled = name;
-      m_demangled_info.reset();
     } else {
       m_demangled = name;
       m_mangled.Clear();
-      m_demangled_info.reset();
     }
   } else {
     m_demangled.Clear();
     m_mangled.Clear();
-    m_demangled_info.reset();
   }
 }
 
@@ -281,31 +278,28 @@ bool Mangled::GetRichManglingInfo(RichManglingContext &context,
   llvm_unreachable("Fully covered switch above!");
 }
 
-ConstString Mangled::GetDemangledName() const {
-  return GetDemangledNameImpl(/*force=*/false);
-}
-
-const DemangledNameInfo *Mangled::GetDemangledInfo() const {
-  if (!m_demangled_info)
-    GetDemangledNameImpl(/*force=*/true);
-  return m_demangled_info.get();
-}
-
 // Generate the demangled name on demand using this accessor. Code in this
 // class will need to use this accessor if it wishes to decode the demangled
 // name. The result is cached and will be kept until a new string value is
 // supplied to this object, or until the end of the object's lifetime.
-ConstString Mangled::GetDemangledNameImpl(bool force) const {
+ConstString Mangled::GetDemangledName() const {
   if (!m_mangled)
     return m_demangled;
 
+  // Work on a local copy of the cached name. Another thread can be demangling
+  // the same name at the same time, so the member is only read and written as
+  // a whole and never left holding an intermediate value.
+  ConstString demangled = m_demangled;
+
   // Re-use previously demangled names.
-  if (!force && !m_demangled.IsNull())
-    return m_demangled;
+  if (!demangled.IsNull())
+    return demangled;
 
-  if (!force && m_mangled.GetMangledCounterpart(m_demangled) &&
-      !m_demangled.IsNull())
-    return m_demangled;
+  ConstString counterpart;
+  if (m_mangled.GetMangledCounterpart(counterpart) && !counterpart.IsNull()) {
+    m_demangled = counterpart;
+    return counterpart;
+  }
 
   // We didn't already mangle this name, demangle it and if all goes well
   // add it to our map.
@@ -314,14 +308,9 @@ ConstString Mangled::GetDemangledNameImpl(bool force) const {
   case eManglingSchemeMSVC:
     demangled_name = GetMSVCDemangledStr(m_mangled);
     break;
-  case eManglingSchemeItanium: {
-    std::pair<char *, DemangledNameInfo> demangled =
-        GetItaniumDemangledStr(m_mangled.GetCString());
-    demangled_name = demangled.first;
-    m_demangled_info =
-        std::make_unique<DemangledNameInfo>(std::move(demangled.second));
+  case eManglingSchemeItanium:
+    demangled_name = GetItaniumDemangledStr(m_mangled.GetCString()).first;
     break;
-  }
   case eManglingSchemeRustV0:
     demangled_name = GetRustV0DemangledStr(m_mangled);
     break;
@@ -338,17 +327,28 @@ ConstString Mangled::GetDemangledNameImpl(bool force) const {
   }
 
   if (demangled_name) {
-    m_demangled.SetStringWithMangledCounterpart(demangled_name, m_mangled);
+    demangled.SetStringWithMangledCounterpart(demangled_name, m_mangled);
     free(demangled_name);
-  }
-
-  if (m_demangled.IsNull()) {
+  } else {
     // Set the demangled string to the empty string to indicate we tried to
     // parse it once and failed.
-    m_demangled.SetCString("");
+    demangled.SetCString("");
   }
 
-  return m_demangled;
+  m_demangled = demangled;
+  return demangled;
+}
+
+std::optional<DemangledNameInfo> Mangled::ComputeDemangledInfo() const {
+  // Itanium is the only scheme that provides any name info.
+  if (!m_mangled ||
+      GetManglingScheme(m_mangled.GetStringRef()) != eManglingSchemeItanium)
+    return std::nullopt;
+
+  std::pair<char *, DemangledNameInfo> demangled =
+      GetItaniumDemangledStr(m_mangled.GetCString());
+  free(demangled.first);
+  return std::move(demangled.second);
 }
 
 ConstString Mangled::GetDisplayDemangledName() const {
@@ -469,7 +469,6 @@ bool Mangled::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr,
                      const StringTableReader &strtab) {
   m_mangled.Clear();
   m_demangled.Clear();
-  m_demangled_info.reset();
   MangledEncoding encoding = (MangledEncoding)data.GetU8(offset_ptr);
   switch (encoding) {
     case Empty:
@@ -549,8 +548,8 @@ void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const {
 }
 
 ConstString Mangled::GetBaseName() const {
-  const auto *demangled_info = GetDemangledInfo();
-  if (demangled_info == nullptr)
+  std::optional<DemangledNameInfo> demangled_info = ComputeDemangledInfo();
+  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 d453f9c9c4ae0..a2b0c1ec39c7e 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -257,6 +257,14 @@ static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream,
   return true;
 }
 
+/// The demangled info of the names that got formatted recently. Formatting a
+/// single frame asks for the info of the same name several times over, so
+/// without this every one of those would re-run the demangler.
+static DemangledNameInfoCache &GetDemangledInfoCache() {
+  static DemangledNameInfoCache g_cache;
+  return g_cache;
+}
+
 static llvm::Expected<std::pair<llvm::StringRef, DemangledNameInfo>>
 GetAndValidateInfo(const SymbolContext &sc) {
   Mangled mangled = sc.GetPossiblyInlinedFunctionName();
@@ -269,7 +277,7 @@ GetAndValidateInfo(const SymbolContext &sc) {
         "function '{0}' does not have a demangled name",
         mangled.GetMangledName());
 
-  const DemangledNameInfo *info = mangled.GetDemangledInfo();
+  std::optional<DemangledNameInfo> info = GetDemangledInfoCache().Get(mangled);
   if (!info)
     return llvm::createStringErrorV(
         "function '{0}' does not have demangled info", demangled_name);
diff --git a/lldb/source/Symbol/Symbol.cpp b/lldb/source/Symbol/Symbol.cpp
index 5fecd758fe84e..35804b600a590 100644
--- a/lldb/source/Symbol/Symbol.cpp
+++ b/lldb/source/Symbol/Symbol.cpp
@@ -772,7 +772,7 @@ bool Symbol::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr,
 // the DataFileCache version number in Symtab::Encode
 // will need to be incremented as well.
 #if __SIZEOF_POINTER__ == 8
-static_assert(sizeof(lldb_private::Symbol) == 80,
+static_assert(sizeof(lldb_private::Symbol) == 72,
               "Symbol size has changed, Symbol::Encode and Decode likely need "
               "to be updated");
 #endif
diff --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp
index 5bef28c58385d..22f3b26ed88c7 100644
--- a/lldb/source/Symbol/Symtab.cpp
+++ b/lldb/source/Symbol/Symtab.cpp
@@ -1234,7 +1234,7 @@ constexpr uint32_t CURRENT_CACHE_VERSION = 2;
 // CURRENT_CACHE_VERSION number will need to be incremented.
 #if __SIZEOF_POINTER__ == 8
 static_assert(
-    sizeof(lldb_private::Symbol) == 80,
+    sizeof(lldb_private::Symbol) == 72,
     "Symbol size has changed, DataFileCache version likely needs updating");
 #endif
 
diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp
index dc0ecde94b174..74f2a17418253 100644
--- a/lldb/unittests/Core/MangledTest.cpp
+++ b/lldb/unittests/Core/MangledTest.cpp
@@ -353,78 +353,130 @@ static bool NameInfoEquals(const DemangledNameInfo &lhs,
                   rhs.QualifiersRange);
 }
 
-TEST(MangledTest, DemangledNameInfo_SetMangledResets) {
+TEST(MangledTest, DemangledNameInfo_SetMangled) {
   Mangled mangled;
-  EXPECT_EQ(mangled.GetDemangledInfo(), nullptr);
+  EXPECT_EQ(mangled.ComputeDemangledInfo(), std::nullopt);
 
   mangled.SetMangledName(ConstString("_Z3foov"));
   ASSERT_TRUE(mangled);
 
-  ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
   // Keep a copy of the original demangled info.
-  DemangledNameInfo info1 = *mangled.GetDemangledInfo();
-  EXPECT_TRUE(info1.hasBasename());
+  std::optional<DemangledNameInfo> info1 = mangled.ComputeDemangledInfo();
+  ASSERT_NE(info1, std::nullopt);
+  EXPECT_TRUE(info1->hasBasename());
 
   mangled.SetMangledName(ConstString("_Z4funcv"));
 
   // Should have re-calculated demangled-info since mangled name changed.
-  ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
-  DemangledNameInfo info2 = *mangled.GetDemangledInfo();
-  EXPECT_TRUE(info2.hasBasename());
+  std::optional<DemangledNameInfo> info2 = mangled.ComputeDemangledInfo();
+  ASSERT_NE(info2, std::nullopt);
+  EXPECT_TRUE(info2->hasBasename());
 
-  EXPECT_FALSE(NameInfoEquals(info1, info2));
+  EXPECT_FALSE(NameInfoEquals(*info1, *info2));
   EXPECT_EQ(mangled.GetDemangledName(), "func()");
 }
 
-TEST(MangledTest, DemangledNameInfo_SetDemangledResets) {
+TEST(MangledTest, DemangledNameInfo_SetDemangled) {
   Mangled mangled("_Z3foov");
   ASSERT_TRUE(mangled);
 
+  std::optional<DemangledNameInfo> info = mangled.ComputeDemangledInfo();
+  ASSERT_NE(info, std::nullopt);
+
   mangled.SetDemangledName(ConstString(""));
 
-  // Mangled name hasn't changed, so GetDemangledInfo causes re-demangling
-  // of previously set mangled name.
-  EXPECT_NE(mangled.GetDemangledInfo(), nullptr);
-  EXPECT_EQ(mangled.GetDemangledName(), "foo()");
+  // The info is computed from the mangled name, which hasn't changed.
+  std::optional<DemangledNameInfo> new_info = mangled.ComputeDemangledInfo();
+  ASSERT_NE(new_info, std::nullopt);
+  EXPECT_TRUE(NameInfoEquals(*new_info, *info));
 }
 
 TEST(MangledTest, DemangledNameInfo_Clear) {
   Mangled mangled("_Z3foov");
   ASSERT_TRUE(mangled);
-  EXPECT_NE(mangled.GetDemangledInfo(), nullptr);
+  EXPECT_NE(mangled.ComputeDemangledInfo(), std::nullopt);
 
   mangled.Clear();
 
-  EXPECT_EQ(mangled.GetDemangledInfo(), nullptr);
+  EXPECT_EQ(mangled.ComputeDemangledInfo(), std::nullopt);
 }
 
 TEST(MangledTest, DemangledNameInfo_SetValue) {
   Mangled mangled("_Z4funcv");
   ASSERT_TRUE(mangled);
 
-  ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
   // Keep a copy of the original demangled info.
-  DemangledNameInfo demangled_func = *mangled.GetDemangledInfo();
+  std::optional<DemangledNameInfo> demangled_func =
+      mangled.ComputeDemangledInfo();
+  ASSERT_NE(demangled_func, std::nullopt);
 
-  // SetValue(mangled) resets demangled-info
+  // SetValue(mangled) re-computes demangled-info
   mangled.SetValue(ConstString("_Z3foov"));
-  ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
-  DemangledNameInfo demangled_foo = *mangled.GetDemangledInfo();
-  EXPECT_FALSE(NameInfoEquals(demangled_foo, demangled_func));
+  std::optional<DemangledNameInfo> demangled_foo =
+      mangled.ComputeDemangledInfo();
+  ASSERT_NE(demangled_foo, std::nullopt);
+  EXPECT_FALSE(NameInfoEquals(*demangled_foo, *demangled_func));
 
-  // SetValue(demangled) resets demangled-info
+  // SetValue(demangled) re-computes demangled-info
   mangled.SetValue(ConstString("_Z4funcv"));
-  EXPECT_TRUE(NameInfoEquals(*mangled.GetDemangledInfo(), demangled_func));
+  ASSERT_NE(mangled.ComputeDemangledInfo(), std::nullopt);
+  EXPECT_TRUE(NameInfoEquals(*mangled.ComputeDemangledInfo(), *demangled_func));
 
-  // SetValue(empty) resets demangled-info
+  // SetValue(empty) leaves nothing to compute demangled-info from
   mangled.SetValue(ConstString());
-  EXPECT_EQ(mangled.GetDemangledInfo(), nullptr);
+  EXPECT_EQ(mangled.ComputeDemangledInfo(), std::nullopt);
 
   // Demangling invalid mangled name will set demangled-info
   // (without a valid basename).
   mangled.SetValue(ConstString("_Zinvalid"));
-  ASSERT_NE(mangled.GetDemangledInfo(), nullptr);
-  EXPECT_FALSE(mangled.GetDemangledInfo()->hasBasename());
+  ASSERT_NE(mangled.ComputeDemangledInfo(), std::nullopt);
+  EXPECT_FALSE(mangled.ComputeDemangledInfo()->hasBasename());
+}
+
+TEST(MangledTest, DemangledNameInfoCache_Get) {
+  DemangledNameInfoCache cache;
+
+  Mangled func("_Z4funcv");
+  Mangled foo("_Z3foov");
+
+  std::optional<DemangledNameInfo> func_info = cache.Get(func);
+  ASSERT_NE(func_info, std::nullopt);
+  ASSERT_TRUE(func_info->hasBasename());
+
+  // Repeated lookups (of both the same and a copied object) hand out the same
+  // info.
+  EXPECT_TRUE(NameInfoEquals(*cache.Get(func), *func_info));
+  EXPECT_TRUE(NameInfoEquals(*cache.Get(Mangled("_Z4funcv")), *func_info));
+
+  // Different names get their own info.
+  std::optional<DemangledNameInfo> foo_info = cache.Get(foo);
+  ASSERT_NE(foo_info, std::nullopt);
+  EXPECT_FALSE(NameInfoEquals(*foo_info, *func_info));
+
+  // Names that have no info at all (not mangled, or a scheme without info
+  // support) are reported as such, no matter how often they are looked up.
+  Mangled not_mangled("not_mangled");
+  EXPECT_EQ(cache.Get(not_mangled), std::nullopt);
+  EXPECT_EQ(cache.Get(not_mangled), std::nullopt);
+
+  cache.Clear();
+
+  EXPECT_TRUE(NameInfoEquals(*cache.Get(func), *func_info));
+}
+
+TEST(MangledTest, DemangledNameInfoCache_MaxEntries) {
+  DemangledNameInfoCache cache(/*max_entries=*/4);
+
+  std::optional<DemangledNameInfo> expected = cache.Get(Mangled("_Z4funcv"));
+  ASSERT_NE(expected, std::nullopt);
+
+  // Overflow the cache a few times over. It drops entries to stay within its
+  // limit, but never hands out anything but the correct info.
+  for (unsigned i = 0; i < 4 * cache.GetMaxEntries(); ++i) {
+    std::string basename = "foo" + std::to_string(i);
+    cache.Get(Mangled("_Z" + std::to_string(basename.size()) + basename + "v"));
+    EXPECT_TRUE(NameInfoEquals(*cache.Get(Mangled("_Z4funcv")), *expected));
+  }
 }
 
 struct DemanglingPartsTestCase {



More information about the lldb-commits mailing list