[llvm] dc4a6f5 - [llvm-objdump] Display locations of variables alongside disassembly
Oliver Stannard via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 9 01:58:18 PDT 2020
Author: Oliver Stannard
Date: 2020-07-09T09:58:00+01:00
New Revision: dc4a6f5db4f0178bae43ef615cc8902c759d6195
URL: https://github.com/llvm/llvm-project/commit/dc4a6f5db4f0178bae43ef615cc8902c759d6195
DIFF: https://github.com/llvm/llvm-project/commit/dc4a6f5db4f0178bae43ef615cc8902c759d6195.diff
LOG: [llvm-objdump] Display locations of variables alongside disassembly
This adds the --debug-vars option to llvm-objdump, which prints
locations (registers/memory) of source-level variables alongside the
disassembly based on DWARF info. A vertical line is printed for each
live-range, with a label at the top giving the variable name and
location, and the position and length of the line indicating the program
counter range in which it is valid.
Differential revision: https://reviews.llvm.org/D70720
Added:
llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c
llvm/test/tools/llvm-objdump/ARM/Inputs/wide-char.c
llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4-sections.s
llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4.s
llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5-sections.s
llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5.s
llvm/test/tools/llvm-objdump/ARM/debug-vars-wide-chars.s
llvm/test/tools/llvm-objdump/PowerPC/debug-vars.s
Modified:
llvm/docs/CommandGuide/llvm-objdump.rst
llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
llvm/tools/llvm-objdump/llvm-objdump.cpp
Removed:
################################################################################
diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst
index 2de48d66536f..df4cf746abbb 100644
--- a/llvm/docs/CommandGuide/llvm-objdump.rst
+++ b/llvm/docs/CommandGuide/llvm-objdump.rst
@@ -123,6 +123,17 @@ OPTIONS
Demangle symbol names in the output.
+.. option:: --debug-vars=<format>
+
+ Print the locations (in registers or memory) of source-level variables
+ alongside disassembly. ``format`` may be ``unicode`` or ``ascii``, defaulting
+ to ``unicode`` if omitted.
+
+.. option:: --debug-vars-indent=<width>
+
+ Distance to indent the source-level variable display, relative to the start
+ of the disassembly. Defaults to 40 characters.
+
.. option:: -j, --section=<section1[,section2,...]>
Perform commands on the specified sections only. For Mach-O use
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
index 28929306ca92..1aff2624990f 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
@@ -142,6 +142,12 @@ class DWARFExpression {
void print(raw_ostream &OS, const MCRegisterInfo *RegInfo, DWARFUnit *U,
bool IsEH = false) const;
+ /// Print the expression in a format intended to be compact and useful to a
+ /// user, but not perfectly unambiguous, or capable of representing every
+ /// valid DWARF expression. Returns true if the expression was sucessfully
+ /// printed.
+ bool printCompact(raw_ostream &OS, const MCRegisterInfo &RegInfo);
+
bool verify(DWARFUnit *U);
private:
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
index 28b24b3baaab..d3c1cd5bb88f 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
@@ -374,4 +374,74 @@ bool DWARFExpression::verify(DWARFUnit *U) {
return true;
}
+/// A user-facing string representation of a DWARF expression. This might be an
+/// Address expression, in which case it will be implicitly dereferenced, or a
+/// Value expression.
+struct PrintedExpr {
+ enum ExprKind {
+ Address,
+ Value,
+ };
+ ExprKind Kind;
+ SmallString<16> String;
+
+ PrintedExpr(ExprKind K = Address) : Kind(K) {}
+};
+
+static bool printCompactDWARFExpr(raw_ostream &OS, DWARFExpression::iterator I,
+ const DWARFExpression::iterator E,
+ const MCRegisterInfo &MRI) {
+ SmallVector<PrintedExpr, 4> Stack;
+
+ while (I != E) {
+ DWARFExpression::Operation &Op = *I;
+ uint8_t Opcode = Op.getCode();
+ switch (Opcode) {
+ case dwarf::DW_OP_regx: {
+ // DW_OP_regx: A register, with the register num given as an operand.
+ // Printed as the plain register name.
+ uint64_t DwarfRegNum = Op.getRawOperand(0);
+ Optional<unsigned> LLVMRegNum = MRI.getLLVMRegNum(DwarfRegNum, false);
+ if (!LLVMRegNum) {
+ OS << "<unknown register " << DwarfRegNum << ">";
+ return false;
+ }
+ raw_svector_ostream S(Stack.emplace_back(PrintedExpr::Value).String);
+ S << MRI.getName(*LLVMRegNum);
+ break;
+ }
+ default:
+ if (Opcode >= dwarf::DW_OP_reg0 && Opcode <= dwarf::DW_OP_reg31) {
+ // DW_OP_reg<N>: A register, with the register num implied by the
+ // opcode. Printed as the plain register name.
+ uint64_t DwarfRegNum = Opcode - dwarf::DW_OP_reg0;
+ Optional<unsigned> LLVMRegNum = MRI.getLLVMRegNum(DwarfRegNum, false);
+ if (!LLVMRegNum) {
+ OS << "<unknown register " << DwarfRegNum << ">";
+ return false;
+ }
+ raw_svector_ostream S(Stack.emplace_back(PrintedExpr::Value).String);
+ S << MRI.getName(*LLVMRegNum);
+ } else {
+ // If we hit an unknown operand, we don't know its effect on the stack,
+ // so bail out on the whole expression.
+ OS << "<unknown op " << dwarf::OperationEncodingString(Opcode) << " ("
+ << (int)Opcode << ")>";
+ return false;
+ }
+ break;
+ }
+ ++I;
+ }
+
+ assert(Stack.size() == 1 && "expected one value on stack");
+ OS << Stack.front().String;
+
+ return true;
+}
+
+bool DWARFExpression::printCompact(raw_ostream &OS, const MCRegisterInfo &MRI) {
+ return printCompactDWARFExpr(OS, begin(), end(), MRI);
+}
+
} // namespace llvm
diff --git a/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c b/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c
new file mode 100644
index 000000000000..20c17f7d9762
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c
@@ -0,0 +1,10 @@
+int foo(int a, int b, int c) {
+ int x = a + b;
+ int y = x + c;
+ return y;
+}
+
+int bar(int a) {
+ a++;
+ return a;
+}
diff --git a/llvm/test/tools/llvm-objdump/ARM/Inputs/wide-char.c b/llvm/test/tools/llvm-objdump/ARM/Inputs/wide-char.c
new file mode 100644
index 000000000000..8d923be01328
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/ARM/Inputs/wide-char.c
@@ -0,0 +1,3 @@
+int foo(int *喵) {
+ return *喵;
+}
diff --git a/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4-sections.s b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4-sections.s
new file mode 100644
index 000000000000..9bbb36f14e3a
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4-sections.s
@@ -0,0 +1,351 @@
+## Check that the --debug-vars option works for simple register locations, when
+## using DWARF4 debug info, with functions in multiple sections.
+
+## Generated with this compile command, with the source code in Inputs/debug.c:
+## clang --target=arm--none-eabi -march=armv7-a -c debug.c -O1 -gdwarf-4 -S -o - -ffunction-sections
+
+# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn | \
+# RUN: FileCheck %s
+
+# CHECK: Disassembly of section .text.foo:
+# CHECK-EMPTY:
+# CHECK-NEXT: 00000000 <foo>:
+# CHECK-NEXT: ┠─ a = R0
+# CHECK-NEXT: ┃ ┠─ b = R1
+# CHECK-NEXT: ┃ ┃ ┠─ c = R2
+# CHECK-NEXT: ┃ ┃ ┃ ┌─ x = R0
+# CHECK-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈
+# CHECK-NEXT: ┌─ y = R0
+# CHECK-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻
+# CHECK-NEXT: 8: bx lr ┻ ┻ ┻
+# CHECK-EMPTY:
+# CHECK-NEXT: Disassembly of section .text.bar:
+# CHECK-EMPTY:
+# CHECK-NEXT: 00000000 <bar>:
+# CHECK-NEXT: ┠─ a = R0
+# CHECK-NEXT: 0: add r0, r0, #1 ┃
+# CHECK-NEXT: 4: bx lr ┻
+
+ .text
+ .syntax unified
+ .eabi_attribute 67, "2.09"
+ .eabi_attribute 6, 10
+ .eabi_attribute 7, 65
+ .eabi_attribute 8, 1
+ .eabi_attribute 9, 2
+ .fpu neon
+ .eabi_attribute 34, 0
+ .eabi_attribute 17, 1
+ .eabi_attribute 20, 1
+ .eabi_attribute 21, 1
+ .eabi_attribute 23, 3
+ .eabi_attribute 24, 1
+ .eabi_attribute 25, 1
+ .eabi_attribute 38, 1
+ .eabi_attribute 18, 4
+ .eabi_attribute 26, 2
+ .eabi_attribute 14, 0
+ .file "debug.c"
+ .section .text.foo,"ax",%progbits
+ .globl foo
+ .p2align 2
+ .type foo,%function
+ .code 32
+foo:
+.Lfunc_begin0:
+ .file 1 "/work" "llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c"
+ .loc 1 1 0
+ .fnstart
+ .cfi_sections .debug_frame
+ .cfi_startproc
+ .loc 1 2 13 prologue_end
+ add r0, r1, r0
+.Ltmp0:
+ .loc 1 3 13
+ add r0, r0, r2
+.Ltmp1:
+ .loc 1 4 3
+ bx lr
+.Ltmp2:
+.Lfunc_end0:
+ .size foo, .Lfunc_end0-foo
+ .cfi_endproc
+ .cantunwind
+ .fnend
+
+ .section .text.bar,"ax",%progbits
+ .globl bar
+ .p2align 2
+ .type bar,%function
+ .code 32
+bar:
+.Lfunc_begin1:
+ .loc 1 7 0
+ .fnstart
+ .cfi_startproc
+ .loc 1 8 4 prologue_end
+ add r0, r0, #1
+.Ltmp3:
+ .loc 1 9 3
+ bx lr
+.Ltmp4:
+.Lfunc_end1:
+ .size bar, .Lfunc_end1-bar
+ .cfi_endproc
+ .cantunwind
+ .fnend
+
+ .section .debug_str,"MS",%progbits,1
+.Linfo_string0:
+ .asciz "clang version 10.0.0 (git at github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)"
+.Linfo_string1:
+ .asciz "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c"
+.Linfo_string2:
+ .asciz "/work/scratch"
+.Linfo_string3:
+ .asciz "foo"
+.Linfo_string4:
+ .asciz "int"
+.Linfo_string5:
+ .asciz "bar"
+.Linfo_string6:
+ .asciz "a"
+.Linfo_string7:
+ .asciz "b"
+.Linfo_string8:
+ .asciz "c"
+.Linfo_string9:
+ .asciz "x"
+.Linfo_string10:
+ .asciz "y"
+ .section .debug_loc,"",%progbits
+.Ldebug_loc0:
+ .long -1
+ .long .Lfunc_begin0
+ .long .Lfunc_begin0-.Lfunc_begin0
+ .long .Ltmp0-.Lfunc_begin0
+ .short 1
+ .byte 80
+ .long 0
+ .long 0
+.Ldebug_loc1:
+ .long -1
+ .long .Lfunc_begin0
+ .long .Ltmp0-.Lfunc_begin0
+ .long .Ltmp1-.Lfunc_begin0
+ .short 1
+ .byte 80
+ .long 0
+ .long 0
+.Ldebug_loc2:
+ .long -1
+ .long .Lfunc_begin0
+ .long .Ltmp1-.Lfunc_begin0
+ .long .Lfunc_end0-.Lfunc_begin0
+ .short 1
+ .byte 80
+ .long 0
+ .long 0
+ .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
+ .byte 17
+ .byte 1
+ .byte 85
+ .byte 23
+ .byte 0
+ .byte 0
+ .byte 2
+ .byte 46
+ .byte 1
+ .byte 17
+ .byte 1
+ .byte 18
+ .byte 6
+ .byte 64
+ .byte 24
+ .ascii "\227B"
+ .byte 25
+ .byte 3
+ .byte 14
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 39
+ .byte 25
+ .byte 73
+ .byte 19
+ .byte 63
+ .byte 25
+ .byte 0
+ .byte 0
+ .byte 3
+ .byte 5
+ .byte 0
+ .byte 2
+ .byte 23
+ .byte 3
+ .byte 14
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 4
+ .byte 5
+ .byte 0
+ .byte 2
+ .byte 24
+ .byte 3
+ .byte 14
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 5
+ .byte 52
+ .byte 0
+ .byte 2
+ .byte 23
+ .byte 3
+ .byte 14
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 6
+ .byte 36
+ .byte 0
+ .byte 3
+ .byte 14
+ .byte 62
+ .byte 11
+ .byte 11
+ .byte 11
+ .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 4
+ .byte 1
+ .long .Linfo_string0
+ .short 12
+ .long .Linfo_string1
+ .long .Lline_table_start0
+ .long .Linfo_string2
+ .long 0
+ .long .Ldebug_ranges0
+ .byte 2
+ .long .Lfunc_begin0
+ .long .Lfunc_end0-.Lfunc_begin0
+ .byte 1
+ .byte 91
+
+ .long .Linfo_string3
+ .byte 1
+ .byte 1
+
+ .long 166
+
+ .byte 3
+ .long .Ldebug_loc0
+ .long .Linfo_string6
+ .byte 1
+ .byte 1
+ .long 166
+ .byte 4
+ .byte 1
+ .byte 81
+ .long .Linfo_string7
+ .byte 1
+ .byte 1
+ .long 166
+ .byte 4
+ .byte 1
+ .byte 82
+ .long .Linfo_string8
+ .byte 1
+ .byte 1
+ .long 166
+ .byte 5
+ .long .Ldebug_loc1
+ .long .Linfo_string9
+ .byte 1
+ .byte 2
+ .long 166
+ .byte 5
+ .long .Ldebug_loc2
+ .long .Linfo_string10
+ .byte 1
+ .byte 3
+ .long 166
+ .byte 0
+ .byte 2
+ .long .Lfunc_begin1
+ .long .Lfunc_end1-.Lfunc_begin1
+ .byte 1
+ .byte 91
+
+ .long .Linfo_string5
+ .byte 1
+ .byte 7
+
+ .long 166
+
+ .byte 4
+ .byte 1
+ .byte 80
+ .long .Linfo_string6
+ .byte 1
+ .byte 7
+ .long 166
+ .byte 0
+ .byte 6
+ .long .Linfo_string4
+ .byte 5
+ .byte 4
+ .byte 0
+.Ldebug_info_end0:
+ .section .debug_ranges,"",%progbits
+.Ldebug_ranges0:
+ .long .Lfunc_begin0
+ .long .Lfunc_end0
+ .long .Lfunc_begin1
+ .long .Lfunc_end1
+ .long 0
+ .long 0
+ .ident "clang version 10.0.0 (git at github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)"
+ .section ".note.GNU-stack","",%progbits
+ .addrsig
+ .eabi_attribute 30, 1
+ .section .debug_line,"",%progbits
+.Lline_table_start0:
diff --git a/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4.s b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4.s
new file mode 100644
index 000000000000..bf0c7bd52feb
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4.s
@@ -0,0 +1,454 @@
+## Check that the --debug-vars option works for simple register locations, when
+## using DWARF4 debug info, with multiple functions in one section. Check that
+## the live-range lines are rendered correctly when using the --no-show-raw-insn,
+## --line-numbers and --source options. These do not affect the DWARF parsing
+## used by --debug-vars, but do add extra lines or columns to the output, so we
+## test to make sure the live ranges are still displayed correctly.
+
+## Generated with this compile command, with the source code in Inputs/debug.c:
+## clang --target=arm--none-eabi -march=armv7-a -c debug.c -O1 -gdwarf-4 -S -o -
+
+# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars | \
+# RUN: FileCheck %s --check-prefix=RAW --strict-whitespace
+
+## Check that passing the default value for --debug-vars-indent (40) makes no
+## change to the output.
+# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars --debug-vars-indent=40 | \
+# RUN: FileCheck %s --check-prefix=RAW --strict-whitespace
+
+# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars --debug-vars-indent=30 | \
+# RUN: FileCheck %s --check-prefix=INDENT --strict-whitespace
+
+# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn | \
+# RUN: FileCheck %s --check-prefix=NO-RAW --strict-whitespace
+
+# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn --line-numbers | \
+# RUN: FileCheck %s --check-prefix=LINE-NUMS --strict-whitespace
+
+# RUN: mkdir -p %t/a
+# RUN: cp %p/Inputs/debug.c %t/a/debug.c
+# RUN: sed -e "s,SRC_COMPDIR,%/t/a,g" %s > %t.s
+# RUN: llvm-mc -triple armv8a--none-eabi < %t.s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn --source | \
+# RUN: FileCheck %s --check-prefix=SOURCE --strict-whitespace
+
+## An optional argument to the --debug-vars= option can be used to switch
+## between unicode and ascii output (with unicode being the default).
+# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars=unicode | \
+# RUN: FileCheck %s --check-prefix=RAW --strict-whitespace
+# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars=ascii | \
+# RUN: FileCheck %s --check-prefix=ASCII --strict-whitespace
+
+## Note that llvm-objdump emits tab characters in the disassembly, assuming an
+## 8-byte tab stop, so these might not look aligned in a text editor.
+
+# RAW: 00000000 <foo>:
+# RAW-NEXT: ┠─ a = R0
+# RAW-NEXT: ┃ ┠─ b = R1
+# RAW-NEXT: ┃ ┃ ┠─ c = R2
+# RAW-NEXT: ┃ ┃ ┃ ┌─ x = R0
+# RAW-NEXT: 0: 00 00 81 e0 add r0, r1, r0 ┻ ┃ ┃ ╈
+# RAW-NEXT: ┌─ y = R0
+# RAW-NEXT: 4: 02 00 80 e0 add r0, r0, r2 ╈ ┃ ┃ ┻
+# RAW-NEXT: 8: 1e ff 2f e1 bx lr ┻ ┻ ┻
+# RAW-EMPTY:
+# RAW-NEXT: 0000000c <bar>:
+# RAW-NEXT: ┠─ a = R0
+# RAW-NEXT: c: 01 00 80 e2 add r0, r0, #1 ┃
+# RAW-NEXT: 10: 1e ff 2f e1 bx lr ┻
+
+
+# INDENT: 00000000 <foo>:
+# INDENT-NEXT: ┠─ a = R0
+# INDENT-NEXT: ┃ ┠─ b = R1
+# INDENT-NEXT: ┃ ┃ ┠─ c = R2
+# INDENT-NEXT: ┃ ┃ ┃ ┌─ x = R0
+# INDENT-NEXT: 0: 00 00 81 e0 add r0, r1, r0 ┻ ┃ ┃ ╈
+# INDENT-NEXT: ┌─ y = R0
+# INDENT-NEXT: 4: 02 00 80 e0 add r0, r0, r2 ╈ ┃ ┃ ┻
+# INDENT-NEXT: 8: 1e ff 2f e1 bx lr ┻ ┻ ┻
+# INDENT-EMPTY:
+# INDENT-NEXT: 0000000c <bar>:
+# INDENT-NEXT: ┠─ a = R0
+# INDENT-NEXT: c: 01 00 80 e2 add r0, r0, #1 ┃
+# INDENT-NEXT: 10: 1e ff 2f e1 bx lr ┻
+
+# NO-RAW: 00000000 <foo>:
+# NO-RAW-NEXT: ┠─ a = R0
+# NO-RAW-NEXT: ┃ ┠─ b = R1
+# NO-RAW-NEXT: ┃ ┃ ┠─ c = R2
+# NO-RAW-NEXT: ┃ ┃ ┃ ┌─ x = R0
+# NO-RAW-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈
+# NO-RAW-NEXT: ┌─ y = R0
+# NO-RAW-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻
+# NO-RAW-NEXT: 8: bx lr ┻ ┻ ┻
+# NO-RAW-EMPTY:
+# NO-RAW-NEXT: 0000000c <bar>:
+# NO-RAW-NEXT: ┠─ a = R0
+# NO-RAW-NEXT: c: add r0, r0, #1 ┃
+# NO-RAW-NEXT: 10: bx lr ┻
+
+# LINE-NUMS: 00000000 <foo>:
+# LINE-NUMS-NEXT: ; foo():
+# LINE-NUMS-NEXT: ; SRC_COMPDIR{{[\\/]}}debug.c:2 ┠─ a = R0
+# LINE-NUMS-NEXT: ┃ ┠─ b = R1
+# LINE-NUMS-NEXT: ┃ ┃ ┠─ c = R2
+# LINE-NUMS-NEXT: ┃ ┃ ┃ ┌─ x = R0
+# LINE-NUMS-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈
+# LINE-NUMS-NEXT: ; SRC_COMPDIR{{[\\/]}}debug.c:3 ┌─ y = R0
+# LINE-NUMS-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻
+# LINE-NUMS-NEXT: ; SRC_COMPDIR{{[\\/]}}debug.c:4 ┃ ┃ ┃
+# LINE-NUMS-NEXT: 8: bx lr ┻ ┻ ┻
+# LINE-NUMS-EMPTY:
+# LINE-NUMS-NEXT: 0000000c <bar>:
+# LINE-NUMS-NEXT: ; bar():
+# LINE-NUMS-NEXT: ; SRC_COMPDIR{{[\\/]}}debug.c:8 ┠─ a = R0
+# LINE-NUMS-NEXT: c: add r0, r0, #1 ┃
+# LINE-NUMS-NEXT: ; SRC_COMPDIR{{[\\/]}}debug.c:9 ┃
+# LINE-NUMS-NEXT: 10: bx lr ┻
+
+# SOURCE: 00000000 <foo>:
+# SOURCE-NEXT: ; int x = a + b; ┠─ a = R0
+# SOURCE-NEXT: ┃ ┠─ b = R1
+# SOURCE-NEXT: ┃ ┃ ┠─ c = R2
+# SOURCE-NEXT: ┃ ┃ ┃ ┌─ x = R0
+# SOURCE-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈
+# SOURCE-NEXT: ; int y = x + c; ┌─ y = R0
+# SOURCE-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻
+# SOURCE-NEXT: ; return y; ┃ ┃ ┃
+# SOURCE-NEXT: 8: bx lr ┻ ┻ ┻
+# SOURCE-EMPTY:
+# SOURCE-NEXT: 0000000c <bar>:
+# SOURCE-NEXT: ; a++; ┠─ a = R0
+# SOURCE-NEXT: c: add r0, r0, #1 ┃
+# SOURCE-NEXT: ; return a; ┃
+# SOURCE-NEXT: 10: bx lr ┻
+
+# ASCII: 00000000 <foo>:
+# ASCII-NEXT: |- a = R0
+# ASCII-NEXT: | |- b = R1
+# ASCII-NEXT: | | |- c = R2
+# ASCII-NEXT: | | | /- x = R0
+# ASCII-NEXT: 0: 00 00 81 e0 add r0, r1, r0 v | | ^
+# ASCII-NEXT: /- y = R0
+# ASCII-NEXT: 4: 02 00 80 e0 add r0, r0, r2 ^ | | v
+# ASCII-NEXT: 8: 1e ff 2f e1 bx lr v v v
+# ASCII-EMPTY:
+# ASCII-NEXT: 0000000c <bar>:
+# ASCII-NEXT: |- a = R0
+# ASCII-NEXT: c: 01 00 80 e2 add r0, r0, #1 |
+# ASCII-NEXT: 10: 1e ff 2f e1 bx lr v
+
+ .text
+ .syntax unified
+ .eabi_attribute 67, "2.09"
+ .eabi_attribute 6, 10
+ .eabi_attribute 7, 65
+ .eabi_attribute 8, 1
+ .eabi_attribute 9, 2
+ .fpu neon
+ .eabi_attribute 34, 0
+ .eabi_attribute 17, 1
+ .eabi_attribute 20, 1
+ .eabi_attribute 21, 1
+ .eabi_attribute 23, 3
+ .eabi_attribute 24, 1
+ .eabi_attribute 25, 1
+ .eabi_attribute 38, 1
+ .eabi_attribute 18, 4
+ .eabi_attribute 26, 2
+ .eabi_attribute 14, 0
+ .file "debug.c"
+ .globl foo
+ .p2align 2
+ .type foo,%function
+ .code 32
+foo:
+.Lfunc_begin0:
+ .file 1 "" "SRC_COMPDIR/debug.c"
+ .loc 1 1 0
+ .fnstart
+ .cfi_sections .debug_frame
+ .cfi_startproc
+ .loc 1 2 13 prologue_end
+ add r0, r1, r0
+.Ltmp0:
+ .loc 1 3 13
+ add r0, r0, r2
+.Ltmp1:
+ .loc 1 4 3
+ bx lr
+.Ltmp2:
+.Lfunc_end0:
+ .size foo, .Lfunc_end0-foo
+ .cfi_endproc
+ .cantunwind
+ .fnend
+
+ .globl bar
+ .p2align 2
+ .type bar,%function
+ .code 32
+bar:
+.Lfunc_begin1:
+ .loc 1 7 0
+ .fnstart
+ .cfi_startproc
+ .loc 1 8 4 prologue_end
+ add r0, r0, #1
+.Ltmp3:
+ .loc 1 9 3
+ bx lr
+.Ltmp4:
+.Lfunc_end1:
+ .size bar, .Lfunc_end1-bar
+ .cfi_endproc
+ .cantunwind
+ .fnend
+
+ .section .debug_str,"MS",%progbits,1
+.Linfo_string0:
+ .asciz "clang version 10.0.0 (git at github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)"
+.Linfo_string1:
+ .asciz "SRC_COMPDIR/debug.c"
+.Linfo_string2:
+ .asciz ""
+.Linfo_string3:
+ .asciz "foo"
+.Linfo_string4:
+ .asciz "int"
+.Linfo_string5:
+ .asciz "bar"
+.Linfo_string6:
+ .asciz "a"
+.Linfo_string7:
+ .asciz "b"
+.Linfo_string8:
+ .asciz "c"
+.Linfo_string9:
+ .asciz "x"
+.Linfo_string10:
+ .asciz "y"
+ .section .debug_loc,"",%progbits
+.Ldebug_loc0:
+ .long .Lfunc_begin0-.Lfunc_begin0
+ .long .Ltmp0-.Lfunc_begin0
+ .short 1
+ .byte 80
+ .long 0
+ .long 0
+.Ldebug_loc1:
+ .long .Ltmp0-.Lfunc_begin0
+ .long .Ltmp1-.Lfunc_begin0
+ .short 1
+ .byte 80
+ .long 0
+ .long 0
+.Ldebug_loc2:
+ .long .Ltmp1-.Lfunc_begin0
+ .long .Lfunc_end0-.Lfunc_begin0
+ .short 1
+ .byte 80
+ .long 0
+ .long 0
+ .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
+ .byte 17
+ .byte 1
+ .byte 18
+ .byte 6
+ .byte 0
+ .byte 0
+ .byte 2
+ .byte 46
+ .byte 1
+ .byte 17
+ .byte 1
+ .byte 18
+ .byte 6
+ .byte 64
+ .byte 24
+ .ascii "\227B"
+ .byte 25
+ .byte 3
+ .byte 14
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 39
+ .byte 25
+ .byte 73
+ .byte 19
+ .byte 63
+ .byte 25
+ .byte 0
+ .byte 0
+ .byte 3
+ .byte 5
+ .byte 0
+ .byte 2
+ .byte 23
+ .byte 3
+ .byte 14
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 4
+ .byte 5
+ .byte 0
+ .byte 2
+ .byte 24
+ .byte 3
+ .byte 14
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 5
+ .byte 52
+ .byte 0
+ .byte 2
+ .byte 23
+ .byte 3
+ .byte 14
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 6
+ .byte 36
+ .byte 0
+ .byte 3
+ .byte 14
+ .byte 62
+ .byte 11
+ .byte 11
+ .byte 11
+ .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 4
+ .byte 1
+ .long .Linfo_string0
+ .short 12
+ .long .Linfo_string1
+ .long .Lline_table_start0
+ .long .Linfo_string2
+ .long .Lfunc_begin0
+ .long .Lfunc_end1-.Lfunc_begin0
+ .byte 2
+ .long .Lfunc_begin0
+ .long .Lfunc_end0-.Lfunc_begin0
+ .byte 1
+ .byte 91
+
+ .long .Linfo_string3
+ .byte 1
+ .byte 1
+
+ .long 166
+
+ .byte 3
+ .long .Ldebug_loc0
+ .long .Linfo_string6
+ .byte 1
+ .byte 1
+ .long 166
+ .byte 4
+ .byte 1
+ .byte 81
+ .long .Linfo_string7
+ .byte 1
+ .byte 1
+ .long 166
+ .byte 4
+ .byte 1
+ .byte 82
+ .long .Linfo_string8
+ .byte 1
+ .byte 1
+ .long 166
+ .byte 5
+ .long .Ldebug_loc1
+ .long .Linfo_string9
+ .byte 1
+ .byte 2
+ .long 166
+ .byte 5
+ .long .Ldebug_loc2
+ .long .Linfo_string10
+ .byte 1
+ .byte 3
+ .long 166
+ .byte 0
+ .byte 2
+ .long .Lfunc_begin1
+ .long .Lfunc_end1-.Lfunc_begin1
+ .byte 1
+ .byte 91
+
+ .long .Linfo_string5
+ .byte 1
+ .byte 7
+
+ .long 166
+
+ .byte 4
+ .byte 1
+ .byte 80
+ .long .Linfo_string6
+ .byte 1
+ .byte 7
+ .long 166
+ .byte 0
+ .byte 6
+ .long .Linfo_string4
+ .byte 5
+ .byte 4
+ .byte 0
+.Ldebug_info_end0:
+ .ident "clang version 10.0.0 (git at github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)"
+ .section ".note.GNU-stack","",%progbits
+ .addrsig
+ .eabi_attribute 30, 1
+ .section .debug_line,"",%progbits
+.Lline_table_start0:
diff --git a/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5-sections.s b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5-sections.s
new file mode 100644
index 000000000000..a9e08ff95556
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5-sections.s
@@ -0,0 +1,411 @@
+## Check that the --debug-vars option works for simple register locations, when
+## using DWARF4 debug info, with functions in multiple sections.
+
+## Generated with this compile command, with the source code in Inputs/debug.c:
+## clang --target=arm--none-eabi -march=armv7-a -c debug.c -O1 -gdwarf-5 -S -o - -ffunction-sections
+
+# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj --dwarf-version=5 | \
+# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn | \
+# RUN: FileCheck %s
+
+# CHECK: Disassembly of section .text.foo:
+# CHECK-EMPTY:
+# CHECK-NEXT: 00000000 <foo>:
+# CHECK-NEXT: ┠─ a = R0
+# CHECK-NEXT: ┃ ┠─ b = R1
+# CHECK-NEXT: ┃ ┃ ┠─ c = R2
+# CHECK-NEXT: ┃ ┃ ┃ ┌─ x = R0
+# CHECK-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈
+# CHECK-NEXT: ┌─ y = R0
+# CHECK-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻
+# CHECK-NEXT: 8: bx lr ┻ ┻ ┻
+# CHECK-EMPTY:
+# CHECK-NEXT: Disassembly of section .text.bar:
+# CHECK-EMPTY:
+# CHECK-NEXT: 00000000 <bar>:
+# CHECK-NEXT: ┠─ a = R0
+# CHECK-NEXT: 0: add r0, r0, #1 ┃
+# CHECK-NEXT: 4: bx lr ┻
+
+ .text
+ .syntax unified
+ .eabi_attribute 67, "2.09"
+ .eabi_attribute 6, 10
+ .eabi_attribute 7, 65
+ .eabi_attribute 8, 1
+ .eabi_attribute 9, 2
+ .fpu neon
+ .eabi_attribute 34, 0
+ .eabi_attribute 17, 1
+ .eabi_attribute 20, 1
+ .eabi_attribute 21, 1
+ .eabi_attribute 23, 3
+ .eabi_attribute 24, 1
+ .eabi_attribute 25, 1
+ .eabi_attribute 38, 1
+ .eabi_attribute 18, 4
+ .eabi_attribute 26, 2
+ .eabi_attribute 14, 0
+ .file "debug.c"
+ .section .text.foo,"ax",%progbits
+ .globl foo
+ .p2align 2
+ .type foo,%function
+ .code 32
+foo:
+.Lfunc_begin0:
+ .file 0 "/work/scratch" "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" md5 0x07374f01ab24ec7c07db73bc13bd778e
+ .file 1 "/work" "llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" md5 0x07374f01ab24ec7c07db73bc13bd778e
+ .loc 1 1 0
+ .fnstart
+ .cfi_sections .debug_frame
+ .cfi_startproc
+ .loc 1 2 13 prologue_end
+ add r0, r1, r0
+.Ltmp0:
+ .loc 1 3 13
+ add r0, r0, r2
+.Ltmp1:
+ .loc 1 4 3
+ bx lr
+.Ltmp2:
+.Lfunc_end0:
+ .size foo, .Lfunc_end0-foo
+ .cfi_endproc
+ .cantunwind
+ .fnend
+
+ .section .text.bar,"ax",%progbits
+ .globl bar
+ .p2align 2
+ .type bar,%function
+ .code 32
+bar:
+.Lfunc_begin1:
+ .loc 1 7 0
+ .fnstart
+ .cfi_startproc
+ .loc 1 8 4 prologue_end
+ add r0, r0, #1
+.Ltmp3:
+ .loc 1 9 3
+ bx lr
+.Ltmp4:
+.Lfunc_end1:
+ .size bar, .Lfunc_end1-bar
+ .cfi_endproc
+ .cantunwind
+ .fnend
+
+ .section .debug_str_offsets,"",%progbits
+ .long 48
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",%progbits,1
+.Linfo_string0:
+ .asciz "clang version 10.0.0 (git at github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)"
+.Linfo_string1:
+ .asciz "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c"
+.Linfo_string2:
+ .asciz "/work/scratch"
+.Linfo_string3:
+ .asciz "foo"
+.Linfo_string4:
+ .asciz "int"
+.Linfo_string5:
+ .asciz "bar"
+.Linfo_string6:
+ .asciz "a"
+.Linfo_string7:
+ .asciz "b"
+.Linfo_string8:
+ .asciz "c"
+.Linfo_string9:
+ .asciz "x"
+.Linfo_string10:
+ .asciz "y"
+ .section .debug_str_offsets,"",%progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .section .debug_loclists,"",%progbits
+ .long .Ldebug_loclist_table_end0-.Ldebug_loclist_table_start0
+.Ldebug_loclist_table_start0:
+ .short 5
+ .byte 4
+ .byte 0
+ .long 3
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+ .long .Ldebug_loc1-.Lloclists_table_base0
+ .long .Ldebug_loc2-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 3
+ .byte 0
+ .uleb128 .Ltmp0-.Lfunc_begin0
+ .byte 1
+ .byte 80
+ .byte 0
+.Ldebug_loc1:
+ .byte 1
+ .byte 0
+ .byte 4
+ .uleb128 .Ltmp0-.Lfunc_begin0
+ .uleb128 .Ltmp1-.Lfunc_begin0
+ .byte 1
+ .byte 80
+ .byte 0
+.Ldebug_loc2:
+ .byte 1
+ .byte 0
+ .byte 4
+ .uleb128 .Ltmp1-.Lfunc_begin0
+ .uleb128 .Lfunc_end0-.Lfunc_begin0
+ .byte 1
+ .byte 80
+ .byte 0
+.Ldebug_loclist_table_end0:
+ .section .debug_abbrev,"",%progbits
+ .byte 1
+ .byte 17
+ .byte 1
+ .byte 37
+ .byte 37
+ .byte 19
+ .byte 5
+ .byte 3
+ .byte 37
+ .byte 114
+ .byte 23
+ .byte 16
+ .byte 23
+ .byte 27
+ .byte 37
+ .byte 17
+ .byte 1
+ .byte 85
+ .byte 35
+ .byte 115
+ .byte 23
+ .byte 116
+ .byte 23
+ .ascii "\214\001"
+ .byte 23
+ .byte 0
+ .byte 0
+ .byte 2
+ .byte 46
+ .byte 1
+ .byte 17
+ .byte 27
+ .byte 18
+ .byte 6
+ .byte 64
+ .byte 24
+ .byte 122
+ .byte 25
+ .byte 3
+ .byte 37
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 39
+ .byte 25
+ .byte 73
+ .byte 19
+ .byte 63
+ .byte 25
+ .byte 0
+ .byte 0
+ .byte 3
+ .byte 5
+ .byte 0
+ .byte 2
+ .byte 34
+ .byte 3
+ .byte 37
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 4
+ .byte 5
+ .byte 0
+ .byte 2
+ .byte 24
+ .byte 3
+ .byte 37
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 5
+ .byte 52
+ .byte 0
+ .byte 2
+ .byte 34
+ .byte 3
+ .byte 37
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 6
+ .byte 36
+ .byte 0
+ .byte 3
+ .byte 37
+ .byte 62
+ .byte 11
+ .byte 11
+ .byte 11
+ .byte 0
+ .byte 0
+ .byte 0
+ .section .debug_info,"",%progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0
+.Ldebug_info_start0:
+ .short 5
+ .byte 1
+ .byte 4
+ .long .debug_abbrev
+ .byte 1
+ .byte 0
+ .short 12
+ .byte 1
+ .long .Lstr_offsets_base0
+ .long .Lline_table_start0
+ .byte 2
+ .long 0
+ .byte 0
+ .long .Laddr_table_base0
+ .long .Lrnglists_table_base0
+ .long .Lloclists_table_base0
+ .byte 2
+ .byte 0
+ .long .Lfunc_end0-.Lfunc_begin0
+ .byte 1
+ .byte 91
+
+ .byte 3
+ .byte 1
+ .byte 1
+
+ .long 132
+
+ .byte 3
+ .byte 0
+ .byte 6
+ .byte 1
+ .byte 1
+ .long 132
+ .byte 4
+ .byte 1
+ .byte 81
+ .byte 7
+ .byte 1
+ .byte 1
+ .long 132
+ .byte 4
+ .byte 1
+ .byte 82
+ .byte 8
+ .byte 1
+ .byte 1
+ .long 132
+ .byte 5
+ .byte 1
+ .byte 9
+ .byte 1
+ .byte 2
+ .long 132
+ .byte 5
+ .byte 2
+ .byte 10
+ .byte 1
+ .byte 3
+ .long 132
+ .byte 0
+ .byte 2
+ .byte 1
+ .long .Lfunc_end1-.Lfunc_begin1
+ .byte 1
+ .byte 91
+
+ .byte 5
+ .byte 1
+ .byte 7
+
+ .long 132
+
+ .byte 4
+ .byte 1
+ .byte 80
+ .byte 6
+ .byte 1
+ .byte 7
+ .long 132
+ .byte 0
+ .byte 6
+ .byte 4
+ .byte 5
+ .byte 4
+ .byte 0
+.Ldebug_info_end0:
+ .section .debug_rnglists,"",%progbits
+ .long .Ldebug_rnglist_table_end0-.Ldebug_rnglist_table_start0
+.Ldebug_rnglist_table_start0:
+ .short 5
+ .byte 4
+ .byte 0
+ .long 1
+.Lrnglists_table_base0:
+ .long .Ldebug_ranges0-.Lrnglists_table_base0
+.Ldebug_ranges0:
+ .byte 3
+ .byte 0
+ .uleb128 .Lfunc_end0-.Lfunc_begin0
+ .byte 3
+ .byte 1
+ .uleb128 .Lfunc_end1-.Lfunc_begin1
+ .byte 0
+.Ldebug_rnglist_table_end0:
+ .section .debug_addr,"",%progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0
+.Ldebug_addr_start0:
+ .short 5
+ .byte 4
+ .byte 0
+.Laddr_table_base0:
+ .long .Lfunc_begin0
+ .long .Lfunc_begin1
+.Ldebug_addr_end0:
+ .ident "clang version 10.0.0 (git at github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)"
+ .section ".note.GNU-stack","",%progbits
+ .addrsig
+ .eabi_attribute 30, 1
+ .section .debug_line,"",%progbits
+.Lline_table_start0:
diff --git a/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5.s b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5.s
new file mode 100644
index 000000000000..8a63a4ee9ec0
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf5.s
@@ -0,0 +1,382 @@
+## Check that the --debug-vars option works for simple register locations, when
+## using DWARF5 debug info, with multiple functions in one section.
+
+## Generated with this compile command, with the source code in Inputs/debug.c:
+## clang --target=arm--none-eabi -march=armv7-a -c debug.c -O1 -gdwarf-3 -S -o -
+
+# RUN: llvm-mc -triple armv8a--none-eabi < %s -filetype=obj --dwarf-version=5 | \
+# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn | \
+# RUN: FileCheck %s
+
+# CHECK: Disassembly of section .text:
+# CHECK-EMPTY:
+# CHECK-NEXT: 00000000 <foo>:
+# CHECK-NEXT: ┠─ a = R0
+# CHECK-NEXT: ┃ ┠─ b = R1
+# CHECK-NEXT: ┃ ┃ ┠─ c = R2
+# CHECK-NEXT: ┃ ┃ ┃ ┌─ x = R0
+# CHECK-NEXT: 0: add r0, r1, r0 ┻ ┃ ┃ ╈
+# CHECK-NEXT: ┌─ y = R0
+# CHECK-NEXT: 4: add r0, r0, r2 ╈ ┃ ┃ ┻
+# CHECK-NEXT: 8: bx lr ┻ ┻ ┻
+# CHECK-EMPTY:
+# CHECK-NEXT: 0000000c <bar>:
+# CHECK-NEXT: ┠─ a = R0
+# CHECK-NEXT: c: add r0, r0, #1 ┃
+# CHECK-NEXT: 10: bx lr ┻
+
+ .text
+ .syntax unified
+ .eabi_attribute 67, "2.09"
+ .eabi_attribute 6, 10
+ .eabi_attribute 7, 65
+ .eabi_attribute 8, 1
+ .eabi_attribute 9, 2
+ .fpu neon
+ .eabi_attribute 34, 0
+ .eabi_attribute 17, 1
+ .eabi_attribute 20, 1
+ .eabi_attribute 21, 1
+ .eabi_attribute 23, 3
+ .eabi_attribute 24, 1
+ .eabi_attribute 25, 1
+ .eabi_attribute 38, 1
+ .eabi_attribute 18, 4
+ .eabi_attribute 26, 2
+ .eabi_attribute 14, 0
+ .file "debug.c"
+ .globl foo
+ .p2align 2
+ .type foo,%function
+ .code 32
+foo:
+.Lfunc_begin0:
+ .file 0 "/work/scratch" "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" md5 0x07374f01ab24ec7c07db73bc13bd778e
+ .file 1 "/work" "llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" md5 0x07374f01ab24ec7c07db73bc13bd778e
+ .loc 1 1 0
+ .fnstart
+ .cfi_sections .debug_frame
+ .cfi_startproc
+ .loc 1 2 13 prologue_end
+ add r0, r1, r0
+.Ltmp0:
+ .loc 1 3 13
+ add r0, r0, r2
+.Ltmp1:
+ .loc 1 4 3
+ bx lr
+.Ltmp2:
+.Lfunc_end0:
+ .size foo, .Lfunc_end0-foo
+ .cfi_endproc
+ .cantunwind
+ .fnend
+
+ .globl bar
+ .p2align 2
+ .type bar,%function
+ .code 32
+bar:
+.Lfunc_begin1:
+ .loc 1 7 0
+ .fnstart
+ .cfi_startproc
+ .loc 1 8 4 prologue_end
+ add r0, r0, #1
+.Ltmp3:
+ .loc 1 9 3
+ bx lr
+.Ltmp4:
+.Lfunc_end1:
+ .size bar, .Lfunc_end1-bar
+ .cfi_endproc
+ .cantunwind
+ .fnend
+
+ .section .debug_str_offsets,"",%progbits
+ .long 48
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",%progbits,1
+.Linfo_string0:
+ .asciz "clang version 10.0.0 (git at github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)"
+.Linfo_string1:
+ .asciz "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c"
+.Linfo_string2:
+ .asciz "/work/scratch"
+.Linfo_string3:
+ .asciz "foo"
+.Linfo_string4:
+ .asciz "int"
+.Linfo_string5:
+ .asciz "bar"
+.Linfo_string6:
+ .asciz "a"
+.Linfo_string7:
+ .asciz "b"
+.Linfo_string8:
+ .asciz "c"
+.Linfo_string9:
+ .asciz "x"
+.Linfo_string10:
+ .asciz "y"
+ .section .debug_str_offsets,"",%progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .section .debug_loclists,"",%progbits
+ .long .Ldebug_loclist_table_end0-.Ldebug_loclist_table_start0
+.Ldebug_loclist_table_start0:
+ .short 5
+ .byte 4
+ .byte 0
+ .long 3
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+ .long .Ldebug_loc1-.Lloclists_table_base0
+ .long .Ldebug_loc2-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0
+ .uleb128 .Ltmp0-.Lfunc_begin0
+ .byte 1
+ .byte 80
+ .byte 0
+.Ldebug_loc1:
+ .byte 4
+ .uleb128 .Ltmp0-.Lfunc_begin0
+ .uleb128 .Ltmp1-.Lfunc_begin0
+ .byte 1
+ .byte 80
+ .byte 0
+.Ldebug_loc2:
+ .byte 4
+ .uleb128 .Ltmp1-.Lfunc_begin0
+ .uleb128 .Lfunc_end0-.Lfunc_begin0
+ .byte 1
+ .byte 80
+ .byte 0
+.Ldebug_loclist_table_end0:
+ .section .debug_abbrev,"",%progbits
+ .byte 1
+ .byte 17
+ .byte 1
+ .byte 37
+ .byte 37
+ .byte 19
+ .byte 5
+ .byte 3
+ .byte 37
+ .byte 114
+ .byte 23
+ .byte 16
+ .byte 23
+ .byte 27
+ .byte 37
+ .byte 17
+ .byte 27
+ .byte 18
+ .byte 6
+ .byte 115
+ .byte 23
+ .ascii "\214\001"
+ .byte 23
+ .byte 0
+ .byte 0
+ .byte 2
+ .byte 46
+ .byte 1
+ .byte 17
+ .byte 27
+ .byte 18
+ .byte 6
+ .byte 64
+ .byte 24
+ .byte 122
+ .byte 25
+ .byte 3
+ .byte 37
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 39
+ .byte 25
+ .byte 73
+ .byte 19
+ .byte 63
+ .byte 25
+ .byte 0
+ .byte 0
+ .byte 3
+ .byte 5
+ .byte 0
+ .byte 2
+ .byte 34
+ .byte 3
+ .byte 37
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 4
+ .byte 5
+ .byte 0
+ .byte 2
+ .byte 24
+ .byte 3
+ .byte 37
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 5
+ .byte 52
+ .byte 0
+ .byte 2
+ .byte 34
+ .byte 3
+ .byte 37
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 6
+ .byte 36
+ .byte 0
+ .byte 3
+ .byte 37
+ .byte 62
+ .byte 11
+ .byte 11
+ .byte 11
+ .byte 0
+ .byte 0
+ .byte 0
+ .section .debug_info,"",%progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0
+.Ldebug_info_start0:
+ .short 5
+ .byte 1
+ .byte 4
+ .long .debug_abbrev
+ .byte 1
+ .byte 0
+ .short 12
+ .byte 1
+ .long .Lstr_offsets_base0
+ .long .Lline_table_start0
+ .byte 2
+ .byte 0
+ .long .Lfunc_end1-.Lfunc_begin0
+ .long .Laddr_table_base0
+ .long .Lloclists_table_base0
+ .byte 2
+ .byte 0
+ .long .Lfunc_end0-.Lfunc_begin0
+ .byte 1
+ .byte 91
+
+ .byte 3
+ .byte 1
+ .byte 1
+
+ .long 128
+
+ .byte 3
+ .byte 0
+ .byte 6
+ .byte 1
+ .byte 1
+ .long 128
+ .byte 4
+ .byte 1
+ .byte 81
+ .byte 7
+ .byte 1
+ .byte 1
+ .long 128
+ .byte 4
+ .byte 1
+ .byte 82
+ .byte 8
+ .byte 1
+ .byte 1
+ .long 128
+ .byte 5
+ .byte 1
+ .byte 9
+ .byte 1
+ .byte 2
+ .long 128
+ .byte 5
+ .byte 2
+ .byte 10
+ .byte 1
+ .byte 3
+ .long 128
+ .byte 0
+ .byte 2
+ .byte 1
+ .long .Lfunc_end1-.Lfunc_begin1
+ .byte 1
+ .byte 91
+
+ .byte 5
+ .byte 1
+ .byte 7
+
+ .long 128
+
+ .byte 4
+ .byte 1
+ .byte 80
+ .byte 6
+ .byte 1
+ .byte 7
+ .long 128
+ .byte 0
+ .byte 6
+ .byte 4
+ .byte 5
+ .byte 4
+ .byte 0
+.Ldebug_info_end0:
+ .section .debug_addr,"",%progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0
+.Ldebug_addr_start0:
+ .short 5
+ .byte 4
+ .byte 0
+.Laddr_table_base0:
+ .long .Lfunc_begin0
+ .long .Lfunc_begin1
+.Ldebug_addr_end0:
+ .ident "clang version 10.0.0 (git at github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)"
+ .section ".note.GNU-stack","",%progbits
+ .addrsig
+ .eabi_attribute 30, 1
+ .section .debug_line,"",%progbits
+.Lline_table_start0:
diff --git a/llvm/test/tools/llvm-objdump/ARM/debug-vars-wide-chars.s b/llvm/test/tools/llvm-objdump/ARM/debug-vars-wide-chars.s
new file mode 100644
index 000000000000..2573dc63513e
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-wide-chars.s
@@ -0,0 +1,232 @@
+# RUN: mkdir -p %t/a
+# RUN: cp %p/Inputs/wide-char.c %t/a/wide-char.c
+# RUN: sed -e "s,SRC_COMPDIR,%/t/a,g" %s > %t.s
+# RUN: llvm-mc -triple armv8a--none-eabi < %t.s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars --source | \
+# RUN: FileCheck %s --strict-whitespace
+
+## The Chinese character in the source does not print correctly on Windows.
+# UNSUPPORTED: system-windows
+
+## Check that the --debug-vars option correctly aligns the variable display when
+## the source code (printed by the -S option) includes East Asian wide
+## characters.
+
+# CHECK: 00000000 <foo>:
+# CHECK-NEXT: ; return *喵; ┠─ 喵 = R0
+# CHECK-NEXT: 0: 00 00 90 e5 ldr r0, [r0] ┻
+# CHECK-NEXT: 4: 1e ff 2f e1 bx lr
+
+ .text
+ .syntax unified
+ .eabi_attribute 67, "2.09"
+ .eabi_attribute 6, 10
+ .eabi_attribute 7, 65
+ .eabi_attribute 8, 1
+ .eabi_attribute 9, 2
+ .fpu vfpv3
+ .eabi_attribute 34, 0
+ .eabi_attribute 17, 1
+ .eabi_attribute 20, 1
+ .eabi_attribute 21, 1
+ .eabi_attribute 23, 3
+ .eabi_attribute 24, 1
+ .eabi_attribute 25, 1
+ .eabi_attribute 38, 1
+ .eabi_attribute 18, 4
+ .eabi_attribute 26, 2
+ .eabi_attribute 14, 0
+ .file "wide.c"
+ .globl foo
+ .p2align 2
+ .type foo,%function
+ .code 32
+foo:
+.Lfunc_begin0:
+ .file 1 "SRC_COMPDIR/wide-char.c"
+ .loc 1 1 0
+ .fnstart
+ .cfi_sections .debug_frame
+ .cfi_startproc
+ .loc 1 2 10 prologue_end
+ ldr r0, [r0]
+.Ltmp0:
+ .loc 1 2 3 is_stmt 0
+ bx lr
+.Ltmp1:
+.Lfunc_end0:
+ .size foo, .Lfunc_end0-foo
+ .cfi_endproc
+ .cantunwind
+ .fnend
+
+ .section .debug_str,"MS",%progbits,1
+.Linfo_string0:
+ .asciz "clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)"
+.Linfo_string1:
+ .asciz "wide-char.c"
+.Linfo_string2:
+ .asciz "SRC_COMPDIR"
+.Linfo_string3:
+ .asciz "foo"
+.Linfo_string4:
+ .asciz "int"
+.Linfo_string5:
+ .asciz "\345\226\265"
+ .section .debug_loc,"",%progbits
+.Ldebug_loc0:
+ .long .Lfunc_begin0-.Lfunc_begin0
+ .long .Ltmp0-.Lfunc_begin0
+ .short 1
+ .byte 80
+ .long 0
+ .long 0
+ .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 46
+ .byte 1
+ .byte 17
+ .byte 1
+ .byte 18
+ .byte 6
+ .byte 64
+ .byte 24
+ .byte 3
+ .byte 14
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 39
+ .byte 25
+ .byte 73
+ .byte 19
+ .byte 63
+ .byte 25
+ .byte 0
+ .byte 0
+ .byte 3
+ .byte 5
+ .byte 0
+ .byte 2
+ .byte 23
+ .byte 3
+ .byte 14
+ .byte 58
+ .byte 11
+ .byte 59
+ .byte 11
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 4
+ .byte 36
+ .byte 0
+ .byte 3
+ .byte 14
+ .byte 62
+ .byte 11
+ .byte 11
+ .byte 11
+ .byte 0
+ .byte 0
+ .byte 5
+ .byte 15
+ .byte 0
+ .byte 73
+ .byte 19
+ .byte 0
+ .byte 0
+ .byte 0
+ .section .debug_info,"",%progbits
+.Lcu_begin0:
+ .long 84
+ .short 4
+ .long .debug_abbrev
+ .byte 4
+ .byte 1
+ .long .Linfo_string0
+ .short 12
+ .long .Linfo_string1
+ .long .Lline_table_start0
+ .long .Linfo_string2
+
+ .long .Lfunc_begin0
+ .long .Lfunc_end0-.Lfunc_begin0
+ .byte 2
+ .long .Lfunc_begin0
+ .long .Lfunc_end0-.Lfunc_begin0
+ .byte 1
+ .byte 91
+ .long .Linfo_string3
+ .byte 1
+ .byte 1
+
+ .long 75
+
+ .byte 3
+ .long .Ldebug_loc0
+ .long .Linfo_string5
+ .byte 1
+ .byte 1
+ .long 82
+ .byte 0
+ .byte 4
+ .long .Linfo_string4
+ .byte 5
+ .byte 4
+ .byte 5
+ .long 75
+ .byte 0
+ .section .debug_ranges,"",%progbits
+ .section .debug_macinfo,"",%progbits
+.Lcu_macro_begin0:
+ .byte 0
+ .section .debug_pubnames,"",%progbits
+ .long .LpubNames_end0-.LpubNames_begin0
+.LpubNames_begin0:
+ .short 2
+ .long .Lcu_begin0
+ .long 88
+ .long 38
+ .asciz "foo"
+ .long 0
+.LpubNames_end0:
+ .section .debug_pubtypes,"",%progbits
+ .long .LpubTypes_end0-.LpubTypes_begin0
+.LpubTypes_begin0:
+ .short 2
+ .long .Lcu_begin0
+ .long 88
+ .long 75
+ .asciz "int"
+ .long 0
+.LpubTypes_end0:
+
+ .ident "clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)"
+ .section ".note.GNU-stack","",%progbits
+ .eabi_attribute 30, 1
+ .section .debug_line,"",%progbits
+.Lline_table_start0:
diff --git a/llvm/test/tools/llvm-objdump/PowerPC/debug-vars.s b/llvm/test/tools/llvm-objdump/PowerPC/debug-vars.s
new file mode 100644
index 000000000000..6c3d326843c7
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/PowerPC/debug-vars.s
@@ -0,0 +1,372 @@
+## Check that the --debug-vars option works for simple register locations, when
+## using DWARF4 debug info, with multiple functions in one section.
+
+## Generated with this compile command and source code:
+## clang --target=arm--none-eabi -march=armv7-a -c debug.c -O1 -gdwarf-3 -S -o -
+
+## clang --target=powerpc64-unknown-linux -c debug.c -O1 -S -o -
+
+## int foo(int a, int b, int c) {
+## int x = a + b;
+## int y = x + c;
+## return y;
+## }
+##
+## int bar(int a) {
+## a++;
+## return a;
+## }
+
+# RUN: llvm-mc -triple powerpc64-unknown-linux < %s -filetype=obj | \
+# RUN: llvm-objdump - -d --debug-vars --no-show-raw-insn | \
+# RUN: FileCheck %s
+
+# CHECK: Disassembly of section .text:
+# CHECK-EMPTY:
+# CHECK-NEXT: 0000000000000000 <.text>:
+# CHECK-NEXT: ┠─ a = S3
+# CHECK-NEXT: ┃ ┠─ b = S4
+# CHECK-NEXT: ┃ ┃ ┠─ c = S5
+# CHECK-NEXT: ┃ ┃ ┃ ┌─ x = S3
+# CHECK-NEXT: 0: add 3, 4, 3 ┻ ┃ ┃ ╈
+# CHECK-NEXT: ┌─ y = S3
+# CHECK-NEXT: 4: add 3, 3, 5 ╈ ┃ ┃ ┻
+# CHECK-NEXT: 8: extsw 3, 3 ┻ ┃ ┃
+# CHECK-NEXT: c: blr ┃ ┃
+# CHECK-NEXT: ...
+# CHECK-NEXT: ┠─ a = S3
+# CHECK-NEXT: 1c: addi 3, 3, 1 ┃
+# CHECK-NEXT: 20: extsw 3, 3 ┻
+# CHECK-NEXT: 24: blr
+# CHECK-NEXT: ...
+
+ .text
+ .file "debug.c"
+ .globl foo # -- Begin function foo
+ .p2align 2
+ .type foo, at function
+ .section .opd,"aw", at progbits
+foo: # @foo
+ .p2align 3
+ .quad .Lfunc_begin0
+ .quad .TOC. at tocbase
+ .quad 0
+ .text
+.Lfunc_begin0:
+ .file 1 "/work" "llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c"
+ .loc 1 1 0 # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:1:0
+ .cfi_sections .debug_frame
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: foo:a <- $x3
+ #DEBUG_VALUE: foo:a <- $r3
+ #DEBUG_VALUE: foo:b <- $x4
+ #DEBUG_VALUE: foo:b <- $x4
+ #DEBUG_VALUE: foo:b <- $r4
+ #DEBUG_VALUE: foo:c <- $x5
+ #DEBUG_VALUE: foo:c <- $x5
+ #DEBUG_VALUE: foo:c <- $r5
+ .loc 1 2 13 prologue_end # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:2:13
+ add 3, 4, 3
+.Ltmp0:
+ #DEBUG_VALUE: foo:x <- $r3
+ .loc 1 3 13 # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:3:13
+ add 3, 3, 5
+.Ltmp1:
+ #DEBUG_VALUE: foo:y <- $r3
+ .loc 1 4 3 # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:4:3
+ extsw 3, 3
+.Ltmp2:
+ blr
+.Ltmp3:
+ .long 0
+ .quad 0
+.Lfunc_end0:
+ .size foo, .Lfunc_end0-.Lfunc_begin0
+ .cfi_endproc
+ # -- End function
+ .globl bar # -- Begin function bar
+ .p2align 2
+ .type bar, at function
+ .section .opd,"aw", at progbits
+bar: # @bar
+ .p2align 3
+ .quad .Lfunc_begin1
+ .quad .TOC. at tocbase
+ .quad 0
+ .text
+.Lfunc_begin1:
+ .loc 1 7 0 # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:7:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: bar:a <- $x3
+ #DEBUG_VALUE: bar:a <- $r3
+ .loc 1 8 4 prologue_end # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:8:4
+ addi 3, 3, 1
+.Ltmp4:
+ #DEBUG_VALUE: bar:a <- $r3
+ .loc 1 9 3 # llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c:9:3
+ extsw 3, 3
+.Ltmp5:
+ blr
+.Ltmp6:
+ .long 0
+ .quad 0
+.Lfunc_end1:
+ .size bar, .Lfunc_end1-.Lfunc_begin1
+ .cfi_endproc
+ # -- End function
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 10.0.0 (git at github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)" # string offset=0
+.Linfo_string1:
+ .asciz "/work/llvm/src/llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c" # string offset=101
+.Linfo_string2:
+ .asciz "/work/scratch" # string offset=164
+.Linfo_string3:
+ .asciz "foo" # string offset=178
+.Linfo_string4:
+ .asciz "int" # string offset=182
+.Linfo_string5:
+ .asciz "bar" # string offset=186
+.Linfo_string6:
+ .asciz "a" # string offset=190
+.Linfo_string7:
+ .asciz "b" # string offset=192
+.Linfo_string8:
+ .asciz "c" # string offset=194
+.Linfo_string9:
+ .asciz "x" # string offset=196
+.Linfo_string10:
+ .asciz "y" # string offset=198
+ .section .debug_loc,"", at progbits
+.Ldebug_loc0:
+ .quad .Lfunc_begin0-.Lfunc_begin0
+ .quad .Ltmp0-.Lfunc_begin0
+ .short 3 # Loc expr size
+ .byte 144 # super-register DW_OP_regx
+ .byte 179 # 1203
+ .byte 9 #
+ .quad 0
+ .quad 0
+.Ldebug_loc1:
+ .quad .Ltmp0-.Lfunc_begin0
+ .quad .Ltmp1-.Lfunc_begin0
+ .short 3 # Loc expr size
+ .byte 144 # super-register DW_OP_regx
+ .byte 179 # 1203
+ .byte 9 #
+ .quad 0
+ .quad 0
+.Ldebug_loc2:
+ .quad .Ltmp1-.Lfunc_begin0
+ .quad .Ltmp2-.Lfunc_begin0
+ .short 3 # Loc expr size
+ .byte 144 # super-register DW_OP_regx
+ .byte 179 # 1203
+ .byte 9 #
+ .quad 0
+ .quad 0
+.Ldebug_loc3:
+ .quad .Lfunc_begin1-.Lfunc_begin0
+ .quad .Ltmp5-.Lfunc_begin0
+ .short 3 # Loc expr size
+ .byte 144 # super-register DW_OP_regx
+ .byte 179 # 1203
+ .byte 9 #
+ .quad 0
+ .quad 0
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 14 # DW_FORM_strp
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 14 # DW_FORM_strp
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .ascii "\227B" # DW_AT_GNU_all_call_sites
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 23 # DW_FORM_sec_offset
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 23 # DW_FORM_sec_offset
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0xb5 DW_TAG_compile_unit
+ .long .Linfo_string0 # DW_AT_producer
+ .short 12 # DW_AT_language
+ .long .Linfo_string1 # DW_AT_name
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .long .Linfo_string2 # DW_AT_comp_dir
+ .quad .Lfunc_begin0 # DW_AT_low_pc
+ .long .Lfunc_end1-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 2 # Abbrev [2] 0x2a:0x65 DW_TAG_subprogram
+ .quad .Lfunc_begin0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 81
+ # DW_AT_GNU_all_call_sites
+ .long .Linfo_string3 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 184 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x43:0xf DW_TAG_formal_parameter
+ .long .Ldebug_loc0 # DW_AT_location
+ .long .Linfo_string6 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .long 184 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x52:0xf DW_TAG_formal_parameter
+ .byte 3 # DW_AT_location
+ .byte 144
+ .ascii "\264\t"
+ .long .Linfo_string7 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .long 184 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x61:0xf DW_TAG_formal_parameter
+ .byte 3 # DW_AT_location
+ .byte 144
+ .ascii "\265\t"
+ .long .Linfo_string8 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .long 184 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x70:0xf DW_TAG_variable
+ .long .Ldebug_loc1 # DW_AT_location
+ .long .Linfo_string9 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 184 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x7f:0xf DW_TAG_variable
+ .long .Ldebug_loc2 # DW_AT_location
+ .long .Linfo_string10 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 184 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 2 # Abbrev [2] 0x8f:0x29 DW_TAG_subprogram
+ .quad .Lfunc_begin1 # DW_AT_low_pc
+ .long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 81
+ # DW_AT_GNU_all_call_sites
+ .long .Linfo_string5 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 7 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 184 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0xa8:0xf DW_TAG_formal_parameter
+ .long .Ldebug_loc3 # DW_AT_location
+ .long .Linfo_string6 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 7 # DW_AT_decl_line
+ .long 184 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 6 # Abbrev [6] 0xb8:0x7 DW_TAG_base_type
+ .long .Linfo_string4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .ident "clang version 10.0.0 (git at github.com:llvm/llvm-project.git e73f78acd34360f7450b81167d9dc858ccddc262)"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 81b3aac5c931..7d282074efa6 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -21,7 +21,9 @@
#include "MachODump.h"
#include "WasmDump.h"
#include "XCOFFDump.h"
+#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/StringExtras.h"
@@ -79,6 +81,8 @@ using namespace llvm;
using namespace llvm::object;
using namespace llvm::objdump;
+#define DEBUG_TYPE "objdump"
+
static cl::OptionCategory ObjdumpCat("llvm-objdump Options");
static cl::opt<uint64_t> AdjustVMA(
@@ -344,6 +348,28 @@ static cl::opt<bool>
cl::cat(ObjdumpCat));
static cl::alias WideShort("w", cl::Grouping, cl::aliasopt(Wide));
+enum DebugVarsFormat {
+ DVDisabled,
+ DVUnicode,
+ DVASCII,
+};
+
+static cl::opt<DebugVarsFormat> DbgVariables(
+ "debug-vars", cl::init(DVDisabled),
+ cl::desc("Print the locations (in registers or memory) of "
+ "source-level variables alongside disassembly"),
+ cl::ValueOptional,
+ cl::values(clEnumValN(DVUnicode, "", "unicode"),
+ clEnumValN(DVUnicode, "unicode", "unicode"),
+ clEnumValN(DVASCII, "ascii", "unicode")),
+ cl::cat(ObjdumpCat));
+
+static cl::opt<int>
+ DbgIndent("debug-vars-indent", cl::init(40),
+ cl::desc("Distance to indent the source-level variable display, "
+ "relative to the start of the disassembly"),
+ cl::cat(ObjdumpCat));
+
static cl::extrahelp
HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
@@ -548,6 +574,357 @@ static bool getHidden(RelocationRef RelRef) {
}
namespace {
+
+/// Get the column at which we want to start printing the instruction
+/// disassembly, taking into account anything which appears to the left of it.
+unsigned getInstStartColumn(const MCSubtargetInfo &STI) {
+ return NoShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
+}
+
+/// Stores a single expression representing the location of a source-level
+/// variable, along with the PC range for which that expression is valid.
+struct LiveVariable {
+ DWARFLocationExpression LocExpr;
+ const char *VarName;
+ DWARFUnit *Unit;
+ const DWARFDie FuncDie;
+
+ LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName,
+ DWARFUnit *Unit, const DWARFDie FuncDie)
+ : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {}
+
+ bool liveAtAddress(object::SectionedAddress Addr) {
+ if (LocExpr.Range == None)
+ return false;
+ return LocExpr.Range->SectionIndex == Addr.SectionIndex &&
+ LocExpr.Range->LowPC <= Addr.Address &&
+ LocExpr.Range->HighPC > Addr.Address;
+ }
+
+ void print(raw_ostream &OS, const MCRegisterInfo &MRI) const {
+ DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()},
+ Unit->getContext().isLittleEndian(), 0);
+ DWARFExpression Expression(Data, Unit->getAddressByteSize());
+ Expression.printCompact(OS, MRI);
+ }
+};
+
+/// Helper class for printing source variable locations alongside disassembly.
+class LiveVariablePrinter {
+ // Information we want to track about one column in which we are printing a
+ // variable live range.
+ struct Column {
+ unsigned VarIdx = NullVarIdx;
+ bool LiveIn = false;
+ bool LiveOut = false;
+ bool MustDrawLabel = false;
+
+ bool isActive() const { return VarIdx != NullVarIdx; }
+
+ static constexpr unsigned NullVarIdx = std::numeric_limits<unsigned>::max();
+ };
+
+ // All live variables we know about in the object/image file.
+ std::vector<LiveVariable> LiveVariables;
+
+ // The columns we are currently drawing.
+ IndexedMap<Column> ActiveCols;
+
+ const MCRegisterInfo &MRI;
+ const MCSubtargetInfo &STI;
+
+ void addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
+ uint64_t FuncLowPC, FuncHighPC, SectionIndex;
+ FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex);
+ const char *VarName = VarDie.getName(DINameKind::ShortName);
+ DWARFUnit *U = VarDie.getDwarfUnit();
+
+ Expected<DWARFLocationExpressionsVector> Locs =
+ VarDie.getLocations(dwarf::DW_AT_location);
+ if (!Locs) {
+ // If the variable doesn't have any locations, just ignore it. We don't
+ // report an error or warning here as that could be noisy on optimised
+ // code.
+ consumeError(Locs.takeError());
+ return;
+ }
+
+ for (const DWARFLocationExpression &LocExpr : *Locs) {
+ if (LocExpr.Range) {
+ LiveVariables.emplace_back(LocExpr, VarName, U, FuncDie);
+ } else {
+ // If the LocExpr does not have an associated range, it is valid for
+ // the whole of the function.
+ // TODO: technically it is not valid for any range covered by another
+ // LocExpr, does that happen in reality?
+ DWARFLocationExpression WholeFuncExpr{
+ DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex),
+ LocExpr.Expr};
+ LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie);
+ }
+ }
+ }
+
+ void addFunction(DWARFDie D) {
+ for (const DWARFDie &Child : D.children()) {
+ if (Child.getTag() == dwarf::DW_TAG_variable ||
+ Child.getTag() == dwarf::DW_TAG_formal_parameter)
+ addVariable(D, Child);
+ else
+ addFunction(Child);
+ }
+ }
+
+ // Get the column number (in characters) at which the first live variable
+ // line should be printed.
+ unsigned getIndentLevel() const {
+ return DbgIndent + getInstStartColumn(STI);
+ }
+
+ // Indent to the first live-range column to the right of the currently
+ // printed line, and return the index of that column.
+ // TODO: formatted_raw_ostream uses "column" to mean a number of characters
+ // since the last \n, and we use it to mean the number of slots in which we
+ // put live variable lines. Pick a less overloaded word.
+ unsigned moveToFirstVarColumn(formatted_raw_ostream &OS) {
+ // Logical column number: column zero is the first column we print in, each
+ // logical column is 2 physical columns wide.
+ unsigned FirstUnprintedLogicalColumn =
+ std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0);
+ // Physical column number: the actual column number in characters, with
+ // zero being the left-most side of the screen.
+ unsigned FirstUnprintedPhysicalColumn =
+ getIndentLevel() + FirstUnprintedLogicalColumn * 2;
+
+ if (FirstUnprintedPhysicalColumn > OS.getColumn())
+ OS.PadToColumn(FirstUnprintedPhysicalColumn);
+
+ return FirstUnprintedLogicalColumn;
+ }
+
+ unsigned findFreeColumn() {
+ for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx)
+ if (!ActiveCols[ColIdx].isActive())
+ return ColIdx;
+
+ size_t OldSize = ActiveCols.size();
+ ActiveCols.grow(std::max<size_t>(OldSize * 2, 1));
+ return OldSize;
+ }
+
+public:
+ LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI)
+ : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {}
+
+ void dump() const {
+ for (const LiveVariable &LV : LiveVariables) {
+ dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": ";
+ LV.print(dbgs(), MRI);
+ dbgs() << "\n";
+ }
+ }
+
+ void addCompileUnit(DWARFDie D) {
+ if (D.getTag() == dwarf::DW_TAG_subprogram)
+ addFunction(D);
+ else
+ for (const DWARFDie &Child : D.children())
+ addFunction(Child);
+ }
+
+ /// Update to match the state of the instruction between ThisAddr and
+ /// NextAddr. In the common case, any live range active at ThisAddr is
+ /// live-in to the instruction, and any live range active at NextAddr is
+ /// live-out of the instruction. If IncludeDefinedVars is false, then live
+ /// ranges starting at NextAddr will be ignored.
+ void update(object::SectionedAddress ThisAddr,
+ object::SectionedAddress NextAddr, bool IncludeDefinedVars) {
+ // First, check variables which have already been assigned a column, so
+ // that we don't change their order.
+ SmallSet<unsigned, 8> CheckedVarIdxs;
+ for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
+ if (!ActiveCols[ColIdx].isActive())
+ continue;
+ CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx);
+ LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx];
+ ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr);
+ ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr);
+ LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-"
+ << NextAddr.Address << ", " << LV.VarName << ", Col "
+ << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn
+ << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n");
+
+ if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
+ ActiveCols[ColIdx].VarIdx = Column::NullVarIdx;
+ }
+
+ // Next, look for variables which don't already have a column, but which
+ // are now live.
+ if (IncludeDefinedVars) {
+ for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End;
+ ++VarIdx) {
+ if (CheckedVarIdxs.count(VarIdx))
+ continue;
+ LiveVariable &LV = LiveVariables[VarIdx];
+ bool LiveIn = LV.liveAtAddress(ThisAddr);
+ bool LiveOut = LV.liveAtAddress(NextAddr);
+ if (!LiveIn && !LiveOut)
+ continue;
+
+ unsigned ColIdx = findFreeColumn();
+ LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-"
+ << NextAddr.Address << ", " << LV.VarName << ", Col "
+ << ColIdx << ": LiveIn=" << LiveIn
+ << ", LiveOut=" << LiveOut << "\n");
+ ActiveCols[ColIdx].VarIdx = VarIdx;
+ ActiveCols[ColIdx].LiveIn = LiveIn;
+ ActiveCols[ColIdx].LiveOut = LiveOut;
+ ActiveCols[ColIdx].MustDrawLabel = true;
+ }
+ }
+ }
+
+ enum class LineChar {
+ RangeStart,
+ RangeMid,
+ RangeEnd,
+ LabelVert,
+ LabelCornerNew,
+ LabelCornerActive,
+ LabelHoriz,
+ };
+ const char *getLineChar(LineChar C) const {
+ bool IsASCII = DbgVariables == DVASCII;
+ switch (C) {
+ case LineChar::RangeStart:
+ return IsASCII ? "^" : u8"\u2548";
+ case LineChar::RangeMid:
+ return IsASCII ? "|" : u8"\u2503";
+ case LineChar::RangeEnd:
+ return IsASCII ? "v" : u8"\u253b";
+ case LineChar::LabelVert:
+ return IsASCII ? "|" : u8"\u2502";
+ case LineChar::LabelCornerNew:
+ return IsASCII ? "/" : u8"\u250c";
+ case LineChar::LabelCornerActive:
+ return IsASCII ? "|" : u8"\u2520";
+ case LineChar::LabelHoriz:
+ return IsASCII ? "-" : u8"\u2500";
+ }
+ }
+
+ /// Print live ranges to the right of an existing line. This assumes the
+ /// line is not an instruction, so doesn't start or end any live ranges, so
+ /// we only need to print active ranges or empty columns. If AfterInst is
+ /// true, this is being printed after the last instruction fed to update(),
+ /// otherwise this is being printed before it.
+ void printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst) {
+ if (ActiveCols.size()) {
+ unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
+ for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
+ ColIdx < End; ++ColIdx) {
+ if (ActiveCols[ColIdx].isActive()) {
+ if ((AfterInst && ActiveCols[ColIdx].LiveOut) ||
+ (!AfterInst && ActiveCols[ColIdx].LiveIn))
+ OS << getLineChar(LineChar::RangeMid);
+ else if (!AfterInst && ActiveCols[ColIdx].LiveOut)
+ OS << getLineChar(LineChar::LabelVert);
+ else
+ OS << " ";
+ }
+ OS << " ";
+ }
+ }
+ OS << "\n";
+ }
+
+ /// Print any live variable range info needed to the right of a
+ /// non-instruction line of disassembly. This is where we print the variable
+ /// names and expressions, with thin line-drawing characters connecting them
+ /// to the live range which starts at the next instruction. If MustPrint is
+ /// true, we have to print at least one line (with the continuation of any
+ /// already-active live ranges) because something has already been printed
+ /// earlier on this line.
+ void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint) {
+ bool PrintedSomething = false;
+ for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
+ if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) {
+ // First we need to print the live range markers for any active
+ // columns to the left of this one.
+ OS.PadToColumn(getIndentLevel());
+ for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) {
+ if (ActiveCols[ColIdx2].isActive()) {
+ if (ActiveCols[ColIdx2].MustDrawLabel &&
+ !ActiveCols[ColIdx2].LiveIn)
+ OS << getLineChar(LineChar::LabelVert) << " ";
+ else
+ OS << getLineChar(LineChar::RangeMid) << " ";
+ } else
+ OS << " ";
+ }
+
+ // Then print the variable name and location of the new live range,
+ // with box drawing characters joining it to the live range line.
+ OS << getLineChar(ActiveCols[ColIdx].LiveIn
+ ? LineChar::LabelCornerActive
+ : LineChar::LabelCornerNew)
+ << getLineChar(LineChar::LabelHoriz) << " ";
+ WithColor(OS, raw_ostream::GREEN)
+ << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName;
+ OS << " = ";
+ {
+ WithColor ExprColor(OS, raw_ostream::CYAN);
+ LiveVariables[ActiveCols[ColIdx].VarIdx].print(OS, MRI);
+ }
+
+ // If there are any columns to the right of the expression we just
+ // printed, then continue their live range lines.
+ unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
+ for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size();
+ ColIdx2 < End; ++ColIdx2) {
+ if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn)
+ OS << getLineChar(LineChar::RangeMid) << " ";
+ else
+ OS << " ";
+ }
+
+ OS << "\n";
+ PrintedSomething = true;
+ }
+ }
+
+ for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx)
+ if (ActiveCols[ColIdx].isActive())
+ ActiveCols[ColIdx].MustDrawLabel = false;
+
+ // If we must print something (because we printed a line/column number),
+ // but don't have any new variables to print, then print a line which
+ // just continues any existing live ranges.
+ if (MustPrint && !PrintedSomething)
+ printAfterOtherLine(OS, false);
+ }
+
+ /// Print the live variable ranges to the right of a disassembled instruction.
+ void printAfterInst(formatted_raw_ostream &OS) {
+ if (!ActiveCols.size())
+ return;
+ unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
+ for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
+ ColIdx < End; ++ColIdx) {
+ if (!ActiveCols[ColIdx].isActive())
+ OS << " ";
+ else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut)
+ OS << getLineChar(LineChar::RangeMid) << " ";
+ else if (ActiveCols[ColIdx].LiveOut)
+ OS << getLineChar(LineChar::RangeStart) << " ";
+ else if (ActiveCols[ColIdx].LiveIn)
+ OS << getLineChar(LineChar::RangeEnd) << " ";
+ else
+ llvm_unreachable("var must be live in or out!");
+ }
+ }
+};
+
class SourcePrinter {
protected:
DILineInfo OldLineInfo;
@@ -565,11 +942,12 @@ class SourcePrinter {
private:
bool cacheSource(const DILineInfo& LineInfoFile);
- void printLines(raw_ostream &OS, const DILineInfo &LineInfo,
- StringRef Delimiter);
+ void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo,
+ StringRef Delimiter, LiveVariablePrinter &LVP);
- void printSources(raw_ostream &OS, const DILineInfo &LineInfo,
- StringRef ObjectFilename, StringRef Delimiter);
+ void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo,
+ StringRef ObjectFilename, StringRef Delimiter,
+ LiveVariablePrinter &LVP);
public:
SourcePrinter() = default;
@@ -583,9 +961,10 @@ class SourcePrinter {
Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
}
virtual ~SourcePrinter() = default;
- virtual void printSourceLine(raw_ostream &OS,
+ virtual void printSourceLine(formatted_raw_ostream &OS,
object::SectionedAddress Address,
StringRef ObjectFilename,
+ LiveVariablePrinter &LVP,
StringRef Delimiter = "; ");
};
@@ -619,9 +998,10 @@ bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) {
return true;
}
-void SourcePrinter::printSourceLine(raw_ostream &OS,
+void SourcePrinter::printSourceLine(formatted_raw_ostream &OS,
object::SectionedAddress Address,
StringRef ObjectFilename,
+ LiveVariablePrinter &LVP,
StringRef Delimiter) {
if (!Symbolizer)
return;
@@ -646,14 +1026,15 @@ void SourcePrinter::printSourceLine(raw_ostream &OS,
}
if (PrintLines)
- printLines(OS, LineInfo, Delimiter);
+ printLines(OS, LineInfo, Delimiter, LVP);
if (PrintSource)
- printSources(OS, LineInfo, ObjectFilename, Delimiter);
+ printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP);
OldLineInfo = LineInfo;
}
-void SourcePrinter::printLines(raw_ostream &OS, const DILineInfo &LineInfo,
- StringRef Delimiter) {
+void SourcePrinter::printLines(formatted_raw_ostream &OS,
+ const DILineInfo &LineInfo, StringRef Delimiter,
+ LiveVariablePrinter &LVP) {
bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString &&
LineInfo.FunctionName != OldLineInfo.FunctionName;
if (PrintFunctionName) {
@@ -666,13 +1047,16 @@ void SourcePrinter::printLines(raw_ostream &OS, const DILineInfo &LineInfo,
}
if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 &&
(OldLineInfo.Line != LineInfo.Line ||
- OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName))
- OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n";
+ OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) {
+ OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line;
+ LVP.printBetweenInsts(OS, true);
+ }
}
-void SourcePrinter::printSources(raw_ostream &OS, const DILineInfo &LineInfo,
- StringRef ObjectFilename,
- StringRef Delimiter) {
+void SourcePrinter::printSources(formatted_raw_ostream &OS,
+ const DILineInfo &LineInfo,
+ StringRef ObjectFilename, StringRef Delimiter,
+ LiveVariablePrinter &LVP) {
if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 ||
(OldLineInfo.Line == LineInfo.Line &&
OldLineInfo.FileName == LineInfo.FileName))
@@ -692,7 +1076,8 @@ void SourcePrinter::printSources(raw_ostream &OS, const DILineInfo &LineInfo,
return;
}
// Vector begins at 0, line numbers are non-zero
- OS << Delimiter << LineBuffer->second[LineInfo.Line - 1] << '\n';
+ OS << Delimiter << LineBuffer->second[LineInfo.Line - 1];
+ LVP.printBetweenInsts(OS, true);
}
}
@@ -710,28 +1095,30 @@ static bool hasMappingSymbols(const ObjectFile *Obj) {
return isArmElf(Obj) || isAArch64Elf(Obj);
}
-static void printRelocation(StringRef FileName, const RelocationRef &Rel,
- uint64_t Address, bool Is64Bits) {
+static void printRelocation(formatted_raw_ostream &OS, StringRef FileName,
+ const RelocationRef &Rel, uint64_t Address,
+ bool Is64Bits) {
StringRef Fmt = Is64Bits ? "\t\t%016" PRIx64 ": " : "\t\t\t%08" PRIx64 ": ";
SmallString<16> Name;
SmallString<32> Val;
Rel.getTypeName(Name);
if (Error E = getRelocationValueString(Rel, Val))
reportError(std::move(E), FileName);
- outs() << format(Fmt.data(), Address) << Name << "\t" << Val << "\n";
+ OS << format(Fmt.data(), Address) << Name << "\t" << Val;
}
class PrettyPrinter {
public:
virtual ~PrettyPrinter() = default;
- virtual void printInst(MCInstPrinter &IP, const MCInst *MI,
- ArrayRef<uint8_t> Bytes,
- object::SectionedAddress Address, raw_ostream &OS,
- StringRef Annot, MCSubtargetInfo const &STI,
- SourcePrinter *SP, StringRef ObjectFilename,
- std::vector<RelocationRef> *Rels = nullptr) {
+ virtual void
+ printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
+ StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) {
if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, ObjectFilename);
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP);
+ LVP.printBetweenInsts(OS, false);
size_t Start = OS.tell();
if (!NoLeadingAddr)
@@ -741,11 +1128,9 @@ class PrettyPrinter {
dumpBytes(Bytes, OS);
}
- // The output of printInst starts with a tab. Print some spaces so that the
- // tab has 1 column and advances to the target tab stop. Give more columns
- // to x86 which may encode an instruction with many bytes.
- unsigned TabStop =
- NoShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
+ // The output of printInst starts with a tab. Print some spaces so that
+ // the tab has 1 column and advances to the target tab stop.
+ unsigned TabStop = getInstStartColumn(STI);
unsigned Column = OS.tell() - Start;
OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8);
@@ -766,7 +1151,7 @@ PrettyPrinter PrettyPrinterInst;
class HexagonPrettyPrinter : public PrettyPrinter {
public:
void printLead(ArrayRef<uint8_t> Bytes, uint64_t Address,
- raw_ostream &OS) {
+ formatted_raw_ostream &OS) {
uint32_t opcode =
(Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0];
if (!NoLeadingAddr)
@@ -778,12 +1163,12 @@ class HexagonPrettyPrinter : public PrettyPrinter {
}
}
void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
- object::SectionedAddress Address, raw_ostream &OS,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
- StringRef ObjectFilename,
- std::vector<RelocationRef> *Rels) override {
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) override {
if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, ObjectFilename, "");
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP, "");
if (!MI) {
printLead(Bytes, Address.Address, OS);
OS << " <unknown>";
@@ -809,7 +1194,7 @@ class HexagonPrettyPrinter : public PrettyPrinter {
auto PrintReloc = [&]() -> void {
while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address.Address)) {
if (RelCur->getOffset() == Address.Address) {
- printRelocation(ObjectFilename, *RelCur, Address.Address, false);
+ printRelocation(OS, ObjectFilename, *RelCur, Address.Address, false);
return;
}
++RelCur;
@@ -820,7 +1205,7 @@ class HexagonPrettyPrinter : public PrettyPrinter {
OS << Separator;
Separator = "\n";
if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, ObjectFilename, "");
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP, "");
printLead(Bytes, Address.Address, OS);
OS << Preamble;
Preamble = " ";
@@ -848,12 +1233,12 @@ HexagonPrettyPrinter HexagonPrettyPrinterInst;
class AMDGCNPrettyPrinter : public PrettyPrinter {
public:
void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
- object::SectionedAddress Address, raw_ostream &OS,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
- StringRef ObjectFilename,
- std::vector<RelocationRef> *Rels) override {
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) override {
if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, ObjectFilename);
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP);
if (MI) {
SmallString<40> InstStr;
@@ -900,12 +1285,12 @@ AMDGCNPrettyPrinter AMDGCNPrettyPrinterInst;
class BPFPrettyPrinter : public PrettyPrinter {
public:
void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
- object::SectionedAddress Address, raw_ostream &OS,
+ object::SectionedAddress Address, formatted_raw_ostream &OS,
StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
- StringRef ObjectFilename,
- std::vector<RelocationRef> *Rels) override {
+ StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
+ LiveVariablePrinter &LVP) override {
if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, ObjectFilename);
+ SP->printSourceLine(OS, Address, ObjectFilename, LVP);
if (!NoLeadingAddr)
OS << format("%8" PRId64 ":", Address.Address / 8);
if (!NoShowRawInsn) {
@@ -1094,26 +1479,27 @@ static char getMappingSymbolKind(ArrayRef<MappingSymbolPair> MappingSymbols,
static uint64_t dumpARMELFData(uint64_t SectionAddr, uint64_t Index,
uint64_t End, const ObjectFile *Obj,
ArrayRef<uint8_t> Bytes,
- ArrayRef<MappingSymbolPair> MappingSymbols) {
+ ArrayRef<MappingSymbolPair> MappingSymbols,
+ raw_ostream &OS) {
support::endianness Endian =
Obj->isLittleEndian() ? support::little : support::big;
- outs() << format("%8" PRIx64 ":\t", SectionAddr + Index);
+ OS << format("%8" PRIx64 ":\t", SectionAddr + Index);
if (Index + 4 <= End) {
- dumpBytes(Bytes.slice(Index, 4), outs());
- outs() << "\t.word\t"
+ dumpBytes(Bytes.slice(Index, 4), OS);
+ OS << "\t.word\t"
<< format_hex(support::endian::read32(Bytes.data() + Index, Endian),
10);
return 4;
}
if (Index + 2 <= End) {
- dumpBytes(Bytes.slice(Index, 2), outs());
- outs() << "\t\t.short\t"
+ dumpBytes(Bytes.slice(Index, 2), OS);
+ OS << "\t\t.short\t"
<< format_hex(support::endian::read16(Bytes.data() + Index, Endian),
6);
return 2;
}
- dumpBytes(Bytes.slice(Index, 1), outs());
- outs() << "\t\t.byte\t" << format_hex(Bytes[0], 4);
+ dumpBytes(Bytes.slice(Index, 1), OS);
+ OS << "\t\t.byte\t" << format_hex(Bytes[0], 4);
return 1;
}
@@ -1288,6 +1674,17 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
stable_sort(SecSyms.second);
stable_sort(AbsoluteSymbols);
+ std::unique_ptr<DWARFContext> DICtx;
+ LiveVariablePrinter LVP(*Ctx.getRegisterInfo(), *STI);
+
+ if (DbgVariables != DVDisabled) {
+ DICtx = DWARFContext::create(*Obj);
+ for (const std::unique_ptr<DWARFUnit> &CU : DICtx->compile_units())
+ LVP.addCompileUnit(CU->getUnitDIE(false));
+ }
+
+ LLVM_DEBUG(LVP.dump());
+
for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
if (FilterSections.empty() && !DisassembleAll &&
(!Section.isText() || Section.isVirtual()))
@@ -1481,6 +1878,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
Symbols[SI].Type != ELF::STT_OBJECT &&
!DisassembleAll;
bool DumpARMELFData = false;
+ formatted_raw_ostream FOS(outs());
while (Index < End) {
// ARM and AArch64 ELF binaries can interleave data and text in the
// same section. We rely on the markers introduced to understand what
@@ -1502,7 +1900,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (DumpARMELFData) {
Size = dumpARMELFData(SectionAddr, Index, End, Obj, Bytes,
- MappingSymbols);
+ MappingSymbols, FOS);
} else {
// When -z or --disassemble-zeroes are given we always dissasemble
// them. Otherwise we might want to skip zero bytes we see.
@@ -1515,7 +1913,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (size_t N =
countSkippableZeroBytes(Bytes.slice(Index, MaxOffset))) {
- outs() << "\t\t..." << '\n';
+ FOS << "\t\t..." << '\n';
Index += N;
continue;
}
@@ -1530,11 +1928,14 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (Size == 0)
Size = 1;
+ LVP.update({Index, Section.getIndex()},
+ {Index + Size, Section.getIndex()}, Index + Size != End);
+
PIP.printInst(
*IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size),
- {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, outs(),
- "", *STI, &SP, Obj->getFileName(), &Rels);
- outs() << CommentStream.str();
+ {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, FOS,
+ "", *STI, &SP, Obj->getFileName(), &Rels, LVP);
+ FOS << CommentStream.str();
Comments.clear();
// If disassembly has failed, avoid analysing invalid/incomplete
@@ -1551,7 +1952,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
Inst, SectionAddr + Index, Size)) {
Target = *MaybeTarget;
PrintTarget = true;
- outs() << " # " << Twine::utohexstr(Target);
+ FOS << " # " << Twine::utohexstr(Target);
}
if (PrintTarget) {
// In a relocatable object, the target's section must reside in
@@ -1607,16 +2008,18 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
if (Demangle)
TargetName = demangle(TargetName);
- outs() << " <" << TargetName;
+ FOS << " <" << TargetName;
uint64_t Disp = Target - TargetAddress;
if (Disp)
- outs() << "+0x" << Twine::utohexstr(Disp);
- outs() << '>';
+ FOS << "+0x" << Twine::utohexstr(Disp);
+ FOS << '>';
}
}
}
}
- outs() << "\n";
+
+ LVP.printAfterInst(FOS);
+ FOS << "\n";
// Hexagon does this in pretty printer
if (Obj->getArch() != Triple::hexagon) {
@@ -1646,8 +2049,9 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
Offset += AdjustVMA;
}
- printRelocation(Obj->getFileName(), *RelCur, SectionAddr + Offset,
- Is64Bits);
+ printRelocation(FOS, Obj->getFileName(), *RelCur,
+ SectionAddr + Offset, Is64Bits);
+ LVP.printAfterOtherLine(FOS, true);
++RelCur;
}
}
More information about the llvm-commits
mailing list