MIPS32: Adds changes neccessary for saving 128 bits of data

Test: mma test-art-host-gtest
Test: ./testrunner.py --optimizing --target in QEMU (MIPS)

Change-Id: I90b7baa1d5f910887bcc3ab80a1a48391ba80c45
diff --git a/compiler/Android.bp b/compiler/Android.bp
index a1269dc..05c4553 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -455,6 +455,7 @@
             srcs: [
                 "optimizing/emit_swap_mips_test.cc",
                 "utils/mips/assembler_mips_test.cc",
+                "utils/mips/assembler_mips32r5_test.cc",
                 "utils/mips/assembler_mips32r6_test.cc",
             ],
         },
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 0b05b75..44b9bb4 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -4440,6 +4440,106 @@
   CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
 }
 
+void MipsAssembler::AdjustBaseOffsetAndElementSizeShift(Register& base,
+                                                        int32_t& offset,
+                                                        int& element_size_shift) {
+  // This method is used to adjust the base register, offset and element_size_shift
+  // for a vector load/store when the offset doesn't fit into allowed number of bits.
+  // MSA ld.df and st.df instructions take signed offsets as arguments, but maximum
+  // offset is dependant on the size of the data format df (10-bit offsets for ld.b,
+  // 11-bit for ld.h, 12-bit for ld.w and 13-bit for ld.d).
+  // If element_size_shift is non-negative at entry, it won't be changed, but offset
+  // will be checked for appropriate alignment. If negative at entry, it will be
+  // adjusted based on offset for maximum fit.
+  // It's assumed that `base` is a multiple of 8.
+  CHECK_NE(base, AT);  // Must not overwrite the register `base` while loading `offset`.
+
+  if (element_size_shift >= 0) {
+    CHECK_LE(element_size_shift, TIMES_8);
+    CHECK_GE(JAVASTYLE_CTZ(offset), element_size_shift);
+  } else if (IsAligned<kMipsDoublewordSize>(offset)) {
+    element_size_shift = TIMES_8;
+  } else if (IsAligned<kMipsWordSize>(offset)) {
+    element_size_shift = TIMES_4;
+  } else if (IsAligned<kMipsHalfwordSize>(offset)) {
+    element_size_shift = TIMES_2;
+  } else {
+    element_size_shift = TIMES_1;
+  }
+
+  const int low_len = 10 + element_size_shift;  // How many low bits of `offset` ld.df/st.df
+                                                // will take.
+  int16_t low = offset & ((1 << low_len) - 1);  // Isolate these bits.
+  low -= (low & (1 << (low_len - 1))) << 1;     // Sign-extend these bits.
+  if (low == offset) {
+    return;  // `offset` fits into ld.df/st.df.
+  }
+
+  // First, see if `offset` can be represented as a sum of two or three signed offsets.
+  // This can save an instruction or two.
+
+  // Max int16_t that's a multiple of element size.
+  const int32_t kMaxDeltaForSimpleAdjustment = 0x8000 - (1 << element_size_shift);
+  // Max ld.df/st.df offset that's a multiple of element size.
+  const int32_t kMaxLoadStoreOffset = 0x1ff << element_size_shift;
+  const int32_t kMaxOffsetForSimpleAdjustment = kMaxDeltaForSimpleAdjustment + kMaxLoadStoreOffset;
+  const int32_t kMinOffsetForMediumAdjustment = 2 * kMaxDeltaForSimpleAdjustment;
+  const int32_t kMaxOffsetForMediumAdjustment = kMinOffsetForMediumAdjustment + kMaxLoadStoreOffset;
+
+  if (IsInt<16>(offset)) {
+    Addiu(AT, base, offset);
+    offset = 0;
+  } else if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
+    Addiu(AT, base, kMaxDeltaForSimpleAdjustment);
+    offset -= kMaxDeltaForSimpleAdjustment;
+  } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
+    Addiu(AT, base, -kMaxDeltaForSimpleAdjustment);
+    offset += kMaxDeltaForSimpleAdjustment;
+  } else if (!IsR6() && 0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
+    Addiu(AT, base, kMaxDeltaForSimpleAdjustment);
+    if (offset <= kMinOffsetForMediumAdjustment) {
+      Addiu(AT, AT, offset - kMaxDeltaForSimpleAdjustment);
+      offset = 0;
+    } else {
+      Addiu(AT, AT, kMaxDeltaForSimpleAdjustment);
+      offset -= kMinOffsetForMediumAdjustment;
+    }
+  } else if (!IsR6() && -kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
+    Addiu(AT, base, -kMaxDeltaForSimpleAdjustment);
+    if (-kMinOffsetForMediumAdjustment <= offset) {
+      Addiu(AT, AT, offset + kMaxDeltaForSimpleAdjustment);
+      offset = 0;
+    } else {
+      Addiu(AT, AT, -kMaxDeltaForSimpleAdjustment);
+      offset += kMinOffsetForMediumAdjustment;
+    }
+  } else {
+    // 16-bit or smaller parts of `offset`:
+    // |31  hi  16|15  mid  13-10|12-9  low  0|
+    //
+    // Instructions that supply each part as a signed integer addend:
+    // |aui       |addiu         |ld.df/st.df |
+    uint32_t tmp = static_cast<uint32_t>(offset) - low;  // Exclude `low` from the rest of `offset`
+                                                         // (accounts for sign of `low`).
+    tmp += (tmp & (UINT32_C(1) << 15)) << 1;  // Account for sign extension in addiu.
+    int16_t mid = Low16Bits(tmp);
+    int16_t hi = High16Bits(tmp);
+    if (IsR6()) {
+      Aui(AT, base, hi);
+    } else {
+      Lui(AT, hi);
+      Addu(AT, AT, base);
+    }
+    if (mid != 0) {
+      Addiu(AT, AT, mid);
+    }
+    offset = low;
+  }
+  base = AT;
+  CHECK_GE(JAVASTYLE_CTZ(offset), element_size_shift);
+  CHECK(IsInt<10>(offset >> element_size_shift));
+}
+
 void MipsAssembler::LoadFromOffset(LoadOperandType type,
                                    Register reg,
                                    Register base,
@@ -4455,6 +4555,10 @@
   LoadDFromOffset<>(reg, base, offset);
 }
 
