Skip to content

Conversation

@Michael137
Copy link
Member

@Michael137 Michael137 commented Aug 24, 2025

Starting with #154686 the compressed_pair children are now wrapped in an anonymous structure.

This patch adjusts the LLDB data-formatters to support that.

Outstanding questions:

  1. Should GetChildMemberWithName look through anonymous structures? That will break users most likely. But maybe introducing a new API is worth it? Then we wouldnt have to do this awkward passing around of anon_struct_index
  2. Do we support the layout without the anonymous structure? It's not too much added complexity. And we did release that version of libc++, so there is code out there compiled against it. But there is no great way of testing it (some of our macOS matrix bots do test it i suppose, but not in a targeted way). We have the layout "simulator" tests for some of the STL types which I will adjust.

@llvmbot
Copy link
Member

llvmbot commented Aug 24, 2025

@llvm/pr-subscribers-lldb

Author: Michael Buch (Michael137)

Changes

Starting with #154686 the compressed_pair children are now wrapped in an anonymous structure.

This patch adjusts the LLDB data-formatters to support that.


Full diff: https://github.com/llvm/llvm-project/pull/155153.diff

6 Files Affected:

  • (modified) lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp (+12-12)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp (+35-2)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxx.h (+16-1)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp (+13-16)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp (+22-37)
  • (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp (+5-7)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp
index ea1edbfd3ac9b..5289027fbd8af 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp
@@ -339,11 +339,18 @@ lldb::ChildCacheState LibCxxForwardListFrontEnd::Update() {
   if (err.Fail() || !backend_addr)
     return lldb::ChildCacheState::eRefetch;
 
-  ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_"));
+  auto list_base_sp = m_backend.GetChildAtIndex(0);
+  if (!list_base_sp)
+    return lldb::ChildCacheState::eRefetch;
+
+  // Anonymous strucutre index is in base class at index 0.
+  auto [impl_sp, is_compressed_pair] =
+      GetValueOrOldCompressedPair(*list_base_sp, /*anon_struct_idx=*/0,
+                                  "__before_begin_", "__before_begin_");
   if (!impl_sp)
     return ChildCacheState::eRefetch;
 
-  if (isOldCompressedPairLayout(*impl_sp))
+  if (is_compressed_pair)
     impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
 
   if (!impl_sp)
@@ -366,17 +373,10 @@ llvm::Expected<uint32_t> LibCxxListFrontEnd::CalculateNumChildren() {
   if (!m_head || !m_tail || m_node_address == 0)
     return 0;
 
-  ValueObjectSP size_node_sp(m_backend.GetChildMemberWithName("__size_"));
-  if (!size_node_sp) {
-    size_node_sp = m_backend.GetChildMemberWithName(
-        "__size_alloc_"); // pre-compressed_pair rework
-
-    if (!isOldCompressedPairLayout(*size_node_sp))
-      return llvm::createStringError("Unexpected std::list layout: expected "
-                                     "old __compressed_pair layout.");
-
+  auto [size_node_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+      m_backend, /*anon_struct_idx=*/1, "__size_", "__size_alloc_");
+  if (is_compressed_pair)
     size_node_sp = GetFirstValueOfLibCXXCompressedPair(*size_node_sp);
-  }
 
   if (size_node_sp)
     m_count = size_node_sp->GetValueAsUnsigned(UINT32_MAX);
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
index a7874047942c4..6d41132122641 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
@@ -49,8 +49,7 @@ static void consumeInlineNamespace(llvm::StringRef &name) {
   }
 }
 
-bool lldb_private::formatters::isOldCompressedPairLayout(
-    ValueObject &pair_obj) {
+static bool isOldCompressedPairLayout(ValueObject &pair_obj) {
   return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair");
 }
 
@@ -105,6 +104,40 @@ lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair(
   return value;
 }
 
+std::pair<lldb::ValueObjectSP, bool>
+lldb_private::formatters::GetValueOrOldCompressedPair(
+    ValueObject &obj, size_t anon_struct_idx, llvm::StringRef child_name,
+    llvm::StringRef compressed_pair_name) {
+  // Try searching the child member in an anonymous structure first.
+  if (auto unwrapped = obj.GetChildAtIndex(anon_struct_idx)) {
+    ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name));
+    if (node_sp)
+      return {node_sp, isOldCompressedPairLayout(*node_sp)};
+  }
+
+  // Older versions of libc++ don't wrap the children in anonymous structures.
+  // Try that instead.
+  ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name));
+  if (node_sp)
+    return {node_sp, isOldCompressedPairLayout(*node_sp)};
+
+  // Try the even older __compressed_pair layout.
+
+  assert(!compressed_pair_name.empty());
+
+  node_sp = obj.GetChildMemberWithName(compressed_pair_name);
+
+  // Unrecognized layout (possibly older than LLDB supports).
+  if (!node_sp)
+    return {nullptr, false};
+
+  // Expected old compressed_pair layout, but got something else.
+  if (!isOldCompressedPairLayout(*node_sp))
+    return {nullptr, false};
+
+  return {node_sp, true};
+}
+
 bool lldb_private::formatters::LibcxxFunctionSummaryProvider(
     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
 
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
index d88a6ecb1fa89..819f8a985f9b9 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
@@ -25,7 +25,22 @@ GetChildMemberWithName(ValueObject &obj,
 
 lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair);
 lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair);
