[llvm] 3a5dded - [llvm-objdump] Display locations of variables alongside disassembly

Oliver Stannard via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 16 03:55:16 PDT 2020


Author: Oliver Stannard
Date: 2020-03-16T10:54:40Z
New Revision: 3a5ddedadb671e485ce5c638142817879ac14a8c

URL: https://github.com/llvm/llvm-project/commit/3a5ddedadb671e485ce5c638142817879ac14a8c
DIFF: https://github.com/llvm/llvm-project/commit/3a5ddedadb671e485ce5c638142817879ac14a8c.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.

Currently, this only works for object files, not executables or shared
libraries.

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/include/llvm/Support/FormattedStream.h
    llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
    llvm/lib/Support/FormattedStream.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 2730374d5810..bb330a0174e3 100644
--- a/llvm/docs/CommandGuide/llvm-objdump.rst
+++ b/llvm/docs/CommandGuide/llvm-objdump.rst
@@ -119,6 +119,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 c4dc53337c07..08cfa3b1daae 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
@@ -137,6 +137,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/include/llvm/Support/FormattedStream.h b/llvm/include/llvm/Support/FormattedStream.h
index b49c8d86531d..88e00647473e 100644
--- a/llvm/include/llvm/Support/FormattedStream.h
+++ b/llvm/include/llvm/Support/FormattedStream.h
@@ -105,11 +105,17 @@ class formatted_raw_ostream : public raw_ostream {
   /// \param NewCol - The column to move to.
   formatted_raw_ostream &PadToColumn(unsigned NewCol);
 
-  /// getColumn - Return the column number
-  unsigned getColumn() { return Position.first; }
+  unsigned getColumn() {
+    // Calculate current position, taking buffer contents into account.
+    ComputePosition(getBufferStart(), GetNumBytesInBuffer());
+    return Position.first;
+  }
 
-  /// getLine - Return the line number
-  unsigned getLine() { return Position.second; }
+  unsigned getLine() {
+    // Calculate current position, taking buffer contents into account.
+    ComputePosition(getBufferStart(), GetNumBytesInBuffer());
+    return Position.second;
+  }
 
   raw_ostream &resetColor() override {
     TheStream->resetColor();

diff  --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
index 0a658034b67b..d180076e73fd 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
@@ -351,4 +351,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/lib/Support/FormattedStream.cpp b/llvm/lib/Support/FormattedStream.cpp
index 4eb747038bb9..f30a9678029f 100644
--- a/llvm/lib/Support/FormattedStream.cpp
+++ b/llvm/lib/Support/FormattedStream.cpp
@@ -11,7 +11,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/ConvertUTF.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/Locale.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 
@@ -19,15 +21,32 @@ using namespace llvm;
 
 /// UpdatePosition - Examine the given char sequence and figure out which
 /// column we end up in after output, and how many line breaks are contained.
-///
-static void UpdatePosition(std::pair<unsigned, unsigned> &Position, const char *Ptr, size_t Size) {
+/// This assumes that the input string is well-formed UTF-8, and takes into
+/// account unicode characters which render as multiple columns wide.
+static void UpdatePosition(std::pair<unsigned, unsigned> &Position,
+                           const char *Ptr, size_t Size) {
   unsigned &Column = Position.first;
   unsigned &Line = Position.second;
 
   // Keep track of the current column and line by scanning the string for
-  // special characters
-  for (const char *End = Ptr + Size; Ptr != End; ++Ptr) {
-    ++Column;
+  // special characters.
+  unsigned NumBytes;
+  for (const char *End = Ptr + Size; Ptr < End; Ptr += NumBytes) {
+    NumBytes = getNumBytesForUTF8(*Ptr);
+
+    // The string should never end part way through a multi-byte sequence.
+    assert((Ptr + NumBytes) <= End && "Malformed multi-byte sequence");
+
+    int Width = sys::locale::columnWidth(StringRef(Ptr, NumBytes));
+    // columnWidth returns -1 for non-printing characters.
+    if (Width != -1)
+      Column += Width;
+
+    // If this is the final byte of a multi-byte sequence, it can't be any of
+    // the special whitespace characters below.
+    if (NumBytes > 1)
+      continue;
+
     switch (*Ptr) {
     case '\n':
       Line += 1;

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..9455726c693c
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/ARM/debug-vars-dwarf4.s
@@ -0,0 +1,453 @@
+## 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..4085bd3cbd73
--- /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 1cfd06d2cef5..4e2c6eaac7c4 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -16,7 +16,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm-objdump.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"
@@ -70,6 +72,8 @@
 #include <unordered_map>
 #include <utility>
 
+#define DEBUG_TYPE "objdump"
+
 using namespace llvm::object;
 
 namespace llvm {
@@ -334,6 +338,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");
 
@@ -536,6 +562,356 @@ 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() {
+  return NoShowRawInsn ? 16 : 40;
+}
+
+/// 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;
+
+  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();
+  }
+
+  // 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)
+      : LiveVariables(), ActiveCols(Column()), MRI(MRI) {}
+
+  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 ? "^" : "╈";
+    case LineChar::RangeMid:
+      return IsASCII ? "|" : "┃";
+    case LineChar::RangeEnd:
+      return IsASCII ? "v" : "┻";
+    case LineChar::LabelVert:
+      return IsASCII ? "|" : "│";
+    case LineChar::LabelCornerNew:
+      return IsASCII ? "/" : "┌";
+    case LineChar::LabelCornerActive:
+      return IsASCII ? "|" : "┠";
+    case LineChar::LabelHoriz:
+      return IsASCII ? "-" : "─";
+    }
+  }
+
+  /// 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;
@@ -553,11 +929,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;
@@ -571,9 +948,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 = "; ");
 };
 
@@ -607,9 +985,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;
@@ -634,14 +1013,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) {
@@ -654,13 +1034,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))
@@ -680,7 +1063,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);
   }
 }
 