+void MipsAssembler::LoadQFromOffset(FRegister reg, Register base, int32_t offset) {
+  LoadQFromOffset<>(reg, base, offset);
+}
+
 void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
                              size_t size) {
   MipsManagedRegister dst = m_dst.AsMips();
@@ -4494,6 +4598,10 @@
   StoreDToOffset<>(reg, base, offset);
 }
 
+void MipsAssembler::StoreQToOffset(FRegister reg, Register base, int32_t offset) {
+  StoreQToOffset<>(reg, base, offset);
+}
+
 static dwarf::Reg DWARFReg(Register reg) {
   return dwarf::Reg::MipsCore(static_cast<int>(reg));
 }
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index dd4ce6d..a229882 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -47,14 +47,16 @@
   kLoadSignedHalfword,
   kLoadUnsignedHalfword,
   kLoadWord,
-  kLoadDoubleword
+  kLoadDoubleword,
+  kLoadQuadword
 };
 
 enum StoreOperandType {
   kStoreByte,
   kStoreHalfword,
   kStoreWord,
-  kStoreDoubleword
+  kStoreDoubleword,
+  kStoreQuadword
 };
 
 // Used to test the values returned by ClassS/ClassD.
@@ -646,6 +648,9 @@
                            int32_t& offset,
                            bool is_doubleword,
                            bool is_float = false);
+  void AdjustBaseOffsetAndElementSizeShift(Register& base,
+                                           int32_t& offset,
+                                           int& element_size_shift);
 
  private:
   // This will be used as an argument for loads/stores
@@ -793,6 +798,24 @@
   }
 
   template <typename ImplicitNullChecker = NoImplicitNullChecker>
