[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