Support additional instructions in ARM and thumb assemblers

This adds the following support for the ARM and thumb assemblers:

1. Shifting by a register.
2. LDR/STR with a register offset, possibly shifted.
3. LDR(literal).
4. STR PC relative.

Also adds tests for them in the thumb assembler gtest.

Change-Id: Ie467e3c1d06b699cacbdef3482ed9a92e4f1809b
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 92a9f53..30aa625 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -329,7 +329,7 @@
       ++reg;
     }
     CHECK_LT(reg, 16);
-    CHECK(am == IA_W);      // Only writeback is supported.
+    CHECK(am == DB_W);      // Only writeback is supported.
     ldr(static_cast<Register>(reg), Address(base, kRegisterSize, Address::PostIndex), cond);
   } else {
     EmitMultiMemOp(cond, am, true, base, regs);
@@ -352,8 +352,8 @@
       ++reg;
     }
     CHECK_LT(reg, 16);
-    CHECK(am == DB || am == DB_W);
-    Address::Mode strmode = am == DB_W ? Address::PreIndex : Address::Offset;
+    CHECK(am == IA || am == IA_W);
+    Address::Mode strmode = am == IA ? Address::PreIndex : Address::Offset;
     str(static_cast<Register>(reg), Address(base, -kRegisterSize, strmode), cond);
   } else {
     EmitMultiMemOp(cond, am, false, base, regs);
@@ -642,7 +642,6 @@
            if (imm > (1 << 9)) {    // 9 bit immediate.
              return true;
            }
-           return false;      // 16 bit good.
          } else if (opcode == ADD && rd != SP && rn == SP) {   // 10 bit immediate.
            if (imm > (1 << 10)) {
              return true;
@@ -781,7 +780,7 @@
            imm8;
     } else {
       // Modified immediate.
-      uint32_t imm = ModifiedImmediate(so.encodingThumb(2));
+      uint32_t imm = ModifiedImmediate(so.encodingThumb());
       if (imm == kInvalidModifiedImmediate) {
         LOG(FATAL) << "Immediate value cannot fit in thumb2 modified immediate";
       }
@@ -799,7 +798,7 @@
          set_cc << 20 |
          rn << 16 |
          rd << 8 |
-         so.encodingThumb(2);
+         so.encodingThumb();
   }
   Emit32(encoding);
 }
@@ -1081,6 +1080,82 @@
   }
 }
 