+  void LoadQFromOffset(FRegister reg,
+                       Register base,
+                       int32_t offset,
+                       ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+    int element_size_shift = -1;
+    AdjustBaseOffsetAndElementSizeShift(base, offset, element_size_shift);
+    switch (element_size_shift) {
+      case TIMES_1: LdB(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_2: LdH(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_4: LdW(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_8: LdD(static_cast<VectorRegister>(reg), base, offset); break;
+      default:
+        LOG(FATAL) << "UNREACHABLE";
+    }
+    null_checker();
+  }
+
+  template <typename ImplicitNullChecker = NoImplicitNullChecker>
   void StoreToOffset(StoreOperandType type,
                      Register reg,
                      Register base,
@@ -861,12 +884,32 @@
     }
   }
 
+  template <typename ImplicitNullChecker = NoImplicitNullChecker>
+  void StoreQToOffset(FRegister reg,
+                      Register base,
+                      int32_t offset,
+                      ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+    int element_size_shift = -1;
+    AdjustBaseOffsetAndElementSizeShift(base, offset, element_size_shift);
+    switch (element_size_shift) {
+      case TIMES_1: StB(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_2: StH(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_4: StW(static_cast<VectorRegister>(reg), base, offset); break;
+      case TIMES_8: StD(static_cast<VectorRegister>(reg), base, offset); break;
+      default:
+        LOG(FATAL) << "UNREACHABLE";
+    }
+    null_checker();
+  }
+
   void LoadFromOffset(LoadOperandType type, Register reg, Register base, int32_t offset);
   void LoadSFromOffset(FRegister reg, Register base, int32_t offset);
   void LoadDFromOffset(FRegister reg, Register base, int32_t offset);
+  void LoadQFromOffset(FRegister reg, Register base, int32_t offset);
   void StoreToOffset(StoreOperandType type, Register reg, Register base, int32_t offset);
   void StoreSToOffset(FRegister reg, Register base, int32_t offset);
   void StoreDToOffset(FRegister reg, Register base, int32_t offset);
+  void StoreQToOffset(FRegister reg, Register base, int32_t offset);
 
   // Emit data (e.g. encoded instruction or immediate) to the instruction stream.
   void Emit(uint32_t value);
diff --git a/compiler/utils/mips/assembler_mips32r5_test.cc b/compiler/utils/mips/assembler_mips32r5_test.cc
new file mode 100644
index 0000000..24b09b5
--- /dev/null
+++ b/compiler/utils/mips/assembler_mips32r5_test.cc
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "assembler_mips.h"
+
+#include <map>
+
+#include "base/stl_util.h"
+#include "utils/assembler_test.h"
+
+#define __ GetAssembler()->
+
+namespace art {
+
+struct MIPSCpuRegisterCompare {
+  bool operator()(const mips::Register& a, const mips::Register& b) const {
+    return a < b;
+  }
+};
+
+class AssemblerMIPS32r5Test : public AssemblerTest<mips::MipsAssembler,
+                                                   mips::Register,
+                                                   mips::FRegister,
+                                                   uint32_t,
+                                                   mips::VectorRegister> {
+ public:
+  typedef AssemblerTest<mips::MipsAssembler,
+                        mips::Register,
+                        mips::FRegister,
+                        uint32_t,
+                        mips::VectorRegister> Base;
+
+  AssemblerMIPS32r5Test() :
+    instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r5", nullptr)) {
+  }
+
+ protected:
+  // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
+  std::string GetArchitectureString() OVERRIDE {
+    return "mips";
+  }
+
+  std::string GetAssemblerParameters() OVERRIDE {
+    return " --no-warn -32 -march=mips32r5 -mmsa";
+  }
+
+  void Pad(std::vector<uint8_t>& data) OVERRIDE {
+    // The GNU linker unconditionally pads the code segment with NOPs to a size that is a multiple
+    // of 16 and there doesn't appear to be a way to suppress this padding. Our assembler doesn't
+    // pad, so, in order for two assembler outputs to match, we need to match the padding as well.
+    // NOP is encoded as four zero bytes on MIPS.
+    size_t pad_size = RoundUp(data.size(), 16u) - data.size();
+    data.insert(data.end(), pad_size, 0);
+  }
+
+  std::string GetDisassembleParameters() OVERRIDE {
+    return " -D -bbinary -mmips:isa32r5";
+  }
+
+  mips::MipsAssembler* CreateAssembler(ArenaAllocator* arena) OVERRIDE {
+    return new (arena) mips::MipsAssembler(arena, instruction_set_features_.get());
+  }
+
+  void SetUpHelpers() OVERRIDE {
+    if (registers_.size() == 0) {
+      registers_.push_back(new mips::Register(mips::ZERO));
+      registers_.push_back(new mips::Register(mips::AT));
+      registers_.push_back(new mips::Register(mips::V0));
+      registers_.push_back(new mips::Register(mips::V1));
+      registers_.push_back(new mips::Register(mips::A0));
+      registers_.push_back(new mips::Register(mips::A1));
+      registers_.push_back(new mips::Register(mips::A2));
+      registers_.push_back(new mips::Register(mips::A3));
+      registers_.push_back(new mips::Register(mips::T0));
+      registers_.push_back(new mips::Register(mips::T1));
+      registers_.push_back(new mips::Register(mips::T2));
+      registers_.push_back(new mips::Register(mips::T3));
+      registers_.push_back(new mips::Register(mips::T4));
+      registers_.push_back(new mips::Register(mips::T5));
+      registers_.push_back(new mips::Register(mips::T6));
+      registers_.push_back(new mips::Register(mips::T7));
+      registers_.push_back(new mips::Register(mips::S0));
+      registers_.push_back(new mips::Register(mips::S1));
+      registers_.push_back(new mips::Register(mips::S2));
+      registers_.push_back(new mips::Register(mips::S3));
+      registers_.push_back(new mips::Register(mips::S4));
+      registers_.push_back(new mips::Register(mips::S5));
+      registers_.push_back(new mips::Register(mips::S6));
+      registers_.push_back(new mips::Register(mips::S7));
+      registers_.push_back(new mips::Register(mips::T8));
+      registers_.push_back(new mips::Register(mips::T9));
+      registers_.push_back(new mips::Register(mips::K0));
+      registers_.push_back(new mips::Register(mips::K1));
+      registers_.push_back(new mips::Register(mips::GP));
+      registers_.push_back(new mips::Register(mips::SP));
+      registers_.push_back(new mips::Register(mips::FP));
+      registers_.push_back(new mips::Register(mips::RA));
+
+      secondary_register_names_.emplace(mips::Register(mips::ZERO), "zero");
+      secondary_register_names_.emplace(mips::Register(mips::AT), "at");
+      secondary_register_names_.emplace(mips::Register(mips::V0), "v0");
+      secondary_register_names_.emplace(mips::Register(mips::V1), "v1");
+      secondary_register_names_.emplace(mips::Register(mips::A0), "a0");
+      secondary_register_names_.emplace(mips::Register(mips::A1), "a1");
+      secondary_register_names_.emplace(mips::Register(mips::A2), "a2");
+      secondary_register_names_.emplace(mips::Register(mips::A3), "a3");
+      secondary_register_names_.emplace(mips::Register(mips::T0), "t0");
+      secondary_register_names_.emplace(mips::Register(mips::T1), "t1");
+      secondary_register_names_.emplace(mips::Register(mips::T2), "t2");
+      secondary_register_names_.emplace(mips::Register(mips::T3), "t3");
+      secondary_register_names_.emplace(mips::Register(mips::T4), "t4");
+      secondary_register_names_.emplace(mips::Register(mips::T5), "t5");
+      secondary_register_names_.emplace(mips::Register(mips::T6), "t6");
+      secondary_register_names_.emplace(mips::Register(mips::T7), "t7");
+      secondary_register_names_.emplace(mips::Register(mips::S0), "s0");
+      secondary_register_names_.emplace(mips::Register(mips::S1), "s1");
+      secondary_register_names_.emplace(mips::Register(mips::S2), "s2");
+      secondary_register_names_.emplace(mips::Register(mips::S3), "s3");
+      secondary_register_names_.emplace(mips::Register(mips::S4), "s4");
+      secondary_register_names_.emplace(mips::Register(mips::S5), "s5");
+      secondary_register_names_.emplace(mips::Register(mips::S6), "s6");
+      secondary_register_names_.emplace(mips::Register(mips::S7), "s7");
+      secondary_register_names_.emplace(mips::Register(mips::T8), "t8");
+      secondary_register_names_.emplace(mips::Register(mips::T9), "t9");
+      secondary_register_names_.emplace(mips::Register(mips::K0), "k0");
+      secondary_register_names_.emplace(mips::Register(mips::K1), "k1");
+      secondary_register_names_.emplace(mips::Register(mips::GP), "gp");
+      secondary_register_names_.emplace(mips::Register(mips::SP), "sp");
+      secondary_register_names_.emplace(mips::Register(mips::FP), "fp");
+      secondary_register_names_.emplace(mips::Register(mips::RA), "ra");
+
+      fp_registers_.push_back(new mips::FRegister(mips::F0));
+      fp_registers_.push_back(new mips::FRegister(mips::F1));
+      fp_registers_.push_back(new mips::FRegister(mips::F2));
+      fp_registers_.push_back(new mips::FRegister(mips::F3));
+      fp_registers_.push_back(new mips::FRegister(mips::F4));
+      fp_registers_.push_back(new mips::FRegister(mips::F5));
+      fp_registers_.push_back(new mips::FRegister(mips::F6));
+      fp_registers_.push_back(new mips::FRegister(mips::F7));
+      fp_registers_.push_back(new mips::FRegister(mips::F8));
+      fp_registers_.push_back(new mips::FRegister(mips::F9));
+      fp_registers_.push_back(new mips::FRegister(mips::F10));
+      fp_registers_.push_back(new mips::FRegister(mips::F11));
+      fp_registers_.push_back(new mips::FRegister(mips::F12));
+      fp_registers_.push_back(new mips::FRegister(mips::F13));
+      fp_registers_.push_back(new mips::FRegister(mips::F14));
+      fp_registers_.push_back(new mips::FRegister(mips::F15));
+      fp_registers_.push_back(new mips::FRegister(mips::F16));
+      fp_registers_.push_back(new mips::FRegister(mips::F17));
+      fp_registers_.push_back(new mips::FRegister(mips::F18));
+      fp_registers_.push_back(new mips::FRegister(mips::F19));
+      fp_registers_.push_back(new mips::FRegister(mips::F20));
+      fp_registers_.push_back(new mips::FRegister(mips::F21));
+      fp_registers_.push_back(new mips::FRegister(mips::F22));
+      fp_registers_.push_back(new mips::FRegister(mips::F23));
+      fp_registers_.push_back(new mips::FRegister(mips::F24));
+      fp_registers_.push_back(new mips::FRegister(mips::F25));
+      fp_registers_.push_back(new mips::FRegister(mips::F26));
+      fp_registers_.push_back(new mips::FRegister(mips::F27));
+      fp_registers_.push_back(new mips::FRegister(mips::F28));
+      fp_registers_.push_back(new mips::FRegister(mips::F29));
+      fp_registers_.push_back(new mips::FRegister(mips::F30));
+      fp_registers_.push_back(new mips::FRegister(mips::F31));
+
+      vec_registers_.push_back(new mips::VectorRegister(mips::W0));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W1));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W2));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W3));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W4));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W5));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W6));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W7));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W8));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W9));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W10));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W11));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W12));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W13));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W14));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W15));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W16));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W17));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W18));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W19));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W20));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W21));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W22));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W23));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W24));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W25));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W26));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W27));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W28));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W29));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W30));
+      vec_registers_.push_back(new mips::VectorRegister(mips::W31));
+    }
+  }
+
+  void TearDown() OVERRIDE {
+    AssemblerTest::TearDown();
+    STLDeleteElements(&registers_);
+    STLDeleteElements(&fp_registers_);
+    STLDeleteElements(&vec_registers_);
+  }
+
+  std::vector<mips::Register*> GetRegisters() OVERRIDE {
+    return registers_;
+  }
+
+  std::vector<mips::FRegister*> GetFPRegisters() OVERRIDE {
+    return fp_registers_;
+  }
+
+  std::vector<mips::VectorRegister*> GetVectorRegisters() OVERRIDE {
+    return vec_registers_;
+  }
+
+  uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
+    return imm_value;
+  }
+
+  std::string GetSecondaryRegisterName(const mips::Register& reg) OVERRIDE {
+    CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end());
+    return secondary_register_names_[reg];
+  }
+
+  std::string RepeatInsn(size_t count, const std::string& insn) {
+    std::string result;
+    for (; count != 0u; --count) {
+      result += insn;
+    }
+    return result;
+  }
+
+ private:
+  std::vector<mips::Register*> registers_;
+  std::map<mips::Register, std::string, MIPSCpuRegisterCompare> secondary_register_names_;
+
+  std::vector<mips::FRegister*> fp_registers_;
+  std::vector<mips::VectorRegister*> vec_registers_;
+  std::unique_ptr<const MipsInstructionSetFeatures> instruction_set_features_;
+};
+
+TEST_F(AssemblerMIPS32r5Test, Toolchain) {
+  EXPECT_TRUE(CheckTools());
+}
+
+TEST_F(AssemblerMIPS32r5Test, LoadQFromOffset) {
+  __ LoadQFromOffset(mips::F0, mips::A0, 0);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4);
+  __ LoadQFromOffset(mips::F0, mips::A0, 8);
+  __ LoadQFromOffset(mips::F0, mips::A0, 511);
+  __ LoadQFromOffset(mips::F0, mips::A0, 512);
+  __ LoadQFromOffset(mips::F0, mips::A0, 513);
+  __ LoadQFromOffset(mips::F0, mips::A0, 514);
+  __ LoadQFromOffset(mips::F0, mips::A0, 516);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1022);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1024);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1025);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1026);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1028);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2044);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2048);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2049);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2050);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2052);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4088);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4096);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4097);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4098);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4100);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4104);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFC);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x8000);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x10000);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x12345678);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x12350078);
+  __ LoadQFromOffset(mips::F0, mips::A0, -256);
+  __ LoadQFromOffset(mips::F0, mips::A0, -511);
+  __ LoadQFromOffset(mips::F0, mips::A0, -513);
+  __ LoadQFromOffset(mips::F0, mips::A0, -1022);
+  __ LoadQFromOffset(mips::F0, mips::A0, -1026);
+  __ LoadQFromOffset(mips::F0, mips::A0, -2044);
+  __ LoadQFromOffset(mips::F0, mips::A0, -2052);
+  __ LoadQFromOffset(mips::F0, mips::A0, -4096);
+  __ LoadQFromOffset(mips::F0, mips::A0, -4104);
+  __ LoadQFromOffset(mips::F0, mips::A0, -32768);
+  __ LoadQFromOffset(mips::F0, mips::A0, -36856);
+  __ LoadQFromOffset(mips::F0, mips::A0, 36856);
+  __ LoadQFromOffset(mips::F0, mips::A0, -69608);
+  __ LoadQFromOffset(mips::F0, mips::A0, 69608);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0xABCDEF00);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+  const char* expected =
+      "ld.d $w0, 0($a0)\n"
+      "ld.b $w0, 1($a0)\n"
+      "ld.h $w0, 2($a0)\n"
+      "ld.w $w0, 4($a0)\n"
+      "ld.d $w0, 8($a0)\n"
+      "ld.b $w0, 511($a0)\n"
+      "ld.d $w0, 512($a0)\n"
+      "addiu $at, $a0, 513\n"
+      "ld.b $w0, 0($at)\n"
+      "ld.h $w0, 514($a0)\n"
+      "ld.w $w0, 516($a0)\n"
+      "ld.h $w0, 1022($a0)\n"
+      "ld.d $w0, 1024($a0)\n"
+      "addiu $at, $a0, 1025\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 1026\n"
+      "ld.h $w0, 0($at)\n"
+      "ld.w $w0, 1028($a0)\n"
+      "ld.w $w0, 2044($a0)\n"
+      "ld.d $w0, 2048($a0)\n"
+      "addiu $at, $a0, 2049\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 2050\n"
+      "ld.h $w0, 0($at)\n"
+      "addiu $at, $a0, 2052\n"
+      "ld.w $w0, 0($at)\n"
+      "ld.d $w0, 4088($a0)\n"
+      "addiu $at, $a0, 4096\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, 4097\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 4098\n"
+      "ld.h $w0, 0($at)\n"
+      "addiu $at, $a0, 4100\n"
+      "ld.w $w0, 0($at)\n"
+      "addiu $at, $a0, 4104\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FFC\n"
+      "ld.w $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FF8\n"
+      "ld.d $w0, 8($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 32760\n"
+      "ld.d $w0, 16($at)\n"
+      "lui $at, 4660\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, 24576\n"
+      "ld.d $w0, -2440($at) # 0xF678\n"
+      "lui $at, 4661\n"
+      "addu $at, $at, $a0\n"
+      "ld.d $w0, 120($at)\n"
+      "ld.d $w0, -256($a0)\n"
+      "ld.b $w0, -511($a0)\n"
+      "addiu $at, $a0, -513\n"
+      "ld.b $w0, 0($at)\n"
+      "ld.h $w0, -1022($a0)\n"
+      "addiu $at, $a0, -1026\n"
+      "ld.h $w0, 0($at)\n"
+      "ld.w $w0, -2044($a0)\n"
+      "addiu $at, $a0, -2052\n"
+      "ld.w $w0, 0($at)\n"
+      "ld.d $w0, -4096($a0)\n"
+      "addiu $at, $a0, -4104\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32768\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32760\n"
+      "addiu $at, $at, -4096\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 4096\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32760\n"
+      "addiu $at, $at, -32760\n"
+      "ld.d $w0, -4088($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 32760\n"
+      "ld.d $w0, 4088($at)\n"
+      "lui $at, 0xABCE\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, -8192 # 0xE000\n"
+      "ld.d $w0, 0xF00($at)\n"
+      "lui $at, 0x8000\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, -21504 # 0xAC00\n"
+      "ld.b $w0, -51($at) # 0xFFCD\n";
+  DriverStr(expected, "LoadQFromOffset");
+}
+
+TEST_F(AssemblerMIPS32r5Test, StoreQToOffset) {
+  __ StoreQToOffset(mips::F0, mips::A0, 0);
+  __ StoreQToOffset(mips::F0, mips::A0, 1);
+  __ StoreQToOffset(mips::F0, mips::A0, 2);
+  __ StoreQToOffset(mips::F0, mips::A0, 4);
+  __ StoreQToOffset(mips::F0, mips::A0, 8);
+  __ StoreQToOffset(mips::F0, mips::A0, 511);
+  __ StoreQToOffset(mips::F0, mips::A0, 512);
+  __ StoreQToOffset(mips::F0, mips::A0, 513);
+  __ StoreQToOffset(mips::F0, mips::A0, 514);
+  __ StoreQToOffset(mips::F0, mips::A0, 516);
+  __ StoreQToOffset(mips::F0, mips::A0, 1022);
+  __ StoreQToOffset(mips::F0, mips::A0, 1024);
+  __ StoreQToOffset(mips::F0, mips::A0, 1025);
+  __ StoreQToOffset(mips::F0, mips::A0, 1026);
+  __ StoreQToOffset(mips::F0, mips::A0, 1028);
+  __ StoreQToOffset(mips::F0, mips::A0, 2044);
+  __ StoreQToOffset(mips::F0, mips::A0, 2048);
+  __ StoreQToOffset(mips::F0, mips::A0, 2049);
+  __ StoreQToOffset(mips::F0, mips::A0, 2050);
+  __ StoreQToOffset(mips::F0, mips::A0, 2052);
+  __ StoreQToOffset(mips::F0, mips::A0, 4088);
+  __ StoreQToOffset(mips::F0, mips::A0, 4096);
+  __ StoreQToOffset(mips::F0, mips::A0, 4097);
+  __ StoreQToOffset(mips::F0, mips::A0, 4098);
+  __ StoreQToOffset(mips::F0, mips::A0, 4100);
+  __ StoreQToOffset(mips::F0, mips::A0, 4104);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x7FFC);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x8000);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x10000);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x12345678);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x12350078);
+  __ StoreQToOffset(mips::F0, mips::A0, -256);
+  __ StoreQToOffset(mips::F0, mips::A0, -511);
+  __ StoreQToOffset(mips::F0, mips::A0, -513);
+  __ StoreQToOffset(mips::F0, mips::A0, -1022);
+  __ StoreQToOffset(mips::F0, mips::A0, -1026);
+  __ StoreQToOffset(mips::F0, mips::A0, -2044);
+  __ StoreQToOffset(mips::F0, mips::A0, -2052);
+  __ StoreQToOffset(mips::F0, mips::A0, -4096);
+  __ StoreQToOffset(mips::F0, mips::A0, -4104);
+  __ StoreQToOffset(mips::F0, mips::A0, -32768);
+  __ StoreQToOffset(mips::F0, mips::A0, -36856);
+  __ StoreQToOffset(mips::F0, mips::A0, 36856);
+  __ StoreQToOffset(mips::F0, mips::A0, -69608);
+  __ StoreQToOffset(mips::F0, mips::A0, 69608);
+  __ StoreQToOffset(mips::F0, mips::A0, 0xABCDEF00);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+  const char* expected =
+      "st.d $w0, 0($a0)\n"
+      "st.b $w0, 1($a0)\n"
+      "st.h $w0, 2($a0)\n"
+      "st.w $w0, 4($a0)\n"
+      "st.d $w0, 8($a0)\n"
+      "st.b $w0, 511($a0)\n"
+      "st.d $w0, 512($a0)\n"
+      "addiu $at, $a0, 513\n"
+      "st.b $w0, 0($at)\n"
+      "st.h $w0, 514($a0)\n"
+      "st.w $w0, 516($a0)\n"
+      "st.h $w0, 1022($a0)\n"
+      "st.d $w0, 1024($a0)\n"
+      "addiu $at, $a0, 1025\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 1026\n"
+      "st.h $w0, 0($at)\n"
+      "st.w $w0, 1028($a0)\n"
+      "st.w $w0, 2044($a0)\n"
+      "st.d $w0, 2048($a0)\n"
+      "addiu $at, $a0, 2049\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 2050\n"
+      "st.h $w0, 0($at)\n"
+      "addiu $at, $a0, 2052\n"
+      "st.w $w0, 0($at)\n"
+      "st.d $w0, 4088($a0)\n"
+      "addiu $at, $a0, 4096\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, 4097\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 4098\n"
+      "st.h $w0, 0($at)\n"
+      "addiu $at, $a0, 4100\n"
+      "st.w $w0, 0($at)\n"
+      "addiu $at, $a0, 4104\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FFC\n"
+      "st.w $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FF8\n"
+      "st.d $w0, 8($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 32760\n"
+      "st.d $w0, 16($at)\n"
+      "lui $at, 4660\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, 24576\n"
+      "st.d $w0, -2440($at) # 0xF678\n"
+      "lui $at, 4661\n"
+      "addu $at, $at, $a0\n"
+      "st.d $w0, 120($at)\n"
+      "st.d $w0, -256($a0)\n"
+      "st.b $w0, -511($a0)\n"
+      "addiu $at, $a0, -513\n"
+      "st.b $w0, 0($at)\n"
+      "st.h $w0, -1022($a0)\n"
+      "addiu $at, $a0, -1026\n"
+      "st.h $w0, 0($at)\n"
+      "st.w $w0, -2044($a0)\n"
+      "addiu $at, $a0, -2052\n"
+      "st.w $w0, 0($at)\n"
+      "st.d $w0, -4096($a0)\n"
+      "addiu $at, $a0, -4104\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32768\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32760\n"
+      "addiu $at, $at, -4096\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 4096\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32760\n"
+      "addiu $at, $at, -32760\n"
+      "st.d $w0, -4088($at)\n"
+      "addiu $at, $a0, 32760\n"
+      "addiu $at, $at, 32760\n"
+      "st.d $w0, 4088($at)\n"
+      "lui $at, 0xABCE\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, -8192 # 0xE000\n"
+      "st.d $w0, 0xF00($at)\n"
+      "lui $at, 0x8000\n"
+      "addu $at, $at, $a0\n"
+      "addiu $at, $at, -21504 # 0xAC00\n"
+      "st.b $w0, -51($at) # 0xFFCD\n";
+  DriverStr(expected, "StoreQToOffset");
+}
+
+#undef __
+}  // namespace art
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index d464260..6ee2a5c 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -627,6 +627,124 @@
   DriverStr(expected, "LoadDFromOffset");
 }
 
