MIPS: Shorten .bss string/class loads

This is a follow-up to
https://android-review.googlesource.com/#/c/384033/.

Test: booted MIPS64 (with 2nd arch MIPS32R6) in QEMU
Test: testrunner.py --target --optimizing
Test: same tests as above on CI20
Test: booted MIPS32R2 and MIPS64 in QEMU in configurations:
      ART_USE_READ_BARRIER=false,
      ART_READ_BARRIER_TYPE=TABLELOOKUP

Change-Id: I4cb2f4ded13c0d9fc960c7eac55396f7931c1e38
diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc
index d99d237..3bec30f 100644
--- a/compiler/linker/mips/relative_patcher_mips.cc
+++ b/compiler/linker/mips/relative_patcher_mips.cc
@@ -49,43 +49,27 @@
                                                    uint32_t target_offset) {
   uint32_t anchor_literal_offset = patch.PcInsnOffset();
   uint32_t literal_offset = patch.LiteralOffset();
-  uint32_t literal_low_offset;
+  bool high_patch = ((*code)[literal_offset + 0] == 0x34) && ((*code)[literal_offset + 1] == 0x12);
 
-  // Perform basic sanity checks and initialize `literal_low_offset` to point
-  // to the instruction containing the 16 least significant bits of the
-  // relative address.
-  if (is_r6) {
-    DCHECK_GE(code->size(), 8u);
-    DCHECK_LE(literal_offset, code->size() - 8u);
-    DCHECK_EQ(literal_offset, anchor_literal_offset);
-    // AUIPC reg, offset_high
-    DCHECK_EQ((*code)[literal_offset + 0], 0x34);
-    DCHECK_EQ((*code)[literal_offset + 1], 0x12);
-    DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
-    DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
-    // instr reg(s), offset_low
-    DCHECK_EQ((*code)[literal_offset + 4], 0x78);
-    DCHECK_EQ((*code)[literal_offset + 5], 0x56);
-    literal_low_offset = literal_offset + 4;
+  // Perform basic sanity checks.
+  if (high_patch) {
+    if (is_r6) {
+      // auipc reg, offset_high
+      DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
+      DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
+    } else {
+      // lui reg, offset_high
+      DCHECK_EQ(((*code)[literal_offset + 2] & 0xE0), 0x00);
+      DCHECK_EQ((*code)[literal_offset + 3], 0x3C);
+      // addu reg, reg, reg2
+      DCHECK_EQ((*code)[literal_offset + 4], 0x21);
+      DCHECK_EQ(((*code)[literal_offset + 5] & 0x07), 0x00);
+      DCHECK_EQ(((*code)[literal_offset + 7] & 0xFC), 0x00);
+    }
   } else {
-    DCHECK_GE(code->size(), 16u);
-    DCHECK_LE(literal_offset, code->size() - 12u);
-    DCHECK_GE(literal_offset, 4u);
-    // The NAL instruction does not precede immediately as the PC+0
-    // comes from HMipsComputeBaseMethodAddress.
-    // LUI reg, offset_high
-    DCHECK_EQ((*code)[literal_offset + 0], 0x34);
-    DCHECK_EQ((*code)[literal_offset + 1], 0x12);
-    DCHECK_EQ(((*code)[literal_offset + 2] & 0xE0), 0x00);
-    DCHECK_EQ((*code)[literal_offset + 3], 0x3C);
-    // ADDU reg, reg, reg2
-    DCHECK_EQ((*code)[literal_offset + 4], 0x21);
-    DCHECK_EQ(((*code)[literal_offset + 5] & 0x07), 0x00);
-    DCHECK_EQ(((*code)[literal_offset + 7] & 0xFC), 0x00);
     // instr reg(s), offset_low
-    DCHECK_EQ((*code)[literal_offset + 8], 0x78);
-    DCHECK_EQ((*code)[literal_offset + 9], 0x56);
-    literal_low_offset = literal_offset + 8;
+    CHECK_EQ((*code)[literal_offset + 0], 0x78);
+    CHECK_EQ((*code)[literal_offset + 1], 0x56);
   }
 
   // Apply patch.
@@ -93,12 +77,15 @@
   uint32_t diff = target_offset - anchor_offset;
   diff += (diff & 0x8000) << 1;  // Account for sign extension in "instr reg(s), offset_low".
 
-  // LUI reg, offset_high / AUIPC reg, offset_high
-  (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
-  (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
-  // instr reg(s), offset_low
-  (*code)[literal_low_offset + 0] = static_cast<uint8_t>(diff >> 0);
-  (*code)[literal_low_offset + 1] = static_cast<uint8_t>(diff >> 8);
+  if (high_patch) {
+    // lui reg, offset_high / auipc reg, offset_high
+    (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
+    (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
+  } else {
+    // instr reg(s), offset_low
+    (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 0);
+    (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 8);
+  }
 }
 
 void MipsRelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
index 63ad8a5..d1a75e2 100644
--- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc
+++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
@@ -26,7 +26,9 @@
 
  protected:
   static const uint8_t kUnpatchedPcRelativeRawCode[];
-  static const uint32_t kLiteralOffset;
+  static const uint32_t kLiteralOffsetHigh;
+  static const uint32_t kLiteralOffsetLow1;
+  static const uint32_t kLiteralOffsetLow2;
   static const uint32_t kAnchorOffset;
   static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
 
@@ -44,9 +46,11 @@
 const uint8_t Mips32r6RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
     0x34, 0x12, 0x5E, 0xEE,  // auipc s2, high(diff); placeholder = 0x1234
     0x78, 0x56, 0x52, 0x26,  // addiu s2, s2, low(diff); placeholder = 0x5678
+    0x78, 0x56, 0x52, 0x8E,  // lw    s2, (low(diff))(s2) ; placeholder = 0x5678
 };
-const uint32_t Mips32r6RelativePatcherTest::kLiteralOffset = 0;  // At auipc (where
-                                                                 // patching starts).
+const uint32_t Mips32r6RelativePatcherTest::kLiteralOffsetHigh = 0;  // At auipc.
+const uint32_t Mips32r6RelativePatcherTest::kLiteralOffsetLow1 = 4;  // At addiu.
+const uint32_t Mips32r6RelativePatcherTest::kLiteralOffsetLow2 = 8;  // At lw.
 const uint32_t Mips32r6RelativePatcherTest::kAnchorOffset = 0;  // At auipc (where PC+0 points).
 const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::kUnpatchedPcRelativeCode(
     kUnpatchedPcRelativeRawCode);
@@ -60,11 +64,12 @@
   ASSERT_TRUE(result.first);
 
   uint32_t diff = target_offset - (result.second + kAnchorOffset);
-  diff += (diff & 0x8000) << 1;  // Account for sign extension in addiu.
+  diff += (diff & 0x8000) << 1;  // Account for sign extension in addiu/lw.
 
   const uint8_t expected_code[] = {
       static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x5E, 0xEE,
       static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x26,
+      static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x8E,
   };
   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
 }
@@ -75,7 +80,9 @@
   string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
   bss_begin_ = bss_begin;
   LinkerPatch patches[] = {
-      LinkerPatch::StringBssEntryPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
+      LinkerPatch::StringBssEntryPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
+      LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
+      LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
   };
   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), bss_begin_ + string_entry_offset);
 }
