OSDN Git Service

ART: Multiview assembler_test, fix x86-64 assembler
authorAndreas Gampe <agampe@google.com>
Wed, 12 Nov 2014 22:05:46 +0000 (14:05 -0800)
committerAndreas Gampe <agampe@google.com>
Fri, 14 Nov 2014 00:31:59 +0000 (16:31 -0800)
Expose "secondary" names for registers so it is possible to test
32b views for 64b architectures.

Add floating-point register testing.

Refactor assembler_test for better code reuse (and simpler adding
of combination drivers).

Fix movss, movsd (MR instead of RM encoding), xchgl, xchgq,
both versions of EmitGenericShift.

Tighten imull(Reg,Imm), imulq(Reg,Imm), xchgl and xchgq encoding.

Clarify cv*** variants with a comment.

Add tests for movl, addl, imull, imuli, mull, subl, cmpqi, cmpl,
xorq (regs), xorl, movss, movsd, addss, addsd, subss, subsd, mulss,
mulsd, divss, divsd, cvtsi2ss, cvtsi2sd, cvtss2si, cvtss2sd, cvtsd2si,
cvttss2si, cvttsd2si, cvtsd2ss, cvtdq2pd, comiss, comisd, sqrtss,
sqrtsd, xorps, xorpd, fincstp, fsin, fcos, fptan, xchgl (disabled,
see code comment), xchgq, testl, andl, andq, orl, orq, shll, shrl,
sarl, negq, negl, notq, notl, enter and leave, call, ret, and jmp,
and make some older ones more exhaustive.

Follow-up TODOs:
1) Support memory (Address).
2) Support tertiary and quaternary register views.

Bug: 18117217
Change-Id: I1d583a3bec552e3cc7c315925e1e006f393ab687

compiler/utils/arm/assembler_arm32_test.cc
compiler/utils/arm/assembler_thumb2_test.cc
compiler/utils/assembler_test.h
compiler/utils/x86_64/assembler_x86_64.cc
compiler/utils/x86_64/assembler_x86_64.h
compiler/utils/x86_64/assembler_x86_64_test.cc
runtime/utils.h

index 3ba77b5..4f5d4c3 100644 (file)
@@ -22,7 +22,7 @@
 namespace art {
 
 class AssemblerArm32Test : public AssemblerTest<arm::Arm32Assembler,
-                                                arm::Register,
+                                                arm::Register, arm::SRegister,
                                                 uint32_t> {
  protected:
   std::string GetArchitectureString() OVERRIDE {
index 3d9c70d..57ba0ca 100644 (file)
@@ -22,8 +22,8 @@
 namespace art {
 
 class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
-                                                arm::Register,
-                                                uint32_t> {
+                                                 arm::Register, arm::SRegister,
+                                                 uint32_t> {
  protected:
   std::string GetArchitectureString() OVERRIDE {
     return "arm";
index 0378176..9d3fa01 100644 (file)
 
 namespace art {
 
+// Helper for a constexpr string length.
+constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) {
+  return ('\0' == str[0]) ? count : ConstexprStrLen(str+1, count+1);
+}
+
 // Use a glocal static variable to keep the same name for all test data. Else we'll just spam the
 // temp directory.
 static std::string tmpnam_;
 
-template<typename Ass, typename Reg, typename Imm>
+template<typename Ass, typename Reg, typename FPReg, typename Imm>
 class AssemblerTest : public testing::Test {
  public:
+  enum class RegisterView {  // private
+    kUsePrimaryName,
+    kUseSecondaryName
+  };
+
   Ass* GetAssembler() {
     return assembler_.get();
   }
 
-  typedef std::string (*TestFn)(Ass* assembler);
+  typedef std::string (*TestFn)(AssemblerTest* assembler_test, Ass* assembler);
 
   void DriverFn(TestFn f, std::string test_name) {
-    Driver(f(assembler_.get()), test_name);
+    Driver(f(this, assembler_.get()), test_name);
   }
 
   // This driver assumes the assembler has already been called.
@@ -52,116 +62,114 @@ class AssemblerTest : public testing::Test {
   }
 
   std::string RepeatR(void (Ass::*f)(Reg), std::string fmt) {
-    const std::vector<Reg*> registers = GetRegisters();
-    std::string str;
-    for (auto reg : registers) {
-      (assembler_.get()->*f)(*reg);
-      std::string base = fmt;
-
-      size_t reg_index = base.find("{reg}");
-      if (reg_index != std::string::npos) {
-        std::ostringstream sreg;
-        sreg << *reg;
-        std::string reg_string = sreg.str();
-        base.replace(reg_index, 5, reg_string);
-      }
+    return RepeatTemplatedRegister<Reg>(f,
+        GetRegisters(),
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        fmt);
+  }
 
-      if (str.size() > 0) {
-        str += "\n";
-      }
-      str += base;
-    }
-    // Add a newline at the end.
-    str += "\n";
-    return str;
+  std::string Repeatr(void (Ass::*f)(Reg), std::string fmt) {
+    return RepeatTemplatedRegister<Reg>(f,
+        GetRegisters(),
+        &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
+        fmt);
   }
 
   std::string RepeatRR(void (Ass::*f)(Reg, Reg), std::string fmt) {
-    const std::vector<Reg*> registers = GetRegisters();
-    std::string str;
-    for (auto reg1 : registers) {
-      for (auto reg2 : registers) {
-        (assembler_.get()->*f)(*reg1, *reg2);
-        std::string base = fmt;
-
-        size_t reg1_index = base.find("{reg1}");
-        if (reg1_index != std::string::npos) {
-          std::ostringstream sreg;
-          sreg << *reg1;
-          std::string reg_string = sreg.str();
-          base.replace(reg1_index, 6, reg_string);
-        }
+    return RepeatTemplatedRegisters<Reg, Reg>(f,
+        GetRegisters(),
+        GetRegisters(),
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        fmt);
+  }
 
-        size_t reg2_index = base.find("{reg2}");
-        if (reg2_index != std::string::npos) {
-          std::ostringstream sreg;
-          sreg << *reg2;
-          std::string reg_string = sreg.str();
-          base.replace(reg2_index, 6, reg_string);
-        }
+  std::string Repeatrr(void (Ass::*f)(Reg, Reg), std::string fmt) {
+    return RepeatTemplatedRegisters<Reg, Reg>(f,
+        GetRegisters(),
+        GetRegisters(),
+        &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
+        &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
+        fmt);
+  }
 
-        if (str.size() > 0) {
-          str += "\n";
-        }
-        str += base;
-      }
-    }
-    // Add a newline at the end.
-    str += "\n";
-    return str;
+  std::string RepeatRr(void (Ass::*f)(Reg, Reg), std::string fmt) {
+    return RepeatTemplatedRegisters<Reg, Reg>(f,
+        GetRegisters(),
+        GetRegisters(),
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
+        fmt);
   }
 
   std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) {
-    const std::vector<Reg*> registers = GetRegisters();
-    std::string str;
-    std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
-    for (auto reg : registers) {
-      for (int64_t imm : imms) {
-        Imm new_imm = CreateImmediate(imm);
-        (assembler_.get()->*f)(*reg, new_imm);
-        std::string base = fmt;
+    return RepeatRegisterImm<RegisterView::kUsePrimaryName>(f, imm_bytes, fmt);
+  }
 
-        size_t reg_index = base.find("{reg}");
-        if (reg_index != std::string::npos) {
-          std::ostringstream sreg;
-          sreg << *reg;
-          std::string reg_string = sreg.str();
-          base.replace(reg_index, 5, reg_string);
-        }
+  std::string Repeatri(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) {
+    return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt);
+  }
 
-        size_t imm_index = base.find("{imm}");
-        if (imm_index != std::string::npos) {
-          std::ostringstream sreg;
-          sreg << imm;
-          std::string imm_string = sreg.str();
-          base.replace(imm_index, 5, imm_string);
-        }
+  std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), std::string fmt) {
+    return RepeatTemplatedRegisters<FPReg, FPReg>(f,
+                                                  GetFPRegisters(),
+                                                  GetFPRegisters(),
+                                                  &AssemblerTest::GetFPRegName,
+                                                  &AssemblerTest::GetFPRegName,
+                                                  fmt);
+  }
 
-        if (str.size() > 0) {
-          str += "\n";
-        }
-        str += base;
-      }
-    }
-    // Add a newline at the end.
-    str += "\n";
-    return str;
+  std::string RepeatFR(void (Ass::*f)(FPReg, Reg), std::string fmt) {
+    return RepeatTemplatedRegisters<FPReg, Reg>(f,
+        GetFPRegisters(),
+        GetRegisters(),
+        &AssemblerTest::GetFPRegName,
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        fmt);
+  }
+
+  std::string RepeatFr(void (Ass::*f)(FPReg, Reg), std::string fmt) {
+    return RepeatTemplatedRegisters<FPReg, Reg>(f,
+        GetFPRegisters(),
+        GetRegisters(),
+        &AssemblerTest::GetFPRegName,
+        &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
+        fmt);
+  }
+
+  std::string RepeatRF(void (Ass::*f)(Reg, FPReg), std::string fmt) {
+    return RepeatTemplatedRegisters<Reg, FPReg>(f,
+        GetRegisters(),
+        GetFPRegisters(),
+        &AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerTest::GetFPRegName,
+        fmt);
   }
 
-  std::string RepeatI(void (Ass::*f)(const Imm&), size_t imm_bytes, std::string fmt) {
+  std::string RepeatrF(void (Ass::*f)(Reg, FPReg), std::string fmt) {
+    return RepeatTemplatedRegisters<Reg, FPReg>(f,
+        GetRegisters(),
+        GetFPRegisters(),
+        &AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
+        &AssemblerTest::GetFPRegName,
+        fmt);
+  }
+
+  std::string RepeatI(void (Ass::*f)(const Imm&), size_t imm_bytes, std::string fmt,
+                      bool as_uint = false) {
     std::string str;
-    std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
+    std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint);
     for (int64_t imm : imms) {
       Imm new_imm = CreateImmediate(imm);
       (assembler_.get()->*f)(new_imm);
       std::string base = fmt;
 
-      size_t imm_index = base.find("{imm}");
+      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, 5, imm_string);
+        base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
       }
 
       if (str.size() > 0) {
@@ -200,7 +208,24 @@ class AssemblerTest : public testing::Test {
     return true;
   }
 
+  // The following functions are public so that TestFn can use them...
+
+  virtual std::vector<Reg*> GetRegisters() = 0;
+
+  virtual std::vector<FPReg*> GetFPRegisters() {
+    UNIMPLEMENTED(FATAL) << "Architecture does not support floating-point registers";
+    UNREACHABLE();
+  }
+
+  // Secondary register names are the secondary view on registers, e.g., 32b on 64b systems.
+  virtual std::string GetSecondaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
+    UNIMPLEMENTED(FATAL) << "Architecture does not support secondary registers";
+    UNREACHABLE();
+  }
+
  protected:
+  explicit AssemblerTest() {}
+
   void SetUp() OVERRIDE {
     assembler_.reset(new Ass());
 
@@ -219,8 +244,6 @@ class AssemblerTest : public testing::Test {
   // Override this to set up any architecture-specific things, e.g., register vectors.
   virtual void SetUpHelpers() {}
 
-  virtual std::vector<Reg*> GetRegisters() = 0;
-
   // Get the typically used name for this architecture, e.g., aarch64, x86_64, ...
   virtual std::string GetArchitectureString() = 0;
 
@@ -305,23 +328,41 @@ class AssemblerTest : public testing::Test {
   }
 
   // Create a couple of immediate values up to the number of bytes given.
-  virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes) {
+  virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes, bool as_uint = false) {
     std::vector<int64_t> res;
     res.push_back(0);
-    res.push_back(-1);
+    if (!as_uint) {
+      res.push_back(-1);
+    } else {
+      res.push_back(0xFF);
+    }
     res.push_back(0x12);
     if (imm_bytes >= 2) {
       res.push_back(0x1234);
-      res.push_back(-0x1234);
+      if (!as_uint) {
+        res.push_back(-0x1234);
+      } else {
+        res.push_back(0xFFFF);
+      }
       if (imm_bytes >= 4) {
         res.push_back(0x12345678);
-        res.push_back(-0x12345678);
+        if (!as_uint) {
+          res.push_back(-0x12345678);
+        } else {
+          res.push_back(0xFFFFFFFF);
+        }
         if (imm_bytes >= 6) {
           res.push_back(0x123456789ABC);
-          res.push_back(-0x123456789ABC);
+          if (!as_uint) {
+            res.push_back(-0x123456789ABC);
+          }
           if (imm_bytes >= 8) {
             res.push_back(0x123456789ABCDEF0);
-            res.push_back(-0x123456789ABCDEF0);
+            if (!as_uint) {
+              res.push_back(-0x123456789ABCDEF0);
+            } else {
+              res.push_back(0xFFFFFFFFFFFFFFFF);
+            }
           }
         }
       }
@@ -332,7 +373,127 @@ class AssemblerTest : public testing::Test {
   // Create an immediate from the specific value.
   virtual Imm CreateImmediate(int64_t imm_value) = 0;
 
+  template <typename RegType>
+  std::string RepeatTemplatedRegister(void (Ass::*f)(RegType),
+                                      const std::vector<RegType*> registers,
+                                      std::string (AssemblerTest::*GetName)(const RegType&),
+                                      std::string fmt) {
+    std::string str;
+    for (auto reg : registers) {
+      (assembler_.get()->*f)(*reg);
+      std::string base = fmt;
+
+      std::string reg_string = (this->*GetName)(*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;
+  }
+
+  template <typename Reg1, typename Reg2>
+  std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2),
+                                       const std::vector<Reg1*> reg1_registers,
+                                       const std::vector<Reg2*> reg2_registers,
+                                       std::string (AssemblerTest::*GetName1)(const Reg1&),
+                                       std::string (AssemblerTest::*GetName2)(const Reg2&),
+                                       std::string fmt) {
+    std::string str;
+    for (auto reg1 : reg1_registers) {
+      for (auto reg2 : reg2_registers) {
+        (assembler_.get()->*f)(*reg1, *reg2);
+        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);
+        }
+
+        if (str.size() > 0) {
+          str += "\n";
+        }
+        str += base;
+      }
+    }
+    // Add a newline at the end.
+    str += "\n";
+    return str;
+  }
+
  private:
+  template <RegisterView kRegView>
+  std::string GetRegName(const Reg& reg) {
+    std::ostringstream sreg;
+    switch (kRegView) {
+      case RegisterView::kUsePrimaryName:
+        sreg << reg;
+        break;
+
+      case RegisterView::kUseSecondaryName:
+        sreg << GetSecondaryRegisterName(reg);
+        break;
+    }
+    return sreg.str();
+  }
+
+  std::string GetFPRegName(const FPReg& reg) {
+    std::ostringstream sreg;
+    sreg << reg;
+    return sreg.str();
+  }
+
+  template <RegisterView kRegView>
+  std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes,
+                                  std::string fmt) {
+    const std::vector<Reg*> registers = GetRegisters();
+    std::string str;
+    std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
+    for (auto reg : registers) {
+      for (int64_t imm : imms) {
+        Imm new_imm = CreateImmediate(imm);
+        (assembler_.get()->*f)(*reg, new_imm);
+        std::string base = fmt;
+
+        std::string reg_string = GetRegName<kRegView>(*reg);
+        size_t reg_index;
+        while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
+          base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_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;
+  }
+
   // Driver() assembles and compares the results. If the results are not equal and we have a
   // disassembler, disassemble both and check whether they have the same mnemonics (in which case
   // we just warn).
@@ -489,12 +650,12 @@ class AssemblerTest : public testing::Test {
 
     bool result = CompareFiles(data_name + ".dis", as_name + ".dis");
 
-    if (result) {
-      std::remove(data_name.c_str());
-      std::remove(as_name.c_str());
-      std::remove((data_name + ".dis").c_str());
-      std::remove((as_name + ".dis").c_str());
-    }
+    // If you want to take a look at the differences between the ART assembler and GCC, comment
+    // out the removal code.
+    std::remove(data_name.c_str());
+    std::remove(as_name.c_str());
+    std::remove((data_name + ".dis").c_str());
+    std::remove((as_name + ".dis").c_str());
 
     return result;
   }
@@ -701,6 +862,13 @@ class AssemblerTest : public testing::Test {
     return tmpnam_;
   }
 
+  static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6;
+
+  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* IMM_TOKEN = "{imm}";
+
   std::unique_ptr<Ass> assembler_;
 
   std::string resolved_assembler_cmd_;
@@ -709,7 +877,7 @@ class AssemblerTest : public testing::Test {
 
   std::string android_data_;
 
-  static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6;
+  DISALLOW_COPY_AND_ASSIGN(AssemblerTest);
 };
 
 }  // namespace art
index bd08b1f..2bb2ed8 100644 (file)
@@ -345,7 +345,7 @@ void X86_64Assembler::movss(const Address& dst, XmmRegister src) {
 void X86_64Assembler::movss(XmmRegister dst, XmmRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xF3);
-  EmitOptionalRex32(dst, src);
+  EmitOptionalRex32(src, dst);  // Movss is MR encoding instead of the usual RM.
   EmitUint8(0x0F);
   EmitUint8(0x11);
   EmitXmmRegisterOperand(src.LowBits(), dst);
@@ -505,7 +505,7 @@ void X86_64Assembler::movsd(const Address& dst, XmmRegister src) {
 void X86_64Assembler::movsd(XmmRegister dst, XmmRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0xF2);
-  EmitOptionalRex32(dst, src);
+  EmitOptionalRex32(src, dst);  // Movsd is MR encoding instead of the usual RM.
   EmitUint8(0x0F);
   EmitUint8(0x11);
   EmitXmmRegisterOperand(src.LowBits(), dst);
@@ -856,17 +856,46 @@ void X86_64Assembler::fptan() {
 
 void X86_64Assembler::xchgl(CpuRegister dst, CpuRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
-  EmitOptionalRex32(dst, src);
+  // There is a short version for rax.
+  // It's a bit awkward, as CpuRegister has a const field, so assignment and thus swapping doesn't
+  // work.
+  const bool src_rax = src.AsRegister() == RAX;
+  const bool dst_rax = dst.AsRegister() == RAX;
+  if (src_rax || dst_rax) {
+    EmitOptionalRex32(src_rax ? dst : src);
+    EmitUint8(0x90 + (src_rax ? dst.LowBits() : src.LowBits()));
+    return;
+  }
+
+  // General case.
+  EmitOptionalRex32(src, dst);
   EmitUint8(0x87);
-  EmitRegisterOperand(dst.LowBits(), src.LowBits());
+  EmitRegisterOperand(src.LowBits(), dst.LowBits());
 }
 
 
 void X86_64Assembler::xchgq(CpuRegister dst, CpuRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
-  EmitRex64(dst, src);
+  // There is a short version for rax.
+  // It's a bit awkward, as CpuRegister has a const field, so assignment and thus swapping doesn't
+  // work.
+  const bool src_rax = src.AsRegister() == RAX;
+  const bool dst_rax = dst.AsRegister() == RAX;
+  if (src_rax || dst_rax) {
+    // If src == target, emit a nop instead.
+    if (src_rax && dst_rax) {
+      EmitUint8(0x90);
+    } else {
+      EmitRex64(src_rax ? dst : src);
+      EmitUint8(0x90 + (src_rax ? dst.LowBits() : src.LowBits()));
+    }
+    return;
+  }
+
+  // General case.
+  EmitRex64(src, dst);
   EmitUint8(0x87);
-  EmitOperand(dst.LowBits(), Operand(src));
+  EmitRegisterOperand(src.LowBits(), dst.LowBits());
 }
 
 
@@ -1314,13 +1343,25 @@ void X86_64Assembler::imull(CpuRegister dst, CpuRegister src) {
   EmitOperand(dst.LowBits(), Operand(src));
 }
 
-
 void X86_64Assembler::imull(CpuRegister reg, const Immediate& imm) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  CHECK(imm.is_int32());  // imull only supports 32b immediate.
+
   EmitOptionalRex32(reg, reg);
-  EmitUint8(0x69);
-  EmitOperand(reg.LowBits(), Operand(reg));
-  EmitImmediate(imm);
+
+  // See whether imm can be represented as a sign-extended 8bit value.
+  int32_t v32 = static_cast<int32_t>(imm.value());
+  if (IsInt32(8, v32)) {
+    // Sign-extension works.
+    EmitUint8(0x6B);
+    EmitOperand(reg.LowBits(), Operand(reg));
+    EmitUint8(static_cast<uint8_t>(v32 & 0xFF));
+  } else {
+    // Not representable, use full immediate.
+    EmitUint8(0x69);
+    EmitOperand(reg.LowBits(), Operand(reg));
+    EmitImmediate(imm);
+  }
 }
 
 
@@ -1345,10 +1386,22 @@ void X86_64Assembler::imulq(CpuRegister dst, CpuRegister src) {
 void X86_64Assembler::imulq(CpuRegister reg, const Immediate& imm) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   CHECK(imm.is_int32());  // imulq only supports 32b immediate.
-  EmitRex64(reg);
-  EmitUint8(0x69);
-  EmitOperand(reg.LowBits(), Operand(reg));
-  EmitImmediate(imm);
+
+  EmitRex64(reg, reg);
+
+  // See whether imm can be represented as a sign-extended 8bit value.
+  int64_t v64 = imm.value();
+  if (IsInt64(8, v64)) {
+    // Sign-extension works.
+    EmitUint8(0x6B);
+    EmitOperand(reg.LowBits(), Operand(reg));
+    EmitUint8(static_cast<uint8_t>(v64 & 0xFF));
+  } else {
+    // Not representable, use full immediate.
+    EmitUint8(0x69);
+    EmitOperand(reg.LowBits(), Operand(reg));
+    EmitImmediate(imm);
+  }
 }
 
 
@@ -1759,6 +1812,8 @@ void X86_64Assembler::EmitGenericShift(bool wide,
   CHECK(imm.is_int8());
   if (wide) {
     EmitRex64(reg);
+  } else {
+    EmitOptionalRex32(reg);
   }
   if (imm.value() == 1) {
     EmitUint8(0xD1);
@@ -1776,6 +1831,7 @@ void X86_64Assembler::EmitGenericShift(int reg_or_opcode,
                                        CpuRegister shifter) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   CHECK_EQ(shifter.AsRegister(), RCX);
+  EmitOptionalRex32(operand);
   EmitUint8(0xD3);
   EmitOperand(reg_or_opcode, Operand(operand));
 }
index b46f6f7..4dd70e2 100644 (file)
@@ -328,17 +328,17 @@ class X86_64Assembler FINAL : public Assembler {
   void divsd(XmmRegister dst, XmmRegister src);
   void divsd(XmmRegister dst, const Address& src);
 
-  void cvtsi2ss(XmmRegister dst, CpuRegister src);
-  void cvtsi2sd(XmmRegister dst, CpuRegister src);
+  void cvtsi2ss(XmmRegister dst, CpuRegister src);  // Note: this is the r/m32 version.
+  void cvtsi2sd(XmmRegister dst, CpuRegister src);  // Note: this is the r/m32 version.
 
-  void cvtss2si(CpuRegister dst, XmmRegister src);
+  void cvtss2si(CpuRegister dst, XmmRegister src);  // Note: this is the r32 version.
   void cvtss2sd(XmmRegister dst, XmmRegister src);
 
-  void cvtsd2si(CpuRegister dst, XmmRegister src);
+  void cvtsd2si(CpuRegister dst, XmmRegister src);  // Note: this is the r32 version.
   void cvtsd2ss(XmmRegister dst, XmmRegister src);
 
-  void cvttss2si(CpuRegister dst, XmmRegister src);
-  void cvttsd2si(CpuRegister dst, XmmRegister src);
+  void cvttss2si(CpuRegister dst, XmmRegister src);  // Note: this is the r32 version.
+  void cvttsd2si(CpuRegister dst, XmmRegister src);  // Note: this is the r32 version.
 
   void cvtdq2pd(XmmRegister dst, XmmRegister src);
 
index 0e8ea5b..af389e6 100644 (file)
 
 #include "assembler_x86_64.h"
 
+#include <inttypes.h>
+#include <map>
+#include <random>
+
 #include "base/stl_util.h"
 #include "utils/assembler_test.h"
+#include "utils.h"
 
 namespace art {
 
@@ -30,8 +35,88 @@ TEST(AssemblerX86_64, CreateBuffer) {
   ASSERT_EQ(static_cast<size_t>(5), buffer.Size());
 }
 
+#ifdef HAVE_ANDROID_OS
+static constexpr size_t kRandomIterations = 1000;  // Devices might be puny, don't stress them...
+#else
+static constexpr size_t kRandomIterations = 100000;  // Hosts are pretty powerful.
+#endif
+
+TEST(AssemblerX86_64, SignExtension) {
+  // 32bit.
+  for (int32_t i = 0; i < 128; i++) {
+    EXPECT_TRUE(IsInt32(8, i)) << i;
+  }
+  for (int32_t i = 128; i < 255; i++) {
+    EXPECT_FALSE(IsInt32(8, i)) << i;
+  }
+  // Do some higher ones randomly.
+  std::random_device rd;
+  std::default_random_engine e1(rd());
+  std::uniform_int_distribution<int32_t> uniform_dist(256, INT32_MAX);
+  for (size_t i = 0; i < kRandomIterations; i++) {
+    int32_t value = uniform_dist(e1);
+    EXPECT_FALSE(IsInt32(8, value)) << value;
+  }
+
+  // Negative ones.
+  for (int32_t i = -1; i >= -128; i--) {
+    EXPECT_TRUE(IsInt32(8, i)) << i;
+  }
+
+  for (int32_t i = -129; i > -256; i--) {
+    EXPECT_FALSE(IsInt32(8, i)) << i;
+  }
+
+  // Do some lower ones randomly.
+  std::uniform_int_distribution<int32_t> uniform_dist2(INT32_MIN, -256);
+  for (size_t i = 0; i < 100; i++) {
+    int32_t value = uniform_dist2(e1);
+    EXPECT_FALSE(IsInt32(8, value)) << value;
+  }
+
+  // 64bit.
+  for (int64_t i = 0; i < 128; i++) {
+    EXPECT_TRUE(IsInt64(8, i)) << i;
+  }
+  for (int32_t i = 128; i < 255; i++) {
+    EXPECT_FALSE(IsInt64(8, i)) << i;
+  }
+  // Do some higher ones randomly.
+  std::uniform_int_distribution<int64_t> uniform_dist3(256, INT64_MAX);
+  for (size_t i = 0; i < 100; i++) {
+    int64_t value = uniform_dist3(e1);
+    EXPECT_FALSE(IsInt64(8, value)) << value;
+  }
+
+  // Negative ones.
+  for (int64_t i = -1; i >= -128; i--) {
+    EXPECT_TRUE(IsInt64(8, i)) << i;
+  }
+
+  for (int64_t i = -129; i > -256; i--) {
+    EXPECT_FALSE(IsInt64(8, i)) << i;
+  }
+
+  // Do some lower ones randomly.
+  std::uniform_int_distribution<int64_t> uniform_dist4(INT64_MIN, -256);
+  for (size_t i = 0; i < kRandomIterations; i++) {
+    int64_t value = uniform_dist4(e1);
+    EXPECT_FALSE(IsInt64(8, value)) << value;
+  }
+}
+
+struct X86_64CpuRegisterCompare {
+    bool operator()(const x86_64::CpuRegister& a, const x86_64::CpuRegister& b) const {
+        return a.AsRegister() < b.AsRegister();
+    }
+};
+
 class AssemblerX86_64Test : public AssemblerTest<x86_64::X86_64Assembler, x86_64::CpuRegister,
-                                                 x86_64::Immediate> {
+                                                 x86_64::XmmRegister, x86_64::Immediate> {
+ public:
+  typedef AssemblerTest<x86_64::X86_64Assembler, x86_64::CpuRegister,
+                        x86_64::XmmRegister, x86_64::Immediate> Base;
+
  protected:
   // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
   std::string GetArchitectureString() OVERRIDE {
@@ -60,24 +145,71 @@ class AssemblerX86_64Test : public AssemblerTest<x86_64::X86_64Assembler, x86_64
       registers_.push_back(new x86_64::CpuRegister(x86_64::R13));
       registers_.push_back(new x86_64::CpuRegister(x86_64::R14));
       registers_.push_back(new x86_64::CpuRegister(x86_64::R15));
+
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::RAX), "eax");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::RBX), "ebx");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::RCX), "ecx");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::RDX), "edx");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::RBP), "ebp");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::RSP), "esp");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::RSI), "esi");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::RDI), "edi");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::R8), "r8d");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::R9), "r9d");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::R10), "r10d");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::R11), "r11d");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::R12), "r12d");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::R13), "r13d");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::R14), "r14d");
+      secondary_register_names_.emplace(x86_64::CpuRegister(x86_64::R15), "r15d");
+
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM0));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM1));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM2));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM3));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM4));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM5));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM6));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM7));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM8));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM9));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM10));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM11));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM12));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM13));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM14));
+      fp_registers_.push_back(new x86_64::XmmRegister(x86_64::XMM15));
     }
   }
 
   void TearDown() OVERRIDE {
     AssemblerTest::TearDown();
     STLDeleteElements(&registers_);
+    STLDeleteElements(&fp_registers_);
   }
 
   std::vector<x86_64::CpuRegister*> GetRegisters() OVERRIDE {
     return registers_;
   }
 
+  std::vector<x86_64::XmmRegister*> GetFPRegisters() OVERRIDE {
+    return fp_registers_;
+  }
+
   x86_64::Immediate CreateImmediate(int64_t imm_value) OVERRIDE {
     return x86_64::Immediate(imm_value);
   }
 
+  std::string GetSecondaryRegisterName(const x86_64::CpuRegister& reg) OVERRIDE {
+    CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end());
+    return secondary_register_names_[reg];
+  }
+
  private:
   std::vector<x86_64::CpuRegister*> registers_;
+  std::map<x86_64::CpuRegister, std::string, X86_64CpuRegisterCompare> secondary_register_names_;
+
+  std::vector<x86_64::XmmRegister*> fp_registers_;
 };
 
 
@@ -94,7 +226,6 @@ TEST_F(AssemblerX86_64Test, PushqImm) {
   DriverStr(RepeatI(&x86_64::X86_64Assembler::pushq, 4U, "pushq ${imm}"), "pushqi");
 }
 
-
 TEST_F(AssemblerX86_64Test, MovqRegs) {
   DriverStr(RepeatRR(&x86_64::X86_64Assembler::movq, "movq %{reg2}, %{reg1}"), "movq");
 }
@@ -103,6 +234,13 @@ TEST_F(AssemblerX86_64Test, MovqImm) {
   DriverStr(RepeatRI(&x86_64::X86_64Assembler::movq, 8U, "movq ${imm}, %{reg}"), "movqi");
 }
 
