[llvm] [llvm][DebugInfo] Add DW_LNAME_ support to DWARFDie::getLanguage (PR #207151)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 2 03:04:32 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-debuginfo

Author: Michael Buch (Michael137)

<details>
<summary>Changes</summary>

Currently `DWARFDie::getLanguage` only looks at `DW_AT_language`. However, with DWARFv6 we may not have such attribute, and instead have a `DW_AT_language_name` (and optional `DW_AT_language_version`).

This patch adjusts the implementation to also consider the DWARFv6 language name/version codes. Currently we just convert it back to `DW_LANG_` (similar to what we do for the `AsmPrinter`'s `DwarfUnit::getSourceLanguage`). Added a FIXME to investigate whether we want to return a variant-like type that can be either an DW_LANG vs. DW_LNAME code.

The way I initially observed this was looking at a DWARFv6 dwarfdump for a `const char[]` type. It displayed as follows:
```
DW_AT_type  (0x... "const char[[?, ? + 3)]")
```
which is our representation for when we [can't derive the default lower bound for the language](https://github.com/llvm/llvm-project/blob/ce787718c3c82255825ebe5f95740c24df55f7d0/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h#L122-L135).

With this patch, we'd display it as expected:
```
DW_AT_type  (0x... "const char[3]")
```

Added unit-tests for the new API and a dwarfdump test.

AI usage:
- Claude wrote the dwarfdump and unit-tests. Manually reviewed and cleaned them up.

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


4 Files Affected:

- (modified) llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h (+1) 
- (modified) llvm/lib/DebugInfo/DWARF/DWARFDie.cpp (+24-4) 
- (added) llvm/test/tools/llvm-dwarfdump/X86/array-type-lower-bound-dwarf6.s (+108) 
- (modified) llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp (+197) 


``````````diff
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h
index 8a0bd1733808a..2a08eac3ad32e 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h
@@ -230,6 +230,7 @@ class DWARFDie {
 
   LLVM_ABI bool addressRangeContainsAddress(const uint64_t Address) const;
 
+  /// Returns the DW_LANG_ code for this DIE's DWARF unit, if it exists.
   LLVM_ABI std::optional<uint64_t> getLanguage() const;
 
   LLVM_ABI Expected<DWARFLocationExpressionsVector>
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
index 25e5bda44975e..650a98a22b4fc 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
@@ -481,12 +481,32 @@ bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const {
   return false;
 }
 
+// FIXME: should we return a structure akin to DISourceLanguageName here
+// encapsulates an unversioned (dwarf::SourceLanguage) and versioned
+// (dwarf::SourceLanguageName) language, and put the burden on the
+// user to determine which to use?
 std::optional<uint64_t> DWARFDie::getLanguage() const {
-  if (isValid()) {
-    if (std::optional<DWARFFormValue> LV =
-            U->getUnitDIE().find(dwarf::DW_AT_language))
-      return LV->getAsUnsignedConstant();
+  if (!isValid())
+    return std::nullopt;
+
+  DWARFDie Unit = U->getUnitDIE();
+
+  if (std::optional<DWARFFormValue> LV = Unit.find(dwarf::DW_AT_language))
+    return LV->getAsUnsignedConstant();
+
+  if (std::optional<DWARFFormValue> Name =
+          Unit.find(dwarf::DW_AT_language_name)) {
+    uint32_t Version = 0;
+    if (std::optional<DWARFFormValue> V =
+            Unit.find(dwarf::DW_AT_language_version))
+      Version = V->getAsUnsignedConstant().value_or(0);
+
+    return llvm::dwarf::toDW_LANG(
+        static_cast<SourceLanguageName>(
+            Name->getAsUnsignedConstant().value_or(0)),
+        Version);
   }
+
   return std::nullopt;
 }
 
diff --git a/llvm/test/tools/llvm-dwarfdump/X86/array-type-lower-bound-dwarf6.s b/llvm/test/tools/llvm-dwarfdump/X86/array-type-lower-bound-dwarf6.s
new file mode 100644
index 0000000000000..8bd8be4842556
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfdump/X86/array-type-lower-bound-dwarf6.s
@@ -0,0 +1,108 @@
+# Check that array type bounds are displayed correctly for DWARFv6 compile units.
+# The presentation of array bounds is driven by the CU's language, which, starting in,
+# DWARFv6 changed.
+
+# RUN: llvm-mc -triple=x86_64--linux -filetype=obj < %s | \
+# RUN:     llvm-dwarfdump --debug-info - | FileCheck %s
+
+# CHECK-LABEL: DW_TAG_compile_unit
+# CHECK:         DW_AT_language_name (DW_LNAME_C_plus_plus)
+# CHECK:         DW_AT_language_version (C++17)
+# CHECK:       DW_TAG_variable
+# CHECK-NEXT:    DW_AT_name  ("arr")
+# CHECK-NEXT:    DW_AT_type  ({{.*}} "int[3]")
+
+        .section        .debug_abbrev,"", at progbits
+
+        .byte   1
+        .byte   17              # DW_TAG_compile_unit
+        .byte   1               # DW_CHILDREN_yes
+        .ascii  "\220\001"      # DW_AT_language_name (0x90)
+        .byte   5               # DW_FORM_data2
+        .ascii  "\221\001"      # DW_AT_language_version (0x91)
+        .byte   6               # DW_FORM_data4
+        .byte   0, 0            # end of attributes
+
+        .byte   2
+        .byte   52              # DW_TAG_variable
+        .byte   0               # DW_CHILDREN_no
+        .byte   3               # DW_AT_name
+        .byte   8               # DW_FORM_string
+        .byte   73              # DW_AT_type
+        .byte   19              # DW_FORM_ref4
+        .byte   0, 0
+
+        .byte   3
+        .byte   1               # DW_TAG_array_type
+        .byte   1               # DW_CHILDREN_yes
+        .byte   73              # DW_AT_type
+        .byte   19              # DW_FORM_ref4
+        .byte   0, 0
+
+        .byte   4
+        .byte   33              # DW_TAG_subrange_type
+        .byte   0               # DW_CHILDREN_no
+        .byte   73              # DW_AT_type
+        .byte   19              # DW_FORM_ref4
+        .byte   55              # DW_AT_count
+        .byte   11              # DW_FORM_data1
+        .byte   0, 0
+
+        .byte   5
+        .byte   36              # DW_TAG_base_type
+        .byte   0               # DW_CHILDREN_no
+        .byte   3               # DW_AT_name
+        .byte   8               # DW_FORM_string
+        .byte   62              # DW_AT_encoding
+        .byte   11              # DW_FORM_data1
+        .byte   11              # DW_AT_byte_size
+        .byte   11              # DW_FORM_data1
+        .byte   0, 0
+
+        .byte   0               # end of abbreviation table
+
+        .section        .debug_info,"", at progbits
+        .long   .Ldebug_info_end0 - .Ldebug_info_start0 # unit length
+.Ldebug_info_start0:
+        .short  6               # DWARF version
+        .byte   1               # DW_UT_compile
+        .byte   8               # address size
+        .long   0               # debug_abbrev_offset
+
+        # DW_TAG_compile_unit
+        .byte   1               # abbrev 1
+        .short  4               # DW_AT_language_name = DW_LNAME_C_plus_plus
+        .long   201703          # DW_AT_language_version = C++17
+
+          # DW_TAG_variable
+          .byte   2             # abbrev 2
+          .ascii  "arr\0"       # DW_AT_name
+          .long   .Larray_type - .Ldebug_info_start0 + 4  # DW_AT_type
+
+.Larray_type:
+          # DW_TAG_array_type
+          .byte   3             # abbrev 3
+          .long   .Lint_type - .Ldebug_info_start0 + 4   # DW_AT_type
+
+            # DW_TAG_subrange_type
+            .byte   4           # abbrev 4
+            .long   .Lsize_type - .Ldebug_info_start0 + 4 # DW_AT_type
+            .byte   3           # DW_AT_count = 3
+            .byte   0           # end of children (array_type)
+
+.Lint_type:
+          # DW_TAG_base_type "int"
+          .byte   5             # abbrev 5
+          .ascii  "int\0"       # DW_AT_name
+          .byte   5             # DW_AT_encoding = DW_ATE_signed
+          .byte   4             # DW_AT_byte_size = 4
+
+.Lsize_type:
+          # DW_TAG_base_type "__ARRAY_SIZE_TYPE__"
+          .byte   5             # abbrev 5
+          .ascii  "__ARRAY_SIZE_TYPE__\0" # DW_AT_name
+          .byte   7             # DW_AT_encoding = DW_ATE_unsigned
+          .byte   8             # DW_AT_byte_size = 8
+
+        .byte   0               # end of children (compile_unit)
+.Ldebug_info_end0:
diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp
index f566bee170236..f6e4073e8607a 100644
--- a/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp
@@ -839,4 +839,201 @@ TEST(DWARFDie, DWARFTypePrinterTest) {
   testAppendQualifiedName(Ctx->getDIEForOffset(0x1a), "t1<t3<int> >::t2");
   testAppendQualifiedName(Ctx->getDIEForOffset(0x28), "t3<int>::my_int");
 }
+
+TEST(DWARFDie, getLanguage) {
+  const char *yamldata = R"(
+    debug_abbrev:
+      - Table:
+          - Code:       1
+            Tag:        DW_TAG_compile_unit
+            Children:   DW_CHILDREN_no
+            Attributes:
+              - Attribute: DW_AT_language
+                Form:      DW_FORM_data2
+    debug_info:
+      - Version:    5
+        UnitType:   DW_UT_compile
+        AddrSize:   4
+        Entries:
+          - AbbrCode: 1
+            Values:
+              - Value: 4
+  )";
+  Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
+      DWARFYAML::emitDebugSections(StringRef(yamldata), /*IsLittleEndian=*/true,
+                                   /*Is64BitAddrSize=*/false);
+  ASSERT_THAT_EXPECTED(Sections, Succeeded());
+  std::unique_ptr<DWARFContext> Ctx =
+      DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
+  DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
+  ASSERT_NE(nullptr, CU);
+  DWARFDie Die = CU->getUnitDIE();
+  ASSERT_TRUE(Die.isValid());
+  EXPECT_EQ(Die.getLanguage(), std::optional<uint64_t>(DW_LANG_C_plus_plus));
+}
+
+TEST(DWARFDie, getLanguageAbsent) {
+  const char *yamldata = R"(
+    debug_abbrev:
+      - Table:
+          - Code:       1
+            Tag:        DW_TAG_compile_unit
+            Children:   DW_CHILDREN_no
+            Attributes: []
+    debug_info:
+      - Version:    5
+        UnitType:   DW_UT_compile
+        AddrSize:   4
+        Entries:
+          - AbbrCode: 1
+            Values:   []
+  )";
+  Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
+      DWARFYAML::emitDebugSections(StringRef(yamldata), /*IsLittleEndian=*/true,
+                                   /*Is64BitAddrSize=*/false);
+  ASSERT_THAT_EXPECTED(Sections, Succeeded());
+  std::unique_ptr<DWARFContext> Ctx =
+      DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
+  DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
+  ASSERT_NE(nullptr, CU);
+  DWARFDie Die = CU->getUnitDIE();
+  ASSERT_TRUE(Die.isValid());
+  EXPECT_FALSE(Die.getLanguage().has_value());
+}
+
+TEST(DWARFDie, getLanguageNameOnly) {
+  const char *yamldata = R"(
+    debug_abbrev:
+      - Table:
+          - Code:       1
+            Tag:        DW_TAG_compile_unit
+            Children:   DW_CHILDREN_no
+            Attributes:
+              - Attribute: DW_AT_language_name
+                Form:      DW_FORM_data2
+    debug_info:
+      - Version:    6
+        UnitType:   DW_UT_compile
+        AddrSize:   4
+        Entries:
+          - AbbrCode: 1
+            Values:
+              - Value: 4
+  )";
+  Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
+      DWARFYAML::emitDebugSections(StringRef(yamldata), /*IsLittleEndian=*/true,
+                                   /*Is64BitAddrSize=*/false);
+  ASSERT_THAT_EXPECTED(Sections, Succeeded());
+  std::unique_ptr<DWARFContext> Ctx =
+      DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
+  DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
+  ASSERT_NE(nullptr, CU);
+  DWARFDie Die = CU->getUnitDIE();
+  ASSERT_TRUE(Die.isValid());
+  EXPECT_EQ(Die.getLanguage(), std::optional<uint64_t>(DW_LANG_C_plus_plus));
+}
+
+TEST(DWARFDie, getLanguageNameAndVersion) {
+  const char *yamldata = R"(
+    debug_abbrev:
+      - Table:
+          - Code:       1
+            Tag:        DW_TAG_compile_unit
+            Children:   DW_CHILDREN_no
+            Attributes:
+              - Attribute: DW_AT_language_name
+                Form:      DW_FORM_data2
+              - Attribute: DW_AT_language_version
+                Form:      DW_FORM_data4
+    debug_info:
+      - Version:    6
+        UnitType:   DW_UT_compile
+        AddrSize:   4
+        Entries:
+          - AbbrCode: 1
+            Values:
+              - Value: 4
+              - Value: 201703
+  )";
+  Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
+      DWARFYAML::emitDebugSections(StringRef(yamldata), /*IsLittleEndian=*/true,
+                                   /*Is64BitAddrSize=*/false);
+  ASSERT_THAT_EXPECTED(Sections, Succeeded());
+  std::unique_ptr<DWARFContext> Ctx =
+      DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
+  DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
+  ASSERT_NE(nullptr, CU);
+  DWARFDie Die = CU->getUnitDIE();
+  ASSERT_TRUE(Die.isValid());
+  EXPECT_EQ(Die.getLanguage(), std::optional<uint64_t>(DW_LANG_C_plus_plus_17));
+}
+
+TEST(DWARFDie, getLanguageNameNoMapping) {
+  // DW_LNAME_CPP_for_OpenCL (0x24) has no corresponding DW_LANG_* constant.
+  const char *yamldata = R"(
+    debug_abbrev:
+      - Table:
+          - Code:       1
+            Tag:        DW_TAG_compile_unit
+            Children:   DW_CHILDREN_no
+            Attributes:
+              - Attribute: DW_AT_language_name
+                Form:      DW_FORM_data2
+    debug_info:
+      - Version:    6
+        UnitType:   DW_UT_compile
+        AddrSize:   4
+        Entries:
+          - AbbrCode: 1
+            Values:
+              - Value: 36
+  )";
+  Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
+      DWARFYAML::emitDebugSections(StringRef(yamldata), /*IsLittleEndian=*/true,
+                                   /*Is64BitAddrSize=*/false);
+  ASSERT_THAT_EXPECTED(Sections, Succeeded());
+  std::unique_ptr<DWARFContext> Ctx =
+      DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
+  DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
+  ASSERT_NE(nullptr, CU);
+  DWARFDie Die = CU->getUnitDIE();
+  ASSERT_TRUE(Die.isValid());
+  EXPECT_EQ(Die.getLanguage(), std::nullopt);
+}
+
+TEST(DWARFDie, getLanguageNameInvalidVersion) {
+  const char *yamldata = R"(
+    debug_abbrev:
+      - Table:
+          - Code:       1
+            Tag:        DW_TAG_compile_unit
+            Children:   DW_CHILDREN_no
+            Attributes:
+              - Attribute: DW_AT_language_name
+                Form:      DW_FORM_data2
+              - Attribute: DW_AT_language_version
+                Form:      DW_FORM_data4
+    debug_info:
+      - Version:    6
+        UnitType:   DW_UT_compile
+        AddrSize:   4
+        Entries:
+          - AbbrCode: 1
+            Values:
+              - Value: 4
+              - Value: 999999
+  )";
+  Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
+      DWARFYAML::emitDebugSections(StringRef(yamldata), /*IsLittleEndian=*/true,
+                                   /*Is64BitAddrSize=*/false);
+  ASSERT_THAT_EXPECTED(Sections, Succeeded());
+  std::unique_ptr<DWARFContext> Ctx =
+      DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true);
+  DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
+  ASSERT_NE(nullptr, CU);
+  DWARFDie Die = CU->getUnitDIE();
+  ASSERT_TRUE(Die.isValid());
+  EXPECT_EQ(Die.getLanguage(), std::nullopt);
+}
+
 } // end anonymous namespace

``````````

</details>


https://github.com/llvm/llvm-project/pull/207151


More information about the llvm-commits mailing list