diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h @@ -160,6 +160,12 @@ unsigned verifyIndex(StringRef Name, DWARFSectionKind SectionKind, StringRef Index); + /// Verifies that definition subprograms cannot be nested within structures + /// such as classes and structs. + /// + /// \returns Number of errors that occurred during verification. + unsigned verifyDebugInfoSubprogram(const DWARFDie &Die); + /// Verifies that a call site entry is nested within a subprogram with a /// DW_AT_call attribute. /// diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -196,7 +196,6 @@ /// variables in this scope then create and insert DIEs for these /// variables. DIE &updateSubprogramScopeDIE(const DISubprogram *SP); - DIE &updateSubprogramScopeDIEImpl(const DISubprogram *SP, DIE *SPDie); void constructScopeDIE(LexicalScope *Scope, DIE &ParentScopeDIE); 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 @@ -458,15 +458,6 @@ addLabelDelta(D, dwarf::DW_AT_high_pc, End, Begin); } -// Find DIE for the given subprogram and attach appropriate DW_AT_low_pc -// and DW_AT_high_pc attributes. If there are global variables in this -// scope then create and insert DIEs for these variables. -DIE &DwarfCompileUnit::updateSubprogramScopeDIE(const DISubprogram *SP) { - DIE *SPDie = getOrCreateSubprogramDIE(SP, includeMinimalInlineScopes()); - auto *ContextCU = static_cast(SPDie->getUnit()); - return ContextCU->updateSubprogramScopeDIEImpl(SP, SPDie); -} - // Add info for Wasm-global-based relocation. // 'GlobalIndex' is used for split dwarf, which currently relies on a few // assumptions that are not guaranteed in a formal way but work in practice. @@ -500,9 +491,12 @@ } } -DIE &DwarfCompileUnit::updateSubprogramScopeDIEImpl(const DISubprogram *SP, - DIE *SPDie) { +// Find DIE for the given subprogram and attach appropriate DW_AT_low_pc +// and DW_AT_high_pc attributes. If there are global variables in this +// scope then create and insert DIEs for these variables. +DIE &DwarfCompileUnit::updateSubprogramScopeDIE(const DISubprogram *SP) { SmallVector BB_List; + DIE *SPDie = getOrCreateSubprogramDIE(SP, includeMinimalInlineScopes()); // If basic block sections are on, ranges for each basic block section has // to be emitted separately. for (const auto &R : Asm->MBBSectionRanges) @@ -1055,7 +1049,6 @@ DIE &DwarfCompileUnit::constructSubprogramScopeDIE(const DISubprogram *Sub, LexicalScope *Scope) { DIE &ScopeDIE = updateSubprogramScopeDIE(Sub); - auto *ContextCU = static_cast(ScopeDIE.getUnit()); if (Scope) { assert(!Scope->getInlinedAt()); @@ -1063,10 +1056,8 @@ // Collect lexical scope children first. // ObjectPointer might be a local (non-argument) local variable if it's a // block's synthetic this pointer. - if (DIE *ObjectPointer = - ContextCU->createAndAddScopeChildren(Scope, ScopeDIE)) - ContextCU->addDIEEntry(ScopeDIE, dwarf::DW_AT_object_pointer, - *ObjectPointer); + if (DIE *ObjectPointer = createAndAddScopeChildren(Scope, ScopeDIE)) + addDIEEntry(ScopeDIE, dwarf::DW_AT_object_pointer, *ObjectPointer); } // If this is a variadic function, add an unspecified parameter. diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -227,6 +227,7 @@ } } + NumUnitErrors += verifyDebugInfoSubprogram(Die); NumUnitErrors += verifyDebugInfoCallSite(Die); } @@ -265,6 +266,31 @@ return NumUnitErrors; } +unsigned DWARFVerifier::verifyDebugInfoSubprogram(const DWARFDie &Die) { + if (!Die.isSubprogramDIE() || Die.find(DW_AT_declaration)) + return 0; + + // Typically, we nest defined subprograms within modules, namespaces, + // subprograms, or similar entries. + DWARFDie Parent = Die.getParent(); + switch (Parent.getTag()) { + case DW_TAG_array_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + case DW_TAG_string_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_set_type: + case DW_TAG_subrange_type: + error() << "Definition subprograms cannot be nested within " + << TagString(Parent.getTag()) << ":"; + Die.dump(OS); + return 1; + default: + return 0; + } +} + unsigned DWARFVerifier::verifyDebugInfoCallSite(const DWARFDie &Die) { if (Die.getTag() != DW_TAG_call_site && Die.getTag() != DW_TAG_GNU_call_site) return 0; diff --git a/llvm/test/DebugInfo/Generic/cross-cu-inlining-2.ll b/llvm/test/DebugInfo/Generic/cross-cu-inlining-2.ll --- a/llvm/test/DebugInfo/Generic/cross-cu-inlining-2.ll +++ b/llvm/test/DebugInfo/Generic/cross-cu-inlining-2.ll @@ -21,19 +21,22 @@ !2 = !{} !3 = distinct !DICompileUnit(language: DW_LANG_Swift, file: !4, isOptimized: true, runtimeVersion: 5, emissionKind: FullDebug, imports: !2) !4 = !DIFile(filename: "B.swift", directory: "") +!5 = !DISubprogram(name: "foo", scope: !25, file: !1, line: 116, type: !27, scopeLine: 116, flags: DIFlagPrototyped, spFlags: 0) +!6 = !DISubprogram(name: "init", scope: !31, file: !30, type: !27, flags: DIFlagPrototyped, spFlags: 0) +!7 = !DISubprogram(name: "bar", scope: !31, file: !4, line: 21, type: !27, scopeLine: 21, flags: DIFlagPrototyped, spFlags: 0) !10 = !{i32 7, !"Dwarf Version", i32 4} !11 = !{i32 2, !"Debug Info Version", i32 3} -!24 = distinct !DISubprogram(name: "foo", scope: !25, file: !1, line: 116, type: !27, scopeLine: 116, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!24 = distinct !DISubprogram(name: "foo", scope: !25, file: !1, line: 116, type: !27, scopeLine: 116, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2, declaration: !5) !25 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "FooTy", scope: !26, file: !1, size: 64, elements: !2) !26 = !DIModule(scope: null, name: "Mod") !27 = !DISubroutineType(types: !2) !28 = !DILocation(line: 0, scope: !29, inlinedAt: !32) -!29 = distinct !DISubprogram(name: "init", scope: !31, file: !30, type: !27, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!29 = distinct !DISubprogram(name: "init", scope: !31, file: !30, type: !27, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2, declaration: !6) !30 = !DIFile(filename: "", directory: "") !31 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "ModController", scope: !26, file: !4, size: 64, elements: !2, runtimeLang: DW_LANG_Swift) !32 = !DILocation(line: 117, column: 29, scope: !33) !33 = distinct !DILexicalBlock(scope: !24, file: !1, line: 116, column: 68) -!34 = distinct !DISubprogram(name: "bar", scope: !31, file: !4, line: 21, type: !27, scopeLine: 21, unit: !3, retainedNodes: !2) +!34 = distinct !DISubprogram(name: "bar", scope: !31, file: !4, line: 21, type: !27, scopeLine: 21, unit: !3, retainedNodes: !2, declaration: !7) !35 = !DILocation(line: 0, scope: !36, inlinedAt: !37) !36 = distinct !DISubprogram(name: "goo", scope: !26, file: !30, type: !27, unit: !3, retainedNodes: !2) !37 = !DILocation(line: 21, column: 26, scope: !34) @@ -41,26 +44,39 @@ ; CHECK: DW_TAG_compile_unit ; CHECK: DW_TAG_module ; CHECK: DW_TAG_structure_type -; CHECK: [[INIT:0x.*]]: DW_TAG_subprogram -; CHECK; DW_AT_name [DW_FORM_strp] ({{.*}} = "init") ; CHECK: DW_TAG_subprogram -; CHECK; DW_AT_name [DW_FORM_strp] ({{.*}} = "bar") -; CHECK: DW_TAG_inlined_subroutine -; CHECK: DW_AT_abstract_origin [DW_FORM_ref_addr] (0x00000000[[GOO:.*]] "goo") -; CHECK: NULL +; CHECK; DW_AT_name [DW_FORM_strp] ({{.*}} = "init") +; CHECK: DW_AT_declaration [DW_FORM_flag_present] (true) +; CHECK: 0x[[BAR_REF_ADDR:.*]]: DW_TAG_subprogram +; CHECK: DW_AT_name [DW_FORM_strp] ({{.*}} = "bar") +; CHECK: DW_AT_declaration [DW_FORM_flag_present] (true) ; CHECK: NULL ; CHECK: DW_TAG_structure_type ; CHECK: DW_TAG_subprogram ; CHECK; DW_AT_name [DW_FORM_strp] ({{.*}} = "foo") -; CHECK: DW_TAG_inlined_subroutine -; CHECK: DW_AT_abstract_origin [DW_FORM_ref4] ({{.*}} => {[[INIT]]} "init") -; CHECK: NULL +; CHECK: DW_AT_declaration [DW_FORM_flag_present] (true) ; CHECK: NULL ; CHECK: NULL +; CHECK: [[INIT:0x.*]]: DW_TAG_subprogram +; CHECK; DW_AT_name [DW_FORM_strp] ({{.*}} = "init") +; CHECK: DW_TAG_subprogram +; CHECK; DW_AT_name [DW_FORM_strp] ({{.*}} = "foo") +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_AT_abstract_origin [DW_FORM_ref4] ({{.*}} => {[[INIT]]} "init") +; CHECK: NULL ; CHECK: NULL ; CHECK: DW_TAG_compile_unit +; CHECK: DW_AT_low_pc [DW_FORM_addr] ([[LOW_PC:0x.*]] ".text") +; CHECK: DW_AT_high_pc [DW_FORM_data4] ([[HIGH_PC:0x.*]]) ; CHECK: DW_TAG_module -; CHECK: 0x[[GOO]]: DW_TAG_subprogram -; CHECK; DW_AT_name [DW_FORM_strp] ({{.*}} = "goo") +; CHECK: [[GOO:0x.*]]: DW_TAG_subprogram +; CHECK: DW_AT_name [DW_FORM_strp] ({{.*}} = "goo") +; CHECK: NULL +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_specification [DW_FORM_ref_addr] (0x00000000[[BAR_REF_ADDR]] "bar") +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x{{.*}} => {[[GOO]]} "goo") +; CHECK: DW_AT_low_pc [DW_FORM_addr] ([[LOW_PC]] ".text") +; CHECK: DW_AT_high_pc [DW_FORM_data4] ([[HIGH_PC]]) ; CHECK: NULL ; CHECK: NULL diff --git a/llvm/test/DebugInfo/Generic/cross-cu-inlining-ranges.ll b/llvm/test/DebugInfo/Generic/cross-cu-inlining-ranges.ll --- a/llvm/test/DebugInfo/Generic/cross-cu-inlining-ranges.ll +++ b/llvm/test/DebugInfo/Generic/cross-cu-inlining-ranges.ll @@ -47,15 +47,18 @@ !7 = !DISubroutineType(types: !8) !8 = !{} !9 = !DILocation(line: 0, scope: !10, inlinedAt: !13) -!10 = distinct !DISubprogram(name: "init", scope: !12, file: !11, type: !7, spFlags: DISPFlagDefinition, unit: !0) +!10 = distinct !DISubprogram(name: "init", scope: !12, file: !11, type: !7, spFlags: DISPFlagDefinition, unit: !0, declaration: !22) !11 = !DIFile(filename: "", directory: "") !12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Type", file: !3, runtimeLang: DW_LANG_Swift, identifier: "Type") !13 = !DILocation(line: 0, scope: !14) !14 = distinct !DILexicalBlock(scope: !6, file: !1) -!15 = distinct !DISubprogram(name: "bar", scope: !12, type: !7, spFlags: DISPFlagDefinition, unit: !2) +!15 = distinct !DISubprogram(name: "bar", scope: !12, type: !7, spFlags: DISPFlagDefinition, unit: !2, declaration: !23) !16 = !DILocation(line: 0, scope: !17, inlinedAt: !19) !17 = distinct !DILexicalBlock(scope: !18, file: !3) -!18 = distinct !DISubprogram(name: "inlined_baz", scope: !12, file: !3, type: !7, spFlags: DISPFlagDefinition, unit: !2) +!18 = distinct !DISubprogram(name: "inlined_baz", scope: !12, file: !3, type: !7, spFlags: DISPFlagDefinition, unit: !2, declaration: !24) !19 = !DILocation(line: 0, scope: !20) !20 = distinct !DILexicalBlock(scope: !15, file: !3) !21 = !DILocation(line: 0, scope: !15) +!22 = !DISubprogram(name: "init", scope: !12, file: !11, type: !7, flags: DIFlagPrototyped, spFlags: 0) +!23 = !DISubprogram(name: "bar", scope: !12, type: !7, flags: DIFlagPrototyped, spFlags: 0) +!24 = !DISubprogram(name: "inlined_baz", scope: !12, file: !3, type: !7, flags: DIFlagPrototyped, spFlags: 0) diff --git a/llvm/test/tools/llvm-dwarfdump/X86/debug_info_subprogram.s b/llvm/test/tools/llvm-dwarfdump/X86/debug_info_subprogram.s new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/X86/debug_info_subprogram.s @@ -0,0 +1,218 @@ +# RUN: llvm-mc -triple x86_64-pc-linux %s -filetype=obj -o %t.o +# RUN: not llvm-dwarfdump -verify %t.o 2>&1 | FileCheck %s + +# CHECK: Definition subprograms cannot be nested within DW_TAG_structure_type: +# CHECK: DW_TAG_subprogram +# CHECK: DW_AT_low_pc +# CHECK: DW_AT_name ("bar") +# CHECK: Errors detected. + +# The Rust source: +# pub struct Foo; +# impl Foo { +# pub fn bar() {} +# } + +# The IR source: +# ; ModuleID = 'foo.7e668e4a-cgu.0' +# source_filename = "foo.7e668e4a-cgu.0" +# 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" +# +# ; foo::Foo::bar +# ; Function Attrs: nonlazybind uwtable +# define void @_ZN3foo3Foo3bar17h32d65c44145019c8E() unnamed_addr #0 !dbg !6 { +# start: +# ret void, !dbg !14 +# } +# +# attributes #0 = { nonlazybind uwtable "probe-stack"="__rust_probestack" "target-cpu"="x86-64" } +# +# !llvm.module.flags = !{!0, !1, !2, !3} +# !llvm.dbg.cu = !{!4} +# +# !0 = !{i32 7, !"PIC Level", i32 2} +# !1 = !{i32 2, !"RtLibUseGOT", i32 1} +# !2 = !{i32 2, !"Dwarf Version", i32 4} +# !3 = !{i32 2, !"Debug Info Version", i32 3} +# !4 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !5, producer: "clang LLVM (rustc version 1.69.0 (84c898d65 2023-04-16))", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false) +# !5 = !DIFile(filename: "foo.rs/@/foo.7e668e4a-cgu.0", directory: "/tmp") +# !6 = distinct !DISubprogram(name: "bar", linkageName: "_ZN3foo3Foo3bar17h32d65c44145019c8E", scope: !8, file: !7, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !4, templateParams: !11, retainedNodes: !11) +# !7 = !DIFile(filename: "foo.rs", directory: "/tmp", checksumkind: CSK_MD5, checksum: "427d9b572596c2f310b6185a1781f222") +# !8 = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", scope: !10, file: !9, align: 8, elements: !11, identifier: "a8fd67db4e906c9c4aceea39cb8b0f61") +# !9 = !DIFile(filename: "", directory: "") +# !10 = !DINamespace(name: "foo", scope: null) +# !11 = !{} +# !12 = !DISubroutineType(types: !13) +# !13 = !{null} +# !14 = !DILocation(line: 6, column: 6, scope: !6) + + .text + .file "foo.7e668e4a-cgu.0" + .section .text._ZN3foo3Foo3bar17h32d65c44145019c8E,"ax",@progbits + .globl _ZN3foo3Foo3bar17h32d65c44145019c8E + .p2align 4, 0x90 + .type _ZN3foo3Foo3bar17h32d65c44145019c8E,@function +_ZN3foo3Foo3bar17h32d65c44145019c8E: +.Lfunc_begin0: + .file 1 "/tmp" "foo.rs" + .loc 1 4 0 + .cfi_startproc + .loc 1 6 6 prologue_end + retq +.Ltmp0: +.Lfunc_end0: + .size _ZN3foo3Foo3bar17h32d65c44145019c8E, .Lfunc_end0-_ZN3foo3Foo3bar17h32d65c44145019c8E + .cfi_endproc + + .section .debug_abbrev,"",@progbits + .byte 1 + .byte 17 + .byte 1 + .byte 37 + .byte 14 + .byte 19 + .byte 5 + .byte 3 + .byte 14 + .byte 16 + .byte 23 + .byte 27 + .byte 14 + .ascii "\264B" + .byte 25 + .byte 17 + .byte 1 + .byte 18 + .byte 6 + .byte 0 + .byte 0 + .byte 2 + .byte 57 + .byte 1 + .byte 3 + .byte 14 + .byte 0 + .byte 0 + .byte 3 + .byte 19 + .byte 1 + .byte 3 + .byte 14 + .byte 11 + .byte 11 + .ascii "\210\001" + .byte 15 + .byte 0 + .byte 0 + .byte 4 + .byte 46 + .byte 0 + .byte 17 + .byte 1 + .byte 18 + .byte 6 + .byte 64 + .byte 24 + .byte 110 + .byte 14 + .byte 3 + .byte 14 + .byte 58 + .byte 11 + .byte 59 + .byte 11 + .byte 63 + .byte 25 + .byte 0 + .byte 0 + .byte 0 + .section .debug_info,"",@progbits +.Lcu_begin0: + .long .Ldebug_info_end0-.Ldebug_info_start0 +.Ldebug_info_start0: + .short 4 + .long .debug_abbrev + .byte 8 + .byte 1 + .long .Linfo_string0 + .short 28 + .long .Linfo_string1 + .long .Lline_table_start0 + .long .Linfo_string2 + + .quad .Lfunc_begin0 + .long .Lfunc_end0-.Lfunc_begin0 + .byte 2 + .long .Linfo_string3 + .byte 3 + .long .Linfo_string4 + .byte 0 + .byte 1 + .byte 4 + .quad .Lfunc_begin0 + .long .Lfunc_end0-.Lfunc_begin0 + .byte 1 + .byte 87 + .long .Linfo_string5 + .long .Linfo_string6 + .byte 1 + .byte 4 + + .byte 0 + .byte 0 + .byte 0 +.Ldebug_info_end0: + .section .text._ZN3foo3Foo3bar17h32d65c44145019c8E,"ax",@progbits +.Lsec_end0: + .section .debug_aranges,"",@progbits + .long 44 + .short 2 + .long .Lcu_begin0 + .byte 8 + .byte 0 + .zero 4,255 + .quad .Lfunc_begin0 + .quad .Lsec_end0-.Lfunc_begin0 + .quad 0 + .quad 0 + .section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "clang LLVM (rustc version 1.69.0 (84c898d65 2023-04-16))" +.Linfo_string1: + .asciz "foo.rs/@/foo.7e668e4a-cgu.0" +.Linfo_string2: + .asciz "/tmp" +.Linfo_string3: + .asciz "foo" +.Linfo_string4: + .asciz "Foo" +.Linfo_string5: + .asciz "_ZN3foo3Foo3bar17h32d65c44145019c8E" +.Linfo_string6: + .asciz "bar" + .section .debug_pubnames,"",@progbits + .long .LpubNames_end0-.LpubNames_start0 +.LpubNames_start0: + .short 2 + .long .Lcu_begin0 + .long 82 + .long 42 + .asciz "foo" + .long 54 + .asciz "bar" + .long 0 +.LpubNames_end0: + .section .debug_pubtypes,"",@progbits + .long .LpubTypes_end0-.LpubTypes_start0 +.LpubTypes_start0: + .short 2 + .long .Lcu_begin0 + .long 82 + .long 47 + .asciz "Foo" + .long 0 +.LpubTypes_end0: + .section ".note.GNU-stack","",@progbits + .section .debug_line,"",@progbits +.Lline_table_start0: