diff options
| -rw-r--r-- | compiler/utils/arm/assembler_arm32_test.cc | 2 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_thumb2_test.cc | 4 | ||||
| -rw-r--r-- | compiler/utils/assembler_test.h | 376 | ||||
| -rw-r--r-- | compiler/utils/x86_64/assembler_x86_64.cc | 84 | ||||
| -rw-r--r-- | compiler/utils/x86_64/assembler_x86_64.h | 12 | ||||
| -rw-r--r-- | compiler/utils/x86_64/assembler_x86_64_test.cc | 586 | ||||
| -rw-r--r-- | runtime/utils.h | 14 |
7 files changed, 891 insertions, 187 deletions
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc index 3ba77b5e74..4f5d4c36a1 100644 --- a/compiler/utils/arm/assembler_arm32_test.cc +++ b/compiler/utils/arm/assembler_arm32_test.cc @@ -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 { diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc index 3d9c70d734..57ba0ca5ee 100644 --- a/compiler/utils/arm/assembler_thumb2_test.cc +++ b/compiler/utils/arm/assembler_thumb2_test.cc @@ -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"; diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index 0378176afd..9d3fa01a9d 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -29,21 +29,31 @@ 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 diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index bd08b1ff2a..2bb2ed8c9c 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -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)); } diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index b46f6f71e3..4dd70e27d7 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -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); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 0e8ea5b3ee..af389e69ad 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -16,8 +16,13 @@ #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(®isters_); + 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(®isters); 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); diff --git a/runtime/utils.h b/runtime/utils.h index 669fe6cd06..a0082c462a 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -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); |