Exercise art::arm::Thumb2Assembler::StoreToOffset for word pairs.

- Augment compiler/utils/arm/assembler_thumb2_test.cc.
- Ensure art::arm::Thumb2Assembler::StoreToOffset properly
  handles IP as (implicit) second source register.
- Remove the parity constraint on the first source register
  for art::arm::Thumb2Assembler::ldrd and
  art::arm::Thumb2Assembler::strd (as they are not required
  by the Thumb-2 encoding).
- Introduce additional versions of
  art::arm::Thumb2Assembler::ldrd and
  art::arm::Thumb2Assembler::strd accepting a second source
  register, which is not necessarily the one following the
  first source register, as it is allowed by the Thumb-2
  encoding.

Change-Id: I7dba168437a96a5cbb117058e9c547fb1ff5c295
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index a894319..602e672 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -373,24 +373,34 @@
 
 
 void Thumb2Assembler::ldrd(Register rd, const Address& ad, Condition cond) {
+  ldrd(rd, Register(rd + 1), ad, cond);
+}
+
+
+void Thumb2Assembler::ldrd(Register rd, Register rd2, const Address& ad, Condition cond) {
   CheckCondition(cond);
-  CHECK_EQ(rd % 2, 0);
+  // Encoding T1.
   // This is different from other loads.  The encoding is like ARM.
   int32_t encoding = B31 | B30 | B29 | B27 | B22 | B20 |
       static_cast<int32_t>(rd) << 12 |
-      (static_cast<int32_t>(rd) + 1) << 8 |
+      static_cast<int32_t>(rd2) << 8 |
       ad.encodingThumbLdrdStrd();
   Emit32(encoding);
 }
 
 
 void Thumb2Assembler::strd(Register rd, const Address& ad, Condition cond) {
+  strd(rd, Register(rd + 1), ad, cond);
+}
+
+
+void Thumb2Assembler::strd(Register rd, Register rd2, const Address& ad, Condition cond) {
   CheckCondition(cond);
-  CHECK_EQ(rd % 2, 0);
+  // Encoding T1.
   // This is different from other loads.  The encoding is like ARM.
   int32_t encoding = B31 | B30 | B29 | B27 | B22 |
       static_cast<int32_t>(rd) << 12 |
-      (static_cast<int32_t>(rd) + 1) << 8 |
+      static_cast<int32_t>(rd2) << 8 |
       ad.encodingThumbLdrdStrd();
   Emit32(encoding);
 }
@@ -2614,14 +2624,16 @@
   Register tmp_reg = kNoRegister;
   if (!Address::CanHoldStoreOffsetThumb(type, offset)) {
     CHECK_NE(base, IP);
-    if (reg != IP) {
+    if (reg != IP &&
+        (type != kStoreWordPair || reg + 1 != IP)) {
       tmp_reg = IP;
     } else {
-      // Be careful not to use IP twice (for `reg` and to build the
-      // Address object used by the store instruction(s) below).
-      // Instead, save R5 on the stack (or R6 if R5 is not available),
-      // use it as secondary temporary register, and restore it after
-      // the store instruction has been emitted.
+      // Be careful not to use IP twice (for `reg` (or `reg` + 1 in
+      // the case of a word-pair store)) and to build the Address
+      // object used by the store instruction(s) below).  Instead,
+      // save R5 on the stack (or R6 if R5 is not available), use it
+      // as secondary temporary register, and restore it after the
+      // store instruction has been emitted.
       tmp_reg = base != R5 ? R5 : R6;
       Push(tmp_reg);
       if (base == SP) {
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 81dd138..e33c240 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -135,9 +135,17 @@
   void ldrsb(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
   void ldrsh(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
 
+  // Load/store register dual instructions using registers `rd` and `rd` + 1.
   void ldrd(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
   void strd(Register rd, const Address& ad, Condition cond = AL) OVERRIDE;
 
+  // Load/store register dual instructions using registers `rd` and `rd2`.
+  // Note that contrary to the ARM A1 encoding, the Thumb-2 T1 encoding
+  // does not require `rd` to be even, nor `rd2' to be equal to `rd` + 1.
+  void ldrd(Register rd, Register rd2, const Address& ad, Condition cond);
+  void strd(Register rd, Register rd2, const Address& ad, Condition cond);
+
+
   void ldm(BlockAddressMode am, Register base,
            RegList regs, Condition cond = AL) OVERRIDE;
   void stm(BlockAddressMode am, Register base,
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 62e0b90..5f5561a 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -291,4 +291,59 @@
   DriverStr(expected, "StoreWordToNonThumbOffset");
 }
 
+TEST_F(AssemblerThumb2Test, StoreWordPairToThumbOffset) {
+  arm::StoreOperandType type = arm::kStoreWordPair;
+  int32_t offset = 1020;
+  ASSERT_TRUE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
+
+  __ StoreToOffset(type, arm::R0, arm::SP, offset);
+  // We cannot use IP (i.e. R12) as first source register, as it would
+  // force us to use SP (i.e. R13) as second source register, which
+  // would have an "unpredictable" effect according to the ARMv7
+  // specification (the T1 encoding describes the result as
+  // UNPREDICTABLE when of the source registers is R13).
+  //
+  // So we use (R11, IP) (e.g. (R11, R12)) as source registers in the
+  // following instructions.
+  __ StoreToOffset(type, arm::R11, arm::SP, offset);
+  __ StoreToOffset(type, arm::R11, arm::R5, offset);
+
+  const char* expected =
+      "strd r0, r1, [sp, #1020]\n"
+      "strd r11, ip, [sp, #1020]\n"
+      "strd r11, ip, [r5, #1020]\n";
+  DriverStr(expected, "StoreWordPairToThumbOffset");
+}
+
+TEST_F(AssemblerThumb2Test, StoreWordPairToNonThumbOffset) {
+  arm::StoreOperandType type = arm::kStoreWordPair;
+  int32_t offset = 1024;
+  ASSERT_FALSE(arm::Address::CanHoldStoreOffsetThumb(type, offset));
+
+  __ StoreToOffset(type, arm::R0, arm::SP, offset);
+  // Same comment as in AssemblerThumb2Test.StoreWordPairToThumbOffset
+  // regarding the use of (R11, IP) (e.g. (R11, R12)) as source
+  // registers in the following instructions.
+  __ StoreToOffset(type, arm::R11, arm::SP, offset);
+  __ StoreToOffset(type, arm::R11, arm::R5, offset);
+
+  const char* expected =
+      "mov ip, #1024\n"           // LoadImmediate(ip, 1024)
+      "add ip, ip, sp\n"
+      "strd r0, r1, [ip, #0]\n"
+
+      "str r5, [sp, #-4]!\n"      // Push(r5)
+      "movw r5, #1028\n"          // LoadImmediate(r5, 1024 + kRegisterSize)
+      "add r5, r5, sp\n"
+      "strd r11, ip, [r5, #0]\n"
+      "ldr r5, [sp], #4\n"        // Pop(r5)
+
+      "str r6, [sp, #-4]!\n"      // Push(r6)
+      "mov r6, #1024\n"           // LoadImmediate(r6, 1024)
+      "add r6, r6, r5\n"
+      "strd r11, ip, [r6, #0]\n"
+      "ldr r6, [sp], #4\n";       // Pop(r6)
+  DriverStr(expected, "StoreWordPairToNonThumbOffset");
+}
+
 }  // namespace art