MIPS32: Improve storing of constants in fields and array elements

Test: booted MIPS32 in QEMU
Test: test-art-target-run-test-optimizing on CI20
Test: test-art-host-gtest

Change-Id: Ifcf8c1e215e3768711c391e8da6f663bba71f8d9
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 099620c..e1255f7 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -496,46 +496,61 @@
 
  public:
   template <typename ImplicitNullChecker = NoImplicitNullChecker>
-  void StoreConst32ToOffset(int32_t value,
-                            Register base,
-                            int32_t offset,
-                            Register temp,
-                            ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+  void StoreConstToOffset(StoreOperandType type,
+                          int64_t value,
+                          Register base,
+                          int32_t offset,
+                          Register temp,
+                          ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+    // We permit `base` and `temp` to coincide (however, we check that neither is AT),
+    // in which case the `base` register may be overwritten in the process.
     CHECK_NE(temp, AT);  // Must not use AT as temp, so as not to overwrite the adjusted base.
-    AdjustBaseAndOffset(base, offset, /* is_doubleword */ false);
-    if (value == 0) {
-      temp = ZERO;
-    } else {
-      LoadConst32(temp, value);
-    }
-    Sw(temp, base, offset);
-    null_checker();
-  }
-
-  template <typename ImplicitNullChecker = NoImplicitNullChecker>
-  void StoreConst64ToOffset(int64_t value,
-                            Register base,
-                            int32_t offset,
-                            Register temp,
-                            ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
-    CHECK_NE(temp, AT);  // Must not use AT as temp, so as not to overwrite the adjusted base.
-    AdjustBaseAndOffset(base, offset, /* is_doubleword */ true);
+    AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kStoreDoubleword));
     uint32_t low = Low32Bits(value);
     uint32_t high = High32Bits(value);
-    if (low == 0) {
-      Sw(ZERO, base, offset);
-    } else {
-      LoadConst32(temp, low);
-      Sw(temp, base, offset);
+    Register reg;
+    // If the adjustment left `base` unchanged and equal to `temp`, we can't use `temp`
+    // to load and hold the value but we can use AT instead as AT hasn't been used yet.
+    // Otherwise, `temp` can be used for the value. And if `temp` is the same as the
+    // original `base` (that is, `base` prior to the adjustment), the original `base`
+    // register will be overwritten.
+    if (base == temp) {
+      temp = AT;
     }
-    null_checker();
-    if (high == 0) {
-      Sw(ZERO, base, offset + kMipsWordSize);
+    if (low == 0) {
+      reg = ZERO;
     } else {
-      if (high != low) {
-        LoadConst32(temp, high);
-      }
-      Sw(temp, base, offset + kMipsWordSize);
+      reg = temp;
+      LoadConst32(reg, low);
+    }
+    switch (type) {
+      case kStoreByte:
+        Sb(reg, base, offset);
+        break;
+      case kStoreHalfword:
+        Sh(reg, base, offset);
+        break;
+      case kStoreWord:
+        Sw(reg, base, offset);
+        break;
+      case kStoreDoubleword:
+        Sw(reg, base, offset);
+        null_checker();
+        if (high == 0) {
+          reg = ZERO;
+        } else {
+          reg = temp;
+          if (high != low) {
+            LoadConst32(reg, high);
+          }
+        }
+        Sw(reg, base, offset + kMipsWordSize);
+        break;
+      default:
+        LOG(FATAL) << "UNREACHABLE";
+    }
+    if (type != kStoreDoubleword) {
+      null_checker();
     }
   }
 
diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc
index a92455f..a9abf2f 100644
--- a/compiler/utils/mips/assembler_mips_test.cc
+++ b/compiler/utils/mips/assembler_mips_test.cc
@@ -1977,6 +1977,85 @@
   DriverStr(expected, "StoreDToOffset");
 }
 
