tools: support decoding ARM EHABI opcodes in readobj
authorSaleem Abdulrasool <compnerd@compnerd.org>
Tue, 21 Jan 2014 02:33:15 +0000 (02:33 +0000)
committerSaleem Abdulrasool <compnerd@compnerd.org>
Tue, 21 Jan 2014 02:33:15 +0000 (02:33 +0000)
Add support to llvm-readobj to decode the actual opcodes.  The ARM EHABI opcodes
are a variable length instruction set that describe the operations required for
properly unwinding stack frames.

The primary motivation for this change is to ease the creation of tests for the
ARM EHABI object emission as well as the unwinding directive handling in the ARM
IAS.

Thanks to Logan Chien for an extra test case!

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@199708 91177308-0d34-0410-b5e6-96231b3b80d8

test/MC/ARM/eh-directive-unwind_raw.s
test/tools/llvm-readobj/ARM/unwind.s
tools/llvm-readobj/ARMEHABIPrinter.h

index ab826d58e6389fa73f77e872462edd8f878ecc36..c617aa37c497e82675decdbbb5ff7625cb3eabde 100644 (file)
@@ -62,18 +62,17 @@ stack_adjust:
 @ CHECK:         Model: Compact (Inline)
 @ CHECK:         PersonalityIndex: 0
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0xB1
-@ CHECK:           Opcode: 0x1
-@ CHECK:           Opcode: 0xB0
+@ CHECK:           0xB1 0x01 ; pop {r0}
+@ CHECK:           0xB0      ; finish
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:       Entry {
 @ CHECK:         Model: Compact (Inline)
 @ CHECK:         PersonalityIndex: 0
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
+@ CHECK:           0xB0      ; finish
+@ CHECK:           0xB0      ; finish
+@ CHECK:           0xB0      ; finish
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:       Entry {
@@ -81,30 +80,28 @@ stack_adjust:
 @ CHECK:         Model: Compact
 @ CHECK:         PersonalityIndex: 1
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0x9B
-@ CHECK:           Opcode: 0x40
-@ CHECK:           Opcode: 0x84
-@ CHECK:           Opcode: 0x80
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
+@ CHECK:           0x9B      ; vsp = r11
+@ CHECK:           0x40      ; vsp = vsp - 4
+@ CHECK:           0x84 0x80 ; pop {fp, lr}
+@ CHECK:           0xB0      ; finish
+@ CHECK:           0xB0      ; finish
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:       Entry {
 @ CHECK:         Model: Compact (Inline)
 @ CHECK:         PersonalityIndex: 0
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0x80
-@ CHECK:           Opcode: 0x0
-@ CHECK:           Opcode: 0xB0
+@ CHECK:           0x80 0x00 ; refuse to unwind
+@ CHECK:           0xB0      ; finish
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:       Entry {
 @ CHECK:         Model: Compact (Inline)
 @ CHECK:         PersonalityIndex: 0
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0x9B
-@ CHECK:           Opcode: 0x4D
-@ CHECK:           Opcode: 0xC2
+@ CHECK:           0x9B      ; vsp = r11
+@ CHECK:           0x4D      ; vsp = vsp - 56
+@ CHECK:           0xC2      ; pop {wR10, wR11, wR12}
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:     ]
index 76068248bb114f0587d96afba0e1afcae5a23b59..afabeb7cd2e76e50e00f3228fe88f4507afdb666 100644 (file)
@@ -1,5 +1,5 @@
-@ RUN: llvm-mc -triple armv7-eabi -filetype obj -o - %s | llvm-readobj -u - \
-@ RUN:   | FileCheck %s
+@ RUN: llvm-mc -triple armv7-linux-eabi -filetype obj -o - %s \
+@ RUN:    | llvm-readobj -u | FileCheck %s
 
        .syntax unified
 
@@ -82,6 +82,69 @@ function2:
        bx lr
        .fnend
 
+       .section .raw
+
+       .type raw,%function
+       .thumb_func
+raw:
+       .fnstart
+       .unwind_raw 12, 0x02
+       .unwind_raw -12, 0x42
+       .unwind_raw 0, 0x80, 0x00
+       .unwind_raw 4, 0x81, 0x00
+       .unwind_raw 4, 0x80, 0x01
+       .unwind_raw 8, 0x80, 0xc0
+       .unwind_raw 12, 0x84, 0xc0
+       .unwind_raw 0, 0x91
+       .unwind_raw 8, 0xa1
+       .unwind_raw 12, 0xa9
+       .unwind_raw 0, 0xb0
+       .unwind_raw 4, 0xb1, 0x01
+       .unwind_raw 0xa04, 0xb2, 0x80, 0x04
+       .unwind_raw 24, 0xb3, 0x12
+       .unwind_raw 24, 0xba
+       .unwind_raw 24, 0xc2
+       .unwind_raw 24, 0xc6, 0x02
+       .unwind_raw 8, 0xc7, 0x03
+       .unwind_raw 24, 0xc8, 0x02
+       .unwind_raw 24, 0xc9, 0x02
+       .unwind_raw 64, 0xd7
+       .fnend
+
+       .section .spare
+
+       .type spare,%function
+spare:
+       .fnstart
+       .unwind_raw 4, 0x00
+       .unwind_raw -4, 0x40
+       .unwind_raw 0, 0x80, 0x00
+       .unwind_raw 4, 0x88, 0x00
+       .unwind_raw 0, 0x91
+       .unwind_raw 0, 0x9d
+       .unwind_raw 0, 0x9f
+       .unwind_raw 0, 0xa0
+       .unwind_raw 0, 0xa8
+       .unwind_raw 0, 0xb0
+       .unwind_raw 0, 0xb1, 0x00
+       .unwind_raw 4, 0xb1, 0x01
+       .unwind_raw 0, 0xb1, 0x10
+       .unwind_raw 0x204, 0xb2, 0x00
+       .unwind_raw 16, 0xb3, 0x00
+       .unwind_raw 0, 0xb4
+       .unwind_raw 16, 0xb8
+       .unwind_raw 4, 0xc0
+       .unwind_raw 4, 0xc6, 0x00
+       .unwind_raw 4, 0xc7, 0x00
+       .unwind_raw 4, 0xc7, 0x01
+       .unwind_raw 0, 0xc7, 0x10
+       .unwind_raw 16, 0xc8, 0x00
+       .unwind_raw 16, 0xc9, 0x00
+       .unwind_raw 0, 0xca
+       .unwind_raw 16, 0xd0
+       .unwind_raw 0, 0xd8
+       .fnend
+
 @ CHECK: UnwindInformation {
 @ CHECK:   UnwindIndexTable {
 @ CHECK:     SectionName: .ARM.exidx.personality
@@ -92,9 +155,9 @@ function2:
 @ CHECK:         Model: Compact (Inline)
 @ CHECK:         PersonalityIndex: 0
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
+@ CHECK:           0xB0       ; finish
+@ CHECK:           0xB0       ; finish
+@ CHECK:           0xB0       ; finish
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:     ]
@@ -108,9 +171,9 @@ function2:
 @ CHECK:         Model: Compact (Inline)
 @ CHECK:         PersonalityIndex: 0
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
+@ CHECK:           0xB0       ; finish
+@ CHECK:           0xB0       ; finish
+@ CHECK:           0xB0       ; finish
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:     ]
@@ -126,12 +189,11 @@ function2:
 @ CHECK:         Model: Compact
 @ CHECK:         PersonalityIndex: 1
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0xB1
-@ CHECK:           Opcode: 0xF
-@ CHECK:           Opcode: 0xA7
-@ CHECK:           Opcode: 0x3F
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
+@ CHECK:           0xB1 0x0F ; pop {r0, r1, r2, r3}
+@ CHECK:           0xA7      ; pop {r4, r5, r6, r7, r8, r9, r10, fp}
+@ CHECK:           0x3F      ; vsp = vsp + 256
+@ CHECK:           0xB0      ; finish
+@ CHECK:           0xB0      ; finish
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:     ]
@@ -158,9 +220,8 @@ function2:
 @ CHECK:         Model: Compact (Inline)
 @ CHECK:         PersonalityIndex: 0
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0xC9
-@ CHECK:           Opcode: 0x84
-@ CHECK:           Opcode: 0xB0
+@ CHECK:           0xC9 0x84 ; pop {d8, d9, d10, d11, d12}
+@ CHECK:           0xB0      ; finish
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:     ]
@@ -174,9 +235,9 @@ function2:
 @ CHECK:         Model: Compact (Inline)
 @ CHECK:         PersonalityIndex: 0
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
+@ CHECK:           0xB0     ; finish
+@ CHECK:           0xB0     ; finish
+@ CHECK:           0xB0     ; finish
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:       Entry {
@@ -192,12 +253,74 @@ function2:
 @ CHECK:         Model: Compact (Inline)
 @ CHECK:         PersonalityIndex: 0
 @ CHECK:         Opcodes [
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
-@ CHECK:           Opcode: 0xB0
+@ CHECK:           0xB0     ; finish
+@ CHECK:           0xB0     ; finish
+@ CHECK:           0xB0     ; finish
 @ CHECK:         ]
 @ CHECK:       }
 @ CHECK:     ]
 @ CHECK:   }
