[llvm] [llvm-objdump] Add the --visualize-jumps option (PR #74858)

via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 13 08:33:02 PST 2023


https://github.com/ostannard updated https://github.com/llvm/llvm-project/pull/74858

>From 8e9c6631be43c71e98fcddc768c5e5f6eeac1eb6 Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Fri, 1 Dec 2023 17:07:00 +0000
Subject: [PATCH 1/9] [llvm-objdump] Use formatted_raw_ostream for more things

---
 llvm/tools/llvm-objdump/llvm-objdump.cpp | 60 +++++++++++++-----------
 1 file changed, 33 insertions(+), 27 deletions(-)

diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 12bb70d5537d8a..25826924ce9ba0 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -1193,7 +1193,7 @@ static uint64_t dumpARMELFData(uint64_t SectionAddr, uint64_t Index,
 }
 
 static void dumpELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End,
-                        ArrayRef<uint8_t> Bytes) {
+                        ArrayRef<uint8_t> Bytes, formatted_raw_ostream &OS) {
   // print out data up to 8 bytes at a time in hex and ascii
   uint8_t AsciiData[9] = {'\0'};
   uint8_t Byte;
@@ -1201,9 +1201,9 @@ static void dumpELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End,
 
   for (; Index < End; ++Index) {
     if (NumBytes == 0)
-      outs() << format("%8" PRIx64 ":", SectionAddr + Index);
+      OS << format("%8" PRIx64 ":", SectionAddr + Index);
     Byte = Bytes.slice(Index)[0];
-    outs() << format(" %02x", Byte);
+    OS << format(" %02x", Byte);
     AsciiData[NumBytes] = isPrint(Byte) ? Byte : '.';
 
     uint8_t IndentOffset = 0;
@@ -1218,9 +1218,9 @@ static void dumpELFData(uint64_t SectionAddr, uint64_t Index, uint64_t End,
     }
     if (NumBytes == 8) {
       AsciiData[8] = '\0';
-      outs() << std::string(IndentOffset, ' ') << "         ";
-      outs() << reinterpret_cast<char *>(AsciiData);
-      outs() << '\n';
+      OS << std::string(IndentOffset, ' ') << "         ";
+      OS << reinterpret_cast<char *>(AsciiData);
+      OS << '\n';
       NumBytes = 0;
     }
   }
@@ -1850,12 +1850,15 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
       Start -= SectionAddr;
       End -= SectionAddr;
 
+
+      formatted_raw_ostream FOS(outs());
+
       if (!PrintedSection) {
         PrintedSection = true;
-        outs() << "\nDisassembly of section ";
+        FOS << "\nDisassembly of section ";
         if (!SegmentName.empty())
-          outs() << SegmentName << ",";
-        outs() << SectionName << ":\n";
+          FOS << SegmentName << ",";
+        FOS << SectionName << ":\n";
       }
 
       bool PrintedLabel = false;
@@ -1867,22 +1870,23 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
         const StringRef SymbolName = SymNamesHere[i];
 
         if (!PrintedLabel) {
-          outs() << '\n';
+          FOS << '\n';
           PrintedLabel = true;
         }
         if (LeadingAddr)
-          outs() << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ",
-                           SectionAddr + Start + VMAAdjustment);
+          FOS << format(Is64Bits ? "%016" PRIx64 " " : "%08" PRIx64 " ",
+                        SectionAddr + Start + VMAAdjustment);
         if (Obj.isXCOFF() && SymbolDescription) {
-          outs() << getXCOFFSymbolDescription(Symbol, SymbolName) << ":\n";
+          FOS << getXCOFFSymbolDescription(Symbol, SymbolName) << ":";
         } else
-          outs() << '<' << SymbolName << ">:\n";
+          FOS << '<' << SymbolName << ">:";
+        LVP.printAfterOtherLine(FOS, false);
       }
 
       // Don't print raw contents of a virtual section. A virtual section
       // doesn't have any contents in the file.
       if (Section.isVirtual()) {
-        outs() << "...\n";
+        FOS << "...\n";
         continue;
       }
 
@@ -1926,12 +1930,12 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
           // distance to the next symbol, and sometimes it will be just a
           // prologue and we should start disassembling instructions from where
           // it left off.
-          outs() << DT->Context->getAsmInfo()->getCommentString()
-                 << " error in decoding " << SymNamesHere[SHI]
-                 << " : decoding failed region as bytes.\n";
+          FOS << DT->Context->getAsmInfo()->getCommentString()
+              << " error in decoding " << SymNamesHere[SHI]
+              << " : decoding failed region as bytes.\n";
           for (uint64_t I = 0; I < Size; ++I) {
-            outs() << "\t.byte\t " << format_hex(Bytes[I], 1, /*Upper=*/true)
-                   << "\n";
+            FOS << "\t.byte\t " << format_hex(Bytes[I], 1, /*Upper=*/true)
+                << "\n";
           }
         }
         Start += Size;
@@ -1943,7 +1947,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
         Index = std::max<uint64_t>(Index, StartAddress - SectionAddr);
 
       if (DisassembleAsELFData) {
-        dumpELFData(SectionAddr, Index, End, Bytes);
+        dumpELFData(SectionAddr, Index, End, Bytes, FOS);
         Index = End;
         continue;
       }
@@ -1954,8 +1958,6 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
           Symbols[SI - 1].XCOFFSymInfo.StorageMappingClass &&
           (*Symbols[SI - 1].XCOFFSymInfo.StorageMappingClass == XCOFF::XMC_PR);
 
-      formatted_raw_ostream FOS(outs());
-
       // FIXME: Workaround for bug in formatted_raw_ostream. Color escape codes
       // are (incorrectly) written directly to the unbuffered raw_ostream
       // wrapped by the formatted_raw_ostream.
@@ -2047,12 +2049,16 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
           // Print local label if there's any.
           auto Iter1 = BBAddrMapLabels.find(SectionAddr + Index);
           if (Iter1 != BBAddrMapLabels.end()) {
-            for (StringRef Label : Iter1->second)
-              FOS << "<" << Label << ">:\n";
+            for (StringRef Label : Iter1->second) {
+              FOS << "<" << Label << ">:";
+              LVP.printAfterOtherLine(FOS, true);
+            }
           } else {
             auto Iter2 = AllLabels.find(SectionAddr + Index);
-            if (Iter2 != AllLabels.end())
-              FOS << "<" << Iter2->second << ">:\n";
+            if (Iter2 != AllLabels.end()) {
+              FOS << "<" << Iter2->second << ">:";
+              LVP.printAfterOtherLine(FOS, true);
+            }
           }
 
           // Disassemble a real instruction or a data when disassemble all is

>From ed7d814ff0b2be24fe975bdbd54379eb50a0ebcb Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Fri, 1 Dec 2023 16:52:29 +0000
Subject: [PATCH 2/9] [llvm-objdump] Refactor column width handling (NFCI)

This moves all of the logic for deciding the widths of different parts
of the disassembly and indenting to them into one place. This will make
it easier to add new columns. It also simplifies some things, because we
were already using formatted_raw_ostream almost everywhere, which tracks
the current column number for us.
---
 llvm/tools/llvm-objdump/SourcePrinter.cpp |  2 +-
 llvm/tools/llvm-objdump/XCOFFDump.cpp     |  2 +-
 llvm/tools/llvm-objdump/llvm-objdump.cpp  | 92 ++++++++++++++++++-----
 llvm/tools/llvm-objdump/llvm-objdump.h    | 11 +++
 4 files changed, 87 insertions(+), 20 deletions(-)

diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp
index 76da86587586ee..497756b1765cda 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.cpp
+++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp
@@ -95,7 +95,7 @@ void LiveVariablePrinter::addFunction(DWARFDie D) {
 // Get the column number (in characters) at which the first live variable
 // line should be printed.
 unsigned LiveVariablePrinter::getIndentLevel() const {
-  return DbgIndent + getInstStartColumn(STI);
+  return GetColumnIndent(STI, DisassemblyColumn::Variables);
 }
 
 // Indent to the first live-range column to the right of the currently
diff --git a/llvm/tools/llvm-objdump/XCOFFDump.cpp b/llvm/tools/llvm-objdump/XCOFFDump.cpp
index 0f6147924f8a1a..e6266dcd774d68 100644
--- a/llvm/tools/llvm-objdump/XCOFFDump.cpp
+++ b/llvm/tools/llvm-objdump/XCOFFDump.cpp
@@ -150,7 +150,7 @@ void objdump::dumpTracebackTable(ArrayRef<uint8_t> Bytes, uint64_t Address,
                                  const MCSubtargetInfo &STI,
                                  const XCOFFObjectFile *Obj) {
   uint64_t Index = 0;
-  unsigned TabStop = getInstStartColumn(STI) - 1;
+  unsigned TabStop = GetColumnIndent(STI, DisassemblyColumn::Assembly) - 1;
   // Print traceback table boundary.
   printRawData(Bytes.slice(Index, 4), Address, OS, STI);
   OS << "\t# Traceback table start\n";
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 25826924ce9ba0..3c17c44a0fa7e4 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -477,26 +477,82 @@ unsigned objdump::getInstStartColumn(const MCSubtargetInfo &STI) {
   return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
 }
 
-static void AlignToInstStartColumn(size_t Start, const MCSubtargetInfo &STI,
-                                   raw_ostream &OS) {
-  // The output of printInst starts with a tab. Print some spaces so that
-  // the tab has 1 column and advances to the target tab stop.
-  unsigned TabStop = getInstStartColumn(STI);
-  unsigned Column = OS.tell() - Start;
-  OS.indent(Column < TabStop - 1 ? TabStop - 1 - Column : 7 - Column % 8);
+unsigned EncodingColumnWidth(Triple const &Triple) {
+  switch (Triple.getArch()) {
+  case Triple::x86:
+  case Triple::x86_64:
+    return 30;
+  default:
+    return 14;
+  }
+}
+
+unsigned objdump::GetColumnIndent(MCSubtargetInfo const &STI,
+                                  DisassemblyColumn Col) {
+  unsigned Indent = 0;
+
+  if (Col == DisassemblyColumn::Address)
+    return Indent;
+
+  // Address: 8 chars of address, followed by ": "
+  Indent += 10;
+
+  if (Col == DisassemblyColumn::Encoding)
+    return Indent;
+
+  // Encoding: depends on architecture
+  if (ShowRawInsn)
+      Indent += EncodingColumnWidth(STI.getTargetTriple());
+
+  // Special case for assembly string: the assembly printer uses tabs, so we
+  // need to ensure we start the instruction on a tab stop (multiple of 8).
+  Indent = alignTo(Indent, 8);
+  if (Col == DisassemblyColumn::Assembly) {
+    return Indent;
+  }
+
+  // Assembly width can be configured with --debug-vars-indent=
+  // FIXME this variable name is confusing.
+  Indent += DbgIndent;
+
+  if (Col == DisassemblyColumn::Variables)
+    return Indent;
+
+  llvm_unreachable("Unhandled DisassemblyColumn");
+}
+
+void objdump::IndentToColumn(MCSubtargetInfo const &STI,
+                             formatted_raw_ostream &OS, DisassemblyColumn Col) {
+  unsigned TargetIndent = GetColumnIndent(STI, Col);
+  unsigned CurrentIndent = OS.getColumn();
+
+  // Special case for assembly string: the output of printInst starts with a
+  // tab, so we want to start printing one character before a tab stop, so it
+  // always has a width of one. GetColumnIndent already guarantees that
+  // TargetIndent will be a multple of 8.
+  // TODO Add a way to avoid printing the leading tab, to simplify this.
+  // TODO 7 characters isn't enough for a lot of mnemonics, add an option to
+  // increase the gap to the first operand.
+  if (Col == DisassemblyColumn::Assembly) {
+    TargetIndent -= 1;
+    if (TargetIndent < CurrentIndent)
+      TargetIndent =  alignTo(CurrentIndent + 1, 8) - 1;
+  }
+
+  if (TargetIndent > CurrentIndent)
+    OS.indent(TargetIndent - CurrentIndent);
 }
 
 void objdump::printRawData(ArrayRef<uint8_t> Bytes, uint64_t Address,
                            formatted_raw_ostream &OS,
                            MCSubtargetInfo const &STI) {
-  size_t Start = OS.tell();
   if (LeadingAddr)
     OS << format("%8" PRIx64 ":", Address);
   if (ShowRawInsn) {
     OS << ' ';
     dumpBytes(Bytes, OS);
   }
-  AlignToInstStartColumn(Start, STI, OS);
+  IndentToColumn(STI, OS, DisassemblyColumn::Assembly);
 }
 
 namespace {
@@ -566,6 +622,8 @@ class PrettyPrinter {
 
     printRawData(Bytes, Address.Address, OS, STI);
 
+    IndentToColumn(STI, OS, DisassemblyColumn::Assembly);
+
     if (MI) {
       // See MCInstPrinter::printInst. On targets where a PC relative immediate
       // is relative to the next instruction and the length of a MCInst is
@@ -749,7 +807,6 @@ class ARMPrettyPrinter : public PrettyPrinter {
       SP->printSourceLine(OS, Address, ObjectFilename, LVP);
     LVP.printBetweenInsts(OS, false);
 
-    size_t Start = OS.tell();
     if (LeadingAddr)
       OS << format("%8" PRIx64 ":", Address.Address);
     if (ShowRawInsn) {
@@ -775,7 +832,7 @@ class ARMPrettyPrinter : public PrettyPrinter {
       }
     }
 
-    AlignToInstStartColumn(Start, STI, OS);
+    IndentToColumn(STI, OS, DisassemblyColumn::Assembly);
 
     if (MI) {
       IP.printInst(MI, Address.Address, "", STI, OS);
@@ -803,7 +860,6 @@ class AArch64PrettyPrinter : public PrettyPrinter {
       SP->printSourceLine(OS, Address, ObjectFilename, LVP);
     LVP.printBetweenInsts(OS, false);
 
-    size_t Start = OS.tell();
     if (LeadingAddr)
       OS << format("%8" PRIx64 ":", Address.Address);
     if (ShowRawInsn) {
@@ -820,7 +876,7 @@ class AArch64PrettyPrinter : public PrettyPrinter {
       }
     }
 
-    AlignToInstStartColumn(Start, STI, OS);
+    IndentToColumn(STI, OS, DisassemblyColumn::Assembly);
 
     if (MI) {
       IP.printInst(MI, Address.Address, "", STI, OS);
@@ -1166,14 +1222,14 @@ static uint64_t dumpARMELFData(uint64_t SectionAddr, uint64_t Index,
                                uint64_t End, const ObjectFile &Obj,
                                ArrayRef<uint8_t> Bytes,
                                ArrayRef<MappingSymbolPair> MappingSymbols,
-                               const MCSubtargetInfo &STI, raw_ostream &OS) {
+                               const MCSubtargetInfo &STI,
+                               formatted_raw_ostream &OS) {
   llvm::endianness Endian =
       Obj.isLittleEndian() ? llvm::endianness::little : llvm::endianness::big;
-  size_t Start = OS.tell();
   OS << format("%8" PRIx64 ": ", SectionAddr + Index);
   if (Index + 4 <= End) {
     dumpBytes(Bytes.slice(Index, 4), OS);
-    AlignToInstStartColumn(Start, STI, OS);
+    IndentToColumn(STI, OS, DisassemblyColumn::Assembly);
     OS << "\t.word\t"
            << format_hex(support::endian::read32(Bytes.data() + Index, Endian),
                          10);
@@ -1181,13 +1237,13 @@ static uint64_t dumpARMELFData(uint64_t SectionAddr, uint64_t Index,
   }
   if (Index + 2 <= End) {
     dumpBytes(Bytes.slice(Index, 2), OS);
-    AlignToInstStartColumn(Start, STI, OS);
+    IndentToColumn(STI, OS, DisassemblyColumn::Assembly);
     OS << "\t.short\t"
        << format_hex(support::endian::read16(Bytes.data() + Index, Endian), 6);
     return 2;
   }
   dumpBytes(Bytes.slice(Index, 1), OS);
-  AlignToInstStartColumn(Start, STI, OS);
+  IndentToColumn(STI, OS, DisassemblyColumn::Assembly);
   OS << "\t.byte\t" << format_hex(Bytes[Index], 4);
   return 1;
 }
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h
index 7778cf6c2784eb..31281ba3d8c2ad 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.h
+++ b/llvm/tools/llvm-objdump/llvm-objdump.h
@@ -144,6 +144,17 @@ void printRawData(llvm::ArrayRef<uint8_t> Bytes, uint64_t Address,
                   llvm::formatted_raw_ostream &OS,
                   llvm::MCSubtargetInfo const &STI);
 
+enum class DisassemblyColumn {
+  Address,
+  Encoding,
+  Assembly,
+  Variables,
+};
+
+unsigned GetColumnIndent(MCSubtargetInfo const &STI, DisassemblyColumn Col);
+void IndentToColumn(MCSubtargetInfo const &STI, formatted_raw_ostream &OS,
+                    DisassemblyColumn Col);
+
 } // namespace objdump
 } // end namespace llvm
 

>From 4dd15eefb5413fbdf5842222008749ac6f5417ec Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Fri, 1 Dec 2023 17:08:02 +0000
Subject: [PATCH 3/9] [llvm-objdump] Collect branch targets for all
 architectures

---
 llvm/tools/llvm-objdump/llvm-objdump.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 3c17c44a0fa7e4..d07d086a5956f9 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -1344,10 +1344,6 @@ collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, MCInstrAnalysis *MIA,
                           const MCSubtargetInfo *STI, uint64_t SectionAddr,
                           uint64_t Start, uint64_t End,
                           std::unordered_map<uint64_t, std::string> &Labels) {
-  // So far only supports PowerPC and X86.
-  if (!STI->getTargetTriple().isPPC() && !STI->getTargetTriple().isX86())
-    return;
-
   if (MIA)
     MIA->resetState();
 

>From aa38a0afdef0b9d075ec4743dee5d0a8547e7858 Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Fri, 1 Dec 2023 17:14:28 +0000
Subject: [PATCH 4/9] [Support] Tab always advances column by at least 1

---
 llvm/lib/Support/FormattedStream.cpp                  | 1 +
 llvm/unittests/Support/formatted_raw_ostream_test.cpp | 9 ++++-----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Support/FormattedStream.cpp b/llvm/lib/Support/FormattedStream.cpp
index c0d28435099570..48b02889c6d3e5 100644
--- a/llvm/lib/Support/FormattedStream.cpp
+++ b/llvm/lib/Support/FormattedStream.cpp
@@ -45,6 +45,7 @@ void formatted_raw_ostream::UpdatePosition(const char *Ptr, size_t Size) {
       break;
     case '\t':
       // Assumes tab stop = 8 characters.
+      Column += 1;
       Column += (8 - (Column & 0x7)) & 0x7;
       break;
     }
diff --git a/llvm/unittests/Support/formatted_raw_ostream_test.cpp b/llvm/unittests/Support/formatted_raw_ostream_test.cpp
index 750dc518fee388..5ae5608d0bdb07 100644
--- a/llvm/unittests/Support/formatted_raw_ostream_test.cpp
+++ b/llvm/unittests/Support/formatted_raw_ostream_test.cpp
@@ -56,15 +56,14 @@ TEST(formatted_raw_ostreamTest, Test_LineColumn) {
   EXPECT_EQ(1U, C.getLine());
   EXPECT_EQ(0U, C.getColumn());
 
-  // '\t' advances column to the next multiple of 8.
-  // FIXME: If the column number is already a multiple of 8 this will do
-  // nothing, is this behaviour correct?
+  // '\t' advances column to the next multiple of 8, and always by at least 1
+  // column.
   C << "1\t";
   EXPECT_EQ(8U, C.getColumn());
   C << "\t";
-  EXPECT_EQ(8U, C.getColumn());
-  C << "1234567\t";
   EXPECT_EQ(16U, C.getColumn());
+  C << "1234567\t";
+  EXPECT_EQ(24U, C.getColumn());
   EXPECT_EQ(1U, C.getLine());
 }
 

>From 2cb0e8dd6a9f884f3e94929a48259673e6b0fbc2 Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Fri, 1 Dec 2023 17:18:19 +0000
Subject: [PATCH 5/9] [llvm-objdump] Add the --visualize-jumps option

This is a feature which GNU objdump currently has, which prints a
control-flow graph showing all of the direct branches alongside the
disassembly.

This should work for any architecture which implements the
MCInstrAnalysis class, I've tested it on ARM, Thumb and AArch64.

There are a few (known) differences between this and the GNU objdump
version:
* This has the option to draw the lines using unicode line-drawing
  charcters, instead of ASCII. This is on by default because I find the
  connected lines much easier to read.
* I haven't yet implemented the extended-color mode yet. With GNU
  objdump this often results in some very similar colors, so we might
  want to carefully pick a pallete which minimises that.
---
 .../Inputs/visualize-jumps-aarch64-ascii.txt  |  31 +++
 .../visualize-jumps-aarch64-unicode-color.txt |  31 +++
 ...visualize-jumps-aarch64-unicode-relocs.txt |  34 +++
 .../visualize-jumps-aarch64-unicode.txt       |  31 +++
 .../Inputs/visualize-jumps-arm-ascii.txt      |  27 +++
 .../Inputs/visualize-jumps-arm-unicode.txt    |  27 +++
 .../Inputs/visualize-jumps-thumb-ascii.txt    |  34 +++
 .../Inputs/visualize-jumps-thumb-unicode.txt  |  34 +++
 .../llvm-objdump/visualize-jumps-aarch64.s    |  69 +++++++
 .../tools/llvm-objdump/visualize-jumps-arm.s  |  54 +++++
 .../llvm-objdump/visualize-jumps-thumb.s      |  63 ++++++
 llvm/tools/llvm-objdump/ObjdumpOpts.td        |   7 +
 llvm/tools/llvm-objdump/SourcePrinter.cpp     | 194 +++++++++++++++++-
 llvm/tools/llvm-objdump/SourcePrinter.h       | 113 +++++++++-
 llvm/tools/llvm-objdump/llvm-objdump.cpp      | 159 +++++++++++---
 llvm/tools/llvm-objdump/llvm-objdump.h        |   2 +
 16 files changed, 877 insertions(+), 33 deletions(-)
 create mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-ascii.txt
 create mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-color.txt
 create mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-relocs.txt
 create mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode.txt
 create mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-ascii.txt
 create mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-unicode.txt
 create mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-ascii.txt
 create mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-unicode.txt
 create mode 100644 llvm/test/tools/llvm-objdump/visualize-jumps-aarch64.s
 create mode 100644 llvm/test/tools/llvm-objdump/visualize-jumps-arm.s
 create mode 100644 llvm/test/tools/llvm-objdump/visualize-jumps-thumb.s

diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-ascii.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-ascii.txt
new file mode 100644
index 00000000000000..592e50d98f8ff7
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-ascii.txt
@@ -0,0 +1,31 @@
+
+<stdin>:	file format elf64-littleaarch64
+
+Disassembly of section .text:
+
+0000000000000000 <test_func>:
+       0:           94000000   	bl	0x0 <test_func>
+       4:           14000000   	b	0x4 <test_func+0x4>
+       8:       /-- 14000001   	b	0xc <test_func+0xc>
+       c:       +-> d503201f   	nop
+      10:       \-- 17ffffff   	b	0xc <test_func+0xc>
+      14:           14000000   	b	0x14 <test_func+0x14>
+      18:       /-- 54000040   	b.eq	0x20 <test_func+0x20>
+      1c:       +-- b4000020   	cbz	x0, 0x20 <test_func+0x20>
+      20:       \-> d503201f   	nop
+      24:   /------ 14000005   	b	0x38 <test_func+0x38>
+      28:   | /---- 14000003   	b	0x34 <test_func+0x34>
+      2c:   | | /-- 14000001   	b	0x30 <test_func+0x30>
+      30:   | | \-> d503201f   	nop
+      34:   | \---> d503201f   	nop
+      38:   \-----> d503201f   	nop
+      3c:       /-- 14000002   	b	0x44 <test_func+0x44>
+      40:     /-|-- 14000002   	b	0x48 <test_func+0x48>
+      44:     | \-> d503201f   	nop
+      48:     \---> d503201f   	nop
+      4c:       /-- 14000001   	b	0x50 <test_func+0x50>
+      50:     /-|-- 14000001   	b	0x54 <test_func+0x54>
+      54:     \---> d503201f   	nop
+      58:       /-- 14000002   	b	0x60 <test_func+0x60>
+      5c:       |   94000000   	bl	0x5c <test_func+0x5c>
+      60:       \-> d503201f   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-color.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-color.txt
new file mode 100644
index 00000000000000..925c5ef52a8d22
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-color.txt
@@ -0,0 +1,31 @@
+
+<stdin>:	file format elf64-littleaarch64
+
+Disassembly of section .text:
+
+0000000000000000 <test_func>:
+       0:           94000000   	bl	0x0 <test_func>
+       4:           14000000   	b	0x4 <test_func+0x4>
+       8:       ╭── 14000001   	b	0xc <test_func+0xc>
+       c:       ├─> d503201f   	nop
+      10:       ╰── 17ffffff   	b	0xc <test_func+0xc>
+      14:           14000000   	b	0x14 <test_func+0x14>
+      18:       ╭── 54000040   	b.eq	0x20 <test_func+0x20>
+      1c:       ├── b4000020   	cbz	x0, 0x20 <test_func+0x20>
+      20:       ╰─> d503201f   	nop
+      24:   ╭────── 14000005   	b	0x38 <test_func+0x38>
+      28:   │ ╭──── 14000003   	b	0x34 <test_func+0x34>
+      2c:   │ │ ╭── 14000001   	b	0x30 <test_func+0x30>
+      30:   │ │ ╰─> d503201f   	nop
+      34:   │ ╰───> d503201f   	nop
+      38:   ╰─────> d503201f   	nop
+      3c:       ╭── 14000002   	b	0x44 <test_func+0x44>
+      40:     ╭─│── 14000002   	b	0x48 <test_func+0x48>
+      44:     │ ╰─> d503201f   	nop
+      48:     ╰───> d503201f   	nop
+      4c:       ╭── 14000001   	b	0x50 <test_func+0x50>
+      50:     ╭─│── 14000001   	b	0x54 <test_func+0x54>
+      54:     ╰───> d503201f   	nop
+      58:       ╭── 14000002   	b	0x60 <test_func+0x60>
+      5c:       │   94000000   	bl	0x5c <test_func+0x5c>
+      60:       ╰─> d503201f   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-relocs.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-relocs.txt
new file mode 100644
index 00000000000000..75ec5741fdaae2
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-relocs.txt
@@ -0,0 +1,34 @@
+
+<stdin>:	file format elf64-littleaarch64
+
+Disassembly of section .text:
+
+0000000000000000 <test_func>:
+       0:           94000000   	bl	0x0 <test_func>
+                   		0000000000000000:  R_AARCH64_CALL26	extern_func
+       4:           14000000   	b	0x4 <test_func+0x4>
+                   		0000000000000004:  R_AARCH64_JUMP26	extern_func
+       8:       ╭── 14000001   	b	0xc <test_func+0xc>
+       c:       ├─> d503201f   	nop
+      10:       ╰── 17ffffff   	b	0xc <test_func+0xc>
+      14:           14000000   	b	0x14 <test_func+0x14>
+      18:       ╭── 54000040   	b.eq	0x20 <test_func+0x20>
+      1c:       ├── b4000020   	cbz	x0, 0x20 <test_func+0x20>
+      20:       ╰─> d503201f   	nop
+      24:   ╭────── 14000005   	b	0x38 <test_func+0x38>
+      28:   │ ╭──── 14000003   	b	0x34 <test_func+0x34>
+      2c:   │ │ ╭── 14000001   	b	0x30 <test_func+0x30>
+      30:   │ │ ╰─> d503201f   	nop
+      34:   │ ╰───> d503201f   	nop
+      38:   ╰─────> d503201f   	nop
+      3c:       ╭── 14000002   	b	0x44 <test_func+0x44>
+      40:     ╭─│── 14000002   	b	0x48 <test_func+0x48>
+      44:     │ ╰─> d503201f   	nop
+      48:     ╰───> d503201f   	nop
+      4c:       ╭── 14000001   	b	0x50 <test_func+0x50>
+      50:     ╭─│── 14000001   	b	0x54 <test_func+0x54>
+      54:     ╰───> d503201f   	nop
+      58:       ╭── 14000002   	b	0x60 <test_func+0x60>
+      5c:       │   94000000   	bl	0x5c <test_func+0x5c>
+                │  		000000000000005c:  R_AARCH64_CALL26	extern_func
+      60:       ╰─> d503201f   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode.txt
new file mode 100644
index 00000000000000..4cabdc92d61c1d
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode.txt
@@ -0,0 +1,31 @@
+
+<stdin>:	file format elf64-littleaarch64
+
+Disassembly of section .text:
+
+0000000000000000 <test_func>:
+       0:           94000000   	bl	0x0 <test_func>
+       4:           14000000   	b	0x4 <test_func+0x4>
+       8:       ╭── 14000001   	b	0xc <test_func+0xc>
+       c:       ├─> d503201f   	nop
+      10:       ╰── 17ffffff   	b	0xc <test_func+0xc>
+      14:           14000000   	b	0x14 <test_func+0x14>
+      18:       ╭── 54000040   	b.eq	0x20 <test_func+0x20>
+      1c:       ├── b4000020   	cbz	x0, 0x20 <test_func+0x20>
+      20:       ╰─> d503201f   	nop
+      24:   ╭────── 14000005   	b	0x38 <test_func+0x38>
+      28:   │ ╭──── 14000003   	b	0x34 <test_func+0x34>
+      2c:   │ │ ╭── 14000001   	b	0x30 <test_func+0x30>
+      30:   │ │ ╰─> d503201f   	nop
+      34:   │ ╰───> d503201f   	nop
+      38:   ╰─────> d503201f   	nop
+      3c:       ╭── 14000002   	b	0x44 <test_func+0x44>
+      40:     ╭─│── 14000002   	b	0x48 <test_func+0x48>
+      44:     │ ╰─> d503201f   	nop
+      48:     ╰───> d503201f   	nop
+      4c:       ╭── 14000001   	b	0x50 <test_func+0x50>
+      50:     ╭─│── 14000001   	b	0x54 <test_func+0x54>
+      54:     ╰───> d503201f   	nop
+      58:       ╭── 14000002   	b	0x60 <test_func+0x60>
+      5c:       │   94000000   	bl	0x5c <test_func+0x5c>
+      60:       ╰─> d503201f   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-ascii.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-ascii.txt
new file mode 100644
index 00000000000000..7e006841980534
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-ascii.txt
@@ -0,0 +1,27 @@
+
+<stdin>:	file format elf32-littlearm
+
+Disassembly of section .text:
+
+00000000 <test_func>:
+       0:           ebfffffe   	bl	0x0 <test_func>         @ imm = #-0x8
+       4:           eafffffe   	b	0x4 <test_func+0x4>     @ imm = #-0x8
+       8:       /-- eaffffff   	b	0xc <test_func+0xc>     @ imm = #-0x4
+       c:       +-> e320f000   	nop
+      10:       \-- eafffffd   	b	0xc <test_func+0xc>     @ imm = #-0xc
+      14:           eafffffe   	b	0x14 <test_func+0x14>   @ imm = #-0x8
+      18:       /-- 0affffff   	beq	0x1c <test_func+0x1c>   @ imm = #-0x4
+      1c:       \-> e320f000   	nop
+      20:   /------ ea000003   	b	0x34 <test_func+0x34>   @ imm = #0xc
+      24:   | /---- ea000001   	b	0x30 <test_func+0x30>   @ imm = #0x4
+      28:   | | /-- eaffffff   	b	0x2c <test_func+0x2c>   @ imm = #-0x4
+      2c:   | | \-> e320f000   	nop
+      30:   | \---> e320f000   	nop
+      34:   \-----> e320f000   	nop
+      38:       /-- ea000000   	b	0x40 <test_func+0x40>   @ imm = #0x0
+      3c:     /-|-- ea000000   	b	0x44 <test_func+0x44>   @ imm = #0x0
+      40:     | \-> e320f000   	nop
+      44:     \---> e320f000   	nop
+      48:     /---- eaffffff   	b	0x4c <test_func+0x4c>   @ imm = #-0x4
+      4c:     \-|-> eaffffff   	b	0x50 <test_func+0x50>   @ imm = #-0x4
+      50:       \-> e320f000   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-unicode.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-unicode.txt
new file mode 100644
index 00000000000000..62194b4b33c172
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-unicode.txt
@@ -0,0 +1,27 @@
+
+<stdin>:	file format elf32-littlearm
+
+Disassembly of section .text:
+
+00000000 <test_func>:
+       0:           ebfffffe   	bl	0x0 <test_func>         @ imm = #-0x8
+       4:           eafffffe   	b	0x4 <test_func+0x4>     @ imm = #-0x8
+       8:       ╭── eaffffff   	b	0xc <test_func+0xc>     @ imm = #-0x4
+       c:       ├─> e320f000   	nop
+      10:       ╰── eafffffd   	b	0xc <test_func+0xc>     @ imm = #-0xc
+      14:           eafffffe   	b	0x14 <test_func+0x14>   @ imm = #-0x8
+      18:       ╭── 0affffff   	beq	0x1c <test_func+0x1c>   @ imm = #-0x4
+      1c:       ╰─> e320f000   	nop
+      20:   ╭────── ea000003   	b	0x34 <test_func+0x34>   @ imm = #0xc
+      24:   │ ╭──── ea000001   	b	0x30 <test_func+0x30>   @ imm = #0x4
+      28:   │ │ ╭── eaffffff   	b	0x2c <test_func+0x2c>   @ imm = #-0x4
+      2c:   │ │ ╰─> e320f000   	nop
+      30:   │ ╰───> e320f000   	nop
+      34:   ╰─────> e320f000   	nop
+      38:       ╭── ea000000   	b	0x40 <test_func+0x40>   @ imm = #0x0
+      3c:     ╭─│── ea000000   	b	0x44 <test_func+0x44>   @ imm = #0x0
+      40:     │ ╰─> e320f000   	nop
+      44:     ╰───> e320f000   	nop
+      48:     ╭──── eaffffff   	b	0x4c <test_func+0x4c>   @ imm = #-0x4
+      4c:     ╰─│─> eaffffff   	b	0x50 <test_func+0x50>   @ imm = #-0x4
+      50:       ╰─> e320f000   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-ascii.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-ascii.txt
new file mode 100644
index 00000000000000..f18062167dd97d
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-ascii.txt
@@ -0,0 +1,34 @@
+
+<stdin>:	file format elf32-littlearm
+
+Disassembly of section .text:
+
+00000000 <test_func>:
+       0:           f7ff fffe  	bl	0x0 <test_func>         @ imm = #-0x4
+       4:           f7ff bffe  	b.w	0x4 <test_func+0x4>     @ imm = #-0x4
+       8:       /-- e7ff       	b	0xa <test_func+0xa>     @ imm = #-0x2
+       a:       +-> bf00       	nop
+       c:       \-- e7fd       	b	0xa <test_func+0xa>     @ imm = #-0x6
+       e:           e7fe       	b	0xe <test_func+0xe>     @ imm = #-0x4
+      10:       /-- f000 b807  	b.w	0x22 <test_func+0x22>   @ imm = #0xe
+      14:       +-- d005       	beq	0x22 <test_func+0x22>   @ imm = #0xa
+      16:       +-- f040 8004  	bne.w	0x22 <test_func+0x22>   @ imm = #0x8
+      1a:       +-- b110       	cbz	r0, 0x22 <test_func+0x22> @ imm = #0x4
+      1c:       |   bfd8       	it	le
+      1e:       +-- e000       	ble	0x22 <test_func+0x22>   @ imm = #0x0
+      20:       |   bf00       	nop
+      22:       \-> bf00       	nop
+      24:           bf00       	nop
+      26:   /------ e003       	b	0x30 <test_func+0x30>   @ imm = #0x6
+      28:   | /---- e001       	b	0x2e <test_func+0x2e>   @ imm = #0x2
+      2a:   | | /-- e7ff       	b	0x2c <test_func+0x2c>   @ imm = #-0x2
+      2c:   | | \-> bf00       	nop
+      2e:   | \---> bf00       	nop
+      30:   \-----> bf00       	nop
+      32:       /-- e000       	b	0x36 <test_func+0x36>   @ imm = #0x0
+      34:     /-|-- e000       	b	0x38 <test_func+0x38>   @ imm = #0x0
+      36:     | \-> bf00       	nop
+      38:     \---> bf00       	nop
+      3a:       /-- e7ff       	b	0x3c <test_func+0x3c>   @ imm = #-0x2
+      3c:     /-|-- e7ff       	b	0x3e <test_func+0x3e>   @ imm = #-0x2
+      3e:     \---> bf00       	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-unicode.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-unicode.txt
new file mode 100644
index 00000000000000..b171fb1c5d28c2
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-unicode.txt
@@ -0,0 +1,34 @@
+
+<stdin>:	file format elf32-littlearm
+
+Disassembly of section .text:
+
+00000000 <test_func>:
+       0:           f7ff fffe  	bl	0x0 <test_func>         @ imm = #-0x4
+       4:           f7ff bffe  	b.w	0x4 <test_func+0x4>     @ imm = #-0x4
+       8:       ╭── e7ff       	b	0xa <test_func+0xa>     @ imm = #-0x2
+       a:       ├─> bf00       	nop
+       c:       ╰── e7fd       	b	0xa <test_func+0xa>     @ imm = #-0x6
+       e:           e7fe       	b	0xe <test_func+0xe>     @ imm = #-0x4
+      10:       ╭── f000 b807  	b.w	0x22 <test_func+0x22>   @ imm = #0xe
+      14:       ├── d005       	beq	0x22 <test_func+0x22>   @ imm = #0xa
+      16:       ├── f040 8004  	bne.w	0x22 <test_func+0x22>   @ imm = #0x8
+      1a:       ├── b110       	cbz	r0, 0x22 <test_func+0x22> @ imm = #0x4
+      1c:       │   bfd8       	it	le
+      1e:       ├── e000       	ble	0x22 <test_func+0x22>   @ imm = #0x0
+      20:       │   bf00       	nop
+      22:       ╰─> bf00       	nop
+      24:           bf00       	nop
+      26:   ╭────── e003       	b	0x30 <test_func+0x30>   @ imm = #0x6
+      28:   │ ╭──── e001       	b	0x2e <test_func+0x2e>   @ imm = #0x2
+      2a:   │ │ ╭── e7ff       	b	0x2c <test_func+0x2c>   @ imm = #-0x2
+      2c:   │ │ ╰─> bf00       	nop
+      2e:   │ ╰───> bf00       	nop
+      30:   ╰─────> bf00       	nop
+      32:       ╭── e000       	b	0x36 <test_func+0x36>   @ imm = #0x0
+      34:     ╭─│── e000       	b	0x38 <test_func+0x38>   @ imm = #0x0
+      36:     │ ╰─> bf00       	nop
+      38:     ╰───> bf00       	nop
+      3a:       ╭── e7ff       	b	0x3c <test_func+0x3c>   @ imm = #-0x2
+      3c:     ╭─│── e7ff       	b	0x3e <test_func+0x3e>   @ imm = #-0x2
+      3e:     ╰───> bf00       	nop
diff --git a/llvm/test/tools/llvm-objdump/visualize-jumps-aarch64.s b/llvm/test/tools/llvm-objdump/visualize-jumps-aarch64.s
new file mode 100644
index 00000000000000..f78b3e27975556
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/visualize-jumps-aarch64.s
@@ -0,0 +1,69 @@
+// RUN: llvm-mc < %s -triple aarch64 -filetype=obj | \
+// RUN:   llvm-objdump --triple aarch64 -d --visualize-jumps=unicode - | \
+// RUN:   diff - %p/Inputs/visualize-jumps-aarch64-unicode.txt
+
+// RUN: llvm-mc < %s -triple aarch64 -filetype=obj | \
+// RUN:   llvm-objdump --triple aarch64 -d --visualize-jumps=ascii - | \
+// RUN:   diff - %p/Inputs/visualize-jumps-aarch64-ascii.txt
+
+// RUN: llvm-mc < %s -triple aarch64 -filetype=obj | \
+// RUN:   llvm-objdump --triple aarch64 -d --visualize-jumps=unicode,color - | \
+// RUN:   diff - %p/Inputs/visualize-jumps-aarch64-unicode-color.txt
+
+// RUN: llvm-mc < %s -triple aarch64 -filetype=obj | \
+// RUN:   llvm-objdump --triple aarch64 -d --visualize-jumps=unicode --reloc - | \
+// RUN:   diff - %p/Inputs/visualize-jumps-aarch64-unicode-relocs.txt
+
+test_func:
+  // Relocated instructions don't get control-flow edges.
+  bl extern_func
+  b extern_func
+  
+  // Two branches to the same label, one forward and one backward.
+  b .Llabel1
+.Llabel1:
+  nop
+  b .Llabel1
+
+  // Branch to self, no CFG edge shown
+  b .
+
+  // Conditional branches
+  b.eq .Llabel2
+  cbz x0, .Llabel2
+.Llabel2:
+  nop
+
+  // Branches are sorted with shorter ones to the right, to reduce number of
+  // crossings, and keep the lines for short branches short themselves.
+  b .Llabel5
+  b .Llabel4
+  b .Llabel3
+.Llabel3:
+  nop
+.Llabel4:
+  nop
+.Llabel5:
+  nop
+
+  // Sometimes crossings can't be avoided.
+  b .Llabel6
+  b .Llabel7
+.Llabel6:
+  nop
+.Llabel7:
+  nop
+
+  // TODO If a branch goes to another branch instruction, we don't have a way
+  // to represent that. Can we improve on this?
+  b .Llabel8
+.Llabel8:
+  b .Llabel9
+.Llabel9:
+  nop
+
+  // Graph lines need to be drawn on the same output line as relocations.
+  b .Llabel10
+  bl extern_func
+.Llabel10:
+  nop
diff --git a/llvm/test/tools/llvm-objdump/visualize-jumps-arm.s b/llvm/test/tools/llvm-objdump/visualize-jumps-arm.s
new file mode 100644
index 00000000000000..6855e6ff84e32a
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/visualize-jumps-arm.s
@@ -0,0 +1,54 @@
+// RUN: llvm-mc < %s -triple armv8a -filetype=obj | \
+// RUN:   llvm-objdump --triple armv8a -d --visualize-jumps=unicode - | \
+// RUN:   diff - %p/Inputs/visualize-jumps-arm-unicode.txt
+
+// RUN: llvm-mc < %s -triple armv8a -filetype=obj | \
+// RUN:   llvm-objdump --triple armv8a -d --visualize-jumps=ascii - | \
+// RUN:   diff - %p/Inputs/visualize-jumps-arm-ascii.txt
+
+test_func:
+  // Relocated instructions don't get control-flow edges.
+  bl extern_func
+  b extern_func
+  
+  // Two branches to the same label, one forward and one backward.
+  b .Llabel1
+.Llabel1:
+  nop
+  b .Llabel1
+
+  // Branch to self, no CFG edge shown
+  b .
+
+  // Conditional branches
+  beq .Llabel2
+.Llabel2:
+  nop
+
+  // Branches are sorted with shorter ones to the right, to reduce number of
+  // crossings, and keep the lines for short branches short themselves.
+  b .Llabel5
+  b .Llabel4
+  b .Llabel3
+.Llabel3:
+  nop
+.Llabel4:
+  nop
+.Llabel5:
+  nop
+
+  // Sometimes crossings can't be avoided.
+  b .Llabel6
+  b .Llabel7
+.Llabel6:
+  nop
+.Llabel7:
+  nop
+
+  // TODO If a branch goes to another branch instruction, we don't have a way
+  // to represent that. Can we improve on this?
+  b .Llabel8
+.Llabel8:
+  b .Llabel9
+.Llabel9:
+  nop
diff --git a/llvm/test/tools/llvm-objdump/visualize-jumps-thumb.s b/llvm/test/tools/llvm-objdump/visualize-jumps-thumb.s
new file mode 100644
index 00000000000000..8be73d3aa4c946
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/visualize-jumps-thumb.s
@@ -0,0 +1,63 @@
+// RUN: llvm-mc < %s -triple thumbv8a -filetype=obj | \
+// RUN:   llvm-objdump --triple thumbv8a -d --visualize-jumps=unicode - | \
+// RUN:   diff - %p/Inputs/visualize-jumps-thumb-unicode.txt
+
+// RUN: llvm-mc < %s -triple thumbv8a -filetype=obj | \
+// RUN:   llvm-objdump --triple thumbv8a -d --visualize-jumps=ascii - | \
+// RUN:   diff - %p/Inputs/visualize-jumps-thumb-ascii.txt
+
+test_func:
+  // Relocated instructions don't get control-flow edges.
+  bl extern_func
+  b extern_func
+  
+  // Two branches to the same label, one forward and one backward.
+  b .Llabel1
+.Llabel1:
+  nop
+  b .Llabel1
+
+  // Branch to self, no CFG edge shown
+  b .
+
+  // Different branch instructions
+  b.w .Llabel2
+  beq .Llabel2
+  bne.w .Llabel2
+  cbz r0, .Llabel2
+  it le
+  ble .Llabel2
+  nop
+.Llabel2:
+  nop
+.Llabel2.1:
+  nop
+
+  // Branches are sorted with shorter ones to the right, to reduce number of
+  // crossings, and keep the lines for short branches short themselves.
+  b .Llabel5
+  b .Llabel4
+  b .Llabel3
+.Llabel3:
+  nop
+.Llabel4:
+  nop
+.Llabel5:
+  nop
+
+  // Sometimes crossings can't be avoided.
+  b .Llabel6
+  b .Llabel7
+.Llabel6:
+  nop
+.Llabel7:
+  nop
+
+  // TODO If a branch goes to another branch instruction, we don't have a way
+  // to represent that. Can we improve on this?
+  b .Llabel8
+.Llabel8:
+  b .Llabel9
+.Llabel9:
+  nop
+
diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td
index 100a95d3d92542..9a43cdaedd4626 100644
--- a/llvm/tools/llvm-objdump/ObjdumpOpts.td
+++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td
@@ -92,6 +92,13 @@ def disassembler_color_EQ : Joined<["--"], "disassembler-color=">,
   HelpText<"Enable or disable disassembler color output. "
            "Valid options are \"on\", \"off\" and \"terminal\" (default)">;
 
+def visualize_jumps : Flag<["--"], "visualize-jumps">;
+def visualize_jumps_EQ : Joined<["--"], "visualize-jumps=">,
+  MetaVarName<"mode,...">,
+  HelpText<"Print a control flow graph along side disassembly. "
+           "Color modes: auto (default), nocolor, color. "
+           "Character modes: unicode (default), ascii.">;
+
 def dynamic_reloc : Flag<["--"], "dynamic-reloc">,
   HelpText<"Display the dynamic relocation entries in the file">;
 def : Flag<["-"], "R">, Alias<dynamic_reloc>,
diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp
index 497756b1765cda..b0c7c7fcc4c862 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.cpp
+++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp
@@ -262,12 +262,15 @@ void LiveVariablePrinter::printAfterOtherLine(formatted_raw_ostream &OS,
 /// already-active live ranges) because something has already been printed
 /// earlier on this line.
 void LiveVariablePrinter::printBetweenInsts(formatted_raw_ostream &OS,
-                                            bool MustPrint) {
+                                            bool MustPrint, uint64_t Addr,
+                                            ControlFlowPrinter *CFP) {
   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.
+      if (CFP)
+        CFP->printOther(OS, Addr);
       OS.PadToColumn(getIndentLevel());
       for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) {
         if (ActiveCols[ColIdx2].isActive()) {
@@ -503,5 +506,194 @@ SourcePrinter::SourcePrinter(const object::ObjectFile *Obj,
   Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
 }
 
+// TODO Light/dark shades? 256-color terminals?
+const raw_ostream::Colors LineColors[] = {
+  raw_ostream::RED,
+  raw_ostream::GREEN,
+  raw_ostream::YELLOW,
+  raw_ostream::BLUE,
+  raw_ostream::MAGENTA,
+  raw_ostream::CYAN,
+};
+
+void ControlFlowPrinter::addEdge(uint64_t From, uint64_t To) {
+  auto It = Targets.find(To);
+  if (It == Targets.end())
+    It = Targets.insert(std::make_pair(To, ControlFlowTarget(To, PickColor()))).first;
+  It->second.addSource(From);
+}
+
+void ControlFlowPrinter::finalise() {
+  if (!enabled()) {
+    setControlFlowColumnWidth(0);
+    return;
+  }
+
+  SmallVector<ControlFlowTarget *> SortedTargets;
+  for (auto &[Addr, Info] : Targets) {
+    SortedTargets.push_back(&Info);
+  }
+  std::sort(SortedTargets.begin(), SortedTargets.end(),
+            [](ControlFlowTarget *LHS, ControlFlowTarget *RHS) {
+              return LHS->Length() < RHS->Length();
+            });
+
+  // FIXME This is O(n^3) in the worst case, can we do better?
+  for (auto &T : SortedTargets) {
+    int Column = 0;
+    for (;; ++Column)
+      if (!std::any_of(SortedTargets.begin(), SortedTargets.end(),
+                       [T, Column](ControlFlowTarget *T2) {
+                         return T != T2 && T2->Column == Column &&
+                                T->Overlaps(*T2);
+                       }))
+        break;
+    T->Column = Column;
+    MaxColumn = std::max(MaxColumn, Column);
+
+#if 1
+    LLVM_DEBUG(
+      dbgs() << "Target: 0x" << Twine::utohexstr(T->Target) << " (" << T->Length()
+             << ") Column " << Column << ":\n";
+      for (auto Source : T->Sources)
+        dbgs() << "  Source: 0x" << Twine::utohexstr(Source) << "\n";
+    );
+#endif
+  }
+
+  setControlFlowColumnWidth(MaxColumn * 2 + 4);
+}
+
+const char *ControlFlowPrinter::getLineChar(LineChar C) const {
+  bool IsASCII =
+      (OutputMode & VisualizeJumpsMode::CharsMask) == VisualizeJumpsMode::CharsASCII;
+  switch (C) {
+  case LineChar::Horiz:
+    return IsASCII ? "-" : (const char *)u8"\u2500";
+  case LineChar::Vert:
+    return IsASCII ? "|" : (const char *)u8"\u2502";
+  case LineChar::TopCorner:
+    return IsASCII ? "/" : (const char *)u8"\u256d";
+  case LineChar::BottomCorner:
+    return IsASCII ? "\\" : (const char *)u8"\u2570";
+  case LineChar::Tee:
+    return IsASCII ? "+" : (const char *)u8"\u251c";
+  case LineChar::Arrow:
+    return ">";
+  }
+  llvm_unreachable("Unhandled LineChar enum");
+}
+
+#define C(id) getLineChar(LineChar::id)
+
+void ControlFlowPrinter::printInst(formatted_raw_ostream &OS,
+                                   uint64_t Addr) const {
+  if (!enabled())
+    return;
+
+  SmallVector<const ControlFlowTarget *, 8> Columns;
+  Columns.resize(MaxColumn + 1);
+  const ControlFlowTarget *Horizontal = nullptr;
+
+  IndentToColumn(STI, OS, DisassemblyColumn::ControlFlow);
+
+  // TODO: What happens if an instruction has both incoming and outgoing edges?
+
+  for (auto &[_, Info] : Targets) {
+    if (Info.ActiveAt(Addr)) {
+      assert(Columns[Info.Column] == nullptr);
+      Columns[Info.Column] = &Info;
+    }
+  }
+
+  auto Color = [&](raw_ostream &OS,
+                   raw_ostream::Colors Color) -> raw_ostream & {
+    if ((OutputMode & VisualizeJumpsMode::ColorMask) !=
+        VisualizeJumpsMode::Off) {
+      OS << Color;
+    }
+    return OS;
+  };
+
+  OS.PadToColumn(getIndentLevel());
+  for (int ColIdx = MaxColumn; ColIdx >= 0; --ColIdx) {
+    if (Horizontal) {
+      if (Columns[ColIdx]) {
+        // Outgoing horizontal lines are drawn "under" vertical line, because
+        // that minimises the gap in the "lower" line.
+        Color(OS, Horizontal->Color) << C(Horiz);
+        Color(OS, Columns[ColIdx]->Color) << C(Vert);
+      } else
+        Color(OS, Horizontal->Color) << C(Horiz) << C(Horiz);
+    } else if (!Columns[ColIdx])
+      OS << "  ";
+    else if (Columns[ColIdx]->HorizontalAt(Addr)) {
+      Horizontal = Columns[ColIdx];
+      if (Columns[ColIdx]->StartsAt(Addr))
+        Color(OS, Horizontal->Color) << " " << C(TopCorner);
+      else if (Columns[ColIdx]->EndsAt(Addr))
+        Color(OS, Horizontal->Color) << " " << C(BottomCorner);
+      else
+        Color(OS, Horizontal->Color) << " " << C(Tee);
+    } else if (Columns[ColIdx]->ActiveAt(Addr))
+      Color(OS, Columns[ColIdx]->Color) << " " << C(Vert);
+    else
+      OS << "  ";
+  }
+
+  if (Horizontal) {
+    if (Horizontal->TargetAt(Addr))
+      Color(OS, Horizontal->Color) << C(Horiz) << C(Arrow);
+    else
+      Color(OS, Horizontal->Color) << C(Horiz) << C(Horiz);
+  } else {
+    OS << "  ";
+  }
+
+  Color(OS, raw_ostream::RESET);
+}
+
+// TODO boolean params -> enum?
+void ControlFlowPrinter::printOther(formatted_raw_ostream &OS, uint64_t Addr,
+                                    bool BeforeInst, bool AfterInst) const {
+  if (!enabled())
+    return;
+
+  assert(!(BeforeInst && AfterInst));
+
+  SmallVector<const ControlFlowTarget *, 8> Columns;
+  Columns.resize(MaxColumn + 1);
+
+  auto Color = [&](raw_ostream &OS,
+                   raw_ostream::Colors Color) -> raw_ostream & {
+    if ((OutputMode & VisualizeJumpsMode::ColorMask) !=
+        VisualizeJumpsMode::Off) {
+      OS << Color;
+    }
+    return OS;
+  };
+
+  IndentToColumn(STI, OS, DisassemblyColumn::ControlFlow);
+
+  for (auto &[_, Info] : Targets) {
+    if (Info.ActiveAt(Addr, BeforeInst, AfterInst)) {
+      assert(Columns[Info.Column] == nullptr);
+      Columns[Info.Column] = &Info;
+    }
+  }
+
+  OS.PadToColumn(getIndentLevel());
+  for (int ColIdx = MaxColumn; ColIdx >= 0; --ColIdx) {
+    if (!Columns[ColIdx])
+      OS << "  ";
+    else
+      Color(OS, Columns[ColIdx]->Color) << " " << C(Vert);
+  }
+
+  Color(OS, raw_ostream::RESET) << "  ";
+}
+
+#undef C
+
 } // namespace objdump
 } // namespace llvm
diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h
index fc67fc65074441..c311277121a620 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.h
+++ b/llvm/tools/llvm-objdump/SourcePrinter.h
@@ -22,6 +22,8 @@
 namespace llvm {
 namespace objdump {
 
+class ControlFlowPrinter;
+
 /// 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 {
@@ -121,7 +123,8 @@ class LiveVariablePrinter {
   /// 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);
+  void printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint,
+                         uint64_t Addr = 0, ControlFlowPrinter *CFP = nullptr);
 
   /// Print the live variable ranges to the right of a disassembled instruction.
   void printAfterInst(formatted_raw_ostream &OS);
@@ -166,6 +169,114 @@ class SourcePrinter {
                                StringRef Delimiter = "; ");
 };
 
+enum VisualizeJumpsMode : int {
+  Off = 0,
+
+  ColorAuto = 0x1,
+  Color3Bit,
+  //Color8Bit, // TODO GNU objdump can use more colors in 256-color terminals
+
+  CharsASCII = 0x10,
+  CharsUnicode = 0x20,
+
+  ColorMask = 0xf,
+  CharsMask = 0xf0,
+};
+
+
+extern const raw_ostream::Colors LineColors[6];
+
+class ControlFlowPrinter {
+  struct ControlFlowTarget {
+    uint64_t Target;
+    SmallVector<uint64_t, 4> Sources;
+    int Column;
+    raw_ostream::Colors Color;
+
+    ControlFlowTarget(uint64_t Target, raw_ostream::Colors Color)
+        : Target(Target), Column(~0U), Color(Color), High(Target), Low(Target) {}
+    ControlFlowTarget(const ControlFlowTarget &) = delete;
+    ControlFlowTarget(ControlFlowTarget &&) = default;
+
+    void addSource(uint64_t Source) {
+      Sources.push_back(Source);
+      Low = std::min(Low, Source);
+      High = std::max(High, Source);
+    }
+
+    uint64_t Length() const { return High - Low; }
+
+    bool Overlaps(ControlFlowTarget &Other) const {
+      return !(Other.Low > High || Other.High < Low);
+    }
+
+    bool ActiveAt(uint64_t Addr, bool BeforeInst = false,
+                  bool AfterInst = false) const {
+      if (BeforeInst)
+        return Addr > Low && Addr <= High;
+      else if (AfterInst)
+        return Addr >= Low && Addr < High;
+      else
+        return Addr >= Low && Addr <= High;
+    }
+
+    bool StartsAt(uint64_t Addr) const { return Addr == Low; }
+    bool EndsAt(uint64_t Addr) const { return Addr == High; }
+    bool TargetAt(uint64_t Addr) const { return Addr == Target; }
+
+    bool HorizontalAt(uint64_t Addr) const {
+      return Addr == Target ||
+             std::any_of(Sources.begin(), Sources.end(),
+                         [Addr](uint64_t Src) { return Src == Addr; });
+    }
+
+  private:
+    uint64_t High, Low;
+  };
+
+  VisualizeJumpsMode OutputMode;
+  DenseMap<uint64_t, ControlFlowTarget> Targets;
+  int MaxColumn;
+  const MCSubtargetInfo &STI;
+
+  int NextColorIdx;
+  raw_ostream::Colors PickColor() {
+    if ((OutputMode & VisualizeJumpsMode::ColorMask) ==
+        VisualizeJumpsMode::Off)
+      return raw_ostream::RESET;
+    auto Ret = LineColors[NextColorIdx];
+    NextColorIdx = (NextColorIdx + 1) % (sizeof(LineColors) / sizeof(LineColors[0]));
+    return Ret;
+  }
+
+  bool enabled() const { return OutputMode != VisualizeJumpsMode::Off; }
+  int getIndentLevel() const { return 10; }
+
+  enum class LineChar {
+    Horiz,
+    Vert,
+    TopCorner,
+    BottomCorner,
+    Tee,
+    Arrow,
+  };
+  const char *getLineChar(LineChar C) const;
+
+public:
+  ControlFlowPrinter(VisualizeJumpsMode OutputMode, const MCSubtargetInfo &STI)
+      : OutputMode(OutputMode), MaxColumn(0), STI(STI), NextColorIdx(0) {}
+
+  // Add a control-flow edge from the instruction at address From to the
+  // instruction at address To.
+  void addEdge(uint64_t From, uint64_t To);
+
+  void finalise();
+
+  void printInst(formatted_raw_ostream &OS, uint64_t Addr) const;
+  void printOther(formatted_raw_ostream &OS, uint64_t Addr,
+                  bool BeforeInst = false, bool AfterInst = false) const;
+};
+
 } // namespace objdump
 } // namespace llvm
 
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index d07d086a5956f9..8fdc95ff8db4af 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -73,6 +73,7 @@
 #include "llvm/Support/GraphWriter.h"
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/LLVMDriver.h"
+#include "llvm/Support/MathExtras.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/StringSaver.h"
@@ -197,6 +198,7 @@ static std::vector<std::string> DisassembleSymbols;
 static bool DisassembleZeroes;
 static std::vector<std::string> DisassemblerOptions;
 static ColorOutput DisassemblyColor;
+static VisualizeJumpsMode VisualizeJumps;
 DIDumpType objdump::DwarfDumpType;
 static bool DynamicRelocations;
 static bool FaultMapSection;
@@ -471,10 +473,9 @@ static bool getHidden(RelocationRef RelRef) {
   return false;
 }
 
-/// 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 objdump::getInstStartColumn(const MCSubtargetInfo &STI) {
-  return !ShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24;
+static int ControlFlowColumnWidth = 0;
+void objdump::setControlFlowColumnWidth(int Width) {
+  ControlFlowColumnWidth = Width;
 }
 
 unsigned EncodingColumnWidth(Triple const &Triple) {
@@ -497,6 +498,12 @@ unsigned objdump::GetColumnIndent(MCSubtargetInfo const &STI,
   // Address: 8 chars of address, followed by ": "
   Indent += 10;
 
+  if (Col == DisassemblyColumn::ControlFlow)
+    return Indent;
+
+  // Control-flow graph: depends on function, disabled by default.
+  Indent += ControlFlowColumnWidth;
+
   if (Col == DisassemblyColumn::Encoding)
     return Indent;
 
@@ -615,12 +622,20 @@ class PrettyPrinter {
             object::SectionedAddress Address, formatted_raw_ostream &OS,
             StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
             StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
-            LiveVariablePrinter &LVP) {
+            LiveVariablePrinter &LVP, ControlFlowPrinter &CFP) {
     if (SP && (PrintSource || PrintLines))
       SP->printSourceLine(OS, Address, ObjectFilename, LVP);
-    LVP.printBetweenInsts(OS, false);
+    LVP.printBetweenInsts(OS, false, Address.Address, &CFP);
 
-    printRawData(Bytes, Address.Address, OS, STI);
+    if (LeadingAddr)
+      OS << format("%8" PRIx64 ":", Address.Address);
+
+    CFP.printInst(OS, Address.Address);
+
+    if (ShowRawInsn) {
+      OS << ' ';
+      dumpBytes(Bytes, OS);
+    }
 
     IndentToColumn(STI, OS, DisassemblyColumn::Assembly);
 
@@ -656,7 +671,7 @@ class HexagonPrettyPrinter : public PrettyPrinter {
                  object::SectionedAddress Address, formatted_raw_ostream &OS,
                  StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
                  StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
-                 LiveVariablePrinter &LVP) override {
+                 LiveVariablePrinter &LVP, ControlFlowPrinter &CFP) override {
     if (SP && (PrintSource || PrintLines))
       SP->printSourceLine(OS, Address, ObjectFilename, LVP, "");
     if (!MI) {
@@ -726,7 +741,7 @@ class AMDGCNPrettyPrinter : public PrettyPrinter {
                  object::SectionedAddress Address, formatted_raw_ostream &OS,
                  StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
                  StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
-                 LiveVariablePrinter &LVP) override {
+                 LiveVariablePrinter &LVP, ControlFlowPrinter &CFP) override {
     if (SP && (PrintSource || PrintLines))
       SP->printSourceLine(OS, Address, ObjectFilename, LVP);
 
@@ -779,7 +794,7 @@ class BPFPrettyPrinter : public PrettyPrinter {
                  object::SectionedAddress Address, formatted_raw_ostream &OS,
                  StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
                  StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
-                 LiveVariablePrinter &LVP) override {
+                 LiveVariablePrinter &LVP, ControlFlowPrinter &CFP) override {
     if (SP && (PrintSource || PrintLines))
       SP->printSourceLine(OS, Address, ObjectFilename, LVP);
     if (LeadingAddr)
@@ -802,13 +817,16 @@ class ARMPrettyPrinter : public PrettyPrinter {
                  object::SectionedAddress Address, formatted_raw_ostream &OS,
                  StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
                  StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
-                 LiveVariablePrinter &LVP) override {
+                 LiveVariablePrinter &LVP, ControlFlowPrinter &CFP) override {
     if (SP && (PrintSource || PrintLines))
       SP->printSourceLine(OS, Address, ObjectFilename, LVP);
-    LVP.printBetweenInsts(OS, false);
+    LVP.printBetweenInsts(OS, false, Address.Address, &CFP);
 
     if (LeadingAddr)
       OS << format("%8" PRIx64 ":", Address.Address);
+
+    CFP.printInst(OS, Address.Address);
+
     if (ShowRawInsn) {
       size_t Pos = 0, End = Bytes.size();
       if (STI.checkFeatures("+thumb-mode")) {
@@ -855,13 +873,16 @@ class AArch64PrettyPrinter : public PrettyPrinter {
                  object::SectionedAddress Address, formatted_raw_ostream &OS,
                  StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
                  StringRef ObjectFilename, std::vector<RelocationRef> *Rels,
-                 LiveVariablePrinter &LVP) override {
+                 LiveVariablePrinter &LVP, ControlFlowPrinter &CFP) override {
     if (SP && (PrintSource || PrintLines))
       SP->printSourceLine(OS, Address, ObjectFilename, LVP);
-    LVP.printBetweenInsts(OS, false);
+    LVP.printBetweenInsts(OS, false, Address.Address, &CFP);
 
     if (LeadingAddr)
       OS << format("%8" PRIx64 ":", Address.Address);
+
+    CFP.printInst(OS, Address.Address);
+
     if (ShowRawInsn) {
       size_t Pos = 0, End = Bytes.size();
       for (; Pos + 4 <= End; Pos += 4)
@@ -1343,7 +1364,9 @@ collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, MCInstrAnalysis *MIA,
                           MCDisassembler *DisAsm, MCInstPrinter *IP,
                           const MCSubtargetInfo *STI, uint64_t SectionAddr,
                           uint64_t Start, uint64_t End,
-                          std::unordered_map<uint64_t, std::string> &Labels) {
+                          std::unordered_map<uint64_t, std::string> &Labels,
+                          ControlFlowPrinter &CFP,
+                          std::vector<RelocationRef> &Relocs) {
   if (MIA)
     MIA->resetState();
 
@@ -1352,6 +1375,10 @@ collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, MCInstrAnalysis *MIA,
   Start += SectionAddr;
   End += SectionAddr;
   uint64_t Index = Start;
+
+  std::vector<RelocationRef>::const_iterator RelCur = Relocs.begin();
+  std::vector<RelocationRef>::const_iterator RelEnd = Relocs.end();
+
   while (Index < End) {
     // Disassemble a real instruction and record function-local branch labels.
     MCInst Inst;
@@ -1363,15 +1390,37 @@ collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, MCInstrAnalysis *MIA,
       Size = std::min<uint64_t>(ThisBytes.size(),
                                 DisAsm->suggestBytesToSkip(ThisBytes, Index));
 
+    // Check for relocations which apply to this instruction.
+    bool Relocated = false;
+    while (RelCur != RelEnd) {
+      // FIXME RelAdjustment for executables & shared objects
+      uint64_t Offset = RelCur->getOffset(); // - RelAdjustment;
+
+      if (Offset >= Index + Size)
+        break;
+
+      Relocated = true;
+      ++RelCur;
+    }
+
     if (Disassembled && MIA) {
       uint64_t Target;
       bool TargetKnown = MIA->evaluateBranch(Inst, Index, Size, Target);
-      // On PowerPC, if the address of a branch is the same as the target, it
-      // means that it's a function call. Do not mark the label for this case.
-      if (TargetKnown && (Target >= Start && Target < End) &&
-          !Labels.count(Target) &&
-          !(STI->getTargetTriple().isPPC() && Target == Index))
-        Labels[Target] = ("L" + Twine(LabelCount++)).str();
+      if (TargetKnown && (Target >= Start && Target < End)) {
+        // On PowerPC and ARM, if the address of a branch is the same as the
+        // target, it means that it's a function call. Do not mark the label for
+        // this case.
+        if (!Labels.count(Target) &&
+            !((STI->getTargetTriple().isPPC() ||
+               STI->getTargetTriple().isARM()) &&
+              Target == Index) &&
+            SymbolizeOperands) {
+          Labels[Target] = ("L" + Twine(LabelCount++)).str();
+        }
+
+        if (Target != Index && !Relocated)
+          CFP.addEdge(Index, Target);
+      }
       MIA->updateState(Inst, Index);
     } else if (!Disassembled && MIA) {
       MIA->resetState();
@@ -1460,9 +1509,10 @@ static void emitPostInstructionInfo(formatted_raw_ostream &FOS,
       StringRef Comment;
       std::tie(Comment, Comments) = Comments.split('\n');
       // MAI.getCommentColumn() assumes that instructions are printed at the
-      // position of 8, while getInstStartColumn() returns the actual position.
+      // position of 8, while GetColumnIndent() returns the actual position.
       unsigned CommentColumn =
-          MAI.getCommentColumn() - 8 + getInstStartColumn(STI);
+          MAI.getCommentColumn() - 8 +
+          GetColumnIndent(STI, DisassemblyColumn::Assembly);
       FOS.PadToColumn(CommentColumn);
       FOS << MAI.getCommentString() << ' ' << Comment;
     }
@@ -1538,7 +1588,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
   }
 
   std::map<SectionRef, std::vector<RelocationRef>> RelocMap;
-  if (InlineRelocs)
+  if (InlineRelocs || VisualizeJumps)
     RelocMap = getRelocsMap(Obj);
   bool Is64Bits = Obj.getBytesInAddress() > 4;
 
@@ -2019,15 +2069,20 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
 
       std::unordered_map<uint64_t, std::string> AllLabels;
       std::unordered_map<uint64_t, std::vector<std::string>> BBAddrMapLabels;
+      ControlFlowPrinter CFP(VisualizeJumps, *DT->SubtargetInfo);
+      if (SymbolizeOperands || VisualizeJumps) {
+        collectLocalBranchTargets(
+            Bytes, DT->InstrAnalysis.get(), DT->DisAsm.get(),
+            DT->InstPrinter.get(), PrimaryTarget.SubtargetInfo.get(),
+            SectionAddr, Index, End, AllLabels, CFP, Rels);
+      }
       if (SymbolizeOperands) {
-        collectLocalBranchTargets(Bytes, DT->InstrAnalysis.get(),
-                                  DT->DisAsm.get(), DT->InstPrinter.get(),
-                                  PrimaryTarget.SubtargetInfo.get(),
-                                  SectionAddr, Index, End, AllLabels);
         collectBBAddrMapLabels(AddrToBBAddrMap, SectionAddr, Index, End,
                                BBAddrMapLabels);
       }
 
+      CFP.finalise();
+
       if (DT->InstrAnalysis)
         DT->InstrAnalysis->resetState();
 
@@ -2076,7 +2131,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
             uint64_t MaxOffset = End - Index;
             // For --reloc: print zero blocks patched by relocations, so that
             // relocations can be shown in the dump.
-            if (RelCur != RelEnd)
+            if (InlineRelocs && RelCur != RelEnd)
               MaxOffset = std::min(RelCur->getOffset() - RelAdjustment - Index,
                                    MaxOffset);
 
@@ -2103,12 +2158,14 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
           if (Iter1 != BBAddrMapLabels.end()) {
             for (StringRef Label : Iter1->second) {
               FOS << "<" << Label << ">:";
+              CFP.printOther(FOS, Index, true);
               LVP.printAfterOtherLine(FOS, true);
             }
           } else {
             auto Iter2 = AllLabels.find(SectionAddr + Index);
             if (Iter2 != AllLabels.end()) {
               FOS << "<" << Iter2->second << ">:";
+              CFP.printOther(FOS, Index, true);
               LVP.printAfterOtherLine(FOS, true);
             }
           }
@@ -2134,7 +2191,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
               *DT->InstPrinter, Disassembled ? &Inst : nullptr,
               Bytes.slice(Index, Size),
               {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, FOS,
-              "", *DT->SubtargetInfo, &SP, Obj.getFileName(), &Rels, LVP);
+              "", *DT->SubtargetInfo, &SP, Obj.getFileName(), &Rels, LVP, CFP);
 
           DT->InstPrinter->setCommentStream(llvm::nulls());
 
@@ -2267,7 +2324,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
           printBTFRelocation(FOS, *BTF, {Index, Section.getIndex()}, LVP);
 
         // Hexagon does this in pretty printer
-        if (Obj.getArch() != Triple::hexagon) {
+        if (Obj.getArch() != Triple::hexagon && InlineRelocs) {
           // Print relocation for instruction and data.
           while (RelCur != RelEnd) {
             uint64_t Offset = RelCur->getOffset() - RelAdjustment;
@@ -2294,6 +2351,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
                 Offset += AdjustVMA;
             }
 
+            CFP.printOther(FOS, Index, false, true);
             printRelocation(FOS, Obj.getFileName(), *RelCur,
                             SectionAddr + Offset, Is64Bits);
             LVP.printAfterOtherLine(FOS, true);
@@ -3336,6 +3394,45 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) {
     if (DisassemblyColor == ColorOutput::Invalid)
       invalidArgValue(A);
   }
+  if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_visualize_jumps, OBJDUMP_visualize_jumps_EQ)) {
+    if (A->getOption().matches(OBJDUMP_visualize_jumps)) {
+      // --visualize-jumps without an argument default to unicode, auto-color.
+      VisualizeJumps = (VisualizeJumpsMode)(VisualizeJumpsMode::CharsUnicode |
+                                            VisualizeJumpsMode::ColorAuto);
+    } else {
+      SmallVector<StringRef, 2> Parts;
+      StringRef(A->getValue()).split(Parts, ",");
+      VisualizeJumpsMode Color = VisualizeJumpsMode::ColorAuto;
+      VisualizeJumpsMode Chars = VisualizeJumpsMode::CharsUnicode;
+
+      for (StringRef Part : Parts) {
+        if (Part == "off") {
+          Color = VisualizeJumpsMode::Off;
+          Chars = VisualizeJumpsMode::Off;
+        } else if (Part == "nocolor") {
+          Color = VisualizeJumpsMode::Off;
+        } else if (Part == "auto") {
+          Color = VisualizeJumpsMode::ColorAuto;
+        } else if (Part == "color") {
+          Color = VisualizeJumpsMode::Color3Bit;
+        } else if (Part == "ascii") {
+          Chars = VisualizeJumpsMode::CharsASCII;
+        } else if (Part == "unicode") {
+          Chars = VisualizeJumpsMode::CharsUnicode;
+        } else {
+          reportCmdLineError("'" + Part + "' is not a valid value for '" +
+                             A->getSpelling() + "'");
+        }
+      }
+
+      if (Color == VisualizeJumpsMode::ColorAuto) {
+        Color = outs().has_colors() ? VisualizeJumpsMode::Color3Bit
+                                    : VisualizeJumpsMode::Off;
+      }
+
+      VisualizeJumps = (VisualizeJumpsMode)(Color | Chars);
+    }
+  }
 
   parseIntArg(InputArgs, OBJDUMP_debug_vars_indent_EQ, DbgIndent);
 
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.h b/llvm/tools/llvm-objdump/llvm-objdump.h
index 31281ba3d8c2ad..74c142aa80ed92 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.h
+++ b/llvm/tools/llvm-objdump/llvm-objdump.h
@@ -146,11 +146,13 @@ void printRawData(llvm::ArrayRef<uint8_t> Bytes, uint64_t Address,
 
 enum class DisassemblyColumn {
   Address,
+  ControlFlow,
   Encoding,
   Assembly,
   Variables,
 };
 
+void setControlFlowColumnWidth(int Width);
 unsigned GetColumnIndent(MCSubtargetInfo const &STI, DisassemblyColumn Col);
 void IndentToColumn(MCSubtargetInfo const &STI, formatted_raw_ostream &OS,
                     DisassemblyColumn Col);

>From 7472b5491ca936d047e388aaa3dc277c8807a3a5 Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Wed, 13 Dec 2023 14:51:42 +0000
Subject: [PATCH 6/9] Replace bit-packed enum with a struct

---
 llvm/tools/llvm-objdump/SourcePrinter.cpp | 15 +++++-------
 llvm/tools/llvm-objdump/SourcePrinter.h   | 29 +++++++++++++---------
 llvm/tools/llvm-objdump/llvm-objdump.cpp  | 30 ++++++++++-------------
 3 files changed, 36 insertions(+), 38 deletions(-)

diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp
index b0c7c7fcc4c862..f479a5e7b1a8bb 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.cpp
+++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp
@@ -524,7 +524,7 @@ void ControlFlowPrinter::addEdge(uint64_t From, uint64_t To) {
 }
 
 void ControlFlowPrinter::finalise() {
-  if (!enabled()) {
+  if (!OutputMode.enabled()) {
     setControlFlowColumnWidth(0);
     return;
   }
@@ -565,8 +565,7 @@ void ControlFlowPrinter::finalise() {
 }
 
 const char *ControlFlowPrinter::getLineChar(LineChar C) const {
-  bool IsASCII =
-      (OutputMode & VisualizeJumpsMode::CharsMask) == VisualizeJumpsMode::CharsASCII;
+  bool IsASCII = OutputMode.Chars == VisualizeJumpsMode::ASCII;
   switch (C) {
   case LineChar::Horiz:
     return IsASCII ? "-" : (const char *)u8"\u2500";
@@ -588,7 +587,7 @@ const char *ControlFlowPrinter::getLineChar(LineChar C) const {
 
 void ControlFlowPrinter::printInst(formatted_raw_ostream &OS,
                                    uint64_t Addr) const {
-  if (!enabled())
+  if (!OutputMode.enabled())
     return;
 
   SmallVector<const ControlFlowTarget *, 8> Columns;
@@ -608,8 +607,7 @@ void ControlFlowPrinter::printInst(formatted_raw_ostream &OS,
 
   auto Color = [&](raw_ostream &OS,
                    raw_ostream::Colors Color) -> raw_ostream & {
-    if ((OutputMode & VisualizeJumpsMode::ColorMask) !=
-        VisualizeJumpsMode::Off) {
+    if (OutputMode.color_enabled()) {
       OS << Color;
     }
     return OS;
@@ -656,7 +654,7 @@ void ControlFlowPrinter::printInst(formatted_raw_ostream &OS,
 // TODO boolean params -> enum?
 void ControlFlowPrinter::printOther(formatted_raw_ostream &OS, uint64_t Addr,
                                     bool BeforeInst, bool AfterInst) const {
-  if (!enabled())
+  if (!OutputMode.enabled())
     return;
 
   assert(!(BeforeInst && AfterInst));
@@ -666,8 +664,7 @@ void ControlFlowPrinter::printOther(formatted_raw_ostream &OS, uint64_t Addr,
 
   auto Color = [&](raw_ostream &OS,
                    raw_ostream::Colors Color) -> raw_ostream & {
-    if ((OutputMode & VisualizeJumpsMode::ColorMask) !=
-        VisualizeJumpsMode::Off) {
+    if (OutputMode.color_enabled()) {
       OS << Color;
     }
     return OS;
diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h
index c311277121a620..1df171fae8d028 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.h
+++ b/llvm/tools/llvm-objdump/SourcePrinter.h
@@ -169,22 +169,28 @@ class SourcePrinter {
                                StringRef Delimiter = "; ");
 };
 
-enum VisualizeJumpsMode : int {
-  Off = 0,
+struct VisualizeJumpsMode {
+  enum Chars_t { Off, ASCII, Unicode };
+  enum Colors_t { BlackAndWhite, ThreeBit, Auto };
 
-  ColorAuto = 0x1,
-  Color3Bit,
-  //Color8Bit, // TODO GNU objdump can use more colors in 256-color terminals
+  Chars_t Chars;
+  Colors_t Colors;
 
-  CharsASCII = 0x10,
-  CharsUnicode = 0x20,
+  VisualizeJumpsMode() : Chars(Off), Colors(BlackAndWhite) {}
+  VisualizeJumpsMode(Chars_t Chars, Colors_t Colors)
+      : Chars(Chars), Colors(Colors) {}
 
-  ColorMask = 0xf,
-  CharsMask = 0xf0,
-};
+  static VisualizeJumpsMode GetDefault() { return VisualizeJumpsMode(Unicode, Auto); }
+
+  bool enabled() const { return Chars != Off; }
+  bool color_enabled() const { return enabled() && Colors != BlackAndWhite; }
 
+  void ResolveAutoColor(raw_ostream &OS) {
+    if (Colors == Auto)
+      Colors = OS.has_colors() ? ThreeBit : BlackAndWhite;
+  }
+};
 
-extern const raw_ostream::Colors LineColors[6];
 
 class ControlFlowPrinter {
   struct ControlFlowTarget {
@@ -249,7 +255,6 @@ class ControlFlowPrinter {
     return Ret;
   }
 
-  bool enabled() const { return OutputMode != VisualizeJumpsMode::Off; }
   int getIndentLevel() const { return 10; }
 
   enum class LineChar {
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 8fdc95ff8db4af..6e6e53cc61fbf9 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -1588,7 +1588,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
   }
 
   std::map<SectionRef, std::vector<RelocationRef>> RelocMap;
-  if (InlineRelocs || VisualizeJumps)
+  if (InlineRelocs || VisualizeJumps.enabled())
     RelocMap = getRelocsMap(Obj);
   bool Is64Bits = Obj.getBytesInAddress() > 4;
 
@@ -2070,7 +2070,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
       std::unordered_map<uint64_t, std::string> AllLabels;
       std::unordered_map<uint64_t, std::vector<std::string>> BBAddrMapLabels;
       ControlFlowPrinter CFP(VisualizeJumps, *DT->SubtargetInfo);
-      if (SymbolizeOperands || VisualizeJumps) {
+      if (SymbolizeOperands || VisualizeJumps.enabled()) {
         collectLocalBranchTargets(
             Bytes, DT->InstrAnalysis.get(), DT->DisAsm.get(),
             DT->InstPrinter.get(), PrimaryTarget.SubtargetInfo.get(),
@@ -3397,41 +3397,37 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) {
   if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_visualize_jumps, OBJDUMP_visualize_jumps_EQ)) {
     if (A->getOption().matches(OBJDUMP_visualize_jumps)) {
       // --visualize-jumps without an argument default to unicode, auto-color.
-      VisualizeJumps = (VisualizeJumpsMode)(VisualizeJumpsMode::CharsUnicode |
-                                            VisualizeJumpsMode::ColorAuto);
+      VisualizeJumps = VisualizeJumpsMode::GetDefault();
     } else {
       SmallVector<StringRef, 2> Parts;
       StringRef(A->getValue()).split(Parts, ",");
-      VisualizeJumpsMode Color = VisualizeJumpsMode::ColorAuto;
-      VisualizeJumpsMode Chars = VisualizeJumpsMode::CharsUnicode;
+      VisualizeJumpsMode::Chars_t Chars = VisualizeJumpsMode::Unicode;
+      VisualizeJumpsMode::Colors_t Color = VisualizeJumpsMode::Auto;
 
       for (StringRef Part : Parts) {
         if (Part == "off") {
-          Color = VisualizeJumpsMode::Off;
           Chars = VisualizeJumpsMode::Off;
         } else if (Part == "nocolor") {
-          Color = VisualizeJumpsMode::Off;
+          Color = VisualizeJumpsMode::BlackAndWhite;
         } else if (Part == "auto") {
-          Color = VisualizeJumpsMode::ColorAuto;
+          Color = VisualizeJumpsMode::Auto;
         } else if (Part == "color") {
-          Color = VisualizeJumpsMode::Color3Bit;
+          Color = VisualizeJumpsMode::ThreeBit;
         } else if (Part == "ascii") {
-          Chars = VisualizeJumpsMode::CharsASCII;
+          Chars = VisualizeJumpsMode::ASCII;
         } else if (Part == "unicode") {
-          Chars = VisualizeJumpsMode::CharsUnicode;
+          Chars = VisualizeJumpsMode::Unicode;
         } else {
           reportCmdLineError("'" + Part + "' is not a valid value for '" +
                              A->getSpelling() + "'");
         }
       }
 
-      if (Color == VisualizeJumpsMode::ColorAuto) {
-        Color = outs().has_colors() ? VisualizeJumpsMode::Color3Bit
-                                    : VisualizeJumpsMode::Off;
-      }
 
-      VisualizeJumps = (VisualizeJumpsMode)(Color | Chars);
+      VisualizeJumps = VisualizeJumpsMode(Chars, Color);
     }
+
+    VisualizeJumps.ResolveAutoColor(outs());
   }
 
   parseIntArg(InputArgs, OBJDUMP_debug_vars_indent_EQ, DbgIndent);

>From a8fd5df6ef14f7d69aa3320bd77b2b70c86022f7 Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Wed, 13 Dec 2023 16:24:54 +0000
Subject: [PATCH 7/9] Change tests to use split-file

---
 .../Inputs/visualize-jumps-aarch64-ascii.txt  |  31 ----
 .../visualize-jumps-aarch64-unicode-color.txt |  31 ----
 ...visualize-jumps-aarch64-unicode-relocs.txt |  34 ----
 .../visualize-jumps-aarch64-unicode.txt       |  31 ----
 .../Inputs/visualize-jumps-arm-ascii.txt      |  27 ----
 .../Inputs/visualize-jumps-arm-unicode.txt    |  27 ----
 .../Inputs/visualize-jumps-thumb-ascii.txt    |  34 ----
 .../Inputs/visualize-jumps-thumb-unicode.txt  |  34 ----
 .../llvm-objdump/visualize-jumps-aarch64.s    | 151 +++++++++++++++++-
 .../tools/llvm-objdump/visualize-jumps-arm.s  |  68 +++++++-
 .../llvm-objdump/visualize-jumps-thumb.s      |  81 +++++++++-
 11 files changed, 284 insertions(+), 265 deletions(-)
 delete mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-ascii.txt
 delete mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-color.txt
 delete mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-relocs.txt
 delete mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode.txt
 delete mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-ascii.txt
 delete mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-unicode.txt
 delete mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-ascii.txt
 delete mode 100644 llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-unicode.txt

diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-ascii.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-ascii.txt
deleted file mode 100644
index 592e50d98f8ff7..00000000000000
--- a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-ascii.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-
-<stdin>:	file format elf64-littleaarch64
-
-Disassembly of section .text:
-
-0000000000000000 <test_func>:
-       0:           94000000   	bl	0x0 <test_func>
-       4:           14000000   	b	0x4 <test_func+0x4>
-       8:       /-- 14000001   	b	0xc <test_func+0xc>
-       c:       +-> d503201f   	nop
-      10:       \-- 17ffffff   	b	0xc <test_func+0xc>
-      14:           14000000   	b	0x14 <test_func+0x14>
-      18:       /-- 54000040   	b.eq	0x20 <test_func+0x20>
-      1c:       +-- b4000020   	cbz	x0, 0x20 <test_func+0x20>
-      20:       \-> d503201f   	nop
-      24:   /------ 14000005   	b	0x38 <test_func+0x38>
-      28:   | /---- 14000003   	b	0x34 <test_func+0x34>
-      2c:   | | /-- 14000001   	b	0x30 <test_func+0x30>
-      30:   | | \-> d503201f   	nop
-      34:   | \---> d503201f   	nop
-      38:   \-----> d503201f   	nop
-      3c:       /-- 14000002   	b	0x44 <test_func+0x44>
-      40:     /-|-- 14000002   	b	0x48 <test_func+0x48>
-      44:     | \-> d503201f   	nop
-      48:     \---> d503201f   	nop
-      4c:       /-- 14000001   	b	0x50 <test_func+0x50>
-      50:     /-|-- 14000001   	b	0x54 <test_func+0x54>
-      54:     \---> d503201f   	nop
-      58:       /-- 14000002   	b	0x60 <test_func+0x60>
-      5c:       |   94000000   	bl	0x5c <test_func+0x5c>
-      60:       \-> d503201f   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-color.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-color.txt
deleted file mode 100644
index 925c5ef52a8d22..00000000000000
--- a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-color.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-
-<stdin>:	file format elf64-littleaarch64
-
-Disassembly of section .text:
-
-0000000000000000 <test_func>:
-       0:           94000000   	bl	0x0 <test_func>
-       4:           14000000   	b	0x4 <test_func+0x4>
-       8:       ╭── 14000001   	b	0xc <test_func+0xc>
-       c:       ├─> d503201f   	nop
-      10:       ╰── 17ffffff   	b	0xc <test_func+0xc>
-      14:           14000000   	b	0x14 <test_func+0x14>
-      18:       ╭── 54000040   	b.eq	0x20 <test_func+0x20>
-      1c:       ├── b4000020   	cbz	x0, 0x20 <test_func+0x20>
-      20:       ╰─> d503201f   	nop
-      24:   ╭────── 14000005   	b	0x38 <test_func+0x38>
-      28:   │ ╭──── 14000003   	b	0x34 <test_func+0x34>
-      2c:   │ │ ╭── 14000001   	b	0x30 <test_func+0x30>
-      30:   │ │ ╰─> d503201f   	nop
-      34:   │ ╰───> d503201f   	nop
-      38:   ╰─────> d503201f   	nop
-      3c:       ╭── 14000002   	b	0x44 <test_func+0x44>
-      40:     ╭─│── 14000002   	b	0x48 <test_func+0x48>
-      44:     │ ╰─> d503201f   	nop
-      48:     ╰───> d503201f   	nop
-      4c:       ╭── 14000001   	b	0x50 <test_func+0x50>
-      50:     ╭─│── 14000001   	b	0x54 <test_func+0x54>
-      54:     ╰───> d503201f   	nop
-      58:       ╭── 14000002   	b	0x60 <test_func+0x60>
-      5c:       │   94000000   	bl	0x5c <test_func+0x5c>
-      60:       ╰─> d503201f   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-relocs.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-relocs.txt
deleted file mode 100644
index 75ec5741fdaae2..00000000000000
--- a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode-relocs.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-
-<stdin>:	file format elf64-littleaarch64
-
-Disassembly of section .text:
-
-0000000000000000 <test_func>:
-       0:           94000000   	bl	0x0 <test_func>
-                   		0000000000000000:  R_AARCH64_CALL26	extern_func
-       4:           14000000   	b	0x4 <test_func+0x4>
-                   		0000000000000004:  R_AARCH64_JUMP26	extern_func
-       8:       ╭── 14000001   	b	0xc <test_func+0xc>
-       c:       ├─> d503201f   	nop
-      10:       ╰── 17ffffff   	b	0xc <test_func+0xc>
-      14:           14000000   	b	0x14 <test_func+0x14>
-      18:       ╭── 54000040   	b.eq	0x20 <test_func+0x20>
-      1c:       ├── b4000020   	cbz	x0, 0x20 <test_func+0x20>
-      20:       ╰─> d503201f   	nop
-      24:   ╭────── 14000005   	b	0x38 <test_func+0x38>
-      28:   │ ╭──── 14000003   	b	0x34 <test_func+0x34>
-      2c:   │ │ ╭── 14000001   	b	0x30 <test_func+0x30>
-      30:   │ │ ╰─> d503201f   	nop
-      34:   │ ╰───> d503201f   	nop
-      38:   ╰─────> d503201f   	nop
-      3c:       ╭── 14000002   	b	0x44 <test_func+0x44>
-      40:     ╭─│── 14000002   	b	0x48 <test_func+0x48>
-      44:     │ ╰─> d503201f   	nop
-      48:     ╰───> d503201f   	nop
-      4c:       ╭── 14000001   	b	0x50 <test_func+0x50>
-      50:     ╭─│── 14000001   	b	0x54 <test_func+0x54>
-      54:     ╰───> d503201f   	nop
-      58:       ╭── 14000002   	b	0x60 <test_func+0x60>
-      5c:       │   94000000   	bl	0x5c <test_func+0x5c>
-                │  		000000000000005c:  R_AARCH64_CALL26	extern_func
-      60:       ╰─> d503201f   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode.txt
deleted file mode 100644
index 4cabdc92d61c1d..00000000000000
--- a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-aarch64-unicode.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-
-<stdin>:	file format elf64-littleaarch64
-
-Disassembly of section .text:
-
-0000000000000000 <test_func>:
-       0:           94000000   	bl	0x0 <test_func>
-       4:           14000000   	b	0x4 <test_func+0x4>
-       8:       ╭── 14000001   	b	0xc <test_func+0xc>
-       c:       ├─> d503201f   	nop
-      10:       ╰── 17ffffff   	b	0xc <test_func+0xc>
-      14:           14000000   	b	0x14 <test_func+0x14>
-      18:       ╭── 54000040   	b.eq	0x20 <test_func+0x20>
-      1c:       ├── b4000020   	cbz	x0, 0x20 <test_func+0x20>
-      20:       ╰─> d503201f   	nop
-      24:   ╭────── 14000005   	b	0x38 <test_func+0x38>
-      28:   │ ╭──── 14000003   	b	0x34 <test_func+0x34>
-      2c:   │ │ ╭── 14000001   	b	0x30 <test_func+0x30>
-      30:   │ │ ╰─> d503201f   	nop
-      34:   │ ╰───> d503201f   	nop
-      38:   ╰─────> d503201f   	nop
-      3c:       ╭── 14000002   	b	0x44 <test_func+0x44>
-      40:     ╭─│── 14000002   	b	0x48 <test_func+0x48>
-      44:     │ ╰─> d503201f   	nop
-      48:     ╰───> d503201f   	nop
-      4c:       ╭── 14000001   	b	0x50 <test_func+0x50>
-      50:     ╭─│── 14000001   	b	0x54 <test_func+0x54>
-      54:     ╰───> d503201f   	nop
-      58:       ╭── 14000002   	b	0x60 <test_func+0x60>
-      5c:       │   94000000   	bl	0x5c <test_func+0x5c>
-      60:       ╰─> d503201f   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-ascii.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-ascii.txt
deleted file mode 100644
index 7e006841980534..00000000000000
--- a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-ascii.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-
-<stdin>:	file format elf32-littlearm
-
-Disassembly of section .text:
-
-00000000 <test_func>:
-       0:           ebfffffe   	bl	0x0 <test_func>         @ imm = #-0x8
-       4:           eafffffe   	b	0x4 <test_func+0x4>     @ imm = #-0x8
-       8:       /-- eaffffff   	b	0xc <test_func+0xc>     @ imm = #-0x4
-       c:       +-> e320f000   	nop
-      10:       \-- eafffffd   	b	0xc <test_func+0xc>     @ imm = #-0xc
-      14:           eafffffe   	b	0x14 <test_func+0x14>   @ imm = #-0x8
-      18:       /-- 0affffff   	beq	0x1c <test_func+0x1c>   @ imm = #-0x4
-      1c:       \-> e320f000   	nop
-      20:   /------ ea000003   	b	0x34 <test_func+0x34>   @ imm = #0xc
-      24:   | /---- ea000001   	b	0x30 <test_func+0x30>   @ imm = #0x4
-      28:   | | /-- eaffffff   	b	0x2c <test_func+0x2c>   @ imm = #-0x4
-      2c:   | | \-> e320f000   	nop
-      30:   | \---> e320f000   	nop
-      34:   \-----> e320f000   	nop
-      38:       /-- ea000000   	b	0x40 <test_func+0x40>   @ imm = #0x0
-      3c:     /-|-- ea000000   	b	0x44 <test_func+0x44>   @ imm = #0x0
-      40:     | \-> e320f000   	nop
-      44:     \---> e320f000   	nop
-      48:     /---- eaffffff   	b	0x4c <test_func+0x4c>   @ imm = #-0x4
-      4c:     \-|-> eaffffff   	b	0x50 <test_func+0x50>   @ imm = #-0x4
-      50:       \-> e320f000   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-unicode.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-unicode.txt
deleted file mode 100644
index 62194b4b33c172..00000000000000
--- a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-arm-unicode.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-
-<stdin>:	file format elf32-littlearm
-
-Disassembly of section .text:
-
-00000000 <test_func>:
-       0:           ebfffffe   	bl	0x0 <test_func>         @ imm = #-0x8
-       4:           eafffffe   	b	0x4 <test_func+0x4>     @ imm = #-0x8
-       8:       ╭── eaffffff   	b	0xc <test_func+0xc>     @ imm = #-0x4
-       c:       ├─> e320f000   	nop
-      10:       ╰── eafffffd   	b	0xc <test_func+0xc>     @ imm = #-0xc
-      14:           eafffffe   	b	0x14 <test_func+0x14>   @ imm = #-0x8
-      18:       ╭── 0affffff   	beq	0x1c <test_func+0x1c>   @ imm = #-0x4
-      1c:       ╰─> e320f000   	nop
-      20:   ╭────── ea000003   	b	0x34 <test_func+0x34>   @ imm = #0xc
-      24:   │ ╭──── ea000001   	b	0x30 <test_func+0x30>   @ imm = #0x4
-      28:   │ │ ╭── eaffffff   	b	0x2c <test_func+0x2c>   @ imm = #-0x4
-      2c:   │ │ ╰─> e320f000   	nop
-      30:   │ ╰───> e320f000   	nop
-      34:   ╰─────> e320f000   	nop
-      38:       ╭── ea000000   	b	0x40 <test_func+0x40>   @ imm = #0x0
-      3c:     ╭─│── ea000000   	b	0x44 <test_func+0x44>   @ imm = #0x0
-      40:     │ ╰─> e320f000   	nop
-      44:     ╰───> e320f000   	nop
-      48:     ╭──── eaffffff   	b	0x4c <test_func+0x4c>   @ imm = #-0x4
-      4c:     ╰─│─> eaffffff   	b	0x50 <test_func+0x50>   @ imm = #-0x4
-      50:       ╰─> e320f000   	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-ascii.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-ascii.txt
deleted file mode 100644
index f18062167dd97d..00000000000000
--- a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-ascii.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-
-<stdin>:	file format elf32-littlearm
-
-Disassembly of section .text:
-
-00000000 <test_func>:
-       0:           f7ff fffe  	bl	0x0 <test_func>         @ imm = #-0x4
-       4:           f7ff bffe  	b.w	0x4 <test_func+0x4>     @ imm = #-0x4
-       8:       /-- e7ff       	b	0xa <test_func+0xa>     @ imm = #-0x2
-       a:       +-> bf00       	nop
-       c:       \-- e7fd       	b	0xa <test_func+0xa>     @ imm = #-0x6
-       e:           e7fe       	b	0xe <test_func+0xe>     @ imm = #-0x4
-      10:       /-- f000 b807  	b.w	0x22 <test_func+0x22>   @ imm = #0xe
-      14:       +-- d005       	beq	0x22 <test_func+0x22>   @ imm = #0xa
-      16:       +-- f040 8004  	bne.w	0x22 <test_func+0x22>   @ imm = #0x8
-      1a:       +-- b110       	cbz	r0, 0x22 <test_func+0x22> @ imm = #0x4
-      1c:       |   bfd8       	it	le
-      1e:       +-- e000       	ble	0x22 <test_func+0x22>   @ imm = #0x0
-      20:       |   bf00       	nop
-      22:       \-> bf00       	nop
-      24:           bf00       	nop
-      26:   /------ e003       	b	0x30 <test_func+0x30>   @ imm = #0x6
-      28:   | /---- e001       	b	0x2e <test_func+0x2e>   @ imm = #0x2
-      2a:   | | /-- e7ff       	b	0x2c <test_func+0x2c>   @ imm = #-0x2
-      2c:   | | \-> bf00       	nop
-      2e:   | \---> bf00       	nop
-      30:   \-----> bf00       	nop
-      32:       /-- e000       	b	0x36 <test_func+0x36>   @ imm = #0x0
-      34:     /-|-- e000       	b	0x38 <test_func+0x38>   @ imm = #0x0
-      36:     | \-> bf00       	nop
-      38:     \---> bf00       	nop
-      3a:       /-- e7ff       	b	0x3c <test_func+0x3c>   @ imm = #-0x2
-      3c:     /-|-- e7ff       	b	0x3e <test_func+0x3e>   @ imm = #-0x2
-      3e:     \---> bf00       	nop
diff --git a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-unicode.txt b/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-unicode.txt
deleted file mode 100644
index b171fb1c5d28c2..00000000000000
--- a/llvm/test/tools/llvm-objdump/Inputs/visualize-jumps-thumb-unicode.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-
-<stdin>:	file format elf32-littlearm
-
-Disassembly of section .text:
-
-00000000 <test_func>:
-       0:           f7ff fffe  	bl	0x0 <test_func>         @ imm = #-0x4
-       4:           f7ff bffe  	b.w	0x4 <test_func+0x4>     @ imm = #-0x4
-       8:       ╭── e7ff       	b	0xa <test_func+0xa>     @ imm = #-0x2
-       a:       ├─> bf00       	nop
-       c:       ╰── e7fd       	b	0xa <test_func+0xa>     @ imm = #-0x6
-       e:           e7fe       	b	0xe <test_func+0xe>     @ imm = #-0x4
-      10:       ╭── f000 b807  	b.w	0x22 <test_func+0x22>   @ imm = #0xe
-      14:       ├── d005       	beq	0x22 <test_func+0x22>   @ imm = #0xa
-      16:       ├── f040 8004  	bne.w	0x22 <test_func+0x22>   @ imm = #0x8
-      1a:       ├── b110       	cbz	r0, 0x22 <test_func+0x22> @ imm = #0x4
-      1c:       │   bfd8       	it	le
-      1e:       ├── e000       	ble	0x22 <test_func+0x22>   @ imm = #0x0
-      20:       │   bf00       	nop
-      22:       ╰─> bf00       	nop
-      24:           bf00       	nop
-      26:   ╭────── e003       	b	0x30 <test_func+0x30>   @ imm = #0x6
-      28:   │ ╭──── e001       	b	0x2e <test_func+0x2e>   @ imm = #0x2
-      2a:   │ │ ╭── e7ff       	b	0x2c <test_func+0x2c>   @ imm = #-0x2
-      2c:   │ │ ╰─> bf00       	nop
-      2e:   │ ╰───> bf00       	nop
-      30:   ╰─────> bf00       	nop
-      32:       ╭── e000       	b	0x36 <test_func+0x36>   @ imm = #0x0
-      34:     ╭─│── e000       	b	0x38 <test_func+0x38>   @ imm = #0x0
-      36:     │ ╰─> bf00       	nop
-      38:     ╰───> bf00       	nop
-      3a:       ╭── e7ff       	b	0x3c <test_func+0x3c>   @ imm = #-0x2
-      3c:     ╭─│── e7ff       	b	0x3e <test_func+0x3e>   @ imm = #-0x2
-      3e:     ╰───> bf00       	nop
diff --git a/llvm/test/tools/llvm-objdump/visualize-jumps-aarch64.s b/llvm/test/tools/llvm-objdump/visualize-jumps-aarch64.s
index f78b3e27975556..633f2d2918d490 100644
--- a/llvm/test/tools/llvm-objdump/visualize-jumps-aarch64.s
+++ b/llvm/test/tools/llvm-objdump/visualize-jumps-aarch64.s
@@ -1,19 +1,22 @@
-// RUN: llvm-mc < %s -triple aarch64 -filetype=obj | \
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+// RUN: llvm-mc < input.s -triple aarch64 -filetype=obj | \
 // RUN:   llvm-objdump --triple aarch64 -d --visualize-jumps=unicode - | \
-// RUN:   diff - %p/Inputs/visualize-jumps-aarch64-unicode.txt
+// RUN:   diff - expected-unicode.txt
 
-// RUN: llvm-mc < %s -triple aarch64 -filetype=obj | \
+// RUN: llvm-mc < input.s -triple aarch64 -filetype=obj | \
 // RUN:   llvm-objdump --triple aarch64 -d --visualize-jumps=ascii - | \
-// RUN:   diff - %p/Inputs/visualize-jumps-aarch64-ascii.txt
+// RUN:   diff - expected-ascii.txt
 
-// RUN: llvm-mc < %s -triple aarch64 -filetype=obj | \
+// RUN: llvm-mc < input.s -triple aarch64 -filetype=obj | \
 // RUN:   llvm-objdump --triple aarch64 -d --visualize-jumps=unicode,color - | \
-// RUN:   diff - %p/Inputs/visualize-jumps-aarch64-unicode-color.txt
+// RUN:   diff - expected-unicode-color.txt
 
-// RUN: llvm-mc < %s -triple aarch64 -filetype=obj | \
+// RUN: llvm-mc < input.s -triple aarch64 -filetype=obj | \
 // RUN:   llvm-objdump --triple aarch64 -d --visualize-jumps=unicode --reloc - | \
-// RUN:   diff - %p/Inputs/visualize-jumps-aarch64-unicode-relocs.txt
+// RUN:   diff - expected-unicode-relocs.txt
 
+//--- input.s
 test_func:
   // Relocated instructions don't get control-flow edges.
   bl extern_func
@@ -67,3 +70,135 @@ test_func:
   bl extern_func
 .Llabel10:
   nop
+
+//--- expected-unicode.txt
+
+<stdin>:	file format elf64-littleaarch64
+
+Disassembly of section .text:
+
+0000000000000000 <test_func>:
+       0:           94000000   	bl	0x0 <test_func>
+       4:           14000000   	b	0x4 <test_func+0x4>
+       8:       ╭── 14000001   	b	0xc <test_func+0xc>
+       c:       ├─> d503201f   	nop
+      10:       ╰── 17ffffff   	b	0xc <test_func+0xc>
+      14:           14000000   	b	0x14 <test_func+0x14>
+      18:       ╭── 54000040   	b.eq	0x20 <test_func+0x20>
+      1c:       ├── b4000020   	cbz	x0, 0x20 <test_func+0x20>
+      20:       ╰─> d503201f   	nop
+      24:   ╭────── 14000005   	b	0x38 <test_func+0x38>
+      28:   │ ╭──── 14000003   	b	0x34 <test_func+0x34>
+      2c:   │ │ ╭── 14000001   	b	0x30 <test_func+0x30>
+      30:   │ │ ╰─> d503201f   	nop
+      34:   │ ╰───> d503201f   	nop
+      38:   ╰─────> d503201f   	nop
+      3c:       ╭── 14000002   	b	0x44 <test_func+0x44>
+      40:     ╭─│── 14000002   	b	0x48 <test_func+0x48>
+      44:     │ ╰─> d503201f   	nop
+      48:     ╰───> d503201f   	nop
+      4c:       ╭── 14000001   	b	0x50 <test_func+0x50>
+      50:     ╭─│── 14000001   	b	0x54 <test_func+0x54>
+      54:     ╰───> d503201f   	nop
+      58:       ╭── 14000002   	b	0x60 <test_func+0x60>
+      5c:       │   94000000   	bl	0x5c <test_func+0x5c>
+      60:       ╰─> d503201f   	nop
+//--- expected-ascii.txt
+
+<stdin>:	file format elf64-littleaarch64
+
+Disassembly of section .text:
+
+0000000000000000 <test_func>:
+       0:           94000000   	bl	0x0 <test_func>
+       4:           14000000   	b	0x4 <test_func+0x4>
+       8:       /-- 14000001   	b	0xc <test_func+0xc>
+       c:       +-> d503201f   	nop
+      10:       \-- 17ffffff   	b	0xc <test_func+0xc>
+      14:           14000000   	b	0x14 <test_func+0x14>
+      18:       /-- 54000040   	b.eq	0x20 <test_func+0x20>
+      1c:       +-- b4000020   	cbz	x0, 0x20 <test_func+0x20>
+      20:       \-> d503201f   	nop
+      24:   /------ 14000005   	b	0x38 <test_func+0x38>
+      28:   | /---- 14000003   	b	0x34 <test_func+0x34>
+      2c:   | | /-- 14000001   	b	0x30 <test_func+0x30>
+      30:   | | \-> d503201f   	nop
+      34:   | \---> d503201f   	nop
+      38:   \-----> d503201f   	nop
+      3c:       /-- 14000002   	b	0x44 <test_func+0x44>
+      40:     /-|-- 14000002   	b	0x48 <test_func+0x48>
+      44:     | \-> d503201f   	nop
+      48:     \---> d503201f   	nop
+      4c:       /-- 14000001   	b	0x50 <test_func+0x50>
+      50:     /-|-- 14000001   	b	0x54 <test_func+0x54>
+      54:     \---> d503201f   	nop
+      58:       /-- 14000002   	b	0x60 <test_func+0x60>
+      5c:       |   94000000   	bl	0x5c <test_func+0x5c>
+      60:       \-> d503201f   	nop
+//--- expected-unicode-color.txt
+
+<stdin>:	file format elf64-littleaarch64
+
+Disassembly of section .text:
+
+0000000000000000 <test_func>:
+       0:           94000000   	bl	0x0 <test_func>
+       4:           14000000   	b	0x4 <test_func+0x4>
+       8:       ╭── 14000001   	b	0xc <test_func+0xc>
+       c:       ├─> d503201f   	nop
+      10:       ╰── 17ffffff   	b	0xc <test_func+0xc>
+      14:           14000000   	b	0x14 <test_func+0x14>
+      18:       ╭── 54000040   	b.eq	0x20 <test_func+0x20>
+      1c:       ├── b4000020   	cbz	x0, 0x20 <test_func+0x20>
+      20:       ╰─> d503201f   	nop
+      24:   ╭────── 14000005   	b	0x38 <test_func+0x38>
+      28:   │ ╭──── 14000003   	b	0x34 <test_func+0x34>
+      2c:   │ │ ╭── 14000001   	b	0x30 <test_func+0x30>
+      30:   │ │ ╰─> d503201f   	nop
+      34:   │ ╰───> d503201f   	nop
+      38:   ╰─────> d503201f   	nop
+      3c:       ╭── 14000002   	b	0x44 <test_func+0x44>
+      40:     ╭─│── 14000002   	b	0x48 <test_func+0x48>
+      44:     │ ╰─> d503201f   	nop
+      48:     ╰───> d503201f   	nop
+      4c:       ╭── 14000001   	b	0x50 <test_func+0x50>
+      50:     ╭─│── 14000001   	b	0x54 <test_func+0x54>
+      54:     ╰───> d503201f   	nop
+      58:       ╭── 14000002   	b	0x60 <test_func+0x60>
+      5c:       │   94000000   	bl	0x5c <test_func+0x5c>
+      60:       ╰─> d503201f   	nop
+//--- expected-unicode-relocs.txt
+
+<stdin>:	file format elf64-littleaarch64
+
+Disassembly of section .text:
+
+0000000000000000 <test_func>:
+       0:           94000000   	bl	0x0 <test_func>
+                   		0000000000000000:  R_AARCH64_CALL26	extern_func
+       4:           14000000   	b	0x4 <test_func+0x4>
+                   		0000000000000004:  R_AARCH64_JUMP26	extern_func
+       8:       ╭── 14000001   	b	0xc <test_func+0xc>
+       c:       ├─> d503201f   	nop
+      10:       ╰── 17ffffff   	b	0xc <test_func+0xc>
+      14:           14000000   	b	0x14 <test_func+0x14>
+      18:       ╭── 54000040   	b.eq	0x20 <test_func+0x20>
+      1c:       ├── b4000020   	cbz	x0, 0x20 <test_func+0x20>
+      20:       ╰─> d503201f   	nop
+      24:   ╭────── 14000005   	b	0x38 <test_func+0x38>
+      28:   │ ╭──── 14000003   	b	0x34 <test_func+0x34>
+      2c:   │ │ ╭── 14000001   	b	0x30 <test_func+0x30>
+      30:   │ │ ╰─> d503201f   	nop
+      34:   │ ╰───> d503201f   	nop
+      38:   ╰─────> d503201f   	nop
+      3c:       ╭── 14000002   	b	0x44 <test_func+0x44>
+      40:     ╭─│── 14000002   	b	0x48 <test_func+0x48>
+      44:     │ ╰─> d503201f   	nop
+      48:     ╰───> d503201f   	nop
+      4c:       ╭── 14000001   	b	0x50 <test_func+0x50>
+      50:     ╭─│── 14000001   	b	0x54 <test_func+0x54>
+      54:     ╰───> d503201f   	nop
+      58:       ╭── 14000002   	b	0x60 <test_func+0x60>
+      5c:       │   94000000   	bl	0x5c <test_func+0x5c>
+                │  		000000000000005c:  R_AARCH64_CALL26	extern_func
+      60:       ╰─> d503201f   	nop
diff --git a/llvm/test/tools/llvm-objdump/visualize-jumps-arm.s b/llvm/test/tools/llvm-objdump/visualize-jumps-arm.s
index 6855e6ff84e32a..886dc0dce50082 100644
--- a/llvm/test/tools/llvm-objdump/visualize-jumps-arm.s
+++ b/llvm/test/tools/llvm-objdump/visualize-jumps-arm.s
@@ -1,11 +1,14 @@
-// RUN: llvm-mc < %s -triple armv8a -filetype=obj | \
+// RUN: rm -rf %t && split-file %s %t && cd %t
+
+// RUN: llvm-mc < input.s -triple armv8a -filetype=obj | \
 // RUN:   llvm-objdump --triple armv8a -d --visualize-jumps=unicode - | \
-// RUN:   diff - %p/Inputs/visualize-jumps-arm-unicode.txt
+// RUN:   diff - expected-unicode.txt
 
-// RUN: llvm-mc < %s -triple armv8a -filetype=obj | \
+// RUN: llvm-mc < input.s -triple armv8a -filetype=obj | \
 // RUN:   llvm-objdump --triple armv8a -d --visualize-jumps=ascii - | \
-// RUN:   diff - %p/Inputs/visualize-jumps-arm-ascii.txt
+// RUN:   diff - expected-ascii.txt
 
+//--- input.s
 test_func:
   // Relocated instructions don't get control-flow edges.
   bl extern_func
@@ -52,3 +55,60 @@ test_func:
   b .Llabel9
 .Llabel9:
   nop
+
+//--- expected-unicode.txt
+
+<stdin>:	file format elf32-littlearm
+
+Disassembly of section .text:
+
+00000000 <test_func>:
+       0:           ebfffffe   	bl	0x0 <test_func>         @ imm = #-0x8
+       4:           eafffffe   	b	0x4 <test_func+0x4>     @ imm = #-0x8
+       8:       ╭── eaffffff   	b	0xc <test_func+0xc>     @ imm = #-0x4
+       c:       ├─> e320f000   	nop
+      10:       ╰── eafffffd   	b	0xc <test_func+0xc>     @ imm = #-0xc
+      14:           eafffffe   	b	0x14 <test_func+0x14>   @ imm = #-0x8
+      18:       ╭── 0affffff   	beq	0x1c <test_func+0x1c>   @ imm = #-0x4
+      1c:       ╰─> e320f000   	nop
+      20:   ╭────── ea000003   	b	0x34 <test_func+0x34>   @ imm = #0xc
+      24:   │ ╭──── ea000001   	b	0x30 <test_func+0x30>   @ imm = #0x4
+      28:   │ │ ╭── eaffffff   	b	0x2c <test_func+0x2c>   @ imm = #-0x4
+      2c:   │ │ ╰─> e320f000   	nop
+      30:   │ ╰───> e320f000   	nop
+      34:   ╰─────> e320f000   	nop
+      38:       ╭── ea000000   	b	0x40 <test_func+0x40>   @ imm = #0x0
+      3c:     ╭─│── ea000000   	b	0x44 <test_func+0x44>   @ imm = #0x0
+      40:     │ ╰─> e320f000   	nop
+      44:     ╰───> e320f000   	nop
+      48:     ╭──── eaffffff   	b	0x4c <test_func+0x4c>   @ imm = #-0x4
+      4c:     ╰─│─> eaffffff   	b	0x50 <test_func+0x50>   @ imm = #-0x4
+      50:       ╰─> e320f000   	nop
+//--- expected-ascii.txt
+
+<stdin>:	file format elf32-littlearm
+
+Disassembly of section .text:
+
+00000000 <test_func>:
+       0:           ebfffffe   	bl	0x0 <test_func>         @ imm = #-0x8
+       4:           eafffffe   	b	0x4 <test_func+0x4>     @ imm = #-0x8
+       8:       /-- eaffffff   	b	0xc <test_func+0xc>     @ imm = #-0x4
+       c:       +-> e320f000   	nop
+      10:       \-- eafffffd   	b	0xc <test_func+0xc>     @ imm = #-0xc
+      14:           eafffffe   	b	0x14 <test_func+0x14>   @ imm = #-0x8
+      18:       /-- 0affffff   	beq	0x1c <test_func+0x1c>   @ imm = #-0x4
+      1c:       \-> e320f000   	nop
+      20:   /------ ea000003   	b	0x34 <test_func+0x34>   @ imm = #0xc
+      24:   | /---- ea000001   	b	0x30 <test_func+0x30>   @ imm = #0x4
+      28:   | | /-- eaffffff   	b	0x2c <test_func+0x2c>   @ imm = #-0x4
+      2c:   | | \-> e320f000   	nop
+      30:   | \---> e320f000   	nop
+      34:   \-----> e320f000   	nop
+      38:       /-- ea000000   	b	0x40 <test_func+0x40>   @ imm = #0x0
+      3c:     /-|-- ea000000   	b	0x44 <test_func+0x44>   @ imm = #0x0
+      40:     | \-> e320f000   	nop
+      44:     \---> e320f000   	nop
+      48:     /---- eaffffff   	b	0x4c <test_func+0x4c>   @ imm = #-0x4
+      4c:     \-|-> eaffffff   	b	0x50 <test_func+0x50>   @ imm = #-0x4
+      50:       \-> e320f000   	nop
diff --git a/llvm/test/tools/llvm-objdump/visualize-jumps-thumb.s b/llvm/test/tools/llvm-objdump/visualize-jumps-thumb.s
index 8be73d3aa4c946..ebe2d8663348e9 100644
--- a/llvm/test/tools/llvm-objdump/visualize-jumps-thumb.s
+++ b/llvm/test/tools/llvm-objdump/visualize-jumps-thumb.s
@@ -1,11 +1,14 @@
-// RUN: llvm-mc < %s -triple thumbv8a -filetype=obj | \
+// RUN: rm -rf %t && split-file %s %t && cd %t
+
+// RUN: llvm-mc < input.s -triple thumbv8a -filetype=obj | \
 // RUN:   llvm-objdump --triple thumbv8a -d --visualize-jumps=unicode - | \
-// RUN:   diff - %p/Inputs/visualize-jumps-thumb-unicode.txt
+// RUN:   diff - expected-unicode.txt
 
-// RUN: llvm-mc < %s -triple thumbv8a -filetype=obj | \
+// RUN: llvm-mc < input.s -triple thumbv8a -filetype=obj | \
 // RUN:   llvm-objdump --triple thumbv8a -d --visualize-jumps=ascii - | \
-// RUN:   diff - %p/Inputs/visualize-jumps-thumb-ascii.txt
+// RUN:   diff - expected-ascii.txt
 
+//--- input.s
 test_func:
   // Relocated instructions don't get control-flow edges.
   bl extern_func
@@ -61,3 +64,73 @@ test_func:
 .Llabel9:
   nop
 
+//--- expected-unicode.txt
+
+<stdin>:	file format elf32-littlearm
+
+Disassembly of section .text:
+
+00000000 <test_func>:
+       0:           f7ff fffe  	bl	0x0 <test_func>         @ imm = #-0x4
+       4:           f7ff bffe  	b.w	0x4 <test_func+0x4>     @ imm = #-0x4
+       8:       ╭── e7ff       	b	0xa <test_func+0xa>     @ imm = #-0x2
+       a:       ├─> bf00       	nop
+       c:       ╰── e7fd       	b	0xa <test_func+0xa>     @ imm = #-0x6
+       e:           e7fe       	b	0xe <test_func+0xe>     @ imm = #-0x4
+      10:       ╭── f000 b807  	b.w	0x22 <test_func+0x22>   @ imm = #0xe
+      14:       ├── d005       	beq	0x22 <test_func+0x22>   @ imm = #0xa
+      16:       ├── f040 8004  	bne.w	0x22 <test_func+0x22>   @ imm = #0x8
+      1a:       ├── b110       	cbz	r0, 0x22 <test_func+0x22> @ imm = #0x4
+      1c:       │   bfd8       	it	le
+      1e:       ├── e000       	ble	0x22 <test_func+0x22>   @ imm = #0x0
+      20:       │   bf00       	nop
+      22:       ╰─> bf00       	nop
+      24:           bf00       	nop
+      26:   ╭────── e003       	b	0x30 <test_func+0x30>   @ imm = #0x6
+      28:   │ ╭──── e001       	b	0x2e <test_func+0x2e>   @ imm = #0x2
+      2a:   │ │ ╭── e7ff       	b	0x2c <test_func+0x2c>   @ imm = #-0x2
+      2c:   │ │ ╰─> bf00       	nop
+      2e:   │ ╰───> bf00       	nop
+      30:   ╰─────> bf00       	nop
+      32:       ╭── e000       	b	0x36 <test_func+0x36>   @ imm = #0x0
+      34:     ╭─│── e000       	b	0x38 <test_func+0x38>   @ imm = #0x0
+      36:     │ ╰─> bf00       	nop
+      38:     ╰───> bf00       	nop
+      3a:       ╭── e7ff       	b	0x3c <test_func+0x3c>   @ imm = #-0x2
+      3c:     ╭─│── e7ff       	b	0x3e <test_func+0x3e>   @ imm = #-0x2
+      3e:     ╰───> bf00       	nop
+//--- expected-ascii.txt
+
+<stdin>:	file format elf32-littlearm
+
+Disassembly of section .text:
+
+00000000 <test_func>:
+       0:           f7ff fffe  	bl	0x0 <test_func>         @ imm = #-0x4
+       4:           f7ff bffe  	b.w	0x4 <test_func+0x4>     @ imm = #-0x4
+       8:       /-- e7ff       	b	0xa <test_func+0xa>     @ imm = #-0x2
+       a:       +-> bf00       	nop
+       c:       \-- e7fd       	b	0xa <test_func+0xa>     @ imm = #-0x6
+       e:           e7fe       	b	0xe <test_func+0xe>     @ imm = #-0x4
+      10:       /-- f000 b807  	b.w	0x22 <test_func+0x22>   @ imm = #0xe
+      14:       +-- d005       	beq	0x22 <test_func+0x22>   @ imm = #0xa
+      16:       +-- f040 8004  	bne.w	0x22 <test_func+0x22>   @ imm = #0x8
+      1a:       +-- b110       	cbz	r0, 0x22 <test_func+0x22> @ imm = #0x4
+      1c:       |   bfd8       	it	le
+      1e:       +-- e000       	ble	0x22 <test_func+0x22>   @ imm = #0x0
+      20:       |   bf00       	nop
+      22:       \-> bf00       	nop
+      24:           bf00       	nop
+      26:   /------ e003       	b	0x30 <test_func+0x30>   @ imm = #0x6
+      28:   | /---- e001       	b	0x2e <test_func+0x2e>   @ imm = #0x2
+      2a:   | | /-- e7ff       	b	0x2c <test_func+0x2c>   @ imm = #-0x2
+      2c:   | | \-> bf00       	nop
+      2e:   | \---> bf00       	nop
+      30:   \-----> bf00       	nop
+      32:       /-- e000       	b	0x36 <test_func+0x36>   @ imm = #0x0
+      34:     /-|-- e000       	b	0x38 <test_func+0x38>   @ imm = #0x0
+      36:     | \-> bf00       	nop
+      38:     \---> bf00       	nop
+      3a:       /-- e7ff       	b	0x3c <test_func+0x3c>   @ imm = #-0x2
+      3c:     /-|-- e7ff       	b	0x3e <test_func+0x3e>   @ imm = #-0x2
+      3e:     \---> bf00       	nop

>From 13771da3a35602793bddb00b45407f8258b8bb0e Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Wed, 13 Dec 2023 16:28:40 +0000
Subject: [PATCH 8/9] Address other review comments

---
 llvm/docs/CommandGuide/llvm-objdump.rst   | 30 ++++++++++++++++
 llvm/tools/llvm-objdump/SourcePrinter.cpp | 43 ++++++++++++-----------
 llvm/tools/llvm-objdump/SourcePrinter.h   | 10 ++----
 llvm/tools/llvm-objdump/llvm-objdump.cpp  |  7 ++--
 4 files changed, 57 insertions(+), 33 deletions(-)

diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst
index b156b212e461f2..db29f6cf970b27 100644
--- a/llvm/docs/CommandGuide/llvm-objdump.rst
+++ b/llvm/docs/CommandGuide/llvm-objdump.rst
@@ -291,6 +291,36 @@ OPTIONS
 
   Target triple to disassemble for, see ``--version`` for available targets.
 
+.. option:: --visualize-jumps=<modes>
+
+  Display a control-flow graph which shows the targets of branch instructions to the left of disasembly. ``modes`` is a comma-separated list of options, which configure the character set and used to print the graph.
+
+  If ``modes`` is omitted, the default is ``unicode,auto``
+
+   .. option:: off
+
+    Disable control-flow graph
+
+   .. option:: ascii
+
+    Render control-flow graph using ASCII characters
+
+   .. option:: unicode
+
+    Render control-flow graph using unicode box-drawing characters
+
+   .. option:: nocolor
+
+    Render control-flow graph without using colors
+
+   .. option:: auto
+
+    Render control-flow graph using colors if supported by the terminal
+
+   .. option:: color
+
+    Render control-flow graph using colors
+
 .. option:: -w, --wide
 
   Ignored for compatibility with GNU objdump.
diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp
index f479a5e7b1a8bb..e7bcdaf97b57fe 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.cpp
+++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp
@@ -211,19 +211,19 @@ const char *LiveVariablePrinter::getLineChar(LineChar C) const {
   bool IsASCII = DbgVariables == DVASCII;
   switch (C) {
   case LineChar::RangeStart:
-    return IsASCII ? "^" : (const char *)u8"\u2548";
+    return IsASCII ? "^" : u8"\u2548";
   case LineChar::RangeMid:
-    return IsASCII ? "|" : (const char *)u8"\u2503";
+    return IsASCII ? "|" : u8"\u2503";
   case LineChar::RangeEnd:
-    return IsASCII ? "v" : (const char *)u8"\u253b";
+    return IsASCII ? "v" : u8"\u253b";
   case LineChar::LabelVert:
-    return IsASCII ? "|" : (const char *)u8"\u2502";
+    return IsASCII ? "|" : u8"\u2502";
   case LineChar::LabelCornerNew:
-    return IsASCII ? "/" : (const char *)u8"\u250c";
+    return IsASCII ? "/" : u8"\u250c";
   case LineChar::LabelCornerActive:
-    return IsASCII ? "|" : (const char *)u8"\u2520";
+    return IsASCII ? "|" : u8"\u2520";
   case LineChar::LabelHoriz:
-    return IsASCII ? "-" : (const char *)u8"\u2500";
+    return IsASCII ? "-" : u8"\u2500";
   }
   llvm_unreachable("Unhandled LineChar enum");
 }
@@ -516,6 +516,15 @@ const raw_ostream::Colors LineColors[] = {
   raw_ostream::CYAN,
 };
 
+raw_ostream::Colors ControlFlowPrinter::PickColor() {
+  if (!OutputMode.color_enabled())
+    return raw_ostream::RESET;
+  auto Ret = LineColors[NextColorIdx];
+  NextColorIdx =
+      (NextColorIdx + 1) % (sizeof(LineColors) / sizeof(LineColors[0]));
+  return Ret;
+}
+
 void ControlFlowPrinter::addEdge(uint64_t From, uint64_t To) {
   auto It = Targets.find(To);
   if (It == Targets.end())
@@ -551,32 +560,24 @@ void ControlFlowPrinter::finalise() {
     T->Column = Column;
     MaxColumn = std::max(MaxColumn, Column);
 
-#if 1
-    LLVM_DEBUG(
-      dbgs() << "Target: 0x" << Twine::utohexstr(T->Target) << " (" << T->Length()
-             << ") Column " << Column << ":\n";
-      for (auto Source : T->Sources)
-        dbgs() << "  Source: 0x" << Twine::utohexstr(Source) << "\n";
-    );
-#endif
   }
 
   setControlFlowColumnWidth(MaxColumn * 2 + 4);
 }
 
 const char *ControlFlowPrinter::getLineChar(LineChar C) const {
-  bool IsASCII = OutputMode.Chars == VisualizeJumpsMode::ASCII;
+  bool IsASCII = !OutputMode.unicode_enabled();
   switch (C) {
   case LineChar::Horiz:
-    return IsASCII ? "-" : (const char *)u8"\u2500";
+    return IsASCII ? "-" : u8"\u2500";
   case LineChar::Vert:
-    return IsASCII ? "|" : (const char *)u8"\u2502";
+    return IsASCII ? "|" : u8"\u2502";
   case LineChar::TopCorner:
-    return IsASCII ? "/" : (const char *)u8"\u256d";
+    return IsASCII ? "/" : u8"\u256d";
   case LineChar::BottomCorner:
-    return IsASCII ? "\\" : (const char *)u8"\u2570";
+    return IsASCII ? "\\" : u8"\u2570";
   case LineChar::Tee:
-    return IsASCII ? "+" : (const char *)u8"\u251c";
+    return IsASCII ? "+" : u8"\u251c";
   case LineChar::Arrow:
     return ">";
   }
diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h
index 1df171fae8d028..51854476e327b5 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.h
+++ b/llvm/tools/llvm-objdump/SourcePrinter.h
@@ -184,6 +184,7 @@ struct VisualizeJumpsMode {
 
   bool enabled() const { return Chars != Off; }
   bool color_enabled() const { return enabled() && Colors != BlackAndWhite; }
+  bool unicode_enabled() const { return Chars == Unicode; }
 
   void ResolveAutoColor(raw_ostream &OS) {
     if (Colors == Auto)
@@ -246,14 +247,7 @@ class ControlFlowPrinter {
   const MCSubtargetInfo &STI;
 
   int NextColorIdx;
-  raw_ostream::Colors PickColor() {
-    if ((OutputMode & VisualizeJumpsMode::ColorMask) ==
-        VisualizeJumpsMode::Off)
-      return raw_ostream::RESET;
-    auto Ret = LineColors[NextColorIdx];
-    NextColorIdx = (NextColorIdx + 1) % (sizeof(LineColors) / sizeof(LineColors[0]));
-    return Ret;
-  }
+  raw_ostream::Colors PickColor();
 
   int getIndentLevel() const { return 10; }
 
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 6e6e53cc61fbf9..a1a0acd753519f 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -514,9 +514,8 @@ unsigned objdump::GetColumnIndent(MCSubtargetInfo const &STI,
   // Special case for assembly string: the assembly printer uses tabs, so we
   // need to ensure we start the instruction on a tab stop (multiple of 8).
   Indent = alignTo(Indent, 8);
-  if (Col == DisassemblyColumn::Assembly) {
+  if (Col == DisassemblyColumn::Assembly)
     return Indent;
-  }
 
   // Assembly width can be configured with --debug-vars-indent=
   // FIXME this variable name is confusing.
@@ -1376,8 +1375,8 @@ collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, MCInstrAnalysis *MIA,
   End += SectionAddr;
   uint64_t Index = Start;
 
-  std::vector<RelocationRef>::const_iterator RelCur = Relocs.begin();
-  std::vector<RelocationRef>::const_iterator RelEnd = Relocs.end();
+  auto RelCur = Relocs.begin();
+  auto RelEnd = Relocs.end();
 
   while (Index < End) {
     // Disassemble a real instruction and record function-local branch labels.

>From cb709ed0065ddb57de9d5c5452ea6ad520b10572 Mon Sep 17 00:00:00 2001
From: Oliver Stannard <oliver.stannard at arm.com>
Date: Wed, 13 Dec 2023 16:29:09 +0000
Subject: [PATCH 9/9] Run clang-format

---
 llvm/tools/llvm-objdump/SourcePrinter.cpp | 12 ++++--------
 llvm/tools/llvm-objdump/SourcePrinter.h   |  8 +++++---
 llvm/tools/llvm-objdump/llvm-objdump.cpp  | 23 ++++++++++-------------
 3 files changed, 19 insertions(+), 24 deletions(-)

diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp
index e7bcdaf97b57fe..7965880be7c0a8 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.cpp
+++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp
@@ -508,12 +508,8 @@ SourcePrinter::SourcePrinter(const object::ObjectFile *Obj,
 
 // TODO Light/dark shades? 256-color terminals?
 const raw_ostream::Colors LineColors[] = {
-  raw_ostream::RED,
-  raw_ostream::GREEN,
-  raw_ostream::YELLOW,
-  raw_ostream::BLUE,
-  raw_ostream::MAGENTA,
-  raw_ostream::CYAN,
+    raw_ostream::RED,  raw_ostream::GREEN,   raw_ostream::YELLOW,
+    raw_ostream::BLUE, raw_ostream::MAGENTA, raw_ostream::CYAN,
 };
 
 raw_ostream::Colors ControlFlowPrinter::PickColor() {
@@ -528,7 +524,8 @@ raw_ostream::Colors ControlFlowPrinter::PickColor() {
 void ControlFlowPrinter::addEdge(uint64_t From, uint64_t To) {
   auto It = Targets.find(To);
   if (It == Targets.end())
-    It = Targets.insert(std::make_pair(To, ControlFlowTarget(To, PickColor()))).first;
+    It = Targets.insert(std::make_pair(To, ControlFlowTarget(To, PickColor())))
+             .first;
   It->second.addSource(From);
 }
 
@@ -559,7 +556,6 @@ void ControlFlowPrinter::finalise() {
         break;
     T->Column = Column;
     MaxColumn = std::max(MaxColumn, Column);
-
   }
 
   setControlFlowColumnWidth(MaxColumn * 2 + 4);
diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h
index 51854476e327b5..450d3f46f49e00 100644
--- a/llvm/tools/llvm-objdump/SourcePrinter.h
+++ b/llvm/tools/llvm-objdump/SourcePrinter.h
@@ -180,7 +180,9 @@ struct VisualizeJumpsMode {
   VisualizeJumpsMode(Chars_t Chars, Colors_t Colors)
       : Chars(Chars), Colors(Colors) {}
 
-  static VisualizeJumpsMode GetDefault() { return VisualizeJumpsMode(Unicode, Auto); }
+  static VisualizeJumpsMode GetDefault() {
+    return VisualizeJumpsMode(Unicode, Auto);
+  }
 
   bool enabled() const { return Chars != Off; }
   bool color_enabled() const { return enabled() && Colors != BlackAndWhite; }
@@ -192,7 +194,6 @@ struct VisualizeJumpsMode {
   }
 };
 
-
 class ControlFlowPrinter {
   struct ControlFlowTarget {
     uint64_t Target;
@@ -201,7 +202,8 @@ class ControlFlowPrinter {
     raw_ostream::Colors Color;
 
     ControlFlowTarget(uint64_t Target, raw_ostream::Colors Color)
-        : Target(Target), Column(~0U), Color(Color), High(Target), Low(Target) {}
+        : Target(Target), Column(~0U), Color(Color), High(Target), Low(Target) {
+    }
     ControlFlowTarget(const ControlFlowTarget &) = delete;
     ControlFlowTarget(ControlFlowTarget &&) = default;
 
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index a1a0acd753519f..ecbd81e02f3c92 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -509,7 +509,7 @@ unsigned objdump::GetColumnIndent(MCSubtargetInfo const &STI,
 
   // Encoding: depends on architecture
   if (ShowRawInsn)
-      Indent += EncodingColumnWidth(STI.getTargetTriple());
+    Indent += EncodingColumnWidth(STI.getTargetTriple());
 
   // Special case for assembly string: the assembly printer uses tabs, so we
   // need to ensure we start the instruction on a tab stop (multiple of 8).
@@ -542,7 +542,7 @@ void objdump::IndentToColumn(MCSubtargetInfo const &STI,
   if (Col == DisassemblyColumn::Assembly) {
     TargetIndent -= 1;
     if (TargetIndent < CurrentIndent)
-      TargetIndent =  alignTo(CurrentIndent + 1, 8) - 1;
+      TargetIndent = alignTo(CurrentIndent + 1, 8) - 1;
   }
 
   if (TargetIndent > CurrentIndent)
@@ -1358,14 +1358,12 @@ collectBBAddrMapLabels(const std::unordered_map<uint64_t, BBAddrMap> &AddrToBBAd
   }
 }
 
-static void
-collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, MCInstrAnalysis *MIA,
-                          MCDisassembler *DisAsm, MCInstPrinter *IP,
-                          const MCSubtargetInfo *STI, uint64_t SectionAddr,
-                          uint64_t Start, uint64_t End,
-                          std::unordered_map<uint64_t, std::string> &Labels,
-                          ControlFlowPrinter &CFP,
-                          std::vector<RelocationRef> &Relocs) {
+static void collectLocalBranchTargets(
+    ArrayRef<uint8_t> Bytes, MCInstrAnalysis *MIA, MCDisassembler *DisAsm,
+    MCInstPrinter *IP, const MCSubtargetInfo *STI, uint64_t SectionAddr,
+    uint64_t Start, uint64_t End,
+    std::unordered_map<uint64_t, std::string> &Labels, ControlFlowPrinter &CFP,
+    std::vector<RelocationRef> &Relocs) {
   if (MIA)
     MIA->resetState();
 
@@ -1951,7 +1949,6 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
       Start -= SectionAddr;
       End -= SectionAddr;
 
-
       formatted_raw_ostream FOS(outs());
 
       if (!PrintedSection) {
@@ -3393,7 +3390,8 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) {
     if (DisassemblyColor == ColorOutput::Invalid)
       invalidArgValue(A);
   }
-  if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_visualize_jumps, OBJDUMP_visualize_jumps_EQ)) {
+  if (const opt::Arg *A = InputArgs.getLastArg(OBJDUMP_visualize_jumps,
+                                               OBJDUMP_visualize_jumps_EQ)) {
     if (A->getOption().matches(OBJDUMP_visualize_jumps)) {
       // --visualize-jumps without an argument default to unicode, auto-color.
       VisualizeJumps = VisualizeJumpsMode::GetDefault();
@@ -3422,7 +3420,6 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) {
         }
       }
 
-
       VisualizeJumps = VisualizeJumpsMode(Chars, Color);
     }
 



More information about the llvm-commits mailing list