[llvm] [ThinLTO][DebugInfo] Emit full type definitions when importing anonymous types. (PR #78461)
Ricky Zhou via llvm-commits
llvm-commits at lists.llvm.org
Wed Jan 17 08:14:07 PST 2024
https://github.com/rickyz created https://github.com/llvm/llvm-project/pull/78461
This fixes some cases of missing debuginfo caused by an interaction between:
https://github.com/llvm/llvm-project/commit/f0d66559ea345db4b2116cae044aaf3399d7e829,
which drops the identifier from a DICompositeType in the module containing its
vtable.
and
https://github.com/llvm/llvm-project/commit/a61f5e379675732666744bcba25efbc9922e016a,
which causes ThinLTO to import composite types as declarations when they have
an identifier.
If a virtual class's DICompositeType has no identifier due to the first change,
and contains a nested anonymous type which does have an identifier, then the
second change can cause ThinLTO to output the classes's DICompositeType as a
type definition that links to a non-defining declaration for the nested type.
Since the nested anonyous type does not have a name, debuggers are unable to
find the definition for the declaration.
Repro case:
cat > a.h <<EOF
class A {
public:
A();
virtual ~A();
private:
union {
int val;
};
};
EOF
cat > a.cc <<EOF
A::A() { asm(""); }
A::~A() {}
EOF
cat > main.cc <<EOF
int main(int argc, char **argv) {
A a;
return 0;
}
EOF
clang++ -O2 -g -flto=thin -mllvm -force-import-all main.cc a.cc
gdb ./a.out -batch -ex 'pt /rmt A'
The gdb command outputs:
type = class A {
private:
union {
<incomplete type>
};
}
and dwarfdump -i a.out shows a DW_TAG_class_type for A with an incomplete union
type (note that there is also a duplicate entry with the full union type that
comes after).
< 1><0x0000001e> DW_TAG_class_type
DW_AT_containing_type <0x0000001e>
DW_AT_calling_convention DW_CC_pass_by_reference
DW_AT_name (indexed string: 0x00000007)A
DW_AT_byte_size 0x00000010
DW_AT_decl_file 0x00000001 /path/to/./a.h
DW_AT_decl_line 0x00000001
...
< 2><0x0000002f> DW_TAG_member
DW_AT_type <0x00000037>
DW_AT_decl_file 0x00000001 /path/to/./a.h
DW_AT_decl_line 0x00000007
DW_AT_data_member_location 8
< 2><0x00000037> DW_TAG_union_type
DW_AT_export_symbols yes(1)
DW_AT_calling_convention DW_CC_pass_by_value
DW_AT_declaration yes(1)
This change works around this by making ThinLTO always import full definitions
for anonymous types.
>From 77a7691817278a96d7d94bcc05a5dcf24959f98a Mon Sep 17 00:00:00 2001
From: Ricky Zhou <ricky at rzhou.org>
Date: Wed, 17 Jan 2024 08:09:51 -0800
Subject: [PATCH] [ThinLTO][DebugInfo] Emit full type definitions when
importing anonymous types.
This fixes some cases of missing debuginfo caused by an interaction between:
https://github.com/llvm/llvm-project/commit/f0d66559ea345db4b2116cae044aaf3399d7e829,
which drops the identifier from a DICompositeType in the module containing its
vtable.
and
https://github.com/llvm/llvm-project/commit/a61f5e379675732666744bcba25efbc9922e016a,
which causes ThinLTO to import composite types as declarations when they have
an identifier.
If a virtual class's DICompositeType has no identifier due to the first change,
and contains a nested anonymous type which does have an identifier, then the
second change can cause ThinLTO to output the classes's DICompositeType as a
type definition that links to a non-defining declaration for the nested type.
Since the nested anonyous type does not have a name, debuggers are unable to
find the definition for the declaration.
Repro case:
cat > a.h <<EOF
class A {
public:
A();
virtual ~A();
private:
union {
int val;
};
};
EOF
cat > a.cc <<EOF
A::A() { asm(""); }
A::~A() {}
EOF
cat > main.cc <<EOF
int main(int argc, char **argv) {
A a;
return 0;
}
EOF
clang++ -O2 -g -flto=thin -mllvm -force-import-all main.cc a.cc
gdb ./a.out -batch -ex 'pt /rmt A'
The gdb command outputs:
type = class A {
private:
union {
<incomplete type>
};
}
and dwarfdump -i a.out shows a DW_TAG_class_type for A with an incomplete union
type (note that there is also a duplicate entry with the full union type that
comes after).
< 1><0x0000001e> DW_TAG_class_type
DW_AT_containing_type <0x0000001e>
DW_AT_calling_convention DW_CC_pass_by_reference
DW_AT_name (indexed string: 0x00000007)A
DW_AT_byte_size 0x00000010
DW_AT_decl_file 0x00000001 /path/to/./a.h
DW_AT_decl_line 0x00000001
...
< 2><0x0000002f> DW_TAG_member
DW_AT_type <0x00000037>
DW_AT_decl_file 0x00000001 /path/to/./a.h
DW_AT_decl_line 0x00000007
DW_AT_data_member_location 8
< 2><0x00000037> DW_TAG_union_type
DW_AT_export_symbols yes(1)
DW_AT_calling_convention DW_CC_pass_by_value
DW_AT_declaration yes(1)
This change works around this by making ThinLTO always import full definitions
for anonymous types.
---
llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 37 +++++++++++--------
.../X86/debuginfo-compositetype-import.ll | 15 +++++---
2 files changed, 30 insertions(+), 22 deletions(-)
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 910e97489dbbe0..770eb83af17f9b 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -1615,27 +1615,32 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
Metadata *Annotations = nullptr;
auto *Identifier = getMDString(Record[15]);
// If this module is being parsed so that it can be ThinLTO imported
- // into another module, composite types only need to be imported
- // as type declarations (unless full type definitions requested).
- // Create type declarations up front to save memory. Also, buildODRType
- // handles the case where this is type ODRed with a definition needed
- // by the importing module, in which case the existing definition is
- // used.
- if (IsImporting && !ImportFullTypeDefinitions && Identifier &&
+ // into another module, composite types only need to be imported as
+ // type declarations (unless full type definitions are requested).
+ // Create type declarations up front to save memory. This is only
+ // done for types which have an Identifier, and are therefore
+ // subject to the ODR.
+ //
+ // buildODRType handles the case where this is type ODRed with a
+ // definition needed by the importing module, in which case the
+ // existing definition is used.
+ //
+ // We always import full definitions for anonymous composite types,
+ // as without a name, debuggers cannot easily resolve a declaration
+ // to its definition.
+ if (IsImporting && !ImportFullTypeDefinitions && Identifier && Name &&
(Tag == dwarf::DW_TAG_enumeration_type ||
Tag == dwarf::DW_TAG_class_type ||
Tag == dwarf::DW_TAG_structure_type ||
Tag == dwarf::DW_TAG_union_type)) {
Flags = Flags | DINode::FlagFwdDecl;
- if (Name) {
- // This is a hack around preserving template parameters for simplified
- // template names - it should probably be replaced with a
- // DICompositeType flag specifying whether template parameters are
- // required on declarations of this type.
- StringRef NameStr = Name->getString();
- if (!NameStr.contains('<') || NameStr.starts_with("_STN|"))
- TemplateParams = getMDOrNull(Record[14]);
- }
+ // This is a hack around preserving template parameters for simplified
+ // template names - it should probably be replaced with a
+ // DICompositeType flag specifying whether template parameters are
+ // required on declarations of this type.
+ StringRef NameStr = Name->getString();
+ if (!NameStr.contains('<') || NameStr.starts_with("_STN|"))
+ TemplateParams = getMDOrNull(Record[14]);
} else {
BaseType = getDITypeRefOrNull(Record[6]);
OffsetInBits = Record[9];
diff --git a/llvm/test/ThinLTO/X86/debuginfo-compositetype-import.ll b/llvm/test/ThinLTO/X86/debuginfo-compositetype-import.ll
index 22e8ec4418eca3..133930ce6c0377 100644
--- a/llvm/test/ThinLTO/X86/debuginfo-compositetype-import.ll
+++ b/llvm/test/ThinLTO/X86/debuginfo-compositetype-import.ll
@@ -16,7 +16,8 @@
; CHECK: distinct !DICompositeType(tag: DW_TAG_enumeration_type, name: "enum", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 50, size: 32, flags: DIFlagFwdDecl, identifier: "enum")
; CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, name: "class<>", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 728, size: 448, flags: DIFlagFwdDecl, identifier: "class")
; CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "struct", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 309, size: 128, flags: DIFlagFwdDecl, templateParams: !{{[0-9]+}}, identifier: "struct_templ_simplified")
-; CHECK: distinct !DICompositeType(tag: DW_TAG_union_type, file: !{{[0-9]+}}, line: 115, size: 384, flags: DIFlagFwdDecl, identifier: "union")
+; CHECK: distinct !DICompositeType(tag: DW_TAG_union_type, name: "union", file: !{{[0-9]+}}, line: 115, size: 384, flags: DIFlagFwdDecl, identifier: "union")
+; CHECK: distinct !DICompositeType(tag: DW_TAG_union_type, file: !{{[0-9]+}}, line: 1, elements: !{{[0-9]+}}, identifier: "anon_union")
; CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "struct<>", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 309, size: 128, flags: DIFlagFwdDecl, identifier: "struct_templ")
; CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "_STN|struct|<>", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 309, size: 128, flags: DIFlagFwdDecl, templateParams: !{{[0-9]+}}, identifier: "struct_templ_simplified_mangled")
@@ -32,7 +33,8 @@
; FULL: distinct !DICompositeType(tag: DW_TAG_enumeration_type, name: "enum", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 50, size: 32, elements: !{{[0-9]+}}, identifier: "enum")
; FULL: distinct !DICompositeType(tag: DW_TAG_class_type, name: "class<>", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 728, size: 448, elements: !{{[0-9]+}}, identifier: "class")
; FULL: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "struct", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 309, baseType: !{{[0-9]+}}, size: 128, offset: 64, elements: !{{[0-9]+}}, vtableHolder: !{{[0-9]+}}, templateParams: !{{[0-9]+}}, identifier: "struct_templ_simplified")
-; FULL: distinct !DICompositeType(tag: DW_TAG_union_type, file: !{{[0-9]+}}, line: 115, size: 384, elements: !{{[0-9]+}}, identifier: "union")
+; FULL: distinct !DICompositeType(tag: DW_TAG_union_type, name: "union", file: !{{[0-9]+}}, line: 115, size: 384, elements: !{{[0-9]+}}, identifier: "union")
+; FULL: distinct !DICompositeType(tag: DW_TAG_union_type, file: !{{[0-9]+}}, line: 1, elements: !{{[0-9]+}}, identifier: "anon_union")
; FULL: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "struct<>", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 309, baseType: !{{[0-9]+}}, size: 128, offset: 64, elements: !{{[0-9]+}}, vtableHolder: !{{[0-9]+}}, templateParams: !{{[0-9]+}}, identifier: "struct_templ")
; FULL: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "_STN|struct|<>", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 309, baseType: !{{[0-9]+}}, size: 128, offset: 64, elements: !{{[0-9]+}}, vtableHolder: !{{[0-9]+}}, templateParams: !{{[0-9]+}}, identifier: "struct_templ_simplified_mangled")
@@ -59,10 +61,11 @@ entry:
!5 = !{}
!6 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: false, unit: !0, retainedNodes: !5)
!7 = !DISubroutineType(types: !8)
-!8 = !{!9, !10, !11, !12, !13, !14}
+!8 = !{!9, !10, !11, !12, !13, !14, !15}
!9 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "enum", scope: !1, file: !1, line: 50, size: 32, elements: !5, identifier: "enum")
!10 = !DICompositeType(tag: DW_TAG_class_type, name: "class<>", scope: !1, file: !1, line: 728, size: 448, elements: !5, identifier: "class")
!11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "struct", scope: !1, file: !1, line: 309, baseType: !10, size: 128, offset: 64, elements: !5, vtableHolder: !10, templateParams: !5, identifier: "struct_templ_simplified")
-!12 = distinct !DICompositeType(tag: DW_TAG_union_type, file: !1, line: 115, size: 384, elements: !5, identifier: "union")
-!13 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "struct<>", scope: !1, file: !1, line: 309, baseType: !10, size: 128, offset: 64, elements: !5, vtableHolder: !10, templateParams: !5, identifier: "struct_templ")
-!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "_STN|struct|<>", scope: !1, file: !1, line: 309, baseType: !10, size: 128, offset: 64, elements: !5, vtableHolder: !10, templateParams: !5, identifier: "struct_templ_simplified_mangled")
+!12 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "union", file: !1, line: 115, size: 384, elements: !5, identifier: "union")
+!13 = distinct !DICompositeType(tag: DW_TAG_union_type, file: !1, line: 1, elements: !5, identifier: "anon_union")
+!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "struct<>", scope: !1, file: !1, line: 309, baseType: !10, size: 128, offset: 64, elements: !5, vtableHolder: !10, templateParams: !5, identifier: "struct_templ")
+!15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "_STN|struct|<>", scope: !1, file: !1, line: 309, baseType: !10, size: 128, offset: 64, elements: !5, vtableHolder: !10, templateParams: !5, identifier: "struct_templ_simplified_mangled")
More information about the llvm-commits
mailing list