MIPS64r6 Assembler Tests

Assembler tests for:

- SQRT.fmt    - ABS.fmt     - ROUND.L.fmt - ROUND.W.fmt
- CEIL.L.fmt  - CEIL.W.fmt  - FLOOR.L.fmt - FLOOR.W.fmt
- SEL.fmt     - RINT.fmt    - CLASS.fmt   - MIN.fmt
- MAX.fmt     - cvt.d.l     - BITSWAP     - DBITSWAP
- DSBH        - DSHD        - WSBH        - ROTR
- SELEQZ      - SELNEZ      - CLZ         - CLO
- DCLZ        - DCLO        - SC          - SCD
- LL          - LLD

These are the assembler instructions which were added to support
intrinsic functions on MIPS64. Tests for additional assembler
instructions will follow.

Support added to the testing infrastructure for:

- Assembler instructions which use three registers; previously
  instructions were limited to one, or two, registers.
- Immediate values which have their sizes specified by the number of
  bits required to store them rather than the number of bytes, in both
  signed and unsigned versions.

Change-Id: I38c07dcbf2539825b25bed13aac05a26fa594b0b
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 71a55bb..9775f6a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -351,6 +351,7 @@
 
 COMPILER_GTEST_HOST_SRC_FILES_mips64 := \
   $(COMPILER_GTEST_COMMON_SRC_FILES_mips64) \
+  compiler/utils/mips64/assembler_mips64_test.cc \
 
 COMPILER_GTEST_HOST_SRC_FILES_x86 := \
   $(COMPILER_GTEST_COMMON_SRC_FILES_x86) \
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index 2a0912e..4380596 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -287,7 +287,7 @@
       case 1:
         return Base::REG2_TOKEN;
       case 2:
-        return REG3_TOKEN;
+        return Base::REG3_TOKEN;
       case 3:
         return REG4_TOKEN;
       default:
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 017402d..bd994f4 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -92,6 +92,17 @@
         fmt);
   }
 
+  std::string RepeatRRR(void (Ass::*f)(Reg, Reg, Reg), std::string fmt) {
+    return RepeatTemplatedRegisters<Reg, Reg, Reg>(f,
+        GetRegisters(),
+        GetRegisters(),
+        GetRegisters(),
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        fmt);
+  }
+
   std::string Repeatrb(void (Ass::*f)(Reg, Reg), std::string fmt) {
     return RepeatTemplatedRegisters<Reg, Reg>(f,
         GetRegisters(),
@@ -118,6 +129,66 @@
     return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt);
   }
 
+  template <typename Reg1Type, typename Reg2Type, typename ImmType,
+            RegisterView Reg1View, RegisterView Reg2View>
+  std::string RepeatRegRegImmBits(void (Ass::*f)(Reg1Type, Reg2Type, ImmType),
+                                  int imm_bits,
+                                  std::string fmt) {
+    const std::vector<Reg1Type*> reg1_registers = GetRegisters();
+    const std::vector<Reg2Type*> reg2_registers = GetRegisters();
+    std::string str;
+    std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), imm_bits > 0);
+
+    for (auto reg1 : reg1_registers) {
+      for (auto reg2 : reg2_registers) {
+        for (int64_t imm : imms) {
+          ImmType new_imm = CreateImmediate(imm);
+          (assembler_.get()->*f)(*reg1, *reg2, new_imm);
+          std::string base = fmt;
+
+          std::string reg1_string = GetRegName<Reg1View>(*reg1);
+          size_t reg1_index;
+          while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
+            base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
+          }
+
+          std::string reg2_string = GetRegName<Reg2View>(*reg2);
+          size_t reg2_index;
+          while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
+            base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
+          }
+
+          size_t imm_index = base.find(IMM_TOKEN);
+          if (imm_index != std::string::npos) {
+            std::ostringstream sreg;
+            sreg << imm;
+            std::string imm_string = sreg.str();
+            base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
+          }
+
+          if (str.size() > 0) {
+            str += "\n";
+          }
+          str += base;
+        }
+      }
+    }
+    // Add a newline at the end.
+    str += "\n";
+    return str;
+  }
+
+  template <typename Reg1Type, typename Reg2Type, typename ImmType>
+  std::string RepeatRRIb(void (Ass::*f)(Reg1Type, Reg2Type, ImmType),
+                         int imm_bits,
+                         std::string fmt) {
+    return RepeatRegRegImmBits<Reg1Type,
+                               Reg2Type,
+                               ImmType,
+                               RegisterView::kUsePrimaryName,
+                               RegisterView::kUsePrimaryName>(f, imm_bits, fmt);
+  }
+
   std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) {
     return RepeatTemplatedRegisters<FPReg, FPReg>(f,
                                                   GetFPRegisters(),
@@ -127,14 +198,27 @@
                                                   fmt);
   }
 