+TEST_F(AssemblerX86_64Test, MovlRegs) {
+  DriverStr(Repeatrr(&x86_64::X86_64Assembler::movl, "mov %{reg2}, %{reg1}"), "movl");
+}
+
+TEST_F(AssemblerX86_64Test, MovlImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::movl, 4U, "mov ${imm}, %{reg}"), "movli");
+}
 
 TEST_F(AssemblerX86_64Test, AddqRegs) {
   DriverStr(RepeatRR(&x86_64::X86_64Assembler::addq, "addq %{reg2}, %{reg1}"), "addq");
@@ -112,10 +250,36 @@ TEST_F(AssemblerX86_64Test, AddqImm) {
   DriverStr(RepeatRI(&x86_64::X86_64Assembler::addq, 4U, "addq ${imm}, %{reg}"), "addqi");
 }
 
+TEST_F(AssemblerX86_64Test, AddlRegs) {
+  DriverStr(Repeatrr(&x86_64::X86_64Assembler::addl, "add %{reg2}, %{reg1}"), "addl");
+}
+
+TEST_F(AssemblerX86_64Test, AddlImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::addl, 4U, "add ${imm}, %{reg}"), "addli");
+}
+
 TEST_F(AssemblerX86_64Test, ImulqRegs) {
   DriverStr(RepeatRR(&x86_64::X86_64Assembler::imulq, "imulq %{reg2}, %{reg1}"), "imulq");
 }
 