+@ CHECK:   UnwindIndexTable {
+@ CHECK:     SectionName: .ARM.exidx.raw
+@ CHECK:     Entries [
+@ CHECK:       Opcodes [
+@ CHECK:         0xD7      ; pop {d8, d9, d10, d11, d12, d13, d14, d15}
+@ CHECK:         0xC9 0x02 ; pop {d0, d1, d2}
+@ CHECK:         0xC8 0x02 ; pop {d16, d17, d18}
+@ CHECK:         0xC7 0x03 ; pop {wCGR0, wCGR1}
+@ CHECK:         0xC6 0x02 ; pop {wR0, wR1, wR2}
+@ CHECK:         0xC2      ; pop {wR10, wR11, wR12}
+@ CHECK:         0xBA      ; pop {d8, d9, d10}
+@ CHECK:         0xB3 0x12 ; pop {d1, d2, d3}
+@ CHECK:         0xB2 0x80 0x04 ; vsp = vsp + 2564
+@ CHECK:         0xB1 0x01 ; pop {r0}
+@ CHECK:         0xB0      ; finish
+@ CHECK:         0xA9      ; pop {r4, r5, lr}
+@ CHECK:         0xA1      ; pop {r4, r5}
+@ CHECK:         0x91      ; vsp = r1
+@ CHECK:         0x84 0xC0 ; pop {r10, fp, lr}
+@ CHECK:         0x80 0xC0 ; pop {r10, fp}
+@ CHECK:         0x80 0x01 ; pop {r4}
+@ CHECK:         0x81 0x00 ; pop {ip}
+@ CHECK:         0x80 0x00 ; refuse to unwind
+@ CHECK:         0x42      ; vsp = vsp - 12
+@ CHECK:         0x02      ; vsp = vsp + 12
+@ CHECK:       ]
+@ CHECK:     ]
+@ CHECK:   }
+@ CHECK:   UnwindIndexTable {
+@ CHECK:     SectionName: .ARM.exidx.spare
+@ CHECK:     Entries [
+@ CHECK:       Opcodes [
+@ CHECK:         0xD8      ; spare
+@ CHECK:         0xD0      ; pop {d8}
+@ CHECK:         0xCA      ; spare
+@ CHECK:         0xC9 0x00 ; pop {d0}
+@ CHECK:         0xC8 0x00 ; pop {d16}
+@ CHECK:         0xC7 0x10 ; spare
+@ CHECK:         0xC7 0x01 ; pop {wCGR0}
+@ CHECK:         0xC7 0x00 ; spare
+@ CHECK:         0xC6 0x00 ; pop {wR0}
+@ CHECK:         0xC0      ; pop {wR10}
+@ CHECK:         0xB8      ; pop {d8}
+@ CHECK:         0xB4      ; spare
+@ CHECK:         0xB3 0x00 ; pop {d0}
+@ CHECK:         0xB2 0x00 ; vsp = vsp + 516
+@ CHECK:         0xB1 0x10 ; spare
+@ CHECK:         0xB1 0x01 ; pop {r0}
+@ CHECK:         0xB1 0x00 ; spare
+@ CHECK:         0xB0      ; finish
+@ CHECK:         0xA8      ; pop {r4, lr}
+@ CHECK:         0xA0      ; pop {r4}
+@ CHECK:         0x9F      ; reserved (WiMMX MOVrr)
+@ CHECK:         0x9D      ; reserved (ARM MOVrr)
+@ CHECK:         0x91      ; vsp = r1
+@ CHECK:         0x88 0x00 ; pop {pc}
+@ CHECK:         0x80 0x00 ; refuse to unwind
+@ CHECK:         0x40      ; vsp = vsp - 4
+@ CHECK:         0x00      ; vsp = vsp + 4
+@ CHECK:       ]
+@ CHECK:     ]
+@ CHECK:   }
 @ CHECK: }
 
