MIPS: Print register names instead of register numbers in disassembler

Test: ./testrunner.py --optimizing --target on CI20 and in QEMU
Test: mma test-art-host-gtest

Change-Id: I1fc375ae34ee8fd994192705c45d8f30a35dfc56
diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc
index 8eecc62..5af51c1 100644
--- a/disassembler/disassembler.cc
+++ b/disassembler/disassembler.cc
@@ -40,8 +40,10 @@
     return new arm::DisassemblerArm(options);
   } else if (instruction_set == kArm64) {
     return new arm64::DisassemblerArm64(options);
-  } else if (instruction_set == kMips || instruction_set == kMips64) {
-    return new mips::DisassemblerMips(options);
+  } else if (instruction_set == kMips) {
+    return new mips::DisassemblerMips(options, /* is_o32_abi */ true);
+  } else if (instruction_set == kMips64) {
+    return new mips::DisassemblerMips(options, /* is_o32_abi */ false);
   } else if (instruction_set == kX86) {
     return new x86::DisassemblerX86(options, false);
   } else if (instruction_set == kX86_64) {
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 91203cb..7cb216e 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -40,6 +40,20 @@
   }
 };
 
+static const char* gO32AbiRegNames[]  = {
+  "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+  "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+  "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+  "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static const char* gN64AbiRegNames[]  = {
+  "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+  "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+  "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+  "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
 static const uint32_t kOpcodeShift = 26;
 
 static const uint32_t kCop1 = (17 << kOpcodeShift);
@@ -470,6 +484,14 @@
   return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
 }
 
+const char* DisassemblerMips::RegName(uint32_t reg) {
+  if (is_o32_abi_) {
+    return gO32AbiRegNames[reg];
+  } else {
+    return gN64AbiRegNames[reg];
+  }
+}
+
 size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) {
   uint32_t instruction = ReadU32(instr_ptr);
 
@@ -518,7 +540,7 @@
           case 'c':  // Floating-point condition code flag in bc1f/bc1t and movf/movt.
             args << "cc" << (rt >> 2);
             break;
-          case 'D': args << 'r' << rd; break;
+          case 'D': args << RegName(rd); break;
           case 'd': args << 'f' << rd; break;
           case 'a': args << 'f' << sa; break;
           case 'F': args << (sa + 32); break;  // dinsu position.
@@ -553,13 +575,13 @@
           case 'l':  // 9-bit signed offset
             {
               int32_t offset = static_cast<int16_t>(instruction) >> 7;
-              args << StringPrintf("%+d(r%d)", offset, rs);
+              args << StringPrintf("%+d(%s)", offset, RegName(rs));
             }
             break;
           case 'O':  // +x(rs)
             {
               int32_t offset = static_cast<int16_t>(instruction & 0xffff);
-              args << StringPrintf("%+d(r%d)", offset, rs);
+              args << StringPrintf("%+d(%s)", offset, RegName(rs));
               if (rs == 17) {
                 args << "  ; ";
                 GetDisassemblerOptions()->thread_offset_name_function_(args, offset);
@@ -595,13 +617,13 @@
           case 'p':  // 19-bit offset in addiupc.
             {
               int32_t offset = (instruction & 0x7ffff) - ((instruction & 0x40000) << 1);
-              args << offset << "  ; move r" << rs << ", ";
+              args << offset << "  ; move " << RegName(rs) << ", ";
               args << FormatInstructionPointer(instr_ptr + (offset << 2));
             }
             break;
-          case 'S': args << 'r' << rs; break;
+          case 'S': args << RegName(rs); break;
           case 's': args << 'f' << rs; break;
-          case 'T': args << 'r' << rt; break;
+          case 'T': args << RegName(rt); break;
           case 't': args << 'f' << rt; break;
           case 'Z': args << (rd + 1); break;  // sz ([d]ext size).
           case 'z': args << (rd - sa + 1); break;  // sz ([d]ins, dinsu size).
@@ -683,7 +705,7 @@
                 case 2: opcode += ".w"; break;
                 case 3: opcode += ".d"; break;
               }
-              args << StringPrintf("%+d(r%d)", s10 << df, rd);
+              args << StringPrintf("%+d(%s)", s10 << df, RegName(rd));
               break;
             }
           case 'X':  // MSA df/n - ws[x].
diff --git a/disassembler/disassembler_mips.h b/disassembler/disassembler_mips.h
index 6342f22..afa6af3 100644
--- a/disassembler/disassembler_mips.h
+++ b/disassembler/disassembler_mips.h
@@ -26,11 +26,13 @@
 
 class DisassemblerMips FINAL : public Disassembler {
  public:
-  explicit DisassemblerMips(DisassemblerOptions* options)
+  explicit DisassemblerMips(DisassemblerOptions* options, bool is_o32_abi)
       : Disassembler(options),
         last_ptr_(nullptr),
-        last_instr_(0) {}
+        last_instr_(0),
+        is_o32_abi_(is_o32_abi) {}
 
+  const char* RegName(uint32_t reg);
   size_t Dump(std::ostream& os, const uint8_t* begin) OVERRIDE;
   void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) OVERRIDE;
 
@@ -39,6 +41,7 @@
   // Needed to produce more readable disassembly of certain 2-instruction sequences.
   const uint8_t* last_ptr_;
   uint32_t last_instr_;
+  const bool is_o32_abi_;
 
   DISALLOW_COPY_AND_ASSIGN(DisassemblerMips);
 };
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index 3dce23f..6d3abb1 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -340,7 +340,7 @@
 
   /// CHECK-START-MIPS: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
   /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
-  /// CHECK-NOT:      beq r0,
+  /// CHECK-NOT:      beq zero,
   /// CHECK-NOT:      beqz
   /// CHECK-NOT:      beqzc
   // Terminate the scope for the CHECK-NOT search at the class field or length comparison,