diff --git a/llvm/test/tools/llvm-objdump/AMDGPU/source-lines.ll b/llvm/test/tools/llvm-objdump/AMDGPU/source-lines.ll --- a/llvm/test/tools/llvm-objdump/AMDGPU/source-lines.ll +++ b/llvm/test/tools/llvm-objdump/AMDGPU/source-lines.ll @@ -5,6 +5,7 @@ ; Prologue. ; LINE: source_lines_test: +; LINE-NEXT: ; source_lines_test(): ; LINE-NEXT: ; {{.*}}source-lines.cl:1 ; Kernel. ; LINE: v_mov_b32_e32 v{{[0-9]+}}, 0x777 diff --git a/llvm/test/tools/llvm-objdump/Hexagon/source-interleave-hexagon.ll b/llvm/test/tools/llvm-objdump/Hexagon/source-interleave-hexagon.ll --- a/llvm/test/tools/llvm-objdump/Hexagon/source-interleave-hexagon.ll +++ b/llvm/test/tools/llvm-objdump/Hexagon/source-interleave-hexagon.ll @@ -66,6 +66,7 @@ !22 = !DILocation(line: 8, column: 13, scope: !14) !23 = !DILocation(line: 8, column: 3, scope: !14) ; LINES: main: +; LINES-NEXT: main(): ; LINES-NEXT: SRC_COMPDIR/source-interleave-hexagon.c:6 ; SOURCE: main: diff --git a/llvm/test/tools/llvm-objdump/X86/source-interleave-function-from-debug.test b/llvm/test/tools/llvm-objdump/X86/source-interleave-function-from-debug.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/X86/source-interleave-function-from-debug.test @@ -0,0 +1,108 @@ +;; Verify that llvm-objdump -l also prints the function name in disassembly +;; output, getting it from the debug info. + +; RUN: llc < %s -o %t.o -filetype=obj -mtriple=x86_64-unknown-linux-gnu +; RUN: llvm-objdump -dl %t.o | FileCheck %s --check-prefixes=CHECK,CHECK-NO-DEMANGLE + +; RUN: llc < %s -o %t.o -filetype=obj -mtriple=x86_64-unknown-linux-gnu +; RUN: llvm-objdump -dlC %t.o | FileCheck %s --check-prefixes=CHECK,CHECK-DEMANGLE + +; CHECK: 0000000000000000 foo: +; CHECK-NEXT: ; foo(): +; CHECK-NEXT: ; /tmp/src.cc:1 +; CHECK-NEXT: 0: b8 05 00 00 00 movl $5, %eax +; CHECK-NEXT: 5: c3 retq + +; CHECK-NO-DEMANGLE: 0000000000000010 _ZN3xyz3barEv: +; CHECK-NO-DEMANGLE-NEXT: ; _ZN3xyz3barEv(): +; CHECK-DEMANGLE: 0000000000000010 xyz::bar(): +; CHECK-DEMANGLE-NEXT: ; xyz::bar(): + +; CHECK-NEXT: ; /tmp/src.cc:3 +; CHECK-NEXT: 10: b8 0a 00 00 00 movl $10, %eax +; CHECK-NEXT: 15: c3 retq + +; CHECK-NO-DEMANGLE: 0000000000000020 _ZN3xyz3bazEv: +; CHECK-NO-DEMANGLE-NEXT: ; _ZN3xyz3bazEv(): +; CHECK-DEMANGLE: 0000000000000020 xyz::baz(): +; CHECK-DEMANGLE-NEXT: ; xyz::baz(): + +; CHECK-NEXT: ; /tmp/src.cc:3 +; CHECK-NEXT: 20: b8 14 00 00 00 movl $20, %eax +; CHECK-NEXT: 25: c3 retq + +;; When symbol information is missing, we can get function names from debug +;; info. The IR is intentionally doctored to have different names in debug info +;; for the test case here. +; RUN: llvm-strip %t.o -N foo -N _ZN3xyz3barEv -N _ZN3xyz3bazEv -o %t-stripped.o +; RUN: llvm-objdump -dlC %t-stripped.o | FileCheck %s --check-prefix=STRIPPED + +; STRIPPED: 0000000000000000 .text: +; STRIPPED-NEXT: ; Function1(): +; STRIPPED-NEXT: ; /tmp/src.cc:1 +; STRIPPED-NEXT: 0: b8 05 00 00 00 movl $5, %eax +; STRIPPED-NEXT: 5: c3 retq + +; STRIPPED: ; xyz::bar(): +; STRIPPED-NEXT: ; /tmp/src.cc:3 +; STRIPPED-NEXT: 10: b8 0a 00 00 00 movl $10, %eax +; STRIPPED-NEXT: 15: c3 retq + +; STRIPPED: ; xyz::baz(): +; STRIPPED-NEXT: ; /tmp/src.cc:3 +; STRIPPED-NEXT: 20: b8 14 00 00 00 movl $20, %eax +; STRIPPED-NEXT: 25: c3 retq + +;; IR adapted from: +;; $ cat /tmp/src.cc +;; extern "C" int foo() { return 5; }; +;; namespace xyz { +;; int bar() { return 10; } int baz() { return 20; } +;; } // namespace xyz +;; $ clang++ -g -c /tmp/src.cc -S -emit-llvm +;; Note: bar() and baz() intentionally written on the same line. + +; ModuleID = '/tmp/src.cc' +source_filename = "/tmp/src.cc" +target triple = "x86_64-unknown-linux-gnu" + +define dso_local i32 @foo() #0 !dbg !7 { +entry: + ret i32 5, !dbg !12 +} + +define dso_local i32 @_ZN3xyz3barEv() #0 !dbg !13 { +entry: + ret i32 10, !dbg !15 +} + +define dso_local i32 @_ZN3xyz3bazEv() #0 !dbg !16 { +entry: + ret i32 20, !dbg !17 +} + +attributes #0 = { "frame-pointer"="none" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang trunk", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +; Note: triggers a bad DILineInfo. We still print "Function1()". +!1 = !DIFile(filename: "", directory: "") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang trunk)"} +!7 = distinct !DISubprogram(name: "Function1", scope: !8, file: !8, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!8 = !DIFile(filename: "/tmp/src.cc", directory: "") +!9 = !DISubroutineType(types: !10) +!10 = !{!11} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DILocation(line: 1, column: 24, scope: !7) +!13 = distinct !DISubprogram(name: "bar", linkageName: "_ZN3xyz3barEv", scope: !14, file: !8, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!14 = !DINamespace(name: "xyz", scope: null) +!15 = !DILocation(line: 3, column: 13, scope: !13) +!16 = distinct !DISubprogram(name: "baz", linkageName: "_ZN3xyz3bazEv", scope: !14, file: !8, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!17 = !DILocation(line: 3, column: 38, scope: !16) diff --git a/llvm/test/tools/llvm-objdump/X86/source-interleave-x86_64.test b/llvm/test/tools/llvm-objdump/X86/source-interleave-x86_64.test --- a/llvm/test/tools/llvm-objdump/X86/source-interleave-x86_64.test +++ b/llvm/test/tools/llvm-objdump/X86/source-interleave-x86_64.test @@ -10,6 +10,7 @@ # RUN: FileCheck --check-prefix=SOURCE --strict-whitespace %s < %t2 # LINES: main: +# LINES-NEXT: ; main(): # LINES-NEXT: ; {{[ -\(\)_A-Za-z0-9.\\/:]+}}source-interleave-x86_64.c:6 # SOURCE: main: diff --git a/llvm/test/tools/llvm-objdump/embedded-source.test b/llvm/test/tools/llvm-objdump/embedded-source.test --- a/llvm/test/tools/llvm-objdump/embedded-source.test +++ b/llvm/test/tools/llvm-objdump/embedded-source.test @@ -13,6 +13,7 @@ ; } ; LINE: main: +; LINE-NEXT: ; main(): ; LINE-NEXT: ; {{.*}}embedded-source.c:1 ; LINE-NEXT: pushq %rbp ; LINE: ; {{.*}}embedded-source.c:2 diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -553,13 +553,20 @@ private: bool cacheSource(const DILineInfo& LineInfoFile); + void printLines(raw_ostream &OS, const DILineInfo &LineInfo, + StringRef Delimiter); + + void printSources(raw_ostream &OS, const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter); + public: SourcePrinter() = default; SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) : Obj(Obj), WarnedNoDebugInfo(false) { symbolize::LLVMSymbolizer::Options SymbolizerOpts; - SymbolizerOpts.PrintFunctions = DILineInfoSpecifier::FunctionNameKind::None; - SymbolizerOpts.Demangle = false; + SymbolizerOpts.PrintFunctions = + DILineInfoSpecifier::FunctionNameKind::LinkageName; + SymbolizerOpts.Demangle = Demangle; SymbolizerOpts.DefaultArch = std::string(DefaultArch); Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts)); } @@ -624,34 +631,57 @@ reportWarning(Warning, ObjectFilename); WarnedNoDebugInfo = true; } - return; } - if (LineInfo.Line == 0 || ((OldLineInfo.Line == LineInfo.Line) && - (OldLineInfo.FileName == LineInfo.FileName))) - return; - if (PrintLines) + printLines(OS, LineInfo, Delimiter); + if (PrintSource) + printSources(OS, LineInfo, ObjectFilename, Delimiter); + OldLineInfo = LineInfo; +} + +void SourcePrinter::printLines(raw_ostream &OS, const DILineInfo &LineInfo, + StringRef Delimiter) { + bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString && + LineInfo.FunctionName != OldLineInfo.FunctionName; + if (PrintFunctionName) { + OS << Delimiter << LineInfo.FunctionName; + // If demangling is successful, FunctionName will end with "()". Print it + // only if demangling did not run or was unsuccessful. + if (!StringRef(LineInfo.FunctionName).endswith("()")) + OS << "()"; + OS << ":\n"; + } + if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 && + (OldLineInfo.Line != LineInfo.Line || + OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n"; - if (PrintSource) { - if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) - if (!cacheSource(LineInfo)) - return; - auto LineBuffer = LineCache.find(LineInfo.FileName); - if (LineBuffer != LineCache.end()) { - if (LineInfo.Line > LineBuffer->second.size()) { - reportWarning( - formatv( - "debug info line number {0} exceeds the number of lines in {1}", - LineInfo.Line, LineInfo.FileName), - ObjectFilename); - return; - } - // Vector begins at 0, line numbers are non-zero - OS << Delimiter << LineBuffer->second[LineInfo.Line - 1] << '\n'; +} + +void SourcePrinter::printSources(raw_ostream &OS, const DILineInfo &LineInfo, + StringRef ObjectFilename, + StringRef Delimiter) { + if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 || + (OldLineInfo.Line == LineInfo.Line && + OldLineInfo.FileName == LineInfo.FileName)) + return; + + if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) + if (!cacheSource(LineInfo)) + return; + auto LineBuffer = LineCache.find(LineInfo.FileName); + if (LineBuffer != LineCache.end()) { + if (LineInfo.Line > LineBuffer->second.size()) { + reportWarning( + formatv( + "debug info line number {0} exceeds the number of lines in {1}", + LineInfo.Line, LineInfo.FileName), + ObjectFilename); + return; } + // Vector begins at 0, line numbers are non-zero + OS << Delimiter << LineBuffer->second[LineInfo.Line - 1] << '\n'; } - OldLineInfo = LineInfo; } static bool isAArch64Elf(const ObjectFile *Obj) {