-bool isOldCompressedPairLayout(ValueObject &pair_obj);
+
+/// Returns the ValueObjectSP of the child of \c obj. If \c obj has no
+/// child named \c child_name, returns the __compressed_pair child instead
+/// with \c compressed_pair_name, if one exists.
+///
+/// Latest libc++ wrap the compressed children in an anonymous structure.
+/// The \c anon_struct_idx indicates the location of this struct.
+///
+/// The returned boolean is \c true if the returned child was has an old-style
+/// libc++ __compressed_pair layout.
+///
+/// If no child was found returns a nullptr.
+std::pair<lldb::ValueObjectSP, bool>
+GetValueOrOldCompressedPair(ValueObject &obj, size_t anon_struct_idx,
+                            llvm::StringRef child_name,
+                            llvm::StringRef compressed_pair_name);
 bool isStdTemplate(ConstString type_name, llvm::StringRef type);
 
 bool LibcxxStringSummaryProviderASCII(
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
index 41441dfbc7180..85766966f1554 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
@@ -200,7 +200,8 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
   llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
 
 private:
-  llvm::Expected<uint32_t> CalculateNumChildrenForOldCompressedPairLayout();
+  llvm::Expected<uint32_t>
+  CalculateNumChildrenForOldCompressedPairLayout(ValueObject &pair);
 
   /// Returns the ValueObject for the __tree_node type that
   /// holds the key/value pair of the node at index \ref idx.
@@ -254,16 +255,8 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
 
 llvm::Expected<uint32_t>
 lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
-    CalculateNumChildrenForOldCompressedPairLayout() {
-  ValueObjectSP node_sp(m_tree->GetChildMemberWithName("__pair3_"));
-  if (!node_sp)
-    return 0;
-
-  if (!isOldCompressedPairLayout(*node_sp))
-    return llvm::createStringError("Unexpected std::map layout: expected "
-                                   "old __compressed_pair layout.");
-
-  node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp);
+    CalculateNumChildrenForOldCompressedPairLayout(ValueObject &pair) {
+  auto node_sp = GetFirstValueOfLibCXXCompressedPair(pair);
 
   if (!node_sp)
     return 0;
@@ -281,12 +274,16 @@ llvm::Expected<uint32_t> lldb_private::formatters::
   if (m_tree == nullptr)
     return 0;
 
-  if (auto node_sp = m_tree->GetChildMemberWithName("__size_")) {
-    m_count = node_sp->GetValueAsUnsigned(0);
-    return m_count;
-  }
+  auto [size_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+      *m_tree, /*anon_struct_idx=*/2, "__size_", "__pair3_");
+  if (!size_sp)
+    return llvm::createStringError("Unexpected std::map layout");
 
-  return CalculateNumChildrenForOldCompressedPairLayout();
+  if (is_compressed_pair)
+    return CalculateNumChildrenForOldCompressedPairLayout(*size_sp);
+
+  m_count = size_sp->GetValueAsUnsigned(0);
+  return m_count;
 }
 
 ValueObjectSP
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
index 501fd0945b82c..f88a5319068a2 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
@@ -130,22 +130,17 @@ CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
 
 CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
     GetNodeType() {
-  auto node_sp = m_backend.GetChildAtNamePath({"__table_", "__first_node_"});
-
-  if (!node_sp) {
-    auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"});
-    if (!p1_sp)
-      return {};
+  auto table_sp = m_backend.GetChildMemberWithName("__table_");
+  if (!table_sp)
+    return {};
 
-    if (!isOldCompressedPairLayout(*p1_sp))
-      return {};
+  auto [node_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+      *table_sp, /*anon_struct_idx=*/1, "__first_node_", "__p1_");
+  if (is_compressed_pair)
+    node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp);
 
-    node_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);
-    if (!node_sp)
-      return {};
-  }
-
-  assert(node_sp);
+  if (!node_sp)
+    return {};
 
   return node_sp->GetCompilerType().GetTypeTemplateArgument(0).GetPointeeType();
 }