+TEST_F(AssemblerX86_64Test, ImulqImm) {
+  DriverStr(RepeatRI(&x86_64::X86_64Assembler::imulq, 4U, "imulq ${imm}, %{reg}, %{reg}"),
+            "imulqi");
+}
+
+TEST_F(AssemblerX86_64Test, ImullRegs) {
+  DriverStr(Repeatrr(&x86_64::X86_64Assembler::imull, "imul %{reg2}, %{reg1}"), "imull");
+}
+
+TEST_F(AssemblerX86_64Test, ImullImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::imull, 4U, "imull ${imm}, %{reg}, %{reg}"),
+            "imulli");
+}
+
+TEST_F(AssemblerX86_64Test, Mull) {
+  DriverStr(Repeatr(&x86_64::X86_64Assembler::mull, "mull %{reg}"), "mull");
+}
+
 TEST_F(AssemblerX86_64Test, SubqRegs) {
   DriverStr(RepeatRR(&x86_64::X86_64Assembler::subq, "subq %{reg2}, %{reg1}"), "subq");
 }
@@ -124,45 +288,178 @@ TEST_F(AssemblerX86_64Test, SubqImm) {
   DriverStr(RepeatRI(&x86_64::X86_64Assembler::subq, 4U, "subq ${imm}, %{reg}"), "subqi");
 }
 
+TEST_F(AssemblerX86_64Test, SublRegs) {
+  DriverStr(Repeatrr(&x86_64::X86_64Assembler::subl, "sub %{reg2}, %{reg1}"), "subl");
+}
+
+TEST_F(AssemblerX86_64Test, SublImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::subl, 4U, "sub ${imm}, %{reg}"), "subli");
+}
+
+// Shll only allows CL as the shift register.
+std::string shll_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) {
+  std::ostringstream str;
+
+  std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters();
+
+  x86_64::CpuRegister shifter(x86_64::RCX);
+  for (auto reg : registers) {
+    assembler->shll(*reg, shifter);
+    str << "shll %cl, %" << assembler_test->GetSecondaryRegisterName(*reg) << "\n";
+  }
+
+  return str.str();
+}
+
+TEST_F(AssemblerX86_64Test, ShllReg) {
+  DriverFn(&shll_fn, "shll");
+}
+
+TEST_F(AssemblerX86_64Test, ShllImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::shll, 1U, "shll ${imm}, %{reg}"), "shlli");
+}
+
+// Shrl only allows CL as the shift register.
+std::string shrl_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) {
+  std::ostringstream str;
+
+  std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters();
+
+  x86_64::CpuRegister shifter(x86_64::RCX);
+  for (auto reg : registers) {
+    assembler->shrl(*reg, shifter);
+    str << "shrl %cl, %" << assembler_test->GetSecondaryRegisterName(*reg) << "\n";
+  }
+
+  return str.str();
+}
+
+TEST_F(AssemblerX86_64Test, ShrlReg) {
+  DriverFn(&shrl_fn, "shrl");
+}
+
+TEST_F(AssemblerX86_64Test, ShrlImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::shrl, 1U, "shrl ${imm}, %{reg}"), "shrli");
+}
+
+// Sarl only allows CL as the shift register.
+std::string sarl_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) {
+  std::ostringstream str;
+
+  std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters();
+
+  x86_64::CpuRegister shifter(x86_64::RCX);
+  for (auto reg : registers) {
+    assembler->sarl(*reg, shifter);
+    str << "sarl %cl, %" << assembler_test->GetSecondaryRegisterName(*reg) << "\n";
+  }
+
+  return str.str();
+}
+
+TEST_F(AssemblerX86_64Test, SarlReg) {
+  DriverFn(&sarl_fn, "sarl");
+}
+
+TEST_F(AssemblerX86_64Test, SarlImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::sarl, 1U, "sarl ${imm}, %{reg}"), "sarli");
+}
 
 TEST_F(AssemblerX86_64Test, CmpqRegs) {
   DriverStr(RepeatRR(&x86_64::X86_64Assembler::cmpq, "cmpq %{reg2}, %{reg1}"), "cmpq");
 }
 