+TEST_F(AssemblerMIPSTest, StoreConstToOffset) {
+  __ StoreConstToOffset(mips::kStoreByte, 0xFF, mips::A1, +0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreHalfword, 0xFFFF, mips::A1, +0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreWord, 0x12345678, mips::A1, +0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreDoubleword, 0x123456789ABCDEF0, mips::A1, +0, mips::T8);
+
+  __ StoreConstToOffset(mips::kStoreByte, 0, mips::A1, +0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreHalfword, 0, mips::A1, +0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreWord, 0, mips::A1, +0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreDoubleword, 0, mips::A1, +0, mips::T8);
+
+  __ StoreConstToOffset(mips::kStoreDoubleword, 0x1234567812345678, mips::A1, +0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreDoubleword, 0x1234567800000000, mips::A1, +0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreDoubleword, 0x0000000012345678, mips::A1, +0, mips::T8);
+
+  __ StoreConstToOffset(mips::kStoreWord, 0, mips::T8, +0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreWord, 0x12345678, mips::T8, +0, mips::T8);
+
+  __ StoreConstToOffset(mips::kStoreWord, 0, mips::A1, -0xFFF0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreWord, 0x12345678, mips::A1, +0xFFF0, mips::T8);
+
+  __ StoreConstToOffset(mips::kStoreWord, 0, mips::T8, -0xFFF0, mips::T8);
+  __ StoreConstToOffset(mips::kStoreWord, 0x12345678, mips::T8, +0xFFF0, mips::T8);
+
+  const char* expected =
+      "ori $t8, $zero, 0xFF\n"
+      "sb $t8, 0($a1)\n"
+      "ori $t8, $zero, 0xFFFF\n"
+      "sh $t8, 0($a1)\n"
+      "lui $t8, 0x1234\n"
+      "ori $t8, $t8, 0x5678\n"
+      "sw $t8, 0($a1)\n"
+      "lui $t8, 0x9ABC\n"
+      "ori $t8, $t8, 0xDEF0\n"
+      "sw $t8, 0($a1)\n"
+      "lui $t8, 0x1234\n"
+      "ori $t8, $t8, 0x5678\n"
+      "sw $t8, 4($a1)\n"
+
+      "sb $zero, 0($a1)\n"
+      "sh $zero, 0($a1)\n"
+      "sw $zero, 0($a1)\n"
+      "sw $zero, 0($a1)\n"
+      "sw $zero, 4($a1)\n"
+
+      "lui $t8, 0x1234\n"
+      "ori $t8, $t8, 0x5678\n"
+      "sw $t8, 0($a1)\n"
+      "sw $t8, 4($a1)\n"
+      "sw $zero, 0($a1)\n"
+      "lui $t8, 0x1234\n"
+      "ori $t8, $t8, 0x5678\n"
+      "sw $t8, 4($a1)\n"
+      "lui $t8, 0x1234\n"
+      "ori $t8, $t8, 0x5678\n"
+      "sw $t8, 0($a1)\n"
+      "sw $zero, 4($a1)\n"
+
+      "sw $zero, 0($t8)\n"
+      "lui $at, 0x1234\n"
+      "ori $at, $at, 0x5678\n"
+      "sw $at, 0($t8)\n"
+
+      "addiu $at, $a1, -0x7FF8\n"
+      "sw $zero, -0x7FF8($at)\n"
+      "addiu $at, $a1, 0x7FF8\n"
+      "lui $t8, 0x1234\n"
+      "ori $t8, $t8, 0x5678\n"
+      "sw $t8, 0x7FF8($at)\n"
+
+      "addiu $at, $t8, -0x7FF8\n"
+      "sw $zero, -0x7FF8($at)\n"
+      "addiu $at, $t8, 0x7FF8\n"
+      "lui $t8, 0x1234\n"
+      "ori $t8, $t8, 0x5678\n"
+      "sw $t8, 0x7FF8($at)\n";
+  DriverStr(expected, "StoreConstToOffset");
+}
+
 TEST_F(AssemblerMIPSTest, B) {
   mips::MipsLabel label1, label2;
   __ B(&label1);