diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -1118,9 +1118,14 @@ ContextCU = DD->lookupCU(ContextDIE->getUnitDie()); } - // Passing null as the associated node because the abstract definition - // shouldn't be found by lookup. - AbsDef = &ContextCU->createAndAddDIE(dwarf::DW_TAG_subprogram, *ContextDIE, nullptr); + // The abstract definition can only be looked up from the associated node if + // the subprogram does not have a concrete function. + if (!ContextCU->DD->IsConcrete(SP)) + AbsDef = ContextCU->getDIE(SP); + if (!AbsDef) + AbsDef = &ContextCU->createAndAddDIE(dwarf::DW_TAG_subprogram, *ContextDIE, + nullptr); + ContextCU->applySubprogramAttributesToDefinition(SP, *AbsDef); ContextCU->addSInt(*AbsDef, dwarf::DW_AT_inline, DD->getDwarfVersion() <= 4 ? Optional() @@ -1302,7 +1307,7 @@ void DwarfCompileUnit::finishSubprogramDefinition(const DISubprogram *SP) { DIE *D = getDIE(SP); if (DIE *AbsSPDIE = getAbstractSPDies().lookup(SP)) { - if (D) + if (D && D != AbsSPDIE) // If this subprogram has an abstract definition, reference that addDIEEntry(*D, dwarf::DW_AT_abstract_origin, *AbsSPDIE); } else { diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -315,11 +315,18 @@ /// can refer to them in spite of insertions into this list. DebugLocStream DebugLocs; + using SubprogramSetVector = + SetVector, + SmallPtrSet>; + /// This is a collection of subprogram MDNodes that are processed to /// create DIEs. - SetVector, - SmallPtrSet> - ProcessedSPNodes; + SubprogramSetVector ProcessedSPNodes; + + /// The set of subprograms that have concrete functions. + /// This does not include subprograms of machine outlined functions because + /// they are created after this set is computed. + SubprogramSetVector ConcreteSPs; /// If nonnull, stores the current machine function we're processing. const MachineFunction *CurFn = nullptr; @@ -851,6 +858,14 @@ /// If the \p File has an MD5 checksum, return it as an MD5Result /// allocated in the MCContext. Optional getMD5AsBytes(const DIFile *File) const; + + /// Returns true if the subprogram has a function that will be emitted. + /// Returns false for subprograms of machine outlined functions even when + /// they do have concrete instances, but they should never have abstract + /// subprograms anyway. + bool IsConcrete(const DISubprogram *SP) const { + return ConcreteSPs.count(SP); + } }; } // end namespace llvm diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1156,6 +1156,12 @@ assert(MMI->hasDebugInfo() && "DebugInfoAvailabilty unexpectedly not initialized"); SingleCU = NumDebugCUs == 1; + + for (const auto &F : M->functions()) + if (!F.isDeclaration()) + if (const auto *SP = F.getSubprogram()) + ConcreteSPs.insert(SP); + DenseMap> GVMap; for (const GlobalVariable &Global : M->globals()) { diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -1146,6 +1146,11 @@ } } + // Try to reference the abstract origin if the subprogram is not concrete. + if (!DD->IsConcrete(SP)) + if (auto *SPDie = DU->getAbstractSPDies().lookup(SP)) + return SPDie; + // DW_TAG_inlined_subroutine may refer to this DIE. DIE &SPDie = createAndAddDIE(dwarf::DW_TAG_subprogram, *ContextDIE, SP); diff --git a/llvm/test/DebugInfo/Generic/import-inlined-declaration.ll b/llvm/test/DebugInfo/Generic/import-inlined-declaration.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/import-inlined-declaration.ll @@ -0,0 +1,72 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s + +; namespace ns { +; inline __attribute__((always_inline)) +; void foo() { int a = 4; } +; } +; +; void goo() { +; using ns::foo; +; foo(); +; } + +; Ensure that imported declarations reference the correct subprograms even if +; those subprograms are inlined. + +; CHECK: DW_TAG_compile_unit +; CHECK: DW_TAG_namespace +; CHECK: DW_AT_name ("ns") +; CHECK: [[FOO:0x.*]]: DW_TAG_subprogram +; CHECK: DW_AT_name ("foo") +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: NULL +; CHECK: DW_TAG_base_type +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("goo") +; CHECK: DW_TAG_imported_declaration +; CHECK: DW_AT_import ([[FOO]]) +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_AT_abstract_origin ([[FOO]] +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL + +; Function Attrs: mustprogress noinline optnone uwtable +define dso_local void @_Z3goov() !dbg !4 { +entry: + %a.i = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %a.i, metadata !16, metadata !DIExpression()), !dbg !18 + store i32 4, i32* %a.i, align 4, !dbg !18 + ret void, !dbg !20 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!10, !11, !12, !13, !14} +!llvm.ident = !{!15} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, imports: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "imported-inlined-declaration.cpp", directory: "") +!2 = !{!3} +!3 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !4, entity: !8, file: !1, line: 7) +!4 = distinct !DISubprogram(name: "goo", linkageName: "_Z3goov", scope: !1, file: !1, line: 6, type: !5, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !7) +!5 = !DISubroutineType(types: !6) +!6 = !{null} +!7 = !{} +!8 = distinct !DISubprogram(name: "foo", linkageName: "_ZN2ns3fooEv", scope: !9, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !7) +!9 = !DINamespace(name: "ns", scope: null) +!10 = !{i32 7, !"Dwarf Version", i32 4} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"wchar_size", i32 4} +!13 = !{i32 7, !"uwtable", i32 1} +!14 = !{i32 7, !"frame-pointer", i32 2} +!15 = !{!"clang version 14.0.0"} +!16 = !DILocalVariable(name: "a", scope: !8, file: !1, line: 3, type: !17) +!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!18 = !DILocation(line: 3, column: 18, scope: !8, inlinedAt: !19) +!19 = distinct !DILocation(line: 8, column: 2, scope: !4) +!20 = !DILocation(line: 9, column: 1, scope: !4) diff --git a/llvm/test/DebugInfo/Generic/inlined-local-type.ll b/llvm/test/DebugInfo/Generic/inlined-local-type.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/inlined-local-type.ll @@ -0,0 +1,125 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s + +; inline __attribute__((always_inline)) +; int removed() { struct A {int i;}; struct A a; return a.i++; } +; +; __attribute__((always_inline)) +; int not_removed() { struct B {int i;}; struct B b; return b.i++; } +; +; int foo() { return removed() + not_removed(); }} + +; Ensure that function-local types have the correct subprogram parent even if +; those subprograms are inlined. + +; CHECK: DW_TAG_compile_unit +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_abstract_origin ({{0x.*}} "not_removed") +; CHECK: DW_TAG_variable +; CHECK: [[B:0x.*]]: DW_TAG_structure_type +; CHECK: DW_AT_name ("B") +; CHECK: DW_TAG_member +; CHECK: NULL +; CHECK: NULL +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("removed") +; CHECK: [[A:0x.*]]: DW_TAG_structure_type +; CHECK: DW_AT_name ("A") +; CHECK: DW_TAG_member +; CHECK: NULL +; CHECK: DW_TAG_variable +; CHECK: DW_AT_type ([[A]] "A") +; CHECK: NULL +; CHECK: DW_TAG_base_type +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("not_removed") +; CHECK: DW_TAG_variable +; CHECK: DW_AT_type ([[B]] "B") +; CHECK: NULL +; CHECK: DW_TAG_subprogram +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL + +%struct.B = type { i32 } +%struct.A = type { i32 } + +define dso_local i32 @not_removed() !dbg !12 { + %1 = alloca %struct.B, align 4 + call void @llvm.dbg.declare(metadata %struct.B* %1, metadata !18, metadata !DIExpression()), !dbg !22 + %2 = getelementptr inbounds %struct.B, %struct.B* %1, i32 0, i32 0, !dbg !23 + %3 = load i32, i32* %2, align 4, !dbg !24 + %4 = add nsw i32 %3, 1, !dbg !24 + store i32 %4, i32* %2, align 4, !dbg !24 + ret i32 %3, !dbg !25 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +define dso_local i32 @foo() !dbg !26 { + %1 = alloca %struct.A, align 4 + %2 = alloca %struct.B, align 4 + call void @llvm.dbg.declare(metadata %struct.A* %1, metadata !27, metadata !DIExpression()), !dbg !32 + %3 = getelementptr inbounds %struct.A, %struct.A* %1, i32 0, i32 0, !dbg !34 + %4 = load i32, i32* %3, align 4, !dbg !35 + %5 = add nsw i32 %4, 1, !dbg !35 + store i32 %5, i32* %3, align 4, !dbg !35 + call void @llvm.dbg.declare(metadata %struct.B* %2, metadata !18, metadata !DIExpression()), !dbg !36 + %6 = getelementptr inbounds %struct.B, %struct.B* %2, i32 0, i32 0, !dbg !38 + %7 = load i32, i32* %6, align 4, !dbg !39 + %8 = add nsw i32 %7, 1, !dbg !39 + store i32 %8, i32* %6, align 4, !dbg !39 + %9 = add nsw i32 %4, %7, !dbg !40 + ret i32 %9, !dbg !41 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8, !9, !10} +!llvm.ident = !{!11} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "inlined-local-type.cpp", directory: "") +!2 = !{i32 7, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 1, !"branch-target-enforcement", i32 0} +!6 = !{i32 1, !"sign-return-address", i32 0} +!7 = !{i32 1, !"sign-return-address-all", i32 0} +!8 = !{i32 1, !"sign-return-address-with-bkey", i32 0} +!9 = !{i32 7, !"uwtable", i32 1} +!10 = !{i32 7, !"frame-pointer", i32 1} +!11 = !{!"clang version 14.0.0"} +!12 = distinct !DISubprogram(name: "not_removed", scope: !13, file: !13, line: 5, type: !14, scopeLine: 5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !17) +!13 = !DIFile(filename: "inlined-local-type.cpp", directory: "") +!14 = !DISubroutineType(types: !15) +!15 = !{!16} +!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!17 = !{} +!18 = !DILocalVariable(name: "b", scope: !12, file: !13, line: 5, type: !19) +!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "B", scope: !12, file: !13, line: 5, size: 32, elements: !20) +!20 = !{!21} +!21 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !19, file: !13, line: 5, baseType: !16, size: 32) +!22 = !DILocation(line: 5, column: 49, scope: !12) +!23 = !DILocation(line: 5, column: 61, scope: !12) +!24 = !DILocation(line: 5, column: 62, scope: !12) +!25 = !DILocation(line: 5, column: 52, scope: !12) +!26 = distinct !DISubprogram(name: "foo", scope: !13, file: !13, line: 7, type: !14, scopeLine: 7, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !17) +!27 = !DILocalVariable(name: "a", scope: !28, file: !13, line: 2, type: !29) +!28 = distinct !DISubprogram(name: "removed", scope: !13, file: !13, line: 2, type: !14, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !17) +!29 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", scope: !28, file: !13, line: 2, size: 32, elements: !30) +!30 = !{!31} +!31 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !29, file: !13, line: 2, baseType: !16, size: 32) +!32 = !DILocation(line: 2, column: 45, scope: !28, inlinedAt: !33) +!33 = distinct !DILocation(line: 7, column: 20, scope: !26) +!34 = !DILocation(line: 2, column: 57, scope: !28, inlinedAt: !33) +!35 = !DILocation(line: 2, column: 58, scope: !28, inlinedAt: !33) +!36 = !DILocation(line: 5, column: 49, scope: !12, inlinedAt: !37) +!37 = distinct !DILocation(line: 7, column: 32, scope: !26) +!38 = !DILocation(line: 5, column: 61, scope: !12, inlinedAt: !37) +!39 = !DILocation(line: 5, column: 62, scope: !12, inlinedAt: !37) +!40 = !DILocation(line: 7, column: 30, scope: !26) +!41 = !DILocation(line: 7, column: 13, scope: !26) diff --git a/llvm/test/DebugInfo/Generic/inlined-static-var.ll b/llvm/test/DebugInfo/Generic/inlined-static-var.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/inlined-static-var.ll @@ -0,0 +1,94 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s + +; inline __attribute__((always_inline)) +; int removed() { static int A; return A++; } +; +; __attribute__((always_inline)) +; int not_removed() { static int B; return B++; } +; +; int foo() { return removed() + not_removed(); } + +; Ensure that global variables belong to the correct subprograms even if those +; subprograms are inlined. + +; CHECK: DW_TAG_compile_unit +; CHECK: DW_TAG_subprogram +; TODO: Place the variable in the abstract DIE rather than this concrete DIE. +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("B") +; CHECK: NULL +; CHECK: DW_TAG_base_type +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("removed") +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("A") +; CHECK: NULL +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("not_removed") +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("foo") +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_TAG_inlined_subroutine +; CHECK: NULL +; CHECK: NULL + +$_ZZ7removedvE1A = comdat any + +@_ZZ11not_removedvE1A = internal global i32 0, align 4, !dbg !0 +@_ZZ7removedvE1A = linkonce_odr dso_local global i32 0, comdat, align 4, !dbg !10 + +define dso_local i32 @_Z11not_removedv() !dbg !2 { + %1 = load i32, i32* @_ZZ11not_removedvE1A, align 4, !dbg !24 + %2 = add nsw i32 %1, 1, !dbg !24 + store i32 %2, i32* @_ZZ11not_removedvE1A, align 4, !dbg !24 + ret i32 %1, !dbg !25 +} + +define dso_local i32 @_Z3foov() !dbg !26 { + %1 = load i32, i32* @_ZZ7removedvE1A, align 4, !dbg !27 + %2 = add nsw i32 %1, 1, !dbg !27 + store i32 %2, i32* @_ZZ7removedvE1A, align 4, !dbg !27 + %3 = load i32, i32* @_ZZ11not_removedvE1A, align 4, !dbg !29 + %4 = add nsw i32 %3, 1, !dbg !29 + store i32 %4, i32* @_ZZ11not_removedvE1A, align 4, !dbg !29 + %5 = add nsw i32 %1, %3, !dbg !31 + ret i32 %5, !dbg !32 +} + +!llvm.dbg.cu = !{!7} +!llvm.module.flags = !{!14, !15, !16, !17, !18, !19, !20, !21, !22} +!llvm.ident = !{!23} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "B", scope: !2, file: !3, line: 5, type: !6, isLocal: true, isDefinition: true) +!2 = distinct !DISubprogram(name: "not_removed", linkageName: "_Z11not_removedv", scope: !3, file: !3, line: 5, type: !4, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !13) +!3 = !DIFile(filename: "example.cpp", directory: "") +!4 = !DISubroutineType(types: !5) +!5 = !{!6} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !8, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None) +!8 = !DIFile(filename: "example.cpp", directory: "") +!9 = !{!0, !10} +!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression()) +!11 = distinct !DIGlobalVariable(name: "A", scope: !12, file: !3, line: 2, type: !6, isLocal: false, isDefinition: true) +!12 = distinct !DISubprogram(name: "removed", linkageName: "_Z7removedv", scope: !3, file: !3, line: 2, type: !4, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !13) +!13 = !{} +!14 = !{i32 7, !"Dwarf Version", i32 4} +!15 = !{i32 2, !"Debug Info Version", i32 3} +!16 = !{i32 1, !"wchar_size", i32 4} +!17 = !{i32 1, !"branch-target-enforcement", i32 0} +!18 = !{i32 1, !"sign-return-address", i32 0} +!19 = !{i32 1, !"sign-return-address-all", i32 0} +!20 = !{i32 1, !"sign-return-address-with-bkey", i32 0} +!21 = !{i32 7, !"uwtable", i32 1} +!22 = !{i32 7, !"frame-pointer", i32 1} +!23 = !{!"clang version 14.0.0"} +!24 = !DILocation(line: 5, column: 43, scope: !2) +!25 = !DILocation(line: 5, column: 35, scope: !2) +!26 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 7, type: !4, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !13) +!27 = !DILocation(line: 2, column: 39, scope: !12, inlinedAt: !28) +!28 = distinct !DILocation(line: 7, column: 20, scope: !26) +!29 = !DILocation(line: 5, column: 43, scope: !2, inlinedAt: !30) +!30 = distinct !DILocation(line: 7, column: 32, scope: !26) +!31 = !DILocation(line: 7, column: 30, scope: !26) +!32 = !DILocation(line: 7, column: 13, scope: !26)