@@ -223,19 +218,15 @@ lldb::ValueObjectSP lldb_private::formatters::
 llvm::Expected<size_t>
 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
     CalculateNumChildrenImpl(ValueObject &table) {
-  if (auto size_sp = table.GetChildMemberWithName("__size_"))
+  auto [size_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+      table, /*anon_struct_idx=*/2, "__size_", "__p2_");
+  if (!is_compressed_pair && size_sp)
     return size_sp->GetValueAsUnsigned(0);
 
-  ValueObjectSP p2_sp = table.GetChildMemberWithName("__p2_");
-  if (!p2_sp)
-    return llvm::createStringError(
-        "Unexpected std::unordered_map layout: __p2_ member not found.");
+  if (!is_compressed_pair)
+    return llvm::createStringError("Unsupported std::unordered_map layout.");
 
-  if (!isOldCompressedPairLayout(*p2_sp))
-    return llvm::createStringError("Unexpected std::unordered_map layout: old "
-                                   "__compressed_pair layout not found.");
-
-  ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp);
+  ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*size_sp);
 
   if (!num_elements_sp)
     return llvm::createStringError(
@@ -246,19 +237,13 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
 }
 
 static ValueObjectSP GetTreePointer(ValueObject &table) {
-  ValueObjectSP tree_sp = table.GetChildMemberWithName("__first_node_");
-  if (!tree_sp) {
-    ValueObjectSP p1_sp = table.GetChildMemberWithName("__p1_");
-    if (!p1_sp)
-      return nullptr;
-
-    if (!isOldCompressedPairLayout(*p1_sp))
-      return nullptr;
-
-    tree_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);
-    if (!tree_sp)
-      return nullptr;
-  }
+  auto [tree_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+      table, /*anon_struct_idx=*/1, "__first_node_", "__p1_");
+  if (is_compressed_pair)
+    tree_sp = GetFirstValueOfLibCXXCompressedPair(*tree_sp);
+
+  if (!tree_sp)
+    return nullptr;
 
   return tree_sp->GetChildMemberWithName("__next_");
 }
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
index 4bcdf01c221a3..60913e5c1ac56 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
@@ -126,17 +126,15 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex(
 }
 
 static ValueObjectSP GetDataPointer(ValueObject &root) {
-  if (auto cap_sp = root.GetChildMemberWithName("__cap_"))
-    return cap_sp;
-
-  ValueObjectSP cap_sp = root.GetChildMemberWithName("__end_cap_");
+  auto [cap_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+      root, /*anon_struct_idx=*/2, "__cap_", "__end_cap_");
   if (!cap_sp)
     return nullptr;
 
-  if (!isOldCompressedPairLayout(*cap_sp))
-    return nullptr;
+  if (is_compressed_pair)
+    return GetFirstValueOfLibCXXCompressedPair(*cap_sp);
 
-  return GetFirstValueOfLibCXXCompressedPair(*cap_sp);
+  return cap_sp;
 }
 
 lldb::ChildCacheState

Starting with llvm#154686 the compressed_pair
children are now wrapped in an anonymous structure.

