Disassemble system registers in mrs/msr.

When a system register's name is not known to VIXL, disassemble it in
S<op0>_<op1>_<Cn>_<Cm>_<op2> format.

Change-Id: Ieee267ce758e39670d8440f7fd1b37fdbbbba735
diff --git a/src/aarch64/assembler-aarch64.h b/src/aarch64/assembler-aarch64.h
index 82f90dc..0931f46 100644
--- a/src/aarch64/assembler-aarch64.h
+++ b/src/aarch64/assembler-aarch64.h
@@ -2804,9 +2804,9 @@
     return imm16 << ImmException_offset;
   }
 
-  static Instr ImmSystemRegister(int imm15) {
-    VIXL_ASSERT(IsUint15(imm15));
-    return imm15 << ImmSystemRegister_offset;
+  static Instr ImmSystemRegister(int imm16) {
+    VIXL_ASSERT(IsUint16(imm16));
+    return imm16 << ImmSystemRegister_offset;
   }
 
   static Instr ImmHint(int imm7) {
diff --git a/src/aarch64/constants-aarch64.h b/src/aarch64/constants-aarch64.h
index 3b2b5da..984c765 100644
--- a/src/aarch64/constants-aarch64.h
+++ b/src/aarch64/constants-aarch64.h
@@ -130,9 +130,10 @@
 V_(ImmBarrierType, 9, 8, ExtractBits)                                        \
                                                                              \
 /* System (MRS, MSR, SYS) */                                                 \
-V_(ImmSystemRegister, 19, 5, ExtractBits)                                    \
+V_(ImmSystemRegister, 20, 5, ExtractBits)                                    \
 V_(SysO0, 19, 19, ExtractBits)                                               \
 V_(SysOp, 18, 5, ExtractBits)                                                \
+V_(SysOp0, 20, 19, ExtractBits)                                              \
 V_(SysOp1, 18, 16, ExtractBits)                                              \
 V_(SysOp2, 7, 5, ExtractBits)                                                \
 V_(CRn, 15, 12, ExtractBits)                                                 \
@@ -338,20 +339,23 @@
   PSTL3STRM = 0x15
 };
 
+template<int op0, int op1, int crn, int crm, int op2>
+class SystemRegisterEncoder {
+ public:
+  static const uint32_t value =
+      ((op0 << SysO0_offset) |
+       (op1 << SysOp1_offset) |
+       (crn << CRn_offset) |
+       (crm << CRm_offset) |
+       (op2 << SysOp2_offset)) >> ImmSystemRegister_offset;
+};
+
 // System/special register names.
 // This information is not encoded as one field but as the concatenation of
 // multiple fields (Op0<0>, Op1, Crn, Crm, Op2).
 enum SystemRegister {
-  NZCV = ((0x1 << SysO0_offset) |
-          (0x3 << SysOp1_offset) |
-          (0x4 << CRn_offset) |
-          (0x2 << CRm_offset) |
-          (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset,
-  FPCR = ((0x1 << SysO0_offset) |
-          (0x3 << SysOp1_offset) |
-          (0x4 << CRn_offset) |
-          (0x4 << CRm_offset) |
-          (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset
+  NZCV = SystemRegisterEncoder<3, 3, 4, 2, 0>::value,
+  FPCR = SystemRegisterEncoder<3, 3, 4, 4, 0>::value
 };
 
 enum InstructionCacheOp {
diff --git a/src/aarch64/disasm-aarch64.cc b/src/aarch64/disasm-aarch64.cc
index b8cd644..4139190 100644
--- a/src/aarch64/disasm-aarch64.cc
+++ b/src/aarch64/disasm-aarch64.cc
@@ -1740,32 +1740,12 @@
     switch (instr->Mask(SystemSysRegMask)) {
       case MRS: {
         mnemonic = "mrs";
-        switch (instr->GetImmSystemRegister()) {
-          case NZCV:
-            form = "'Xt, nzcv";
-            break;
-          case FPCR:
-            form = "'Xt, fpcr";
-            break;
-          default:
-            form = "'Xt, (unknown)";
-            break;
-        }
+        form = "'Xt, 'IY";
         break;
       }
       case MSR: {
         mnemonic = "msr";
-        switch (instr->GetImmSystemRegister()) {
-          case NZCV:
-            form = "nzcv, 'Xt";
-            break;
-          case FPCR:
-            form = "fpcr, 'Xt";
-            break;
-          default:
-            form = "(unknown), 'Xt";
-            break;
-        }
+        form = "'IY, 'Xt";
         break;
       }
     }
@@ -4589,6 +4569,25 @@
       AppendToOutput("#0x%" PRIx32, instr->GetCRm());
       return 2;
     }
+    case 'Y': {  // IY - system register immediate.
+      switch (instr->GetImmSystemRegister()) {
+        case NZCV:
+          AppendToOutput("nzcv");
+          break;
+        case FPCR:
+          AppendToOutput("fpcr");
+          break;
+        default:
+          AppendToOutput("S%d_%d_%d_%d_%d",
+                         instr->GetSysOp0(),
+                         instr->GetSysOp1(),
+                         instr->GetCRn(),
+                         instr->GetCRm(),
+                         instr->GetSysOp2());
+          break;
+      }
+      return 2;
+    }
     default: {
       VIXL_UNIMPLEMENTED();
       return 0;
diff --git a/test/aarch64/test-disasm-aarch64.cc b/test/aarch64/test-disasm-aarch64.cc
index a4789d7..b4dc79e 100644
--- a/test/aarch64/test-disasm-aarch64.cc
+++ b/test/aarch64/test-disasm-aarch64.cc
@@ -2790,6 +2790,10 @@
   COMPARE(mrs(x30, NZCV), "mrs x30, nzcv");
   COMPARE(mrs(x15, FPCR), "mrs x15, fpcr");
 
+  // Test mrs that use system registers we haven't named.
+  COMPARE(dci(MRS | (0x5555 << 5)), "mrs x0, S3_2_10_10_5");
+  COMPARE(dci(0xd53e1000), "mrs x0, S3_6_1_0_0");
+
   CLEANUP();
 }
 
@@ -2801,6 +2805,10 @@
   COMPARE(msr(NZCV, x30), "msr nzcv, x30");
   COMPARE(msr(FPCR, x15), "msr fpcr, x15");
 
+  // Test msr that use system registers we haven't named.
+  COMPARE(dci(MSR | (0x1234 << 5)), "msr S2_2_4_6_4, x0");
+  COMPARE(dci(0xd51e1000), "msr S3_6_1_0_0, x0");
+
   CLEANUP();
 }