@@ -84,7 +91,9 @@
   constexpr uint32_t kStringIndex = 1u;
   string_index_to_offset_map_.Put(kStringIndex, string_offset);
   LinkerPatch patches[] = {
-      LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
+      LinkerPatch::RelativeStringPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
+      LinkerPatch::RelativeStringPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
+      LinkerPatch::RelativeStringPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
   };
   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
 }
diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc
index 49af7c6..2f7a075 100644
--- a/compiler/linker/mips/relative_patcher_mips_test.cc
+++ b/compiler/linker/mips/relative_patcher_mips_test.cc
@@ -26,7 +26,9 @@
 
  protected:
   static const uint8_t kUnpatchedPcRelativeRawCode[];
-  static const uint32_t kLiteralOffset;
+  static const uint32_t kLiteralOffsetHigh;
+  static const uint32_t kLiteralOffsetLow1;
+  static const uint32_t kLiteralOffsetLow2;
   static const uint32_t kAnchorOffset;
   static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
 
@@ -46,8 +48,11 @@
     0x34, 0x12, 0x12, 0x3C,  // lui   s2, high(diff); placeholder = 0x1234
     0x21, 0x90, 0x5F, 0x02,  // addu  s2, s2, ra
     0x78, 0x56, 0x52, 0x26,  // addiu s2, s2, low(diff); placeholder = 0x5678
+    0x78, 0x56, 0x52, 0x8E,  // lw    s2, (low(diff))(s2) ; placeholder = 0x5678
 };
-const uint32_t MipsRelativePatcherTest::kLiteralOffset = 4;  // At lui (where patching starts).
+const uint32_t MipsRelativePatcherTest::kLiteralOffsetHigh = 4;  // At lui.
+const uint32_t MipsRelativePatcherTest::kLiteralOffsetLow1 = 12;  // At addiu.
+const uint32_t MipsRelativePatcherTest::kLiteralOffsetLow2 = 16;  // At lw.
 const uint32_t MipsRelativePatcherTest::kAnchorOffset = 8;  // At addu (where PC+0 points).
 const ArrayRef<const uint8_t> MipsRelativePatcherTest::kUnpatchedPcRelativeCode(
     kUnpatchedPcRelativeRawCode);
@@ -61,13 +66,14 @@
   ASSERT_TRUE(result.first);
 
   uint32_t diff = target_offset - (result.second + kAnchorOffset);
-  diff += (diff & 0x8000) << 1;  // Account for sign extension in addiu.
+  diff += (diff & 0x8000) << 1;  // Account for sign extension in addiu/lw.
 
   const uint8_t expected_code[] = {
       0x00, 0x00, 0x10, 0x04,
       static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x12, 0x3C,
       0x21, 0x90, 0x5F, 0x02,
       static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x26,
+      static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x8E,
   };
   EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
 }
@@ -78,7 +84,9 @@
   string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
   bss_begin_ = bss_begin;
   LinkerPatch patches[] = {
-      LinkerPatch::StringBssEntryPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
+      LinkerPatch::StringBssEntryPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
+      LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
+      LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
   };
   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), bss_begin_ + string_entry_offset);
 }
@@ -87,7 +95,9 @@
   constexpr uint32_t kStringIndex = 1u;
   string_index_to_offset_map_.Put(kStringIndex, string_offset);
   LinkerPatch patches[] = {
-      LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
+      LinkerPatch::RelativeStringPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
+      LinkerPatch::RelativeStringPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
+      LinkerPatch::RelativeStringPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
   };
   CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
 }