ARM: Don't generate memory instructions with writeback where the data and address registers are the same.

BUG=http://dartbug.com/24855
R=fschneider@google.com

Review URL: https://codereview.chromium.org/1434323003 .
diff --git a/runtime/vm/assembler_arm.cc b/runtime/vm/assembler_arm.cc
index 815b796..12fb7ff 100644
--- a/runtime/vm/assembler_arm.cc
+++ b/runtime/vm/assembler_arm.cc
@@ -104,6 +104,8 @@
                           Address ad) {
   ASSERT(rd != kNoRegister);
   ASSERT(cond != kNoCondition);
+  ASSERT(!ad.has_writeback() || (ad.rn() != rd));  // Unpredictable.
+
   int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
                      B26 | (ad.kind() == Address::Immediate ? 0 : B25) |
                      (load ? L : 0) |
diff --git a/runtime/vm/assembler_arm.h b/runtime/vm/assembler_arm.h
index 9ba279c..78fb22d 100644
--- a/runtime/vm/assembler_arm.h
+++ b/runtime/vm/assembler_arm.h
@@ -321,6 +321,11 @@
 
   Mode mode() const { return static_cast<Mode>(encoding() & kModeMask); }
 
+  bool has_writeback() const {
+    return (mode() == PreIndex) || (mode() == PostIndex) ||
+           (mode() == NegPreIndex) || (mode() == NegPostIndex);
+  }
+
   uint32_t encoding() const { return encoding_; }
 
   // Encoding for addressing mode 3.
diff --git a/runtime/vm/simulator_arm.cc b/runtime/vm/simulator_arm.cc
index 5419aaa..531ed4a 100644
--- a/runtime/vm/simulator_arm.cc
+++ b/runtime/vm/simulator_arm.cc
@@ -1996,6 +1996,7 @@
         HandleIllegalAccess(addr, instr);
       } else {
         if (write_back) {
+          ASSERT(rd != rn);  // Unpredictable.
           set_register(rn, rn_val);
         }
         if (!instr->HasSign()) {
@@ -2312,6 +2313,7 @@
     HandleIllegalAccess(addr, instr);
   } else {
     if (write_back) {
+      ASSERT(rd != rn);  // Unpredictable.
       set_register(rn, rn_val);
     }
     if (instr->HasB()) {
@@ -2424,6 +2426,7 @@
     HandleIllegalAccess(addr, instr);
   } else {
     if (write_back) {
+      ASSERT(rd != rn);  // Unpredictable.
       set_register(rn, rn_val);
     }
     if (instr->HasB()) {
diff --git a/runtime/vm/stub_code_arm.cc b/runtime/vm/stub_code_arm.cc
index 5acd669..1629107 100644
--- a/runtime/vm/stub_code_arm.cc
+++ b/runtime/vm/stub_code_arm.cc
@@ -419,6 +419,9 @@
   __ eor(IP, IP, Operand(LR));
 
   // Set up the frame manually with return address now stored in IP.
+  COMPILE_ASSERT(PP < CODE_REG);
+  COMPILE_ASSERT(CODE_REG < FP);
+  COMPILE_ASSERT(FP < IP);
   __ EnterFrame((1 << PP) | (1 << CODE_REG) | (1 << FP) | (1 << IP), 0);
   __ LoadPoolPointer();
 
@@ -434,9 +437,12 @@
     if (i == CODE_REG) {
       // Save the original value of CODE_REG pushed before invoking this stub
       // instead of the value used to call this stub.
-      COMPILE_ASSERT(IP > CODE_REG);  // Assert IP is pushed first.
       __ ldr(IP, Address(FP, kCallerSpSlotFromFp * kWordSize));
       __ Push(IP);
+    } else if (i == SP) {
+      // Push(SP) has unpredictable behavior.
+      __ mov(IP, Operand(SP));
+      __ Push(IP);
     } else {
       __ Push(static_cast<Register>(i));
     }