Skip to content

[lldb][DWARFASTParserClang] Support constant index encoding of DW_AT_object_pointer #144998

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 23, 2025

Conversation

Michael137
Copy link
Member

@Michael137 Michael137 commented Jun 20, 2025

Starting with #124790, Clang emits DW_AT_object_pointer encoded as integer constants rather than DIE references. This patch accounts for this.

Depends on #145328 and #145126

@llvmbot
Copy link
Member

llvmbot commented Jun 20, 2025

@llvm/pr-subscribers-lldb

Author: Michael Buch (Michael137)

Changes

Starting with #124790, Clang emits DW_AT_object_pointer encoded as integer constants rather than DIE references. This patch accounts for this.


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

3 Files Affected:

  • (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp (+35-12)
  • (modified) lldb/test/Shell/SymbolFile/DWARF/x86/explicit-member-function-quals.cpp (+13-8)
  • (modified) lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp (+196)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 4f79c8aa3f811..7d699434ca3c8 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -174,23 +174,46 @@ DWARFASTParserClang::GetCXXObjectParameter(const DWARFDIE &subprogram,
   if (!decl_ctx_die.IsStructUnionOrClass())
     return {};
 
-  if (DWARFDIE object_parameter =
-          subprogram.GetAttributeValueAsReferenceDIE(DW_AT_object_pointer))
-    return object_parameter;
+  // The DW_AT_object_pointer may be either encoded as a reference to a DIE,
+  // in which case that's the object parameter we want. Or it can be a constant
+  // index of the parameter.
+  std::optional<size_t> object_pointer_index;
+  DWARFFormValue form_value;
+  if (subprogram.GetDIE()->GetAttributeValue(
+          subprogram.GetCU(), DW_AT_object_pointer, form_value,
+          /*end_attr_offset_ptr=*/nullptr, /*check_elaborating_dies=*/true)) {
+    if (auto ref = form_value.Reference())
+      return ref;
+
+    object_pointer_index = form_value.Unsigned();
+  }
+
+  // Try to find the DW_TAG_formal_parameter via object_pointer_index.
+  DWARFDIE object_pointer;
+  size_t param_index = 0;
+  for (const auto &child : subprogram.children()) {
+    if (child.Tag() != DW_TAG_formal_parameter)
+      continue;
 
-  // If no DW_AT_object_pointer was specified, assume the implicit object
-  // parameter is the first parameter to the function, is called "this" and is
-  // artificial (which is what most compilers would generate).
-  auto children = subprogram.children();
-  auto it = llvm::find_if(children, [](const DWARFDIE &child) {
-    return child.Tag() == DW_TAG_formal_parameter;
-  });
+    if (param_index == object_pointer_index.value_or(0))
+      object_pointer = child;
+
+    ++param_index;
+  }
 
-  if (it == children.end())
+  // No formal parameter found for object pointer index.
+  // Nothing to be done.
+  if (!object_pointer)
     return {};
 
-  DWARFDIE object_pointer = *it;
+  // We found the object pointer encoded via DW_AT_object_pointer.
+  // No need for the remaining heuristics.
+  if (object_pointer_index)
+    return object_pointer;
 
+  // If no DW_AT_object_pointer was specified, assume the implicit object
+  // parameter is the first parameter to the function, is called "this" and is
+  // artificial (which is what most compilers would generate).
   if (!object_pointer.GetAttributeValueAsUnsigned(DW_AT_artificial, 0))
     return {};
 
diff --git a/lldb/test/Shell/SymbolFile/DWARF/x86/explicit-member-function-quals.cpp b/lldb/test/Shell/SymbolFile/DWARF/x86/explicit-member-function-quals.cpp
index 33001db69f83e..f89f0f4a4f0bf 100644
--- a/lldb/test/Shell/SymbolFile/DWARF/x86/explicit-member-function-quals.cpp
+++ b/lldb/test/Shell/SymbolFile/DWARF/x86/explicit-member-function-quals.cpp
@@ -1,4 +1,9 @@
 // XFAIL: *
+//
+// FIXME: Explicit object parameter is not shown in
+// type lookup output. This is because we don't attach
+// valid source locations to decls in the DWARF AST,
+// so the ParmVarDecl::isExplicitObjectParameter fails.
 
 // Tests that we correctly deduce the CV-quals and storage
 // class of explicit object member functions.
@@ -8,15 +13,15 @@
 //
 // CHECK:      (lldb) type lookup Foo
 // CHECK-NEXT: struct Foo {
-// CHECK-NEXT:      void Method(Foo);
-// CHECK-NEXT:      void cMethod(const Foo &) const;
-// CHECK-NEXT:      void vMethod(volatile Foo &) volatile;
-// CHECK-NEXT:      void cvMethod(const volatile Foo &) const volatile;
+// CHECK-NEXT:      void Method(this Foo);
+// CHECK-NEXT:      void cMethod(this const Foo &) const;
+// CHECK-NEXT:      void vMethod(this volatile Foo &) volatile;
+// CHECK-NEXT:      void cvMethod(this const volatile Foo &) const volatile;
 // CHECK-NEXT: }
 
 struct Foo {
-  void Method(this Foo) {}
-  void cMethod(this Foo const &) {}
-  void vMethod(this Foo volatile &) {}
-  void cvMethod(this Foo const volatile &) {}
+  [[gnu::always_inline]] void Method(this Foo) {}
+  [[gnu::always_inline]] void cMethod(this Foo const &) {}
+  [[gnu::always_inline]] void vMethod(this Foo volatile &) {}
+  [[gnu::always_inline]] void cvMethod(this Foo const volatile &) {}
 } f;
diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
index 2d4b79fed4a55..3de73a64f2758 100644
--- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
@@ -1266,3 +1266,199 @@ TEST_F(DWARFASTParserClangTests, TestParseSubroutine_ParameterCreation) {
   EXPECT_EQ(func->getParamDecl(1)->getDeclContext(), func);
   EXPECT_EQ(func->getParamDecl(1)->getName(), "namedParam");
 }
+
+TEST_F(DWARFASTParserClangTests, TestParseDWARFAttributes_ObjectPointerIndex) {
+  // This tests the behaviour of ParsedDWARFTypeAttributes
+  // for DW_TAG_subprogram definitions which have a DW_AT_object_pointer
+  // that encodes a constant index (instead of a DIE reference).
+
+  const char *yamldata = R"(
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_AARCH64
+DWARF:
+  debug_str:
+    - Context
+    - func
+    - this
+    - self
+    - arg
+  debug_abbrev:
+    - ID:              0
+      Table:
+        - Code:            0x1
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_language
+              Form:            DW_FORM_data2
+        - Code:            0x2
+          Tag:             DW_TAG_structure_type
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+        - Code:            0x3
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_declaration
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_object_pointer
+              Form:            DW_FORM_implicit_const
+              Value:           1
+            - Attribute:       DW_AT_external
+              Form:            DW_FORM_flag_present
+        - Code:            0x4
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_declaration
+              Form:            DW_FORM_flag_present
+            - Attribute:       DW_AT_object_pointer
+              Form:            DW_FORM_implicit_const
+              Value:           0
+            - Attribute:       DW_AT_external
+              Form:            DW_FORM_flag_present
+
+        - Code:            0x5
+          Tag:             DW_TAG_formal_parameter
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+
+        - Code:            0x6
+          Tag:             DW_TAG_formal_parameter
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_strp
+            - Attribute:       DW_AT_artificial
+              Form:            DW_FORM_flag_present
+
+  debug_info:
+     - Version:  5
+       UnitType: DW_UT_compile
+       AddrSize: 8
+       Entries:
+
+# DW_TAG_compile_unit
+#   DW_AT_language [DW_FORM_data2]    (DW_LANG_C_plus_plus)
+
+        - AbbrCode: 0x1
+          Values:
+            - Value: 0x04
+
+#   DW_TAG_structure_type
+#     DW_AT_name [DW_FORM_strp] ("Context")
+
+        - AbbrCode: 0x2
+          Values:
+            - Value: 0x0
+
+#     DW_TAG_subprogram
+#       DW_AT_name [DW_FORM_strp] ("func")
+#       DW_AT_object_pointer [DW_FORM_implicit_const] (1)
+        - AbbrCode: 0x3
+          Values:
+            - Value: 0x8
+            - Value: 0x1
+            - Value: 0x1
+            - Value: 0x1
+
+#       DW_TAG_formal_parameter
+#         DW_AT_name [DW_FORM_strp] ("arg")
+        - AbbrCode: 0x5
+          Values:
+          - Value: 0x17
+
+#       DW_TAG_formal_parameter
+#         DW_AT_name [DW_FORM_strp] ("self")
+#         DW_AT_artificial
+        - AbbrCode: 0x6
+          Values:
+          - Value: 0x12
+          - Value: 0x1
+
+        - AbbrCode: 0x0
+        - AbbrCode: 0x0
+
+#     DW_TAG_subprogram
+#       DW_AT_object_pointer [DW_FORM_implicit_const] (0)
+#       DW_AT_name [DW_FORM_strp] ("func")
+        - AbbrCode:        0x4
+          Values:
+            - Value: 0x8
+            - Value: 0x1
+            - Value: 0x1
+            - Value: 0x1
+
+#       DW_TAG_formal_parameter
+#         DW_AT_name [DW_FORM_strp] ("this")
+#         DW_AT_artificial
+        - AbbrCode:        0x6
+          Values:
+            - Value:           0xd
+            - Value:           0x1
+
+#       DW_TAG_formal_parameter
+#         DW_AT_name [DW_FORM_strp] ("arg")
+        - AbbrCode: 0x5
+          Values:
+          - Value: 0x17
+
+        - AbbrCode: 0x0
+        - AbbrCode: 0x0
+...
+)";
+
+  YAMLModuleTester t(yamldata);
+
+  DWARFUnit *unit = t.GetDwarfUnit();
+  ASSERT_NE(unit, nullptr);
+  const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
+  ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
+  ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
+  DWARFDIE cu_die(unit, cu_entry);
+
+  auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast");
+  auto &ast_ctx = *holder->GetAST();
+  DWARFASTParserClangStub ast_parser(ast_ctx);
+
+  auto context_die = cu_die.GetFirstChild();
+  ASSERT_TRUE(context_die.IsValid());
+  ASSERT_EQ(context_die.Tag(), DW_TAG_structure_type);
+
+  auto sub1 = context_die.GetFirstChild();
+  ASSERT_TRUE(sub1.IsValid());
+  ASSERT_EQ(sub1.Tag(), DW_TAG_subprogram);
+
+  auto sub2 = sub1.GetSibling();
+  ASSERT_TRUE(sub2.IsValid());
+  ASSERT_EQ(sub2.Tag(), DW_TAG_subprogram);
+
+  // Object parameter is at constant index 1
+  {
+    auto param_die = sub1.GetFirstChild().GetSibling();
+    ASSERT_TRUE(param_die.IsValid());
+
+    EXPECT_EQ(param_die, ast_parser.GetCXXObjectParameter(sub1, context_die));
+  }
+
+  // Object parameter is at constant index 0
+  {
+    auto param_die = sub2.GetFirstChild();
+    ASSERT_TRUE(param_die.IsValid());
+
+    EXPECT_EQ(param_die,
+              ast_parser.GetCXXObjectParameter(param_die, context_die));
+  }
+}

@Michael137 Michael137 force-pushed the lldb/object-pointer-support branch 8 times, most recently from 0fd5f5a to e7594af Compare June 23, 2025 09:39
@Michael137 Michael137 force-pushed the lldb/object-pointer-support branch from e7594af to 3df8d0a Compare June 23, 2025 16:31
@Michael137 Michael137 merged commit 1bc6326 into llvm:main Jun 23, 2025
7 checks passed
@Michael137 Michael137 deleted the lldb/object-pointer-support branch June 23, 2025 16:58
@slydiman
Copy link
Contributor

It seems the buildbot lldb-remote-linux-ubuntu is broken after this patch.

Unresolved Tests (3):
  lldb-api :: commands/expression/import-std-module/basic/TestImportStdModule.py
  lldb-api :: commands/expression/import-std-module/conflicts/TestStdModuleWithConflicts.py
  lldb-api :: commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py
UNSUPPORTED: LLDB (/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/bin/clang-aarch64) :: test_dsym
(TestStdModuleWithConflicts.TestImportStdModuleConflicts.test_dsym) 
(test case does not fall in any category of interest for this run) 
python3.12: /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/llvm/../clang/include/clang/AST/Type.h:952: 
const clang::ExtQualsTypeCommonBase* clang::QualType::getCommonPtr() const: Assertion `!isNull() && "Cannot retrieve a NULL type pointer"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
...

Jaddyen pushed a commit to Jaddyen/llvm-project that referenced this pull request Jun 23, 2025
…object_pointer (llvm#144998)

Starting with llvm#124790, Clang
emits `DW_AT_object_pointer` encoded as integer constants rather than
DIE references. This patch accounts for this.

Depends on llvm#145328 and
llvm#145126
@Michael137
Copy link
Member Author

It seems the buildbot lldb-remote-linux-ubuntu is broken after this patch.

Unresolved Tests (3):
  lldb-api :: commands/expression/import-std-module/basic/TestImportStdModule.py
  lldb-api :: commands/expression/import-std-module/conflicts/TestStdModuleWithConflicts.py
  lldb-api :: commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py
UNSUPPORTED: LLDB (/home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/build/bin/clang-aarch64) :: test_dsym
(TestStdModuleWithConflicts.TestImportStdModuleConflicts.test_dsym) 
(test case does not fall in any category of interest for this run) 
python3.12: /home/buildbot/worker/as-builder-9/lldb-remote-linux-ubuntu/llvm-project/llvm/../clang/include/clang/AST/Type.h:952: 
const clang::ExtQualsTypeCommonBase* clang::QualType::getCommonPtr() const: Assertion `!isNull() && "Cannot retrieve a NULL type pointer"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
...

Looks like it got resolved? Possibly a different root cause? I think I saw some Modules related changes flying around that could've caused this

@slydiman
Copy link
Contributor

Looks like it got resolved?

Yes, sorry for the inconvenience.

anthonyhatran pushed a commit to anthonyhatran/llvm-project that referenced this pull request Jun 26, 2025
…object_pointer (llvm#144998)

Starting with llvm#124790, Clang
emits `DW_AT_object_pointer` encoded as integer constants rather than
DIE references. This patch accounts for this.

Depends on llvm#145328 and
llvm#145126
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants