Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -665,6 +665,12 @@ void addDwarfTypeUnitType(DwarfCompileUnit &CU, StringRef Identifier, DIE &Die, const DICompositeType *CTy); + /// Return the type unit signature of \p CTy after finding or creating its + /// type unit. Return None if a type unit cannot be created for \p CTy. + Optional getOrCreateDwarfTypeUnit(DwarfCompileUnit &CU, + StringRef Identifier, + const DICompositeType *CTy); + class NonTypeUnitContext { DwarfDebug *DD; decltype(DwarfDebug::TypeUnitsUnderConstruction) TypeUnitsUnderConstruction; Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1378,6 +1378,21 @@ return GVEs; } +/// Create \p Ty if it doesn't already exist. If type units are enabled, try to +/// emit a type unit without a CU skeleton DIE. +static void createMaybeUnusedType(DwarfDebug &DD, DwarfCompileUnit &CU, + DIType &Ty) { + // Try to generate a type unit without creating a skeleton DIE in this CU. + if (DICompositeType const *CTy = dyn_cast(&Ty)) { + MDString const *TypeId = CTy->getRawIdentifier(); + if (DD.generateTypeUnits() && TypeId && !Ty.isForwardDecl()) + if (DD.getOrCreateDwarfTypeUnit(CU, TypeId->getString(), CTy)) + return; + } + // We couldn't or shouldn't add a type unit so create the DIE normally. + CU.getOrCreateTypeDIE(&Ty); +}; + // Emit all Dwarf sections that should come after the content. void DwarfDebug::endModule() { // Terminate the pending line table. @@ -1437,12 +1452,12 @@ } for (auto *Ty : CUNode->getEnumTypes()) - CU->getOrCreateTypeDIE(cast(Ty)); + createMaybeUnusedType(*this, *CU, *Ty); - for (auto *Ty : CUNode->getRetainedTypes()) + for (auto *Ty : CUNode->getRetainedTypes()) { if (DIType *RT = dyn_cast(Ty)) - // There is no point in force-emitting a forward declaration. - CU->getOrCreateTypeDIE(RT); + createMaybeUnusedType(*this, *CU, *RT); + } // Emit imported entities last so that the relevant context // is already available. @@ -3401,17 +3416,30 @@ void DwarfDebug::addDwarfTypeUnitType(DwarfCompileUnit &CU, StringRef Identifier, DIE &RefDie, const DICompositeType *CTy) { + bool TopLevelType = TypeUnitsUnderConstruction.empty(); + if (auto Signature = getOrCreateDwarfTypeUnit(CU, Identifier, CTy)) { + CU.addDIETypeSignature(RefDie, *Signature); + } else if (TopLevelType) { + // Construct this type in the CU directly. + // This is inefficient because all the dependent types will be rebuilt + // from scratch, including building them in type units, discovering that + // they depend on addresses, throwing them out and rebuilding them. + CU.constructTypeDIE(RefDie, cast(CTy)); + } +} + +Optional +DwarfDebug::getOrCreateDwarfTypeUnit(DwarfCompileUnit &CU, StringRef Identifier, + const DICompositeType *CTy) { // Fast path if we're building some type units and one has already used the // address pool we know we're going to throw away all this work anyway, so // don't bother building dependent types. if (!TypeUnitsUnderConstruction.empty() && AddrPool.hasBeenUsed()) - return; + return None; auto Ins = TypeSignatures.insert(std::make_pair(CTy, 0)); - if (!Ins.second) { - CU.addDIETypeSignature(RefDie, Ins.first->second); - return; - } + if (!Ins.second) + return Ins.first->second; bool TopLevelType = TypeUnitsUnderConstruction.empty(); AddrPool.resetUsedFlag(); @@ -3465,13 +3493,7 @@ // the type that used an address. for (const auto &TU : TypeUnitsToAdd) TypeSignatures.erase(TU.second); - - // Construct this type in the CU directly. - // This is inefficient because all the dependent types will be rebuilt - // from scratch, including building them in type units, discovering that - // they depend on addresses, throwing them out and rebuilding them. - CU.constructTypeDIE(RefDie, cast(CTy)); - return; + return None; } // If the type wasn't dependent on fission addresses, finish adding the type @@ -3481,7 +3503,7 @@ InfoHolder.emitUnit(TU.first.get(), useSplitDwarf()); } } - CU.addDIETypeSignature(RefDie, Signature); + return Signature; } DwarfDebug::NonTypeUnitContext::NonTypeUnitContext(DwarfDebug *DD) Index: llvm/test/DebugInfo/X86/type-units-unused-nested-enums.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/type-units-unused-nested-enums.mir @@ -0,0 +1,83 @@ +# RUN: %llc_dwarf %s -generate-type-units -o - -filetype=obj \ +# RUN: | llvm-dwarfdump -o - - \ +# RUN: | FileCheck %s + +## PR51087 +## Check that Outer's CU DIE doesn't get a child entry for any of its +## unreferenced nested types, and that the enums still have type units +## generated for them (this forces Inner's type unit to be generated too). +## +## $ cat test.cpp +## class Outer { +## public: +## struct Inner { +## enum Enum1 { X }; +## enum Enum2 { Y }; +## Enum1 one; +## Enum2 two; +## }; +## +## Inner n; +## }; +## +## Outer O; + +## 1. Check we have type units for each of the types. +# CHECK: 0x00000000: Type Unit{{.+}} name = 'Outer'{{.+}} type_signature = [[SIG_Outer:[0-9a-fx]+]] +# CHECK: 0x00000000: Type Unit{{.+}} name = 'Inner'{{.+}} +# CHECK: 0x00000000: Type Unit{{.+}} name = 'Enum1'{{.+}} +# CHECK: 0x00000000: Type Unit{{.+}} name = 'Enum2'{{.+}} + +## Compile unit. +# CHECK: DW_TAG_compile_unit +# CHECK: DW_AT_name ("O") +## 2. Check that the type DIE reference is ok. +# CHECK-NEXT: DW_AT_type ([[DIE_Outer:[0-9a-fx]+]] "Outer") + +# CHECK: [[DIE_Outer]]: DW_TAG_class_type +# CHECK-NEXT: DW_AT_declaration (true) +# CHECK-NEXT: DW_AT_signature ([[SIG_Outer]]) +## 3. Check that the type DIE for Outer doesn't have any children. +# CHECK-NOT: DW_TAG +# CHECK: NULL + +--- | + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + %class.Outer = type { %"struct.Outer::Inner" } + %"struct.Outer::Inner" = type { i32, i32 } + + @O = dso_local local_unnamed_addr global %class.Outer zeroinitializer, align 4, !dbg !0 + + !llvm.dbg.cu = !{!2} + !llvm.module.flags = !{!20, !21, !22, !23} + !llvm.ident = !{!24} + + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(name: "O", scope: !2, file: !3, line: 13, type: !7, isLocal: false, isDefinition: true) + !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !19, splitDebugInlining: false, nameTableKind: None) + !3 = !DIFile(filename: "test.cpp", directory: "/") + !4 = !{!5, !13} + !5 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum1", scope: !6, file: !3, line: 4, baseType: !14, size: 32, elements: !17, identifier: "_ZTSN5Outer5Inner5Enum1E") + !6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Inner", scope: !7, file: !3, line: 3, size: 64, flags: DIFlagTypePassByValue, elements: !10, identifier: "_ZTSN5Outer5InnerE") + !7 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "Outer", file: !3, line: 1, size: 64, flags: DIFlagTypePassByValue, elements: !8, identifier: "_ZTS5Outer") + !8 = !{!9} + !9 = !DIDerivedType(tag: DW_TAG_member, name: "n", scope: !7, file: !3, line: 10, baseType: !6, size: 64, flags: DIFlagPublic) + !10 = !{!11, !12} + !11 = !DIDerivedType(tag: DW_TAG_member, name: "one", scope: !6, file: !3, line: 6, baseType: !5, size: 32) + !12 = !DIDerivedType(tag: DW_TAG_member, name: "two", scope: !6, file: !3, line: 7, baseType: !13, size: 32, offset: 32) + !13 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum2", scope: !6, file: !3, line: 5, baseType: !14, size: 32, elements: !15, identifier: "_ZTSN5Outer5Inner5Enum2E") + !14 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) + !15 = !{!16} + !16 = !DIEnumerator(name: "Y", value: 0, isUnsigned: true) + !17 = !{!18} + !18 = !DIEnumerator(name: "X", value: 0, isUnsigned: true) + !19 = !{!0} + !20 = !{i32 7, !"Dwarf Version", i32 5} + !21 = !{i32 2, !"Debug Info Version", i32 3} + !22 = !{i32 1, !"wchar_size", i32 4} + !23 = !{i32 7, !"uwtable", i32 1} + !24 = !{!"clang version 14.0.0"} + +... Index: llvm/test/DebugInfo/X86/type-units-unused-type.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/type-units-unused-type.mir @@ -0,0 +1,36 @@ +# RUN: %llc_dwarf %s -generate-type-units -o - -filetype=obj \ +# RUN: | llvm-dwarfdump -o - - \ +# RUN: | FileCheck %s + +## PR51087 +## Check that a retained type (X) that has a type unit generated for it does +## not get a skeleton DIE in the CU. +## +## Generated with `-Xclang -debug-info-kind=unused-types` from: +## $ cat test.cpp +## struct X{}; + +# CHECK: 0x00000000: Type Unit{{.+}} name = 'X'{{.+}} +# CHECK: DW_TAG_compile_unit +# CHECK-NOT: DW_TAG_structure_type + +--- | + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!5, !6, !7, !8} + !llvm.ident = !{!9} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{!3} + !3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "X", file: !1, line: 1, size: 8, flags: DIFlagTypePassByValue, elements: !4, identifier: "_ZTS1X") + !4 = !{} + !5 = !{i32 7, !"Dwarf Version", i32 5} + !6 = !{i32 2, !"Debug Info Version", i32 3} + !7 = !{i32 1, !"wchar_size", i32 4} + !8 = !{i32 7, !"uwtable", i32 1} + !9 = !{!"clang version 14.0.0"} + +... Index: llvm/test/DebugInfo/X86/type-units-used-enum.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/type-units-used-enum.mir @@ -0,0 +1,75 @@ +# RUN: %llc_dwarf %s -generate-type-units -o - -filetype=obj \ +# RUN: | llvm-dwarfdump -o - - \ +# RUN: | FileCheck %s + +## PR51087 +## Check that an enum type (Enum) that has a type unit generated for it and is +## used by a non-global (local) gets a skeleton DIE in the CU. +## +## $ cat test.cpp +## struct Ex { enum Enum { X }; }; +## void fun() { Ex::Enum local; } + +## 1. Check we have type units for each of the types. +# CHECK: 0x00000000: Type Unit{{.+}} name = 'Enum'{{.+}} type_signature = [[SIG_Enum:[0-9a-fx]+]] +# CHECK: 0x00000000: Type Unit{{.+}} name = 'Ex'{{.+}} type_signature = [[SIG_Ex:[0-9a-fx]+]] + +# CHECK: DW_TAG_compile_unit +## 2. Check that the type DIE reference for "local" is ok. +# CHECK: DW_AT_type ([[DIE_Enum:[0-9a-fx]+]] "Ex::Enum") +## 3. Check the skeleton DIEs for Ex and EX::Enum are emitted correctly. +# CHECK: DW_TAG_structure_type +# CHECK: DW_AT_declaration (true) +# CHECK: DW_AT_signature ([[SIG_Ex]]) +# CHECK: [[DIE_Enum]]: DW_TAG_enumeration_type +# CHECK: DW_AT_declaration (true) +# CHECK: DW_AT_signature ([[SIG_Enum]]) + + +--- | + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + define dso_local void @_Z3funv() local_unnamed_addr #0 !dbg !14 { + entry: + call void @llvm.dbg.declare(metadata i32* undef, metadata !18, metadata !DIExpression()), !dbg !19 + ret void, !dbg !20 + } + + declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!9, !10, !11, !12} + !llvm.ident = !{!13} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{!3} + !3 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum", scope: !4, file: !1, line: 1, baseType: !6, size: 32, elements: !7, identifier: "_ZTSN2Ex4EnumE") + !4 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Ex", file: !1, line: 1, size: 8, flags: DIFlagTypePassByValue, elements: !5, identifier: "_ZTS2Ex") + !5 = !{} + !6 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) + !7 = !{!8} + !8 = !DIEnumerator(name: "X", value: 0, isUnsigned: true) + !9 = !{i32 7, !"Dwarf Version", i32 5} + !10 = !{i32 2, !"Debug Info Version", i32 3} + !11 = !{i32 1, !"wchar_size", i32 4} + !12 = !{i32 7, !"uwtable", i32 1} + !13 = !{!"clang version 14.0.0"} + !14 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funv", scope: !1, file: !1, line: 2, type: !15, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !17) + !15 = !DISubroutineType(types: !16) + !16 = !{null} + !17 = !{!18} + !18 = !DILocalVariable(name: "local", scope: !14, file: !1, line: 2, type: !3) + !19 = !DILocation(line: 2, column: 23, scope: !14) + !20 = !DILocation(line: 2, column: 30, scope: !14) + +... +--- +name: _Z3funv +tracksRegLiveness: true +body: | + bb.0.entry: + RET64 debug-location !20 + +...