+TEST_F(AssemblerX86_64Test, CmpqImm) {
+  DriverStr(RepeatRI(&x86_64::X86_64Assembler::cmpq, 4U  /* cmpq only supports 32b imm */,
+                     "cmpq ${imm}, %{reg}"), "cmpqi");
+}
+
+TEST_F(AssemblerX86_64Test, CmplRegs) {
+  DriverStr(Repeatrr(&x86_64::X86_64Assembler::cmpl, "cmp %{reg2}, %{reg1}"), "cmpl");
+}
+
+TEST_F(AssemblerX86_64Test, CmplImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::cmpl, 4U, "cmpl ${imm}, %{reg}"), "cmpli");
+}
+
+TEST_F(AssemblerX86_64Test, Testl) {
+  // Note: uses different order for GCC than usual. This makes GCC happy, and doesn't have an
+  // impact on functional correctness.
+  DriverStr(Repeatrr(&x86_64::X86_64Assembler::testl, "testl %{reg1}, %{reg2}"), "testl");
+}
+
+TEST_F(AssemblerX86_64Test, Negq) {
+  DriverStr(RepeatR(&x86_64::X86_64Assembler::negq, "negq %{reg}"), "negq");
+}
+
+TEST_F(AssemblerX86_64Test, Negl) {
+  DriverStr(Repeatr(&x86_64::X86_64Assembler::negl, "negl %{reg}"), "negl");
+}
+
+TEST_F(AssemblerX86_64Test, Notq) {
+  DriverStr(RepeatR(&x86_64::X86_64Assembler::notq, "notq %{reg}"), "notq");
+}
+
+TEST_F(AssemblerX86_64Test, Notl) {
+  DriverStr(Repeatr(&x86_64::X86_64Assembler::notl, "notl %{reg}"), "notl");
+}
+
+TEST_F(AssemblerX86_64Test, AndqRegs) {
+  DriverStr(RepeatRR(&x86_64::X86_64Assembler::andq, "andq %{reg2}, %{reg1}"), "andq");
+}
+
+TEST_F(AssemblerX86_64Test, AndqImm) {
+  DriverStr(RepeatRI(&x86_64::X86_64Assembler::andq, 4U  /* andq only supports 32b imm */,
+                     "andq ${imm}, %{reg}"), "andqi");
+}
+
+TEST_F(AssemblerX86_64Test, AndlRegs) {
+  DriverStr(Repeatrr(&x86_64::X86_64Assembler::andl, "andl %{reg2}, %{reg1}"), "andl");
+}
+
+TEST_F(AssemblerX86_64Test, AndlImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::andl, 4U, "andl ${imm}, %{reg}"), "andli");
+}
+
+TEST_F(AssemblerX86_64Test, OrqRegs) {
+  DriverStr(RepeatRR(&x86_64::X86_64Assembler::orq, "orq %{reg2}, %{reg1}"), "orq");
+}
+
+TEST_F(AssemblerX86_64Test, OrlRegs) {
+  DriverStr(Repeatrr(&x86_64::X86_64Assembler::orl, "orl %{reg2}, %{reg1}"), "orl");
+}
+
+TEST_F(AssemblerX86_64Test, OrlImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::orl, 4U, "orl ${imm}, %{reg}"), "orli");
+}
+
+TEST_F(AssemblerX86_64Test, XorqRegs) {
+  DriverStr(RepeatRR(&x86_64::X86_64Assembler::xorq, "xorq %{reg2}, %{reg1}"), "xorq");
+}
 
 TEST_F(AssemblerX86_64Test, XorqImm) {
   DriverStr(RepeatRI(&x86_64::X86_64Assembler::xorq, 4U, "xorq ${imm}, %{reg}"), "xorqi");
 }
 
-TEST_F(AssemblerX86_64Test, Movaps) {
-  GetAssembler()->movaps(x86_64::XmmRegister(x86_64::XMM0), x86_64::XmmRegister(x86_64::XMM8));
-  DriverStr("movaps %xmm8, %xmm0", "movaps");
-}
-
-TEST_F(AssemblerX86_64Test, Movd) {
-  GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM0), x86_64::CpuRegister(x86_64::R11));
-  GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM0), x86_64::CpuRegister(x86_64::RAX));
-  GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM8), x86_64::CpuRegister(x86_64::R11));
-  GetAssembler()->movd(x86_64::XmmRegister(x86_64::XMM8), x86_64::CpuRegister(x86_64::RAX));
-  GetAssembler()->movd(x86_64::CpuRegister(x86_64::R11), x86_64::XmmRegister(x86_64::XMM0));
-  GetAssembler()->movd(x86_64::CpuRegister(x86_64::RAX), x86_64::XmmRegister(x86_64::XMM0));
-  GetAssembler()->movd(x86_64::CpuRegister(x86_64::R11), x86_64::XmmRegister(x86_64::XMM8));
-  GetAssembler()->movd(x86_64::CpuRegister(x86_64::RAX), x86_64::XmmRegister(x86_64::XMM8));
-  const char* expected =
-    "movd %r11, %xmm0\n"
-    "movd %rax, %xmm0\n"
-    "movd %r11, %xmm8\n"
-    "movd %rax, %xmm8\n"
-    "movd %xmm0, %r11\n"
-    "movd %xmm0, %rax\n"
-    "movd %xmm8, %r11\n"
-    "movd %xmm8, %rax\n";
-  DriverStr(expected, "movd");
+TEST_F(AssemblerX86_64Test, XorlRegs) {
+  DriverStr(Repeatrr(&x86_64::X86_64Assembler::xorl, "xor %{reg2}, %{reg1}"), "xorl");
+}
+
+TEST_F(AssemblerX86_64Test, XorlImm) {
+  DriverStr(Repeatri(&x86_64::X86_64Assembler::xorl, 4U, "xor ${imm}, %{reg}"), "xorli");
+}
+
+TEST_F(AssemblerX86_64Test, Xchgq) {
+  DriverStr(RepeatRR(&x86_64::X86_64Assembler::xchgq, "xchgq %{reg2}, %{reg1}"), "xchgq");
+}
+
+TEST_F(AssemblerX86_64Test, Xchgl) {
+  // Test is disabled because GCC generates 0x87 0xC0 for xchgl eax, eax. All other cases are the
+  // same. Anyone know why it doesn't emit a simple 0x90? It does so for xchgq rax, rax...
+  // DriverStr(Repeatrr(&x86_64::X86_64Assembler::xchgl, "xchgl %{reg2}, %{reg1}"), "xchgl");
 }
 
 TEST_F(AssemblerX86_64Test, Movl) {
-  GetAssembler()->movl(x86_64::CpuRegister(x86_64::R8), x86_64::CpuRegister(x86_64::R11));
-  GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::CpuRegister(x86_64::R11));
   GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address(
       x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12));
   GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::Address(
@@ -170,8 +467,6 @@ TEST_F(AssemblerX86_64Test, Movl) {
   GetAssembler()->movl(x86_64::CpuRegister(x86_64::R8), x86_64::Address(
       x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12));
   const char* expected =
-    "movl %R11d, %R8d\n"
-    "movl %R11d, %EAX\n"
     "movl 0xc(%RDI,%RBX,4), %EAX\n"
     "movl 0xc(%RDI,%R9,4), %EAX\n"
     "movl 0xc(%RDI,%R9,4), %R8d\n";
@@ -186,17 +481,201 @@ TEST_F(AssemblerX86_64Test, Movw) {
   DriverStr(expected, "movw");
 }
 
-TEST_F(AssemblerX86_64Test, IMulImmediate) {
-  GetAssembler()->imull(x86_64::CpuRegister(x86_64::RAX), x86_64::Immediate(0x40000));
-  GetAssembler()->imull(x86_64::CpuRegister(x86_64::R8), x86_64::Immediate(0x40000));
-  const char* expected =
-    "imull $0x40000,%eax,%eax\n"
-    "imull $0x40000,%r8d,%r8d\n";
-  DriverStr(expected, "imul");
+TEST_F(AssemblerX86_64Test, Movsxd) {
+  DriverStr(RepeatRr(&x86_64::X86_64Assembler::movsxd, "movsxd %{reg2}, %{reg1}"), "movsxd");
+}
+
+///////////////////
+// FP Operations //
+///////////////////
+
+TEST_F(AssemblerX86_64Test, Movaps) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::movaps, "movaps %{reg2}, %{reg1}"), "movaps");
+}
+
+TEST_F(AssemblerX86_64Test, Movss) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::movss, "movss %{reg2}, %{reg1}"), "movss");
+}
+
+TEST_F(AssemblerX86_64Test, Movsd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::movsd, "movsd %{reg2}, %{reg1}"), "movsd");
+}
+
+TEST_F(AssemblerX86_64Test, Movd1) {
+  DriverStr(RepeatFR(&x86_64::X86_64Assembler::movd, "movd %{reg2}, %{reg1}"), "movd.1");
+}
+
+TEST_F(AssemblerX86_64Test, Movd2) {
+  DriverStr(RepeatRF(&x86_64::X86_64Assembler::movd, "movd %{reg2}, %{reg1}"), "movd.2");
+}
+
+TEST_F(AssemblerX86_64Test, Addss) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::addss, "addss %{reg2}, %{reg1}"), "addss");
+}
+
+TEST_F(AssemblerX86_64Test, Addsd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::addsd, "addsd %{reg2}, %{reg1}"), "addsd");
+}
+
+TEST_F(AssemblerX86_64Test, Subss) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::subss, "subss %{reg2}, %{reg1}"), "subss");
+}
+
+TEST_F(AssemblerX86_64Test, Subsd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::subsd, "subsd %{reg2}, %{reg1}"), "subsd");
+}
+
+TEST_F(AssemblerX86_64Test, Mulss) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::mulss, "mulss %{reg2}, %{reg1}"), "mulss");
+}
+
+TEST_F(AssemblerX86_64Test, Mulsd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::mulsd, "mulsd %{reg2}, %{reg1}"), "mulsd");
+}
+
+TEST_F(AssemblerX86_64Test, Divss) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::divss, "divss %{reg2}, %{reg1}"), "divss");
+}
+
+TEST_F(AssemblerX86_64Test, Divsd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::divsd, "divsd %{reg2}, %{reg1}"), "divsd");
+}
+
+TEST_F(AssemblerX86_64Test, Cvtsi2ss) {
+  DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss");
+}
+
+TEST_F(AssemblerX86_64Test, Cvtsi2sd) {
+  DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2sd, "cvtsi2sd %{reg2}, %{reg1}"), "cvtsi2sd");
+}
+
+
+TEST_F(AssemblerX86_64Test, Cvtss2si) {
+  DriverStr(RepeatrF(&x86_64::X86_64Assembler::cvtss2si, "cvtss2si %{reg2}, %{reg1}"), "cvtss2si");
+}
+
+
+TEST_F(AssemblerX86_64Test, Cvtss2sd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::cvtss2sd, "cvtss2sd %{reg2}, %{reg1}"), "cvtss2sd");
+}
+
+
+TEST_F(AssemblerX86_64Test, Cvtsd2si) {
+  DriverStr(RepeatrF(&x86_64::X86_64Assembler::cvtsd2si, "cvtsd2si %{reg2}, %{reg1}"), "cvtsd2si");
+}
+
+TEST_F(AssemblerX86_64Test, Cvttss2si) {
+  DriverStr(RepeatrF(&x86_64::X86_64Assembler::cvttss2si, "cvttss2si %{reg2}, %{reg1}"),
+            "cvttss2si");
+}
+
+TEST_F(AssemblerX86_64Test, Cvttsd2si) {
+  DriverStr(RepeatrF(&x86_64::X86_64Assembler::cvttsd2si, "cvttsd2si %{reg2}, %{reg1}"),
+            "cvttsd2si");
+}
+
+TEST_F(AssemblerX86_64Test, Cvtsd2ss) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::cvtsd2ss, "cvtsd2ss %{reg2}, %{reg1}"), "cvtsd2ss");
+}
+
+TEST_F(AssemblerX86_64Test, Cvtdq2pd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::cvtdq2pd, "cvtdq2pd %{reg2}, %{reg1}"), "cvtdq2pd");
+}
+
+TEST_F(AssemblerX86_64Test, Comiss) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::comiss, "comiss %{reg2}, %{reg1}"), "comiss");
+}
+
+TEST_F(AssemblerX86_64Test, Comisd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::comisd, "comisd %{reg2}, %{reg1}"), "comisd");
+}
+
+TEST_F(AssemblerX86_64Test, Sqrtss) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::sqrtss, "sqrtss %{reg2}, %{reg1}"), "sqrtss");
+}
+
+TEST_F(AssemblerX86_64Test, Sqrtsd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::sqrtsd, "sqrtsd %{reg2}, %{reg1}"), "sqrtsd");
+}
+
+TEST_F(AssemblerX86_64Test, Xorps) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::xorps, "xorps %{reg2}, %{reg1}"), "xorps");
+}
+
+TEST_F(AssemblerX86_64Test, Xorpd) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd");
+}
+
+// X87
+
+std::string x87_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED,
+                   x86_64::X86_64Assembler* assembler) {
+  std::ostringstream str;
+
+  assembler->fincstp();
+  str << "fincstp\n";
+
+  assembler->fsin();
+  str << "fsin\n";
+
+  assembler->fcos();
+  str << "fcos\n";
+
+  assembler->fptan();
+  str << "fptan\n";
+
+  return str.str();
+}
+
+TEST_F(AssemblerX86_64Test, X87) {
+  DriverFn(&x87_fn, "x87");
+}
+
+////////////////
+// CALL / JMP //
+////////////////
+
+TEST_F(AssemblerX86_64Test, Call) {
+  DriverStr(RepeatR(&x86_64::X86_64Assembler::call, "call *%{reg}"), "call");
+}
+
+TEST_F(AssemblerX86_64Test, Jmp) {
+  DriverStr(RepeatR(&x86_64::X86_64Assembler::jmp, "jmp *%{reg}"), "jmp");
+}
+
+TEST_F(AssemblerX86_64Test, Enter) {
+  DriverStr(RepeatI(&x86_64::X86_64Assembler::enter, 2U  /* 16b immediate */, "enter ${imm}, $0",
+                    true  /* Only non-negative number */), "enter");
+}
+
+TEST_F(AssemblerX86_64Test, RetImm) {
+  DriverStr(RepeatI(&x86_64::X86_64Assembler::ret, 2U  /* 16b immediate */, "ret ${imm}",
+                    true  /* Only non-negative number */), "reti");
+}
+
+std::string ret_and_leave_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED,
+                             x86_64::X86_64Assembler* assembler) {
+  std::ostringstream str;
+
+  assembler->ret();
+  str << "ret\n";
+
+  assembler->leave();
+  str << "leave\n";
+
+  return str.str();
+}
+
+TEST_F(AssemblerX86_64Test, RetAndLeave) {
+  DriverFn(&ret_and_leave_fn, "retleave");
 }
 