-  std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&), size_t imm_bytes, std::string fmt) {
+  std::string RepeatFFF(void (Ass::*f)(FPReg, FPReg, FPReg), std::string fmt) {
+    return RepeatTemplatedRegisters<FPReg, FPReg, FPReg>(f,
+                                                         GetFPRegisters(),
+                                                         GetFPRegisters(),
+                                                         GetFPRegisters(),
+                                                         &AssemblerTest::GetFPRegName,
+                                                         &AssemblerTest::GetFPRegName,
+                                                         &AssemblerTest::GetFPRegName,
+                                                         fmt);
+  }
+
+  std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&),
+                        size_t imm_bytes,
+                        std::string fmt) {
     return RepeatTemplatedRegistersImm<FPReg, FPReg>(f,
-                                                  GetFPRegisters(),
-                                                  GetFPRegisters(),
-                                                  &AssemblerTest::GetFPRegName,
-                                                  &AssemblerTest::GetFPRegName,
-                                                  imm_bytes,
-                                                  fmt);
+                                                     GetFPRegisters(),
+                                                     GetFPRegisters(),
+                                                     &AssemblerTest::GetFPRegName,
+                                                     &AssemblerTest::GetFPRegName,
+                                                     imm_bytes,
+                                                     fmt);
   }
 
   std::string RepeatFR(void (Ass::*f)(FPReg, Reg), std::string fmt) {
@@ -339,6 +423,63 @@
     return res;
   }
 
+  const int kMaxBitsExhaustiveTest = 8;
+
+  // Create a couple of immediate values up to the number of bits given.
+  virtual std::vector<int64_t> CreateImmediateValuesBits(const int imm_bits, bool as_uint = false) {
+    CHECK_GT(imm_bits, 0);
+    CHECK_LE(imm_bits, 64);
+    std::vector<int64_t> res;
+
+    if (imm_bits <= kMaxBitsExhaustiveTest) {
+      if (as_uint) {
+        for (uint64_t i = MinInt<uint64_t>(imm_bits); i <= MaxInt<uint64_t>(imm_bits); i++) {
+          res.push_back(static_cast<int64_t>(i));
+        }
+      } else {
+        for (int64_t i = MinInt<int64_t>(imm_bits); i <= MaxInt<int64_t>(imm_bits); i++) {
+          res.push_back(i);
+        }
+      }
+    } else {
+      if (as_uint) {
+        for (uint64_t i = MinInt<uint64_t>(kMaxBitsExhaustiveTest);
+             i <= MaxInt<uint64_t>(kMaxBitsExhaustiveTest);
+             i++) {
+          res.push_back(static_cast<int64_t>(i));
+        }
+        for (int i = 0; i <= imm_bits; i++) {
+          uint64_t j = (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1) +
+                       ((MaxInt<uint64_t>(imm_bits) -
+                        (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1))
+                        * i / imm_bits);
+          res.push_back(static_cast<int64_t>(j));
+        }
+      } else {
+        for (int i = 0; i <= imm_bits; i++) {
+          int64_t j = MinInt<int64_t>(imm_bits) +
+                      ((((MinInt<int64_t>(kMaxBitsExhaustiveTest) - 1) -
+                         MinInt<int64_t>(imm_bits))
+                        * i) / imm_bits);
+          res.push_back(static_cast<int64_t>(j));
+        }
+        for (int64_t i = MinInt<int64_t>(kMaxBitsExhaustiveTest);
+             i <= MaxInt<int64_t>(kMaxBitsExhaustiveTest);
+             i++) {
+          res.push_back(static_cast<int64_t>(i));
+        }
+        for (int i = 0; i <= imm_bits; i++) {
+          int64_t j = (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1) +
+                      ((MaxInt<int64_t>(imm_bits) - (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1))
+                       * i / imm_bits);
+          res.push_back(static_cast<int64_t>(j));
+        }
+      }
+    }
+
+    return res;
+  }
+
   // Create an immediate from the specific value.
   virtual Imm CreateImmediate(int64_t imm_value) = 0;
 
