Update ARM disassembler.

Fixes disassembly of Thumb data processing shifted-register instructions:
- Fix the opcode selection criteria: Rn/Rd==0xF (only Rn was tested).
- Print shift operand: type + immediate (for instance "lsl #3").

These instructions can be seen with oatdump when disassembling an oat file
generated by the Portable compiler.

Note: we do not use canonical form of shifted-mov here. We could improve this
by printing "lsl r1, r1, #3" rather than "mov r1, r1, lsl #3" to the output.

Change-Id: I9ebb1f6852b621b62160b744f4d0fca66cc24d3c
diff --git a/src/disassembler_arm.cc b/src/disassembler_arm.cc
index ea28aa1..91da6f7 100644
--- a/src/disassembler_arm.cc
+++ b/src/disassembler_arm.cc
@@ -361,68 +361,116 @@
         }
       } else if ((op2 & 0x60) == 0x20) {  // 01x xxxx
         // Data-processing (shifted register)
-        // |111|1110|0000|0|0000|1111|1100|0000|0000|
-        // |5 3|2109|8765|4|3  0|5   |10 8|7 5 |3  0|
-        // |---|----|----|-|----|----|----|----|----|
-        // |332|2222|2222|2|1111|1111|1100|0000|0000|
-        // |1 9|8765|4321|0|9  6|5   |10 8|7 5 |3  0|
-        // |---|----|----|-|----|----|----|----|----|
-        // |111|0101| op3|S| Rn |    | Rd |    | Rm |
+        // |111|1110|0000|0|0000|1111|1100|00|00|0000|
+        // |5 3|2109|8765|4|3  0|5   |10 8|7 |5 |3  0|
+        // |---|----|----|-|----|----|----|--|--|----|
+        // |332|2222|2222|2|1111|1111|1100|00|00|0000|
+        // |1 9|8765|4321|0|9  6|5   |10 8|7 |5 |3  0|
+        // |---|----|----|-|----|----|----|--|--|----|
+        // |111|0101| op3|S| Rn |imm3| Rd |i2|ty| Rm |
         uint32_t op3 = (instr >> 21) & 0xF;
         uint32_t S = (instr >> 20) & 1;
-        uint32_t Rn = (instr >> 16) & 0xF;
+        uint32_t imm3 = ((instr >> 12) & 0x7);
+        uint32_t imm2 = ((instr >> 6) & 0x3);
+        uint32_t imm5 = ((imm3 << 3) | imm2) & 0x1F;
+        uint32_t shift_type = ((instr >> 4) & 0x2);
         ArmRegister Rd(instr, 8);
+        ArmRegister Rn(instr, 16);
         ArmRegister Rm(instr, 0);
         switch (op3) {
           case 0x0:
-            if (Rn != 0xF) {
+            if (Rd.r != 0xF) {
               opcode << "and";
             } else {
               opcode << "tst";
+              DCHECK_EQ(S, 1U);
               S = 0;  // don't print 's'
             }
             break;
           case 0x1: opcode << "bic"; break;
           case 0x2:
-            if (Rn != 0xF) {
+            if (Rn.r != 0xF) {
               opcode << "orr";
             } else {
+              // TODO: use canonical form if there is a shift (lsl, ...).
               opcode << "mov";
             }
             break;
           case 0x3:
-            if (Rn != 0xF) {
+            if (Rn.r != 0xF) {
               opcode << "orn";
             } else {
               opcode << "mvn";
             }
             break;
           case 0x4:
-            if (Rn != 0xF) {
+            if (Rd.r != 0xF) {
               opcode << "eor";
             } else {
               opcode << "teq";
+              DCHECK_EQ(S, 1U);
               S = 0;  // don't print 's'
             }
             break;
           case 0x6: opcode << "pkh"; break;
           case 0x8:
-            if (Rn != 0xF) {
+            if (Rd.r != 0xF) {
               opcode << "add";
             } else {
               opcode << "cmn";
+              DCHECK_EQ(S, 1U);
               S = 0;  // don't print 's'
             }
             break;
           case 0xA: opcode << "adc"; break;
           case 0xB: opcode << "sbc"; break;
+          case 0xD:
+            if (Rd.r != 0xF) {
+              opcode << "sub";
+            } else {
+              opcode << "cmp";
+              DCHECK_EQ(S, 1U);
+              S = 0;  // don't print 's'
+            }
+            break;
+          case 0xE: opcode << "rsb"; break;
+          default: opcode << "UNKNOWN DPSR-" << op3; break;
         }
 
         if (S == 1) {
           opcode << "s";
         }
         opcode << ".w";
-        args << Rd << ", " << Rm;
+
+        if (Rd.r != 0xF) {
+          args << Rd << ", ";
+        }
+        if (Rn.r != 0xF) {
+          args << Rn << ", ";
+        }
+        args << Rm;
+
+        // Shift operand.
+        bool noShift = (imm5 == 0 && shift_type != 0x3);
+        if (!noShift) {
+          args << ", ";
+          switch (shift_type) {
+            case 0x0: args << "lsl"; break;
+            case 0x1: args << "lsr"; break;
+            case 0x2: args << "asr"; break;
+            case 0x3:
+              if (imm5 == 0) {
+                args << "rrx";
+              } else {
+                args << "ror";
+              }
+              break;
+          }
+          if (shift_type != 0x3 /* rrx */) {
+            args << StringPrintf(" #%d", imm5);
+          }
+        }
+
       } else if ((op2 & 0x40) == 0x40) {  // 1xx xxxx
         // Co-processor instructions
         // |111|1|11|000000|0000|1111|1100|000|0  |0000|