+//////////
+// MISC //
+//////////
 
-std::string setcc_test_fn(x86_64::X86_64Assembler* assembler) {
+std::string setcc_test_fn(AssemblerX86_64Test::Base* assembler_test,
+                          x86_64::X86_64Assembler* assembler) {
   // From Condition
   /*
   kOverflow     =  0,
@@ -218,23 +697,7 @@ std::string setcc_test_fn(x86_64::X86_64Assembler* assembler) {
   std::string suffixes[15] = { "o", "no", "b", "ae", "e", "ne", "be", "a", "s", "ns", "pe", "po",
                                "l", "ge", "le" };
 
-  std::vector<x86_64::CpuRegister*> registers;
-  registers.push_back(new x86_64::CpuRegister(x86_64::RAX));
-  registers.push_back(new x86_64::CpuRegister(x86_64::RBX));
-  registers.push_back(new x86_64::CpuRegister(x86_64::RCX));
-  registers.push_back(new x86_64::CpuRegister(x86_64::RDX));
-  registers.push_back(new x86_64::CpuRegister(x86_64::RBP));
-  registers.push_back(new x86_64::CpuRegister(x86_64::RSP));
-  registers.push_back(new x86_64::CpuRegister(x86_64::RSI));
-  registers.push_back(new x86_64::CpuRegister(x86_64::RDI));
-  registers.push_back(new x86_64::CpuRegister(x86_64::R8));
-  registers.push_back(new x86_64::CpuRegister(x86_64::R9));
-  registers.push_back(new x86_64::CpuRegister(x86_64::R10));
-  registers.push_back(new x86_64::CpuRegister(x86_64::R11));
-  registers.push_back(new x86_64::CpuRegister(x86_64::R12));
-  registers.push_back(new x86_64::CpuRegister(x86_64::R13));
-  registers.push_back(new x86_64::CpuRegister(x86_64::R14));
-  registers.push_back(new x86_64::CpuRegister(x86_64::R15));
+  std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters();
 
   std::string byte_regs[16];
   byte_regs[x86_64::RAX] = "al";
@@ -263,7 +726,6 @@ std::string setcc_test_fn(x86_64::X86_64Assembler* assembler) {
     }
   }
 
-  STLDeleteElements(&registers);
   return str.str();
 }
 
@@ -279,7 +741,8 @@ static x86_64::X86_64ManagedRegister ManagedFromFpu(x86_64::FloatRegister r) {
   return x86_64::X86_64ManagedRegister::FromXmmRegister(r);
 }
 
-std::string buildframe_test_fn(x86_64::X86_64Assembler* assembler) {
+std::string buildframe_test_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED,
+                               x86_64::X86_64Assembler* assembler) {
   // TODO: more interesting spill registers / entry spills.
 
   // Two random spill regs.
@@ -323,7 +786,8 @@ TEST_F(AssemblerX86_64Test, BuildFrame) {
   DriverFn(&buildframe_test_fn, "BuildFrame");
 }
 
-std::string removeframe_test_fn(x86_64::X86_64Assembler* assembler) {
+std::string removeframe_test_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED,
+                                x86_64::X86_64Assembler* assembler) {
   // TODO: more interesting spill registers / entry spills.
 
   // Two random spill regs.
@@ -351,7 +815,8 @@ TEST_F(AssemblerX86_64Test, RemoveFrame) {
   DriverFn(&removeframe_test_fn, "RemoveFrame");
 }
 
-std::string increaseframe_test_fn(x86_64::X86_64Assembler* assembler) {
+std::string increaseframe_test_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED,
+                                  x86_64::X86_64Assembler* assembler) {
   assembler->IncreaseFrameSize(0U);
   assembler->IncreaseFrameSize(kStackAlignment);
   assembler->IncreaseFrameSize(10 * kStackAlignment);
@@ -369,7 +834,8 @@ TEST_F(AssemblerX86_64Test, IncreaseFrame) {
   DriverFn(&increaseframe_test_fn, "IncreaseFrame");
 }
 
-std::string decreaseframe_test_fn(x86_64::X86_64Assembler* assembler) {
+std::string decreaseframe_test_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED,
+                                  x86_64::X86_64Assembler* assembler) {
   assembler->DecreaseFrameSize(0U);
   assembler->DecreaseFrameSize(kStackAlignment);
   assembler->DecreaseFrameSize(10 * kStackAlignment);
index 669fe6c..a0082c4 100644 (file)
@@ -115,6 +115,20 @@ static inline bool IsInt(int N, intptr_t value) {
   return (-limit <= value) && (value < limit);
 }
 
+static inline bool IsInt32(int N, int32_t value) {
+  CHECK_LT(0, N);
+  CHECK_LT(static_cast<size_t>(N), 8 * sizeof(int32_t));
+  int32_t limit = static_cast<int32_t>(1) << (N - 1);
+  return (-limit <= value) && (value < limit);
+}
+
+static inline bool IsInt64(int N, int64_t value) {
+  CHECK_LT(0, N);
+  CHECK_LT(static_cast<size_t>(N), 8 * sizeof(int64_t));
+  int64_t limit = static_cast<int64_t>(1) << (N - 1);
+  return (-limit <= value) && (value < limit);
+}
+
 static inline bool IsUint(int N, intptr_t value) {
   CHECK_LT(0, N);
   CHECK_LT(N, kBitsPerIntPtrT);