@@ -406,6 +547,52 @@
     return str;
   }
 
+  template <typename Reg1, typename Reg2, typename Reg3>
+  std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2, Reg3),
+                                       const std::vector<Reg1*> reg1_registers,
+                                       const std::vector<Reg2*> reg2_registers,
+                                       const std::vector<Reg3*> reg3_registers,
+                                       std::string (AssemblerTest::*GetName1)(const Reg1&),
+                                       std::string (AssemblerTest::*GetName2)(const Reg2&),
+                                       std::string (AssemblerTest::*GetName3)(const Reg3&),
+                                       std::string fmt) {
+    std::string str;
+    for (auto reg1 : reg1_registers) {
+      for (auto reg2 : reg2_registers) {
+        for (auto reg3 : reg3_registers) {
+          (assembler_.get()->*f)(*reg1, *reg2, *reg3);
+          std::string base = fmt;
+
+          std::string reg1_string = (this->*GetName1)(*reg1);
+          size_t reg1_index;
+          while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
+            base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
+          }
+
+          std::string reg2_string = (this->*GetName2)(*reg2);
+          size_t reg2_index;
+          while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
+            base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
+          }
+
+          std::string reg3_string = (this->*GetName3)(*reg3);
+          size_t reg3_index;
+          while ((reg3_index = base.find(REG3_TOKEN)) != std::string::npos) {
+            base.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
+          }
+
+          if (str.size() > 0) {
+            str += "\n";
+          }
+          str += base;
+        }
+      }
+    }
+    // Add a newline at the end.
+    str += "\n";
+    return str;
+  }
+
   template <typename Reg1, typename Reg2>
   std::string RepeatTemplatedRegistersImm(void (Ass::*f)(Reg1, Reg2, const Imm&),
                                           const std::vector<Reg1*> reg1_registers,
@@ -500,6 +687,7 @@
   static constexpr const char* REG_TOKEN = "{reg}";
   static constexpr const char* REG1_TOKEN = "{reg1}";
   static constexpr const char* REG2_TOKEN = "{reg2}";
+  static constexpr const char* REG3_TOKEN = "{reg3}";
   static constexpr const char* IMM_TOKEN = "{imm}";
 
  private:
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
new file mode 100644
index 0000000..2071aca
--- /dev/null
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2015 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_mips64.h"
+
+#include <inttypes.h>
+#include <map>
+#include <random>
+
+#include "base/bit_utils.h"
+#include "base/stl_util.h"
+#include "utils/assembler_test.h"
+
+namespace art {
+
+struct MIPS64CpuRegisterCompare {
+  bool operator()(const mips64::GpuRegister& a, const mips64::GpuRegister& b) const {
+    return a < b;
+  }
+};
+
+class AssemblerMIPS64Test : public AssemblerTest<mips64::Mips64Assembler,
+                                                 mips64::GpuRegister,
+                                                 mips64::FpuRegister,
+                                                 uint32_t> {
+ public:
+  typedef AssemblerTest<mips64::Mips64Assembler,
+                        mips64::GpuRegister,
+                        mips64::FpuRegister,
+                        uint32_t> Base;
+
+ protected:
+  // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
+  std::string GetArchitectureString() OVERRIDE {
+    return "mips64";
+  }
+
+  std::string GetAssemblerParameters() OVERRIDE {
+    return " --no-warn -march=mips64r6";
+  }
+
+  std::string GetDisassembleParameters() OVERRIDE {
+    return " -D -bbinary -mmips:isa64r6";
+  }
+
+  void SetUpHelpers() OVERRIDE {
+    if (registers_.size() == 0) {
+      registers_.push_back(new mips64::GpuRegister(mips64::ZERO));
+      registers_.push_back(new mips64::GpuRegister(mips64::AT));
+      registers_.push_back(new mips64::GpuRegister(mips64::V0));
+      registers_.push_back(new mips64::GpuRegister(mips64::V1));
+      registers_.push_back(new mips64::GpuRegister(mips64::A0));
+      registers_.push_back(new mips64::GpuRegister(mips64::A1));
+      registers_.push_back(new mips64::GpuRegister(mips64::A2));
+      registers_.push_back(new mips64::GpuRegister(mips64::A3));
+      registers_.push_back(new mips64::GpuRegister(mips64::A4));
+      registers_.push_back(new mips64::GpuRegister(mips64::A5));
+      registers_.push_back(new mips64::GpuRegister(mips64::A6));
+      registers_.push_back(new mips64::GpuRegister(mips64::A7));
+      registers_.push_back(new mips64::GpuRegister(mips64::T0));
+      registers_.push_back(new mips64::GpuRegister(mips64::T1));
+      registers_.push_back(new mips64::GpuRegister(mips64::T2));
+      registers_.push_back(new mips64::GpuRegister(mips64::T3));
+      registers_.push_back(new mips64::GpuRegister(mips64::S0));
+      registers_.push_back(new mips64::GpuRegister(mips64::S1));
+      registers_.push_back(new mips64::GpuRegister(mips64::S2));
+      registers_.push_back(new mips64::GpuRegister(mips64::S3));
+      registers_.push_back(new mips64::GpuRegister(mips64::S4));
+      registers_.push_back(new mips64::GpuRegister(mips64::S5));
+      registers_.push_back(new mips64::GpuRegister(mips64::S6));
+      registers_.push_back(new mips64::GpuRegister(mips64::S7));
+      registers_.push_back(new mips64::GpuRegister(mips64::T8));
+      registers_.push_back(new mips64::GpuRegister(mips64::T9));
+      registers_.push_back(new mips64::GpuRegister(mips64::K0));
+      registers_.push_back(new mips64::GpuRegister(mips64::K1));
+      registers_.push_back(new mips64::GpuRegister(mips64::GP));
+      registers_.push_back(new mips64::GpuRegister(mips64::SP));
+      registers_.push_back(new mips64::GpuRegister(mips64::S8));
+      registers_.push_back(new mips64::GpuRegister(mips64::RA));
+
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::ZERO), "zero");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::AT), "at");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::V0), "v0");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::V1), "v1");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A0), "a0");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A1), "a1");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A2), "a2");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A3), "a3");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A4), "a4");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A5), "a5");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A6), "a6");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::A7), "a7");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T0), "t0");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T1), "t1");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T2), "t2");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T3), "t3");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S0), "s0");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S1), "s1");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S2), "s2");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S3), "s3");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S4), "s4");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S5), "s5");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S6), "s6");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S7), "s7");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T8), "t8");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::T9), "t9");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::K0), "k0");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::K1), "k1");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::GP), "gp");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::SP), "sp");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::S8), "s8");
+      secondary_register_names_.emplace(mips64::GpuRegister(mips64::RA), "ra");
+
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F0));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F1));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F2));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F3));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F4));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F5));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F6));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F7));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F8));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F9));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F10));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F11));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F12));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F13));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F14));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F15));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F16));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F17));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F18));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F19));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F20));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F21));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F22));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F23));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F24));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F25));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F26));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F27));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F28));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F29));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F30));
+      fp_registers_.push_back(new mips64::FpuRegister(mips64::F31));
+    }
+  }
+
+  void TearDown() OVERRIDE {
+    AssemblerTest::TearDown();
+    STLDeleteElements(&registers_);
+    STLDeleteElements(&fp_registers_);
+  }
+
+  std::vector<mips64::GpuRegister*> GetRegisters() OVERRIDE {
+    return registers_;
+  }
+
+  std::vector<mips64::FpuRegister*> GetFPRegisters() OVERRIDE {
+    return fp_registers_;
+  }
+
+  uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
+    return imm_value;
+  }
+
+  std::string GetSecondaryRegisterName(const mips64::GpuRegister& reg) OVERRIDE {
+    CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end());
+    return secondary_register_names_[reg];
+  }
+
+ private:
+  std::vector<mips64::GpuRegister*> registers_;
+  std::map<mips64::GpuRegister, std::string, MIPS64CpuRegisterCompare> secondary_register_names_;
+
+  std::vector<mips64::FpuRegister*> fp_registers_;
+};
+
+
+TEST_F(AssemblerMIPS64Test, Toolchain) {
+  EXPECT_TRUE(CheckTools());
+}
+
+
+///////////////////
+// FP Operations //
+///////////////////
+
+TEST_F(AssemblerMIPS64Test, SqrtS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::SqrtS, "sqrt.s ${reg1}, ${reg2}"), "sqrt.s");
+}
+
+TEST_F(AssemblerMIPS64Test, SqrtD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::SqrtD, "sqrt.d ${reg1}, ${reg2}"), "sqrt.d");
+}
+
+TEST_F(AssemblerMIPS64Test, AbsS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::AbsS, "abs.s ${reg1}, ${reg2}"), "abs.s");
+}
+
+TEST_F(AssemblerMIPS64Test, AbsD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::AbsD, "abs.d ${reg1}, ${reg2}"), "abs.d");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundLS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundLS, "round.l.s ${reg1}, ${reg2}"), "round.l.s");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundLD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundLD, "round.l.d ${reg1}, ${reg2}"), "round.l.d");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundWS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundWS, "round.w.s ${reg1}, ${reg2}"), "round.w.s");
+}
+
+TEST_F(AssemblerMIPS64Test, RoundWD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RoundWD, "round.w.d ${reg1}, ${reg2}"), "round.w.d");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilLS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilLS, "ceil.l.s ${reg1}, ${reg2}"), "ceil.l.s");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilLD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilLD, "ceil.l.d ${reg1}, ${reg2}"), "ceil.l.d");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilWS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilWS, "ceil.w.s ${reg1}, ${reg2}"), "ceil.w.s");
+}
+
+TEST_F(AssemblerMIPS64Test, CeilWD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::CeilWD, "ceil.w.d ${reg1}, ${reg2}"), "ceil.w.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorLS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorLS, "floor.l.s ${reg1}, ${reg2}"), "floor.l.s");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorLD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorLD, "floor.l.d ${reg1}, ${reg2}"), "floor.l.d");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorWS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorWS, "floor.w.s ${reg1}, ${reg2}"), "floor.w.s");
+}
+
+TEST_F(AssemblerMIPS64Test, FloorWD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::FloorWD, "floor.w.d ${reg1}, ${reg2}"), "floor.w.d");
+}
+
+TEST_F(AssemblerMIPS64Test, SelS) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::SelS, "sel.s ${reg1}, ${reg2}, ${reg3}"), "sel.s");
+}
+
+TEST_F(AssemblerMIPS64Test, SelD) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::SelD, "sel.d ${reg1}, ${reg2}, ${reg3}"), "sel.d");
+}
+
+TEST_F(AssemblerMIPS64Test, RintS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RintS, "rint.s ${reg1}, ${reg2}"), "rint.s");
+}
+
+TEST_F(AssemblerMIPS64Test, RintD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::RintD, "rint.d ${reg1}, ${reg2}"), "rint.d");
+}
+
+TEST_F(AssemblerMIPS64Test, ClassS) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::ClassS, "class.s ${reg1}, ${reg2}"), "class.s");
+}
+
+TEST_F(AssemblerMIPS64Test, ClassD) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::ClassD, "class.d ${reg1}, ${reg2}"), "class.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MinS) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::MinS, "min.s ${reg1}, ${reg2}, ${reg3}"), "min.s");
+}
+
+TEST_F(AssemblerMIPS64Test, MinD) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::MinD, "min.d ${reg1}, ${reg2}, ${reg3}"), "min.d");
+}
+
+TEST_F(AssemblerMIPS64Test, MaxS) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::MaxS, "max.s ${reg1}, ${reg2}, ${reg3}"), "max.s");
+}
+
+TEST_F(AssemblerMIPS64Test, MaxD) {
+  DriverStr(RepeatFFF(&mips64::Mips64Assembler::MaxD, "max.d ${reg1}, ${reg2}, ${reg3}"), "max.d");
+}
+
+TEST_F(AssemblerMIPS64Test, CvtDL) {
+  DriverStr(RepeatFF(&mips64::Mips64Assembler::Cvtdl, "cvt.d.l ${reg1}, ${reg2}"), "cvt.d.l");
+}
+
+//////////
+// MISC //
+//////////
+
+TEST_F(AssemblerMIPS64Test, Bitswap) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap");
+}
+
+TEST_F(AssemblerMIPS64Test, Dbitswap) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Dbitswap, "dbitswap ${reg1}, ${reg2}"), "dbitswap");
+}
+
+TEST_F(AssemblerMIPS64Test, Dsbh) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Dsbh, "dsbh ${reg1}, ${reg2}"), "dsbh");
+}
+
+TEST_F(AssemblerMIPS64Test, Dshd) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Dshd, "dshd ${reg1}, ${reg2}"), "dshd");
+}
+
+TEST_F(AssemblerMIPS64Test, Wsbh) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Wsbh, "wsbh ${reg1}, ${reg2}"), "wsbh");
+}
+
+TEST_F(AssemblerMIPS64Test, Sc) {
+  DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Sc, -9, "sc ${reg1}, {imm}(${reg2})"), "sc");
+}
+
+TEST_F(AssemblerMIPS64Test, Scd) {
+  DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Scd, -9, "scd ${reg1}, {imm}(${reg2})"), "scd");
+}
+
+TEST_F(AssemblerMIPS64Test, Ll) {
+  DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Ll, -9, "ll ${reg1}, {imm}(${reg2})"), "ll");
+}
+
+TEST_F(AssemblerMIPS64Test, Lld) {
+  DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Lld, -9, "lld ${reg1}, {imm}(${reg2})"), "lld");
+}
+
+TEST_F(AssemblerMIPS64Test, Rotr) {
+  DriverStr(RepeatRRIb(&mips64::Mips64Assembler::Rotr, 5, "rotr ${reg1}, ${reg2}, {imm}"), "rotr");
+}
+
+TEST_F(AssemblerMIPS64Test, Seleqz) {
+  DriverStr(RepeatRRR(&mips64::Mips64Assembler::Seleqz, "seleqz ${reg1}, ${reg2}, ${reg3}"),
+            "seleqz");
+}
+
+TEST_F(AssemblerMIPS64Test, Selnez) {
+  DriverStr(RepeatRRR(&mips64::Mips64Assembler::Selnez, "selnez ${reg1}, ${reg2}, ${reg3}"),
+            "selnez");
+}
+
+TEST_F(AssemblerMIPS64Test, Clz) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Clz, "clz ${reg1}, ${reg2}"), "clz");
+}
+
+TEST_F(AssemblerMIPS64Test, Clo) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Clo, "clo ${reg1}, ${reg2}"), "clo");
+}
+
+TEST_F(AssemblerMIPS64Test, Dclz) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Dclz, "dclz ${reg1}, ${reg2}"), "dclz");
+}
+
+TEST_F(AssemblerMIPS64Test, Dclo) {
+  DriverStr(RepeatRR(&mips64::Mips64Assembler::Dclo, "dclo ${reg1}, ${reg2}"), "dclo");
+}
+
+}  // namespace art
diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h
index 1b0d774..9c78ee5 100644
--- a/runtime/base/bit_utils.h
+++ b/runtime/base/bit_utils.h
@@ -263,6 +263,33 @@
                       : static_cast<unsigned_type>(value));
 }
 