@@ -698,28 +1082,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)
@@ -731,7 +1117,7 @@ class PrettyPrinter {
 
     // 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 = NoShowRawInsn ? 16 : 40;
+    unsigned TabStop = getInstStartColumn();
     unsigned Column = OS.tell() - Start;
     OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8);
 
@@ -746,7 +1132,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)
@@ -758,12 +1144,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>";
@@ -789,7 +1175,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;
@@ -800,7 +1186,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 = "   ";
@@ -828,12 +1214,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;
@@ -880,12 +1266,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) {
@@ -1071,33 +1457,34 @@ static char getMappingSymbolKind(ArrayRef<MappingSymbolPair> MappingSymbols,
   return (It - 1)->second;
 }
 
-static uint64_t
-dumpARMELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End,
-               const ObjectFile *Obj, ArrayRef<uint8_t> Bytes,
-               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,
+                               raw_ostream &OS) {
   support::endianness Endian =
       Obj->isLittleEndian() ? support::little : support::big;
   while (Index < End) {
-    outs() << format("%8" PRIx64 ":", SectionAddr + Index);
-    outs() << "\t";
+    OS << format("%8" PRIx64 ":", SectionAddr + Index);
+    OS << "\t";
     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);
       Index += 4;
     } else 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);
       Index += 2;
     } else {
-      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);
       ++Index;
     }
-    outs() << "\n";
+    OS << "\n";
     if (getMappingSymbolKind(MappingSymbols, Index) != 'd')
       break;
   }
@@ -1242,6 +1629,17 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
     array_pod_sort(SecSyms.second.begin(), SecSyms.second.end());
   array_pod_sort(AbsoluteSymbols.begin(), AbsoluteSymbols.end());
 
+  std::unique_ptr<DWARFContext> DICtx;
+  LiveVariablePrinter LVP(*Ctx.getRegisterInfo());
+
+  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()))
@@ -1405,6 +1803,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
       bool CheckARMELFData = hasMappingSymbols(Obj) &&
                              Symbols[SI].Type != ELF::STT_OBJECT &&
                              !DisassembleAll;
+      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
@@ -1413,7 +1812,7 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
         if (CheckARMELFData &&
             getMappingSymbolKind(MappingSymbols, Index) == 'd') {
           Index = dumpARMELFData(SectionAddr, Index, End, Obj, Bytes,
-                                 MappingSymbols);
+                                 MappingSymbols, FOS);
           continue;
         }
 
@@ -1428,7 +1827,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;
           }
@@ -1452,17 +1851,20 @@ 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();
+                      FOS, "", *STI, &SP, Obj->getFileName(), &Rels, LVP);
+        FOS << CommentStream.str();
         Comments.clear();
 
         // If disassembly has failed, continue with the next instruction, to
         // avoid analysing invalid/incomplete instruction information.
         if (!Disassembled) {
-          outs() << "\n";
+          FOS << "\n";
           Index += Size;
           continue;
         }
@@ -1515,15 +1917,17 @@ static void disassembleObject(const Target *TheTarget, const ObjectFile *Obj,
               --TargetSym;
               uint64_t TargetAddress = TargetSym->Addr;
               StringRef TargetName = TargetSym->Name;
-              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) {
@@ -1549,8 +1953,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