This patch adjusts the LLDB data-formatters to support that.
@Michael137 Michael137 force-pushed the lldb/compressed-pair-anon-struct branch from 9ad9bfb to ad05d0f Compare August 24, 2025 12:42
@Michael137 Michael137 changed the title [lldb][DataFormatters] Support newer _LIBCPP_COMPRESSED_PAIR layouts [lldb][DataFormatters] Support newer _LIBCPP_COMPRESSED_PAIR layout Aug 25, 2025
@adrian-prantl adrian-prantl merged commit 20dd053 into llvm:main Aug 25, 2025
9 checks passed
@Michael137 Michael137 deleted the lldb/compressed-pair-anon-struct branch August 25, 2025 21:25
@nikic nikic added this to the LLVM 21.x Release milestone Aug 26, 2025
@github-project-automation github-project-automation bot moved this to Needs Triage in LLVM Release Status Aug 26, 2025
@nikic
Copy link
Contributor

nikic commented Aug 26, 2025

/cherry-pick 7b22660 20dd053

@llvmbot
Copy link
Member

llvmbot commented Aug 26, 2025

/pull-request #155366

@llvmbot llvmbot moved this from Needs Triage to Done in LLVM Release Status Aug 26, 2025
tru pushed a commit to llvmbot/llvm-project that referenced this pull request Aug 26, 2025
…lvm#155153)

Starting with llvm#154686 the
compressed_pair children are now wrapped in an anonymous structure.

This patch adjusts the LLDB data-formatters to support that.

Outstanding questions:
1. Should GetChildMemberWithName look through anonymous structures? That
will break users most likely. But maybe introducing a new API is worth
it? Then we wouldnt have to do this awkward passing around of
`anon_struct_index`
2. Do we support the layout without the anonymous structure? It's not
too much added complexity. And we did release that version of libc++, so
there is code out there compiled against it. But there is no great way
of testing it (some of our macOS matrix bots do test it i suppose, but
not in a targeted way). We have the layout "simulator" tests for some of
the STL types which I will adjust.

(cherry picked from commit 20dd053)
Michael137 added a commit to swiftlang/llvm-project that referenced this pull request Aug 26, 2025
…lvm#155153)

Starting with llvm#154686 the
compressed_pair children are now wrapped in an anonymous structure.

This patch adjusts the LLDB data-formatters to support that.

Outstanding questions:
1. Should GetChildMemberWithName look through anonymous structures? That
will break users most likely. But maybe introducing a new API is worth
it? Then we wouldnt have to do this awkward passing around of
`anon_struct_index`
2. Do we support the layout without the anonymous structure? It's not
too much added complexity. And we did release that version of libc++, so
there is code out there compiled against it. But there is no great way
of testing it (some of our macOS matrix bots do test it i suppose, but
not in a targeted way). We have the layout "simulator" tests for some of
the STL types which I will adjust.

(cherry picked from commit 20dd053)
Michael137 added a commit to swiftlang/llvm-project that referenced this pull request Aug 26, 2025
…lvm#155153)

Starting with llvm#154686 the
compressed_pair children are now wrapped in an anonymous structure.

This patch adjusts the LLDB data-formatters to support that.

Outstanding questions:
1. Should GetChildMemberWithName look through anonymous structures? That
will break users most likely. But maybe introducing a new API is worth
it? Then we wouldnt have to do this awkward passing around of
`anon_struct_index`
2. Do we support the layout without the anonymous structure? It's not
too much added complexity. And we did release that version of libc++, so
there is code out there compiled against it. But there is no great way
of testing it (some of our macOS matrix bots do test it i suppose, but
not in a targeted way). We have the layout "simulator" tests for some of
the STL types which I will adjust.

(cherry picked from commit 20dd053)
(cherry picked from commit f3ec20b)
Michael137 added a commit to swiftlang/llvm-project that referenced this pull request Aug 26, 2025
…lvm#155153)

Starting with llvm#154686 the
compressed_pair children are now wrapped in an anonymous structure.

This patch adjusts the LLDB data-formatters to support that.

Outstanding questions:
1. Should GetChildMemberWithName look through anonymous structures? That
will break users most likely. But maybe introducing a new API is worth
it? Then we wouldnt have to do this awkward passing around of
`anon_struct_index`
2. Do we support the layout without the anonymous structure? It's not
too much added complexity. And we did release that version of libc++, so
there is code out there compiled against it. But there is no great way
of testing it (some of our macOS matrix bots do test it i suppose, but
not in a targeted way). We have the layout "simulator" tests for some of
the STL types which I will adjust.

(cherry picked from commit 20dd053)
(cherry picked from commit f3ec20b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Development

Successfully merging this pull request may close these issues.

4 participants