+// Generate maximum/minimum values for signed/unsigned n-bit integers
+template <typename T>
+static constexpr T MaxInt(size_t bits) {
+  return
+      DCHECK_CONSTEXPR(bits > 0, "bits cannot be zero", 0)
+      DCHECK_CONSTEXPR(bits <= BitSizeOf<T>(), "kBits must be < max.", 0)
+      bits == BitSizeOf<T>()
+          ? std::numeric_limits<T>::max()
+          : std::is_signed<T>::value
+                ? (bits == 1
+                       ? 0
+                       : static_cast<T>(MaxInt<typename std::make_unsigned<T>::type>(bits - 1)))
+                : static_cast<T>(UINT64_C(1) << bits) - static_cast<T>(1);
+}
+
+template <typename T>
+static constexpr T MinInt(size_t bits) {
+  return
+      DCHECK_CONSTEXPR(bits > 0, "bits cannot be zero", 0)
+      DCHECK_CONSTEXPR(bits <= BitSizeOf<T>(), "kBits must be < max.", 0)
+      bits == BitSizeOf<T>()
+          ? std::numeric_limits<T>::min()
+          : std::is_signed<T>::value
+                ? (bits == 1 ? -1 : static_cast<T>(-1) - MaxInt<T>(bits))
+                : static_cast<T>(0);
+}
+
 // Using the Curiously Recurring Template Pattern to implement everything shared
 // by LowToHighBitIterator and HighToLowBitIterator, i.e. everything but operator*().
 template <typename T, typename Iter>