+TEST_F(AssemblerMIPS32r6Test, LoadQFromOffset) {
+  __ LoadQFromOffset(mips::F0, mips::A0, 0);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4);
+  __ LoadQFromOffset(mips::F0, mips::A0, 8);
+  __ LoadQFromOffset(mips::F0, mips::A0, 511);
+  __ LoadQFromOffset(mips::F0, mips::A0, 512);
+  __ LoadQFromOffset(mips::F0, mips::A0, 513);
+  __ LoadQFromOffset(mips::F0, mips::A0, 514);
+  __ LoadQFromOffset(mips::F0, mips::A0, 516);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1022);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1024);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1025);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1026);
+  __ LoadQFromOffset(mips::F0, mips::A0, 1028);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2044);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2048);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2049);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2050);
+  __ LoadQFromOffset(mips::F0, mips::A0, 2052);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4088);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4096);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4097);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4098);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4100);
+  __ LoadQFromOffset(mips::F0, mips::A0, 4104);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFC);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x8000);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x10000);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x12345678);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x12350078);
+  __ LoadQFromOffset(mips::F0, mips::A0, -256);
+  __ LoadQFromOffset(mips::F0, mips::A0, -511);
+  __ LoadQFromOffset(mips::F0, mips::A0, -513);
+  __ LoadQFromOffset(mips::F0, mips::A0, -1022);
+  __ LoadQFromOffset(mips::F0, mips::A0, -1026);
+  __ LoadQFromOffset(mips::F0, mips::A0, -2044);
+  __ LoadQFromOffset(mips::F0, mips::A0, -2052);
+  __ LoadQFromOffset(mips::F0, mips::A0, -4096);
+  __ LoadQFromOffset(mips::F0, mips::A0, -4104);
+  __ LoadQFromOffset(mips::F0, mips::A0, -32768);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0xABCDEF00);
+  __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+  const char* expected =
+      "ld.d $w0, 0($a0)\n"
+      "ld.b $w0, 1($a0)\n"
+      "ld.h $w0, 2($a0)\n"
+      "ld.w $w0, 4($a0)\n"
+      "ld.d $w0, 8($a0)\n"
+      "ld.b $w0, 511($a0)\n"
+      "ld.d $w0, 512($a0)\n"
+      "addiu $at, $a0, 513\n"
+      "ld.b $w0, 0($at)\n"
+      "ld.h $w0, 514($a0)\n"
+      "ld.w $w0, 516($a0)\n"
+      "ld.h $w0, 1022($a0)\n"
+      "ld.d $w0, 1024($a0)\n"
+      "addiu $at, $a0, 1025\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 1026\n"
+      "ld.h $w0, 0($at)\n"
+      "ld.w $w0, 1028($a0)\n"
+      "ld.w $w0, 2044($a0)\n"
+      "ld.d $w0, 2048($a0)\n"
+      "addiu $at, $a0, 2049\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 2050\n"
+      "ld.h $w0, 0($at)\n"
+      "addiu $at, $a0, 2052\n"
+      "ld.w $w0, 0($at)\n"
+      "ld.d $w0, 4088($a0)\n"
+      "addiu $at, $a0, 4096\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, 4097\n"
+      "ld.b $w0, 0($at)\n"
+      "addiu $at, $a0, 4098\n"
+      "ld.h $w0, 0($at)\n"
+      "addiu $at, $a0, 4100\n"
+      "ld.w $w0, 0($at)\n"
+      "addiu $at, $a0, 4104\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FFC\n"
+      "ld.w $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FF8\n"
+      "ld.d $w0, 8($at)\n"
+      "aui $at, $a0, 0x1\n"
+      "ld.d $w0, 0($at)\n"
+      "aui $at, $a0, 0x1234\n"
+      "addiu $at, $at, 0x6000\n"
+      "ld.d $w0, -2440($at) # 0xF678\n"
+      "aui $at, $a0, 0x1235\n"
+      "ld.d $w0, 0x78($at)\n"
+      "ld.d $w0, -256($a0)\n"
+      "ld.b $w0, -511($a0)\n"
+      "addiu $at, $a0, -513\n"
+      "ld.b $w0, 0($at)\n"
+      "ld.h $w0, -1022($a0)\n"
+      "addiu $at, $a0, -1026\n"
+      "ld.h $w0, 0($at)\n"
+      "ld.w $w0, -2044($a0)\n"
+      "addiu $at, $a0, -2052\n"
+      "ld.w $w0, 0($at)\n"
+      "ld.d $w0, -4096($a0)\n"
+      "addiu $at, $a0, -4104\n"
+      "ld.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32768\n"
+      "ld.d $w0, 0($at)\n"
+      "aui $at, $a0, 0xABCE\n"
+      "addiu $at, $at, -8192 # 0xE000\n"
+      "ld.d $w0, 0xF00($at)\n"
+      "aui $at, $a0, 0x8000\n"
+      "addiu $at, $at, -21504 # 0xAC00\n"
+      "ld.b $w0, -51($at) # 0xFFCD\n";
+  DriverStr(expected, "LoadQFromOffset");
+}
+
 TEST_F(AssemblerMIPS32r6Test, StoreDToOffset) {
   __ StoreDToOffset(mips::F0, mips::A0, -0x8000);
   __ StoreDToOffset(mips::F0, mips::A0, +0);
@@ -711,6 +829,124 @@
   DriverStr(expected, "StoreDToOffset");
 }
 
