Add repeat support for memory addresses.

Rationale:
This enables exhaustive testing of instructions
that use memory addresses. First use case of
the generics is x86.

Bug: 18380245
Bug: 18380559
Bug: 18380348

Test: assembler_x86[_64]_test

Change-Id: Ib0ad6fa65477b0c6fc04642ff980a4b9543d16d5
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 5ab558b..12954a4 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -46,7 +46,12 @@
 // For use in the template as the default type to get a nonvector registers version.
 struct NoVectorRegs {};
 
-template<typename Ass, typename Reg, typename FPReg, typename Imm, typename VecReg = NoVectorRegs>
+template<typename Ass,
+         typename Addr,
+         typename Reg,
+         typename FPReg,
+         typename Imm,
+         typename VecReg = NoVectorRegs>
 class AssemblerTest : public testing::Test {
  public:
   Ass* GetAssembler() {
@@ -64,6 +69,10 @@
     DriverWrapper(assembly_string, test_name);
   }
 
+  //
+  // Register repeats.
+  //
+
   std::string RepeatR(void (Ass::*f)(Reg), const std::string& fmt) {
     return RepeatTemplatedRegister<Reg>(f,
         GetRegisters(),
@@ -636,13 +645,23 @@
 
   // The following functions are public so that TestFn can use them...
 
+  // Returns a vector of address used by any of the repeat methods
+  // involving an "A" (e.g. RepeatA).
+  virtual std::vector<Addr> GetAddresses() = 0;
+
+  // Returns a vector of registers used by any of the repeat methods
+  // involving an "R" (e.g. RepeatR).
   virtual std::vector<Reg*> GetRegisters() = 0;
 
+  // Returns a vector of fp-registers used by any of the repeat methods
+  // involving an "F" (e.g. RepeatFF).
   virtual std::vector<FPReg*> GetFPRegisters() {
     UNIMPLEMENTED(FATAL) << "Architecture does not support floating-point registers";
     UNREACHABLE();
   }
 
+  // Returns a vector of dedicated simd-registers used by any of the repeat
+  // methods involving an "V" (e.g. RepeatVV).
   virtual std::vector<VecReg*> GetVectorRegisters() {
     UNIMPLEMENTED(FATAL) << "Architecture does not support vector registers";
     UNREACHABLE();
@@ -838,6 +857,268 @@
   // Create an immediate from the specific value.
   virtual Imm CreateImmediate(int64_t imm_value) = 0;
 
+  //
+  // Addresses repeats.
+  //
+
+  // Repeats over addresses provided by fixture.
+  std::string RepeatA(void (Ass::*f)(const Addr&), const std::string& fmt) {
+    return RepeatA(f, GetAddresses(), fmt);
+  }
+
+  // Variant that takes explicit vector of addresss
+  // (to test restricted addressing modes set).
+  std::string RepeatA(void (Ass::*f)(const Addr&),
+                      const std::vector<Addr>& a,
+                      const std::string& fmt) {
+    return RepeatTemplatedMem<Addr>(f, a, &AssemblerTest::GetAddrName, fmt);
+  }
+
+  // Repeats over addresses and immediates provided by fixture.
+  std::string RepeatAI(void (Ass::*f)(const Addr&, const Imm&),
+                       size_t imm_bytes,
+                       const std::string& fmt) {
+    return RepeatAI(f, imm_bytes, GetAddresses(), fmt);
+  }
+
+  // Variant that takes explicit vector of addresss
+  // (to test restricted addressing modes set).
+  std::string RepeatAI(void (Ass::*f)(const Addr&, const Imm&),
+                       size_t imm_bytes,
+                       const std::vector<Addr>& a,
+                       const std::string& fmt) {
+    return RepeatTemplatedMemImm<Addr>(f, imm_bytes, a, &AssemblerTest::GetAddrName, fmt);
+  }
+
+  // Repeats over registers and addresses provided by fixture.
+  std::string RepeatRA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) {
+    return RepeatRA(f, GetAddresses(), fmt);
+  }
+
+  // Variant that takes explicit vector of addresss
+  // (to test restricted addressing modes set).
+  std::string RepeatRA(void (Ass::*f)(Reg, const Addr&),
+                       const std::vector<Addr>& a,
+                       const std::string& fmt) {
+    return RepeatTemplatedRegMem<Reg, Addr>(
+        f,
+        GetRegisters(),
+        a,
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerTest::GetAddrName,
+        fmt);
+  }
+
+  // Repeats over fp-registers and addresses provided by fixture.
+  std::string RepeatFA(void (Ass::*f)(FPReg, const Addr&), const std::string& fmt) {
+    return RepeatFA(f, GetAddresses(), fmt);
+  }
+
+  // Variant that takes explicit vector of addresss
+  // (to test restricted addressing modes set).
+  std::string RepeatFA(void (Ass::*f)(FPReg, const Addr&),
+                       const std::vector<Addr>& a,
+                       const std::string& fmt) {
+    return RepeatTemplatedRegMem<FPReg, Addr>(
+        f,
+        GetFPRegisters(),
+        a,
+        &AssemblerTest::GetFPRegName,
+        &AssemblerTest::GetAddrName,
+        fmt);
+  }
+
+  // Repeats over addresses and registers provided by fixture.
+  std::string RepeatAR(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) {
+    return RepeatAR(f, GetAddresses(), fmt);
+  }
+
+  // Variant that takes explicit vector of addresss
+  // (to test restricted addressing modes set).
+  std::string RepeatAR(void (Ass::*f)(const Addr&, Reg),
+                       const std::vector<Addr>& a,
+                       const std::string& fmt) {
+    return RepeatTemplatedMemReg<Addr, Reg>(
+        f,
+        a,
+        GetRegisters(),
+        &AssemblerTest::GetAddrName,
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        fmt);
+  }
+
+  // Repeats over addresses and fp-registers provided by fixture.
+  std::string RepeatAF(void (Ass::*f)(const Addr&, FPReg), const std::string& fmt) {
+    return RepeatAF(f, GetAddresses(), fmt);
+  }
+
+  // Variant that takes explicit vector of addresss
+  // (to test restricted addressing modes set).
+  std::string RepeatAF(void (Ass::*f)(const Addr&, FPReg),
+                       const std::vector<Addr>& a,
+                       const std::string& fmt) {
+    return RepeatTemplatedMemReg<Addr, FPReg>(
+        f,
+        a,
+        GetFPRegisters(),
+        &AssemblerTest::GetAddrName,
+        &AssemblerTest::GetFPRegName,
+        fmt);
+  }
+
+  template <typename AddrType>
+  std::string RepeatTemplatedMem(void (Ass::*f)(const AddrType&),
+                                 const std::vector<AddrType> addresses,
+                                 std::string (AssemblerTest::*GetAName)(const AddrType&),
+                                 const std::string& fmt) {
+    WarnOnCombinations(addresses.size());
+    std::string str;
+    for (auto addr : addresses) {
+      if (f != nullptr) {
+        (assembler_.get()->*f)(addr);
+      }
+      std::string base = fmt;
+
+      std::string addr_string = (this->*GetAName)(addr);
+      size_t addr_index;
+      if ((addr_index = base.find(ADDRESS_TOKEN)) != std::string::npos) {
+        base.replace(addr_index, ConstexprStrLen(ADDRESS_TOKEN), addr_string);
+      }
+
+      if (str.size() > 0) {
+        str += "\n";
+      }
+      str += base;
+    }
+    // Add a newline at the end.
+    str += "\n";
+    return str;
+  }
+
+  template <typename AddrType>
+  std::string RepeatTemplatedMemImm(void (Ass::*f)(const AddrType&, const Imm&),
+                                    size_t imm_bytes,
+                                    const std::vector<AddrType> addresses,
+                                    std::string (AssemblerTest::*GetAName)(const AddrType&),
+                                    const std::string& fmt) {
+    std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
+    WarnOnCombinations(addresses.size() * imms.size());
+    std::string str;
+    for (auto addr : addresses) {
+      for (int64_t imm : imms) {
+        Imm new_imm = CreateImmediate(imm);
+        if (f != nullptr) {
+          (assembler_.get()->*f)(addr, new_imm);
+        }
+        std::string base = fmt;
+
+        std::string addr_string = (this->*GetAName)(addr);
+        size_t addr_index;
+        if ((addr_index = base.find(ADDRESS_TOKEN)) != std::string::npos) {
+          base.replace(addr_index, ConstexprStrLen(ADDRESS_TOKEN), addr_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 RegType, typename AddrType>
+  std::string RepeatTemplatedRegMem(void (Ass::*f)(RegType, const AddrType&),
+                                    const std::vector<RegType*> registers,
+                                    const std::vector<AddrType> addresses,
+                                    std::string (AssemblerTest::*GetRName)(const RegType&),
+                                    std::string (AssemblerTest::*GetAName)(const AddrType&),
+                                    const std::string& fmt) {
+    WarnOnCombinations(addresses.size() * registers.size());
+    std::string str;
+    for (auto reg : registers) {
+      for (auto addr : addresses) {
+        if (f != nullptr) {
+          (assembler_.get()->*f)(*reg, addr);
+        }
+        std::string base = fmt;
+
+        std::string reg_string = (this->*GetRName)(*reg);
+        size_t reg_index;
+        if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
+          base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
+        }
+
+        std::string addr_string = (this->*GetAName)(addr);
+        size_t addr_index;
+        if ((addr_index = base.find(ADDRESS_TOKEN)) != std::string::npos) {
+          base.replace(addr_index, ConstexprStrLen(ADDRESS_TOKEN), addr_string);
+        }
+
+        if (str.size() > 0) {
+          str += "\n";
+        }
+        str += base;
+      }
+    }
+    // Add a newline at the end.
+    str += "\n";
+    return str;
+  }
+
+  template <typename AddrType, typename RegType>
+  std::string RepeatTemplatedMemReg(void (Ass::*f)(const AddrType&, RegType),
+                                    const std::vector<AddrType> addresses,
+                                    const std::vector<RegType*> registers,
+                                    std::string (AssemblerTest::*GetAName)(const AddrType&),
+                                    std::string (AssemblerTest::*GetRName)(const RegType&),
+                                    const std::string& fmt) {
+    WarnOnCombinations(addresses.size() * registers.size());
+    std::string str;
+    for (auto addr : addresses) {
+      for (auto reg : registers) {
+        if (f != nullptr) {
+          (assembler_.get()->*f)(addr, *reg);
+        }
+        std::string base = fmt;
+
+        std::string addr_string = (this->*GetAName)(addr);
+        size_t addr_index;
+        if ((addr_index = base.find(ADDRESS_TOKEN)) != std::string::npos) {
+          base.replace(addr_index, ConstexprStrLen(ADDRESS_TOKEN), addr_string);
+        }
+
+        std::string reg_string = (this->*GetRName)(*reg);
+        size_t reg_index;
+        if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
+          base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
+        }
+
+        if (str.size() > 0) {
+          str += "\n";
+        }
+        str += base;
+      }
+    }
+    // Add a newline at the end.
+    str += "\n";
+    return str;
+  }
+
+  //
+  // Register repeats.
+  //
+
   template <typename RegType>
   std::string RepeatTemplatedRegister(void (Ass::*f)(RegType),
                                       const std::vector<RegType*> registers,
@@ -1048,6 +1329,12 @@
     return str;
   }
 
+  std::string GetAddrName(const Addr& addr) {
+    std::ostringstream saddr;
+    saddr << addr;
+    return saddr.str();
+  }
+
   template <RegisterView kRegView>
   std::string GetRegName(const Reg& reg) {
     std::ostringstream sreg;
@@ -1094,6 +1381,7 @@
     }
   }
 
+  static constexpr const char* ADDRESS_TOKEN = "{mem}";
   static constexpr const char* REG_TOKEN = "{reg}";
   static constexpr const char* REG1_TOKEN = "{reg1}";
   static constexpr const char* REG2_TOKEN = "{reg2}";