+void Thumb2Assembler::EmitShift(Register rd, Register rm, Shift shift, uint8_t amount, bool setcc) {
+  CHECK_LT(amount, (1 << 5));
+  if (IsHighRegister(rd) || IsHighRegister(rm) || shift == ROR || shift == RRX) {
+    uint16_t opcode = 0;
+    switch (shift) {
+      case LSL: opcode = 0b00; break;
+      case LSR: opcode = 0b01; break;
+      case ASR: opcode = 0b10; break;
+      case ROR: opcode = 0b11; break;
+      case RRX: opcode = 0b11; amount = 0; break;
+      default:
+        LOG(FATAL) << "Unsupported thumb2 shift opcode";
+    }
+    // 32 bit.
+    int32_t encoding = B31 | B30 | B29 | B27 | B25 | B22 |
+        0xf << 16 | (setcc ? B20 : 0);
+    uint32_t imm3 = amount >> 2;
+    uint32_t imm2 = amount & 0b11;
+    encoding |= imm3 << 12 | imm2 << 6 | static_cast<int16_t>(rm) |
+        static_cast<int16_t>(rd) << 8 | opcode << 4;
+    Emit32(encoding);
+  } else {
+    // 16 bit shift
+    uint16_t opcode = 0;
+    switch (shift) {
+      case LSL: opcode = 0b00; break;
+      case LSR: opcode = 0b01; break;
+      case ASR: opcode = 0b10; break;
+      default:
+         LOG(FATAL) << "Unsupported thumb2 shift opcode";
+    }
+    int16_t encoding = opcode << 11 | amount << 6 | static_cast<int16_t>(rm) << 3 |
+        static_cast<int16_t>(rd);
+    Emit16(encoding);
+  }
+}
+
+void Thumb2Assembler::EmitShift(Register rd, Register rn, Shift shift, Register rm, bool setcc) {
+  CHECK_NE(shift, RRX);
+  bool must_be_32bit = false;
+  if (IsHighRegister(rd) || IsHighRegister(rm) || IsHighRegister(rn) || rd != rn) {
+    must_be_32bit = true;
+  }
+
+  if (must_be_32bit) {
+    uint16_t opcode = 0;
+     switch (shift) {
+       case LSL: opcode = 0b00; break;
+       case LSR: opcode = 0b01; break;
+       case ASR: opcode = 0b10; break;
+       case ROR: opcode = 0b11; break;
+       default:
+         LOG(FATAL) << "Unsupported thumb2 shift opcode";
+     }
+     // 32 bit.
+     int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 |
+         0xf << 12 | (setcc ? B20 : 0);
+     encoding |= static_cast<int16_t>(rn) << 16 | static_cast<int16_t>(rm) |
+         static_cast<int16_t>(rd) << 8 | opcode << 21;
+     Emit32(encoding);
+  } else {
+    uint16_t opcode = 0;
+    switch (shift) {
+      case LSL: opcode = 0b0010; break;
+      case LSR: opcode = 0b0011; break;
+      case ASR: opcode = 0b0100; break;
+      default:
+         LOG(FATAL) << "Unsupported thumb2 shift opcode";
+    }
+    int16_t encoding = B14 | opcode << 6 | static_cast<int16_t>(rm) << 3 |
+        static_cast<int16_t>(rd);
+    Emit16(encoding);
+  }
+}
+
+
 
 void Thumb2Assembler::Branch::Emit(AssemblerBuffer* buffer) const {
   bool link = type_ == kUnconditionalLinkX || type_ == kUnconditionalLink;
@@ -1172,7 +1247,7 @@
   }
 
   Register rn = ad.GetRegister();
-  if (IsHighRegister(rn) && rn != SP) {
+  if (IsHighRegister(rn) && rn != SP && rn != PC) {
     must_be_32bit = true;
   }
 
@@ -1180,87 +1255,132 @@
     must_be_32bit = true;
   }
 
-  int32_t offset = ad.GetOffset();
+  if (ad.IsImmediate()) {
+    // Immediate offset
+    int32_t offset = ad.GetOffset();
 
-  // The 16 bit SP relative instruction can only have a 10 bit offset.
-  if (rn == SP && offset > 1024) {
-    must_be_32bit = true;
-  }
-
-  if (byte) {
-    // 5 bit offset, no shift.
-    if (offset > 32) {
+    // The 16 bit SP relative instruction can only have a 10 bit offset.
+    if (rn == SP && offset > 1024) {
       must_be_32bit = true;
     }
-  } else if (half) {
-    // 6 bit offset, shifted by 1.
-    if (offset > 64) {
-      must_be_32bit = true;
-    }
-  } else {
-    // 7 bit offset, shifted by 2.
-    if (offset > 128) {
-       must_be_32bit = true;
-     }
-  }
-
-  if (must_be_32bit) {
-    int32_t encoding = B31 | B30 | B29 | B28 | B27 |
-                  (load ? B20 : 0) |
-                  (is_signed ? B24 : 0) |
-                  static_cast<uint32_t>(rd) << 12 |
-                  ad.encodingThumb(2) |
-                  (byte ? 0 : half ? B21 : B22);
-    Emit32(encoding);
-  } else {
-    // 16 bit thumb1.
-    uint8_t opA = 0;
-    bool sp_relative = false;
 
     if (byte) {
-      opA = 0b0111;
+      // 5 bit offset, no shift.
+      if (offset > 32) {
+        must_be_32bit = true;
+      }
     } else if (half) {
-      opA = 0b1000;
+      // 6 bit offset, shifted by 1.
+      if (offset > 64) {
+        must_be_32bit = true;
+      }
     } else {
-      if (rn == SP) {
-        opA = 0b1001;
-        sp_relative = true;
-      } else {
-        opA = 0b0110;
+      // 7 bit offset, shifted by 2.
+      if (offset > 128) {
+        must_be_32bit = true;
       }
     }
-    int16_t encoding = opA << 12 |
-                (load ? B11 : 0);
 
-    CHECK_GE(offset, 0);
-    if (sp_relative) {
-      // SP relative, 10 bit offset.
-      CHECK_LT(offset, 1024);
-      CHECK_EQ((offset & 0b11), 0);
-      encoding |= rd << 8 | offset >> 2;
+    if (must_be_32bit) {
+      int32_t encoding = B31 | B30 | B29 | B28 | B27 |
+          (load ? B20 : 0) |
+          (is_signed ? B24 : 0) |
+          static_cast<uint32_t>(rd) << 12 |
+          ad.encodingThumb(true) |
+          (byte ? 0 : half ? B21 : B22);
+      Emit32(encoding);
     } else {
-      // No SP relative.  The offset is shifted right depending on
-      // the size of the load/store.
-      encoding |= static_cast<uint32_t>(rd);
+      // 16 bit thumb1.
+      uint8_t opA = 0;
+      bool sp_relative = false;
 
       if (byte) {
-        // 5 bit offset, no shift.
-        CHECK_LT(offset, 32);
+        opA = 0b0111;
       } else if (half) {
-        // 6 bit offset, shifted by 1.
-        CHECK_LT(offset, 64);
-        CHECK_EQ((offset & 0b1), 0);
-        offset >>= 1;
+        opA = 0b1000;
       } else {
-        // 7 bit offset, shifted by 2.
-        CHECK_LT(offset, 128);
-        CHECK_EQ((offset & 0b11), 0);
-        offset >>= 2;
+        if (rn == SP) {
+          opA = 0b1001;
+          sp_relative = true;
+        } else {
+          opA = 0b0110;
+        }
       }
-      encoding |= rn << 3 | offset  << 6;
-    }
+      int16_t encoding = opA << 12 |
+          (load ? B11 : 0);
 
-    Emit16(encoding);
+      CHECK_GE(offset, 0);
+      if (sp_relative) {
+        // SP relative, 10 bit offset.
+        CHECK_LT(offset, 1024);
+        CHECK_EQ((offset & 0b11), 0);
+        encoding |= rd << 8 | offset >> 2;
+      } else {
+        // No SP relative.  The offset is shifted right depending on
+        // the size of the load/store.
+        encoding |= static_cast<uint32_t>(rd);
+
+        if (byte) {
+          // 5 bit offset, no shift.
+          CHECK_LT(offset, 32);
+        } else if (half) {
+          // 6 bit offset, shifted by 1.
+          CHECK_LT(offset, 64);
+          CHECK_EQ((offset & 0b1), 0);
+          offset >>= 1;
+        } else {
+          // 7 bit offset, shifted by 2.
+          CHECK_LT(offset, 128);
+          CHECK_EQ((offset & 0b11), 0);
+          offset >>= 2;
+        }
+        encoding |= rn << 3 | offset  << 6;
+      }
+
+      Emit16(encoding);
+    }
+  } else {
+    // Register shift.
+    if (ad.GetRegister() == PC) {
+       // PC relative literal encoding.
+      int32_t offset = ad.GetOffset();
+      if (must_be_32bit || offset < 0 || offset > (1 << 10) || !load) {
+        int32_t up = B23;
+        if (offset < 0) {
+          offset = -offset;
+          up = 0;
+        }
+        CHECK_LT(offset, (1 << 12));
+        int32_t encoding = 0x1f << 27 | 0xf << 16 | B22 | (load ? B20 : 0) |
+            offset | up |
+            static_cast<uint32_t>(rd) << 12;
+        Emit32(encoding);
+      } else {
+        // 16 bit literal load.
+        CHECK_GE(offset, 0);
+        CHECK_LT(offset, (1 << 10));
+        int32_t encoding = B14 | (load ? B11 : 0) | static_cast<uint32_t>(rd) << 8 | offset >> 2;
+        Emit16(encoding);
+      }
+    } else {
+      if (ad.GetShiftCount() != 0) {
+        // If there is a shift count this must be 32 bit.
+        must_be_32bit = true;
+      } else if (IsHighRegister(ad.GetRegisterOffset())) {
+        must_be_32bit = true;
+      }
+
+      if (must_be_32bit) {
+        int32_t encoding = 0x1f << 27 | B22 | (load ? B20 : 0) | static_cast<uint32_t>(rd) << 12 |
+            ad.encodingThumb(true);
+        Emit32(encoding);
+      } else {
+        // 16 bit register offset.
+        int32_t encoding = B14 | B12 | (load ? B11 : 0) | static_cast<uint32_t>(rd) |
+            ad.encodingThumb(false);
+        Emit16(encoding);
+      }
+    }
   }
 }
 
@@ -2012,37 +2132,70 @@
 
 
 void Thumb2Assembler::Lsl(Register rd, Register rm, uint32_t shift_imm,
-                          Condition cond) {
+                          bool setcc, Condition cond) {
   CHECK_NE(shift_imm, 0u);  // Do not use Lsl if no shift is wanted.
-  mov(rd, ShifterOperand(rm, LSL, shift_imm), cond);
+  CheckCondition(cond);
+  EmitShift(rd, rm, LSL, shift_imm, setcc);
 }
 
 
 void Thumb2Assembler::Lsr(Register rd, Register rm, uint32_t shift_imm,
-                          Condition cond) {
+                          bool setcc, Condition cond) {
   CHECK_NE(shift_imm, 0u);  // Do not use Lsr if no shift is wanted.
   if (shift_imm == 32) shift_imm = 0;  // Comply to UAL syntax.
-  mov(rd, ShifterOperand(rm, LSR, shift_imm), cond);
+  CheckCondition(cond);
+  EmitShift(rd, rm, LSR, shift_imm, setcc);
 }
 
 
 void Thumb2Assembler::Asr(Register rd, Register rm, uint32_t shift_imm,
-                          Condition cond) {
+                          bool setcc, Condition cond) {
   CHECK_NE(shift_imm, 0u);  // Do not use Asr if no shift is wanted.
   if (shift_imm == 32) shift_imm = 0;  // Comply to UAL syntax.
-  mov(rd, ShifterOperand(rm, ASR, shift_imm), cond);
+  CheckCondition(cond);
+  EmitShift(rd, rm, ASR, shift_imm, setcc);
 }
 
 
 void Thumb2Assembler::Ror(Register rd, Register rm, uint32_t shift_imm,
-                          Condition cond) {
+                          bool setcc, Condition cond) {
   CHECK_NE(shift_imm, 0u);  // Use Rrx instruction.
-  mov(rd, ShifterOperand(rm, ROR, shift_imm), cond);
+  CheckCondition(cond);
+  EmitShift(rd, rm, ROR, shift_imm, setcc);
 }
 
 
-void Thumb2Assembler::Rrx(Register rd, Register rm, Condition cond) {
-  mov(rd, ShifterOperand(rm, ROR, 0), cond);
+void Thumb2Assembler::Rrx(Register rd, Register rm, bool setcc, Condition cond) {
+  CheckCondition(cond);
+  EmitShift(rd, rm, RRX, rm, setcc);
+}
+
+
+void Thumb2Assembler::Lsl(Register rd, Register rm, Register rn,
+                          bool setcc, Condition cond) {
+  CheckCondition(cond);
+  EmitShift(rd, rm, LSL, rn, setcc);
+}
+
+
+void Thumb2Assembler::Lsr(Register rd, Register rm, Register rn,
+                          bool setcc, Condition cond) {
+  CheckCondition(cond);
+  EmitShift(rd, rm, LSR, rn, setcc);
+}
+
+
+void Thumb2Assembler::Asr(Register rd, Register rm, Register rn,
+                          bool setcc, Condition cond) {
+  CheckCondition(cond);
+  EmitShift(rd, rm, ASR, rn, setcc);
+}
+
+
+void Thumb2Assembler::Ror(Register rd, Register rm, Register rn,
+                          bool setcc, Condition cond) {
+  CheckCondition(cond);
+  EmitShift(rd, rm, ROR, rn, setcc);
 }