index 2cba126aadf3ef8791790ad87e6d434b53c7bd92..6a56373e26b3d0428e53f391365ea7052c6fb0fb 100644 (file)
 #include "llvm/Object/ELF.h"
 #include "llvm/Object/ELFTypes.h"
 #include "llvm/Support/ARMEHABI.h"
+#include "llvm/Support/Debug.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/Format.h"
 #include "llvm/Support/type_traits.h"
 
+namespace {
+template <typename type_, size_t N>
+size_t countof(const type_ (&)[N]) {
+  return N;
+}
+}
+
 namespace llvm {
 namespace ARM {
 namespace EHABI {
+
+class OpcodeDecoder {
+  StreamWriter &SW;
+  raw_ostream &OS;
+
+  struct RingEntry {
+    uint8_t Mask;
+    uint8_t Value;
+    void (OpcodeDecoder::*Routine)(const uint8_t *Opcodes, unsigned &OI);
+  };
+  static const RingEntry Ring[];
+
+  void Decode_00xxxxxx(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_01xxxxxx(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_1000iiii_iiiiiiii(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_10011101(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_10011111(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_1001nnnn(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_10100nnn(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_10101nnn(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_10110000(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_10110001_0000iiii(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_10110010_uleb128(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_10110011_sssscccc(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_101101nn(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_10111nnn(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_11000110_sssscccc(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_11000111_0000iiii(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_11001000_sssscccc(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_11001001_sssscccc(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_11001yyy(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_11000nnn(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_11010nnn(const uint8_t *Opcodes, unsigned &OI);
+  void Decode_11xxxyyy(const uint8_t *Opcodes, unsigned &OI);
+
+  void PrintGPR(uint16_t GPRMask);
+  void PrintRegisters(uint32_t Mask, StringRef Prefix);
+
+public:
+  OpcodeDecoder(StreamWriter &SW) : SW(SW), OS(SW.getOStream()) {}
+  void Decode(const uint8_t *Opcodes, off_t Offset, size_t Length);
+};
+
+const OpcodeDecoder::RingEntry OpcodeDecoder::Ring[] = {
+  { 0xc0, 0x00, &OpcodeDecoder::Decode_00xxxxxx },
+  { 0xc0, 0x40, &OpcodeDecoder::Decode_01xxxxxx },
+  { 0xf0, 0x80, &OpcodeDecoder::Decode_1000iiii_iiiiiiii },
+  { 0xff, 0x9d, &OpcodeDecoder::Decode_10011101 },
+  { 0xff, 0x9f, &OpcodeDecoder::Decode_10011111 },
+  { 0xf0, 0x90, &OpcodeDecoder::Decode_1001nnnn },
+  { 0xf8, 0xa0, &OpcodeDecoder::Decode_10100nnn },
+  { 0xf8, 0xa8, &OpcodeDecoder::Decode_10101nnn },
+  { 0xff, 0xb0, &OpcodeDecoder::Decode_10110000 },
+  { 0xff, 0xb1, &OpcodeDecoder::Decode_10110001_0000iiii },
+  { 0xff, 0xb2, &OpcodeDecoder::Decode_10110010_uleb128 },
+  { 0xff, 0xb3, &OpcodeDecoder::Decode_10110011_sssscccc },
+  { 0xfc, 0xb4, &OpcodeDecoder::Decode_101101nn },
+  { 0xf8, 0xb8, &OpcodeDecoder::Decode_10111nnn },
+  { 0xff, 0xc6, &OpcodeDecoder::Decode_11000110_sssscccc },
+  { 0xff, 0xc7, &OpcodeDecoder::Decode_11000111_0000iiii },
+  { 0xff, 0xc8, &OpcodeDecoder::Decode_11001000_sssscccc },
+  { 0xff, 0xc9, &OpcodeDecoder::Decode_11001001_sssscccc },
+  { 0xc8, 0xc8, &OpcodeDecoder::Decode_11001yyy },
+  { 0xf8, 0xc0, &OpcodeDecoder::Decode_11000nnn },
+  { 0xf8, 0xd0, &OpcodeDecoder::Decode_11010nnn },
+  { 0xc0, 0xc0, &OpcodeDecoder::Decode_11xxxyyy },
+};
+
+void OpcodeDecoder::Decode_00xxxxxx(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; vsp = vsp + %u\n", Opcode,
+                           ((Opcode & 0x3f) << 2) + 4);
+}
+void OpcodeDecoder::Decode_01xxxxxx(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; vsp = vsp - %u\n", Opcode,
+                           ((Opcode & 0x3f) << 2) + 4);
+}
+void OpcodeDecoder::Decode_1000iiii_iiiiiiii(const uint8_t *Opcodes,
+                                             unsigned &OI) {
+  uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+  uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+
+  uint16_t GPRMask = (Opcode1 << 4) | ((Opcode0 & 0x0f) << 12);
+  SW.startLine()
+    << format("0x%02X 0x%02X ; %s",
+              Opcode0, Opcode1, GPRMask ? "pop " : "refuse to unwind");
+  if (GPRMask)
+    PrintGPR(GPRMask);
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_10011101(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; reserved (ARM MOVrr)\n", Opcode);
+}
+void OpcodeDecoder::Decode_10011111(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; reserved (WiMMX MOVrr)\n", Opcode);
+}
+void OpcodeDecoder::Decode_1001nnnn(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; vsp = r%u\n", Opcode, (Opcode & 0x0f));
+}
+void OpcodeDecoder::Decode_10100nnn(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; pop ", Opcode);
+  PrintGPR((((1 << ((Opcode & 0x7) + 1)) - 1) << 4));
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_10101nnn(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; pop ", Opcode);
+  PrintGPR((((1 << ((Opcode & 0x7) + 1)) - 1) << 4) | (1 << 14));
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_10110000(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; finish\n", Opcode);
+}
+void OpcodeDecoder::Decode_10110001_0000iiii(const uint8_t *Opcodes,
+                                             unsigned &OI) {
+  uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+  uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+
+  SW.startLine()
+    << format("0x%02X 0x%02X ; %s", Opcode0, Opcode1,
+              ((Opcode1 & 0xf0) || Opcode1 == 0x00) ? "spare" : "pop ");
+  if (((Opcode1 & 0xf0) == 0x00) && Opcode1)
+    PrintGPR((Opcode1 & 0x0f));
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_10110010_uleb128(const uint8_t *Opcodes,
+                                            unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X ", Opcode);
+
+  SmallVector<uint8_t, 4> ULEB;
+  do { ULEB.push_back(Opcodes[OI ^ 3]); } while (Opcodes[OI++ ^ 3] & 0x80);
+
+  for (unsigned BI = 0, BE = ULEB.size(); BI != BE; ++BI)
+    OS << format("0x%02X ", ULEB[BI]);
+
+  uint64_t Value = 0;
+  for (unsigned BI = 0, BE = ULEB.size(); BI != BE; ++BI)
+    Value = Value | ((ULEB[BI] & 0x7f) << (7 * BI));
+
+  OS << format("; vsp = vsp + %u\n", 0x204 + (Value << 2));
+}
+void OpcodeDecoder::Decode_10110011_sssscccc(const uint8_t *Opcodes,
+                                             unsigned &OI) {
+  uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+  uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1);
+  uint8_t Start = ((Opcode1 & 0xf0) >> 4);
+  uint8_t Count = ((Opcode1 & 0x0f) >> 0);
+  PrintRegisters((((1 << (Count + 1)) - 1) << Start), "d");
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_101101nn(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; spare\n", Opcode);
+}
+void OpcodeDecoder::Decode_10111nnn(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; pop ", Opcode);
+  PrintRegisters((((1 << ((Opcode & 0x07) + 1)) - 1) << 8), "d");
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_11000110_sssscccc(const uint8_t *Opcodes,
+                                             unsigned &OI) {
+  uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+  uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1);
+  uint8_t Start = ((Opcode1 & 0xf0) >> 4);
+  uint8_t Count = ((Opcode1 & 0x0f) >> 0);
+  PrintRegisters((((1 << (Count + 1)) - 1) << Start), "wR");
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_11000111_0000iiii(const uint8_t *Opcodes,
+                                             unsigned &OI) {
+  uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+  uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+  SW.startLine()
+    << format("0x%02X 0x%02X ; %s", Opcode0, Opcode1,
+              ((Opcode1 & 0xf0) || Opcode1 == 0x00) ? "spare" : "pop ");
+  if ((Opcode1 & 0xf0) == 0x00 && Opcode1)
+      PrintRegisters(Opcode1 & 0x0f, "wCGR");
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_11001000_sssscccc(const uint8_t *Opcodes,
+                                             unsigned &OI) {
+  uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+  uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1);
+  uint8_t Start = 16 + ((Opcode1 & 0xf0) >> 4);
+  uint8_t Count = ((Opcode1 & 0x0f) >> 0);
+  PrintRegisters((((1 << (Count + 1)) - 1) << Start), "d");
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_11001001_sssscccc(const uint8_t *Opcodes,
+                                             unsigned &OI) {
+  uint8_t Opcode0 = Opcodes[OI++ ^ 3];
+  uint8_t Opcode1 = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X 0x%02X ; pop ", Opcode0, Opcode1);
+  uint8_t Start = ((Opcode1 & 0xf0) >> 4);
+  uint8_t Count = ((Opcode1 & 0x0f) >> 0);
+  PrintRegisters((((1 << (Count + 1)) - 1) << Start), "d");
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_11001yyy(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; spare\n", Opcode);
+}
+void OpcodeDecoder::Decode_11000nnn(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; pop ", Opcode);
+  PrintRegisters((((1 << ((Opcode & 0x07) + 1)) - 1) << 10), "wR");
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_11010nnn(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; pop ", Opcode);
+  PrintRegisters((((1 << ((Opcode & 0x07) + 1)) - 1) << 8), "d");
+  OS << '\n';
+}
+void OpcodeDecoder::Decode_11xxxyyy(const uint8_t *Opcodes, unsigned &OI) {
+  uint8_t Opcode = Opcodes[OI++ ^ 3];
+  SW.startLine() << format("0x%02X      ; spare\n", Opcode);
+}
+
+void OpcodeDecoder::PrintGPR(uint16_t GPRMask) {
+  static const char *GPRRegisterNames[16] = {
+    "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
+    "fp", "ip", "sp", "lr", "pc"
+  };
+
+  OS << '{';
+  bool Comma = false;
+  for (unsigned RI = 0, RE = 17; RI < RE; ++RI) {
+    if (GPRMask & (1 << RI)) {
+      if (Comma)
+        OS << ", ";
+      OS << GPRRegisterNames[RI];
+      Comma = true;
+    }
+  }
+  OS << '}';
+}
+
+void OpcodeDecoder::PrintRegisters(uint32_t VFPMask, StringRef Prefix) {
+  OS << '{';
+  bool Comma = false;
+  for (unsigned RI = 0, RE = 32; RI < RE; ++RI) {
+    if (VFPMask & (1 << RI)) {
+      if (Comma)
+        OS << ", ";
+      OS << Prefix << RI;
+      Comma = true;
+    }
+  }
+  OS << '}';
+}
+
+void OpcodeDecoder::Decode(const uint8_t *Opcodes, off_t Offset, size_t Length) {
+  for (unsigned OCI = Offset; OCI < Length + Offset; ) {
+    bool Decoded = false;
+    for (unsigned REI = 0, REE = countof(Ring); REI != REE && !Decoded; ++REI) {
+      if ((Opcodes[OCI ^ 3] & Ring[REI].Mask) == Ring[REI].Value) {
+        (this->*Ring[REI].Routine)(Opcodes, OCI);
+        Decoded = true;
+        break;
+      }
+    }
+    if (!Decoded)
+      SW.startLine() << format("0x%02X      ; reserved\n", Opcodes[OCI++ ^ 3]);
+  }
+}
+
 template <typename ET>
 class PrinterContext {
   StreamWriter &SW;
@@ -171,8 +458,7 @@ template <typename ET>
 void PrinterContext<ET>::PrintOpcodes(const uint8_t *Entry,
                                       size_t Length, off_t Offset) const {
   ListScope OCC(SW, "Opcodes");
-  for (unsigned OCI = Offset; OCI < Length + Offset; OCI++)
-    SW.printHex("Opcode", Entry[OCI ^ 0x3]);
+  OpcodeDecoder(OCC.W).Decode(Entry, Offset, Length);
 }
 
 template <typename ET>