+TEST_F(AssemblerMIPS32r6Test, StoreQToOffset) {
+  __ StoreQToOffset(mips::F0, mips::A0, 0);
+  __ StoreQToOffset(mips::F0, mips::A0, 1);
+  __ StoreQToOffset(mips::F0, mips::A0, 2);
+  __ StoreQToOffset(mips::F0, mips::A0, 4);
+  __ StoreQToOffset(mips::F0, mips::A0, 8);
+  __ StoreQToOffset(mips::F0, mips::A0, 511);
+  __ StoreQToOffset(mips::F0, mips::A0, 512);
+  __ StoreQToOffset(mips::F0, mips::A0, 513);
+  __ StoreQToOffset(mips::F0, mips::A0, 514);
+  __ StoreQToOffset(mips::F0, mips::A0, 516);
+  __ StoreQToOffset(mips::F0, mips::A0, 1022);
+  __ StoreQToOffset(mips::F0, mips::A0, 1024);
+  __ StoreQToOffset(mips::F0, mips::A0, 1025);
+  __ StoreQToOffset(mips::F0, mips::A0, 1026);
+  __ StoreQToOffset(mips::F0, mips::A0, 1028);
+  __ StoreQToOffset(mips::F0, mips::A0, 2044);
+  __ StoreQToOffset(mips::F0, mips::A0, 2048);
+  __ StoreQToOffset(mips::F0, mips::A0, 2049);
+  __ StoreQToOffset(mips::F0, mips::A0, 2050);
+  __ StoreQToOffset(mips::F0, mips::A0, 2052);
+  __ StoreQToOffset(mips::F0, mips::A0, 4088);
+  __ StoreQToOffset(mips::F0, mips::A0, 4096);
+  __ StoreQToOffset(mips::F0, mips::A0, 4097);
+  __ StoreQToOffset(mips::F0, mips::A0, 4098);
+  __ StoreQToOffset(mips::F0, mips::A0, 4100);
+  __ StoreQToOffset(mips::F0, mips::A0, 4104);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x7FFC);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x8000);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x10000);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x12345678);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x12350078);
+  __ StoreQToOffset(mips::F0, mips::A0, -256);
+  __ StoreQToOffset(mips::F0, mips::A0, -511);
+  __ StoreQToOffset(mips::F0, mips::A0, -513);
+  __ StoreQToOffset(mips::F0, mips::A0, -1022);
+  __ StoreQToOffset(mips::F0, mips::A0, -1026);
+  __ StoreQToOffset(mips::F0, mips::A0, -2044);
+  __ StoreQToOffset(mips::F0, mips::A0, -2052);
+  __ StoreQToOffset(mips::F0, mips::A0, -4096);
+  __ StoreQToOffset(mips::F0, mips::A0, -4104);
+  __ StoreQToOffset(mips::F0, mips::A0, -32768);
+  __ StoreQToOffset(mips::F0, mips::A0, 0xABCDEF00);
+  __ StoreQToOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+  const char* expected =
+      "st.d $w0, 0($a0)\n"
+      "st.b $w0, 1($a0)\n"
+      "st.h $w0, 2($a0)\n"
+      "st.w $w0, 4($a0)\n"
+      "st.d $w0, 8($a0)\n"
+      "st.b $w0, 511($a0)\n"
+      "st.d $w0, 512($a0)\n"
+      "addiu $at, $a0, 513\n"
+      "st.b $w0, 0($at)\n"
+      "st.h $w0, 514($a0)\n"
+      "st.w $w0, 516($a0)\n"
+      "st.h $w0, 1022($a0)\n"
+      "st.d $w0, 1024($a0)\n"
+      "addiu $at, $a0, 1025\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 1026\n"
+      "st.h $w0, 0($at)\n"
+      "st.w $w0, 1028($a0)\n"
+      "st.w $w0, 2044($a0)\n"
+      "st.d $w0, 2048($a0)\n"
+      "addiu $at, $a0, 2049\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 2050\n"
+      "st.h $w0, 0($at)\n"
+      "addiu $at, $a0, 2052\n"
+      "st.w $w0, 0($at)\n"
+      "st.d $w0, 4088($a0)\n"
+      "addiu $at, $a0, 4096\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, 4097\n"
+      "st.b $w0, 0($at)\n"
+      "addiu $at, $a0, 4098\n"
+      "st.h $w0, 0($at)\n"
+      "addiu $at, $a0, 4100\n"
+      "st.w $w0, 0($at)\n"
+      "addiu $at, $a0, 4104\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FFC\n"
+      "st.w $w0, 0($at)\n"
+      "addiu $at, $a0, 0x7FF8\n"
+      "st.d $w0, 8($at)\n"
+      "aui $at, $a0, 0x1\n"
+      "st.d $w0, 0($at)\n"
+      "aui $at, $a0, 0x1234\n"
+      "addiu $at, $at, 0x6000\n"
+      "st.d $w0, -2440($at) # 0xF678\n"
+      "aui $at, $a0, 0x1235\n"
+      "st.d $w0, 0x78($at)\n"
+      "st.d $w0, -256($a0)\n"
+      "st.b $w0, -511($a0)\n"
+      "addiu $at, $a0, -513\n"
+      "st.b $w0, 0($at)\n"
+      "st.h $w0, -1022($a0)\n"
+      "addiu $at, $a0, -1026\n"
+      "st.h $w0, 0($at)\n"
+      "st.w $w0, -2044($a0)\n"
+      "addiu $at, $a0, -2052\n"
+      "st.w $w0, 0($at)\n"
+      "st.d $w0, -4096($a0)\n"
+      "addiu $at, $a0, -4104\n"
+      "st.d $w0, 0($at)\n"
+      "addiu $at, $a0, -32768\n"
+      "st.d $w0, 0($at)\n"
+      "aui $at, $a0, 0xABCE\n"
+      "addiu $at, $at, -8192 # 0xE000\n"
+      "st.d $w0, 0xF00($at)\n"
+      "aui $at, $a0, 0x8000\n"
+      "addiu $at, $at, -21504 # 0xAC00\n"
+      "st.b $w0, -51($at) # 0xFFCD\n";
+  DriverStr(expected, "StoreQToOffset");
+}
+
 TEST_F(AssemblerMIPS32r6Test, LoadFarthestNearLabelAddress) {
   mips::MipsLabel label;
   __ LoadLabelAddress(mips::V0, mips::ZERO, &label);