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/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: