Chromium Code Reviews| Index: unittest/IceAssemblerX8632Test.cpp |
| diff --git a/unittest/IceAssemblerX8632Test.cpp b/unittest/IceAssemblerX8632Test.cpp |
| index 3e3d7f3da24101986dab67a426fe6448bec32b7a..1184c4e48bf31269e05a0cf71c0faab181daadc1 100644 |
| --- a/unittest/IceAssemblerX8632Test.cpp |
| +++ b/unittest/IceAssemblerX8632Test.cpp |
| @@ -13,9 +13,11 @@ |
| #include "gtest/gtest.h" |
| +#include <algorithm> |
| #include <cstring> |
| #include <errno.h> |
| #include <iostream> |
| +#include <limits> |
| #include <memory> |
| #include <sys/mman.h> |
| #include <type_traits> |
| @@ -23,12 +25,13 @@ |
| namespace Ice { |
| namespace X8632 { |
| namespace { |
| - |
| class AssemblerX8632TestBase : public ::testing::Test { |
| protected: |
| using Address = AssemblerX8632::Traits::Address; |
| + using ByteRegister = AssemblerX8632::Traits::ByteRegister; |
| using Cond = AssemblerX8632::Traits::Cond; |
| using GPRRegister = AssemblerX8632::Traits::GPRRegister; |
| + using Traits = AssemblerX8632::Traits; |
| using XmmRegister = AssemblerX8632::Traits::XmmRegister; |
| using X87STRegister = AssemblerX8632::Traits::X87STRegister; |
| @@ -66,17 +69,17 @@ protected: |
| // verifyBytes is a template helper that takes a Buffer, and a variable number |
| // of bytes. As the name indicates, it is used to verify the bytes for an |
| // instruction encoding. |
| - template <int N, int I> static void verifyBytes(const uint8_t *) { |
| + template <int N, int I> static bool verifyBytes(const uint8_t *) { |
| static_assert(I == N, "Invalid template instantiation."); |
| + return true; |
| } |
| template <int N, int I = 0, typename... Args> |
| - static void verifyBytes(const uint8_t *Buffer, uint8_t Byte, |
| + static bool verifyBytes(const uint8_t *Buffer, uint8_t Byte, |
| Args... OtherBytes) { |
| static_assert(I < N, "Invalid template instantiation."); |
| EXPECT_EQ(Byte, Buffer[I]) << "Byte " << (I + 1) << " of " << N; |
| - verifyBytes<N, I + 1>(Buffer, OtherBytes...); |
| - assert(Buffer[I] == Byte); |
| + return verifyBytes<N, I + 1>(Buffer, OtherBytes...) && Buffer[I] == Byte; |
| } |
| }; |
| @@ -89,6 +92,15 @@ TEST_F(AssemblerX8632LowLevelTest, Ret) { |
| verifyBytes<ByteCount>(codeBytes(), 0xc3); |
|
jvoung (off chromium)
2015/07/23 17:59:36
Now that verifyBytes returns a bool, does this nee
John
2015/07/27 20:35:58
See the comment below.
|
| } |
| +TEST_F(AssemblerX8632LowLevelTest, RetImm) { |
| + __ ret(Immediate(0x20)); |
| + |
| + constexpr size_t ByteCount = 3; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + |
| + verifyBytes<ByteCount>(codeBytes(), 0xC2, 0x20, 0x00); |
| +} |
| + |
| TEST_F(AssemblerX8632LowLevelTest, CallImm4) { |
| __ call(Immediate(4)); |
| @@ -164,39 +176,650 @@ TEST_F(AssemblerX8632LowLevelTest, MovRegisterZero) { |
| MovOpcode | GPRRegister::Encoded_Reg_esi, 0x00, 0x00, 0x00, 0x00); |
| } |
| -TEST_F(AssemblerX8632LowLevelTest, CmpRegReg) { |
| - __ cmp(IceType_i32, GPRRegister::Encoded_Reg_eax, |
| - GPRRegister::Encoded_Reg_ebx); |
| - __ cmp(IceType_i32, GPRRegister::Encoded_Reg_ebx, |
| - GPRRegister::Encoded_Reg_ecx); |
| - __ cmp(IceType_i32, GPRRegister::Encoded_Reg_ecx, |
| - GPRRegister::Encoded_Reg_edx); |
| - __ cmp(IceType_i32, GPRRegister::Encoded_Reg_edx, |
| - GPRRegister::Encoded_Reg_edi); |
| - __ cmp(IceType_i32, GPRRegister::Encoded_Reg_edi, |
| - GPRRegister::Encoded_Reg_esi); |
| - __ cmp(IceType_i32, GPRRegister::Encoded_Reg_esi, |
| - GPRRegister::Encoded_Reg_eax); |
| +TEST_F(AssemblerX8632LowLevelTest, Cmp) { |
| +#define TestRegReg(Inst, Dst, Src, OpType, ByteCountUntyped, ...) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Src ", " #OpType ", " #ByteCountUntyped \ |
| + ", " #__VA_ARGS__ ")"; \ |
| + static constexpr uint8_t ByteCount = ByteCountUntyped; \ |
| + __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \ |
| + ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestRegImm(Inst, Dst, Imm, OpType, ByteCountUntyped, ...) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Imm ", " #OpType ", " #ByteCountUntyped \ |
| + ", " #__VA_ARGS__ ")"; \ |
| + static constexpr uint8_t ByteCount = ByteCountUntyped; \ |
| + __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, Immediate(Imm)); \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \ |
| + ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestRegAbsoluteAddr(Inst, Dst, Disp, OpType, ByteCountUntyped, ...) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Disp ", " #OpType ", " #ByteCountUntyped \ |
| + ", " #__VA_ARGS__ ")"; \ |
| + static constexpr uint8_t ByteCount = ByteCountUntyped; \ |
| + __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, \ |
| + Address(Address::ABSOLUTE, Disp)); \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \ |
| + ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestRegAddrBase(Inst, Dst, Base, Disp, OpType, ByteCountUntyped, ...) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Base ", " #Disp ", " #OpType \ |
| + ", " #ByteCountUntyped ", " #__VA_ARGS__ ")"; \ |
| + static constexpr uint8_t ByteCount = ByteCountUntyped; \ |
| + __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, \ |
| + Address(GPRRegister::Encoded_Reg_##Base, Disp)); \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \ |
| + ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestRegAddrScaledIndex(Inst, Dst, Index, Scale, Disp, OpType, \ |
| + ByteCountUntyped, ...) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Index ", " #Scale ", " #Disp ", " #OpType \ |
| + ", " #ByteCountUntyped ", " #__VA_ARGS__ ")"; \ |
| + static constexpr uint8_t ByteCount = ByteCountUntyped; \ |
| + __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, \ |
| + Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_##Scale, \ |
| + Disp)); \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \ |
| + ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestRegAddrBaseScaledIndex(Inst, Dst, Base, Index, Scale, Disp, \ |
| + OpType, ByteCountUntyped, ...) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Base ", " #Index ", " #Scale ", " #Disp \ |
| + ", " #OpType ", " #ByteCountUntyped ", " #__VA_ARGS__ ")"; \ |
| + static constexpr uint8_t ByteCount = ByteCountUntyped; \ |
| + __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, \ |
| + Address(GPRRegister::Encoded_Reg_##Base, \ |
| + GPRRegister::Encoded_Reg_##Index, Traits::TIMES_##Scale, \ |
| + Disp)); \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \ |
| + ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestAddrBaseScaledIndexImm(Inst, Base, Index, Scale, Disp, Imm, \ |
| + OpType, ByteCountUntyped, ...) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Base ", " #Index ", " #Scale ", " #Disp ", " #Imm \ |
| + ", " #OpType ", " #ByteCountUntyped ", " #__VA_ARGS__ ")"; \ |
| + static constexpr uint8_t ByteCount = ByteCountUntyped; \ |
| + __ Inst(IceType_##OpType, Address(GPRRegister::Encoded_Reg_##Base, \ |
| + GPRRegister::Encoded_Reg_##Index, \ |
| + Traits::TIMES_##Scale, Disp), \ |
| + Immediate(Imm)); \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \ |
| + ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestAddrBaseScaledIndexReg(Inst, Base, Index, Scale, Disp, Src, \ |
| + OpType, ByteCountUntyped, ...) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Base ", " #Index ", " #Scale ", " #Disp ", " #Src \ |
| + ", " #OpType ", " #ByteCountUntyped ", " #__VA_ARGS__ ")"; \ |
| + static constexpr uint8_t ByteCount = ByteCountUntyped; \ |
| + __ Inst(IceType_##OpType, Address(GPRRegister::Encoded_Reg_##Base, \ |
| + GPRRegister::Encoded_Reg_##Index, \ |
| + Traits::TIMES_##Scale, Disp), \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \ |
| + ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + /* cmp GPR, GPR */ |
| + TestRegReg(cmp, eax, ecx, i32, 2, 0x3B, 0xC1); |
| + TestRegReg(cmp, ecx, edx, i32, 2, 0x3B, 0xCA); |
| + TestRegReg(cmp, edx, ebx, i32, 2, 0x3B, 0xD3); |
| + TestRegReg(cmp, ebx, esp, i32, 2, 0x3B, 0xDC); |
| + TestRegReg(cmp, esp, ebp, i32, 2, 0x3B, 0xE5); |
| + TestRegReg(cmp, ebp, esi, i32, 2, 0x3B, 0xEE); |
| + TestRegReg(cmp, esi, edi, i32, 2, 0x3B, 0xF7); |
| + TestRegReg(cmp, edi, eax, i32, 2, 0x3B, 0xF8); |
| + |
| + TestRegReg(cmp, eax, ecx, i16, 3, 0x66, 0x3B, 0xC1); |
| + TestRegReg(cmp, ecx, edx, i16, 3, 0x66, 0x3B, 0xCA); |
| + TestRegReg(cmp, edx, ebx, i16, 3, 0x66, 0x3B, 0xD3); |
| + TestRegReg(cmp, ebx, esp, i16, 3, 0x66, 0x3B, 0xDC); |
| + TestRegReg(cmp, esp, ebp, i16, 3, 0x66, 0x3B, 0xE5); |
| + TestRegReg(cmp, ebp, esi, i16, 3, 0x66, 0x3B, 0xEE); |
| + TestRegReg(cmp, esi, edi, i16, 3, 0x66, 0x3B, 0xF7); |
| + TestRegReg(cmp, edi, eax, i16, 3, 0x66, 0x3B, 0xF8); |
| + |
| + TestRegReg(cmp, eax, ecx, i8, 2, 0x3A, 0xC1); |
| + TestRegReg(cmp, ecx, edx, i8, 2, 0x3A, 0xCA); |
| + TestRegReg(cmp, edx, ebx, i8, 2, 0x3A, 0xD3); |
| + TestRegReg(cmp, ebx, esp, i8, 2, 0x3A, 0xDC); |
| + TestRegReg(cmp, esp, ebp, i8, 2, 0x3A, 0xE5); |
| + TestRegReg(cmp, ebp, esi, i8, 2, 0x3A, 0xEE); |
| + TestRegReg(cmp, esi, edi, i8, 2, 0x3A, 0xF7); |
| + TestRegReg(cmp, edi, eax, i8, 2, 0x3A, 0xF8); |
| + |
| + /* cmp GPR, Imm8 */ |
| + TestRegImm(cmp, eax, 5, i32, 3, 0x83, 0xF8, 0x05); |
| + TestRegImm(cmp, ecx, 5, i32, 3, 0x83, 0xF9, 0x05); |
| + TestRegImm(cmp, edx, 5, i32, 3, 0x83, 0xFA, 0x05); |
| + TestRegImm(cmp, ebx, 5, i32, 3, 0x83, 0xFB, 0x05); |
| + TestRegImm(cmp, esp, 5, i32, 3, 0x83, 0xFC, 0x05); |
| + TestRegImm(cmp, ebp, 5, i32, 3, 0x83, 0xFD, 0x05); |
| + TestRegImm(cmp, esi, 5, i32, 3, 0x83, 0xFE, 0x05); |
| + TestRegImm(cmp, edi, 5, i32, 3, 0x83, 0xFF, 0x05); |
| + |
| + TestRegImm(cmp, eax, 5, i16, 4, 0x66, 0x83, 0xF8, 0x05); |
| + TestRegImm(cmp, ecx, 5, i16, 4, 0x66, 0x83, 0xF9, 0x05); |
| + TestRegImm(cmp, edx, 5, i16, 4, 0x66, 0x83, 0xFA, 0x05); |
| + TestRegImm(cmp, ebx, 5, i16, 4, 0x66, 0x83, 0xFB, 0x05); |
| + TestRegImm(cmp, esp, 5, i16, 4, 0x66, 0x83, 0xFC, 0x05); |
| + TestRegImm(cmp, ebp, 5, i16, 4, 0x66, 0x83, 0xFD, 0x05); |
| + TestRegImm(cmp, esi, 5, i16, 4, 0x66, 0x83, 0xFE, 0x05); |
| + TestRegImm(cmp, edi, 5, i16, 4, 0x66, 0x83, 0xFF, 0x05); |
| + |
| + TestRegImm(cmp, eax, 5, i8, 2, 0x3C, 0x05); |
| + TestRegImm(cmp, ecx, 5, i8, 3, 0x80, 0xF9, 0x05); |
| + TestRegImm(cmp, edx, 5, i8, 3, 0x80, 0xFA, 0x05); |
| + TestRegImm(cmp, ebx, 5, i8, 3, 0x80, 0xFB, 0x05); |
| + TestRegImm(cmp, esp, 5, i8, 3, 0x80, 0xFC, 0x05); |
| + TestRegImm(cmp, ebp, 5, i8, 3, 0x80, 0xFD, 0x05); |
| + TestRegImm(cmp, esi, 5, i8, 3, 0x80, 0xFE, 0x05); |
| + TestRegImm(cmp, edi, 5, i8, 3, 0x80, 0xFF, 0x05); |
| + |
| + /* cmp GPR, Imm16 */ |
| + TestRegImm(cmp, eax, 0x100, i32, 5, 0x3D, 0x00, 0x01, 0x00, 0x00); |
| + TestRegImm(cmp, ecx, 0x100, i32, 6, 0x81, 0xF9, 0x00, 0x01, 0x00, 0x00); |
| + TestRegImm(cmp, edx, 0x100, i32, 6, 0x81, 0xFA, 0x00, 0x01, 0x00, 0x00); |
| + TestRegImm(cmp, ebx, 0x100, i32, 6, 0x81, 0xFB, 0x00, 0x01, 0x00, 0x00); |
| + TestRegImm(cmp, esp, 0x100, i32, 6, 0x81, 0xFC, 0x00, 0x01, 0x00, 0x00); |
| + TestRegImm(cmp, ebp, 0x100, i32, 6, 0x81, 0xFD, 0x00, 0x01, 0x00, 0x00); |
| + TestRegImm(cmp, esi, 0x100, i32, 6, 0x81, 0xFE, 0x00, 0x01, 0x00, 0x00); |
| + TestRegImm(cmp, edi, 0x100, i32, 6, 0x81, 0xFF, 0x00, 0x01, 0x00, 0x00); |
| + |
| + TestRegImm(cmp, eax, 0x100, i16, 4, 0x66, 0x3D, 0x00, 0x01); |
| + TestRegImm(cmp, ecx, 0x100, i16, 5, 0x66, 0x81, 0xF9, 0x00, 0x01); |
| + TestRegImm(cmp, edx, 0x100, i16, 5, 0x66, 0x81, 0xFA, 0x00, 0x01); |
| + TestRegImm(cmp, ebx, 0x100, i16, 5, 0x66, 0x81, 0xFB, 0x00, 0x01); |
| + TestRegImm(cmp, esp, 0x100, i16, 5, 0x66, 0x81, 0xFC, 0x00, 0x01); |
| + TestRegImm(cmp, ebp, 0x100, i16, 5, 0x66, 0x81, 0xFD, 0x00, 0x01); |
| + TestRegImm(cmp, esi, 0x100, i16, 5, 0x66, 0x81, 0xFE, 0x00, 0x01); |
| + TestRegImm(cmp, edi, 0x100, i16, 5, 0x66, 0x81, 0xFF, 0x00, 0x01); |
| + |
| + /* cmp GPR, Absolute */ |
| + TestRegAbsoluteAddr(cmp, eax, 0xF00FBEEF, i32, 6, 0x3B, 0x05, 0xEF, 0xBE, |
| + 0x0F, 0xF0); |
| + TestRegAbsoluteAddr(cmp, eax, 0xF00FBEEF, i16, 7, 0x66, 0x3B, 0x05, 0xEF, |
| + 0xBE, 0x0F, 0xF0); |
| + TestRegAbsoluteAddr(cmp, eax, 0xF00FBEEF, i8, 6, 0x3A, 0x05, 0xEF, 0xBE, 0x0F, |
| + 0xF0); |
| + |
| + /* cmp GPR, 0(Base) */ |
| + TestRegAddrBase(cmp, eax, ecx, 0, i32, 2, 0x3B, 0x01); |
| + TestRegAddrBase(cmp, ecx, edx, 0, i32, 2, 0x3B, 0x0A); |
| + TestRegAddrBase(cmp, edx, ebx, 0, i32, 2, 0x3B, 0x13); |
| + TestRegAddrBase(cmp, ebx, esp, 0, i32, 3, 0x3B, 0x1C, 0x24); |
| + TestRegAddrBase(cmp, esp, ebp, 0, i32, 3, 0x3B, 0x65, 0x00); |
| + TestRegAddrBase(cmp, ebp, esi, 0, i32, 2, 0x3B, 0x2E); |
| + TestRegAddrBase(cmp, esi, edi, 0, i32, 2, 0x3B, 0x37); |
| + TestRegAddrBase(cmp, edi, eax, 0, i32, 2, 0x3B, 0x38); |
| + |
| + TestRegAddrBase(cmp, eax, ecx, 0, i16, 3, 0x66, 0x3B, 0x01); |
| + TestRegAddrBase(cmp, ecx, edx, 0, i16, 3, 0x66, 0x3B, 0x0A); |
| + TestRegAddrBase(cmp, edx, ebx, 0, i16, 3, 0x66, 0x3B, 0x13); |
| + TestRegAddrBase(cmp, ebx, esp, 0, i16, 4, 0x66, 0x3B, 0x1C, 0x24); |
| + TestRegAddrBase(cmp, esp, ebp, 0, i16, 4, 0x66, 0x3B, 0x65, 0x00); |
| + TestRegAddrBase(cmp, ebp, esi, 0, i16, 3, 0x66, 0x3B, 0x2E); |
| + TestRegAddrBase(cmp, esi, edi, 0, i16, 3, 0x66, 0x3B, 0x37); |
| + TestRegAddrBase(cmp, edi, eax, 0, i16, 3, 0x66, 0x3B, 0x38); |
| + |
| + TestRegAddrBase(cmp, eax, ecx, 0, i8, 2, 0x3A, 0x01); |
| + TestRegAddrBase(cmp, ecx, edx, 0, i8, 2, 0x3A, 0x0A); |
| + TestRegAddrBase(cmp, edx, ebx, 0, i8, 2, 0x3A, 0x13); |
| + TestRegAddrBase(cmp, ebx, esp, 0, i8, 3, 0x3A, 0x1C, 0x24); |
| + TestRegAddrBase(cmp, esp, ebp, 0, i8, 3, 0x3A, 0x65, 0x00); |
| + TestRegAddrBase(cmp, ebp, esi, 0, i8, 2, 0x3A, 0x2E); |
| + TestRegAddrBase(cmp, esi, edi, 0, i8, 2, 0x3A, 0x37); |
| + TestRegAddrBase(cmp, edi, eax, 0, i8, 2, 0x3A, 0x38); |
| + |
| + /* cmp GPR, Imm8(Base) */ |
| + TestRegAddrBase(cmp, eax, ecx, 0x40, i32, 3, 0x3B, 0x41, 0x40); |
| + TestRegAddrBase(cmp, ecx, edx, 0x40, i32, 3, 0x3B, 0x4A, 0x40); |
| + TestRegAddrBase(cmp, edx, ebx, 0x40, i32, 3, 0x3B, 0x53, 0x40); |
| + TestRegAddrBase(cmp, ebx, esp, 0x40, i32, 4, 0x3B, 0x5C, 0x24, 0x40); |
| + TestRegAddrBase(cmp, esp, ebp, 0x40, i32, 3, 0x3B, 0x65, 0x40); |
| + TestRegAddrBase(cmp, ebp, esi, 0x40, i32, 3, 0x3B, 0x6E, 0x40); |
| + TestRegAddrBase(cmp, esi, edi, 0x40, i32, 3, 0x3B, 0x77, 0x40); |
| + TestRegAddrBase(cmp, edi, eax, 0x40, i32, 3, 0x3B, 0x78, 0x40); |
| + |
| + TestRegAddrBase(cmp, eax, ecx, 0x40, i16, 4, 0x66, 0x3B, 0x41, 0x40); |
| + TestRegAddrBase(cmp, ecx, edx, 0x40, i16, 4, 0x66, 0x3B, 0x4A, 0x40); |
| + TestRegAddrBase(cmp, edx, ebx, 0x40, i16, 4, 0x66, 0x3B, 0x53, 0x40); |
| + TestRegAddrBase(cmp, ebx, esp, 0x40, i16, 5, 0x66, 0x3B, 0x5C, 0x24, 0x40); |
| + TestRegAddrBase(cmp, esp, ebp, 0x40, i16, 4, 0x66, 0x3B, 0x65, 0x40); |
| + TestRegAddrBase(cmp, ebp, esi, 0x40, i16, 4, 0x66, 0x3B, 0x6E, 0x40); |
| + TestRegAddrBase(cmp, esi, edi, 0x40, i16, 4, 0x66, 0x3B, 0x77, 0x40); |
| + TestRegAddrBase(cmp, edi, eax, 0x40, i16, 4, 0x66, 0x3B, 0x78, 0x40); |
| + |
| + TestRegAddrBase(cmp, eax, ecx, 0x40, i8, 3, 0x3A, 0x41, 0x40); |
| + TestRegAddrBase(cmp, ecx, edx, 0x40, i8, 3, 0x3A, 0x4A, 0x40); |
| + TestRegAddrBase(cmp, edx, ebx, 0x40, i8, 3, 0x3A, 0x53, 0x40); |
| + TestRegAddrBase(cmp, ebx, esp, 0x40, i8, 4, 0x3A, 0x5C, 0x24, 0x40); |
| + TestRegAddrBase(cmp, esp, ebp, 0x40, i8, 3, 0x3A, 0x65, 0x40); |
| + TestRegAddrBase(cmp, ebp, esi, 0x40, i8, 3, 0x3A, 0x6E, 0x40); |
| + TestRegAddrBase(cmp, esi, edi, 0x40, i8, 3, 0x3A, 0x77, 0x40); |
| + TestRegAddrBase(cmp, edi, eax, 0x40, i8, 3, 0x3A, 0x78, 0x40); |
| + |
| + /* cmp GPR, Imm32(Base) */ |
| + TestRegAddrBase(cmp, eax, ecx, 0xF0, i32, 6, 0x3B, 0x81, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, ecx, edx, 0xF0, i32, 6, 0x3B, 0x8A, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, edx, ebx, 0xF0, i32, 6, 0x3B, 0x93, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, ebx, esp, 0xF0, i32, 7, 0x3B, 0x9C, 0x24, 0xF0, 0x00, |
| + 0x00, 0x00); |
| + TestRegAddrBase(cmp, esp, ebp, 0xF0, i32, 6, 0x3B, 0xA5, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, ebp, esi, 0xF0, i32, 6, 0x3B, 0xAE, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, esi, edi, 0xF0, i32, 6, 0x3B, 0xB7, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, edi, eax, 0xF0, i32, 6, 0x3B, 0xB8, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + |
| + TestRegAddrBase(cmp, eax, ecx, 0xF0, i16, 7, 0x66, 0x3B, 0x81, 0xF0, 0x00, |
| + 0x00, 0x00); |
| + TestRegAddrBase(cmp, ecx, edx, 0xF0, i16, 7, 0x66, 0x3B, 0x8A, 0xF0, 0x00, |
| + 0x00, 0x00); |
| + TestRegAddrBase(cmp, edx, ebx, 0xF0, i16, 7, 0x66, 0x3B, 0x93, 0xF0, 0x00, |
| + 0x00, 0x00); |
| + TestRegAddrBase(cmp, ebx, esp, 0xF0, i16, 8, 0x66, 0x3B, 0x9C, 0x24, 0xF0, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrBase(cmp, esp, ebp, 0xF0, i16, 7, 0x66, 0x3B, 0xa5, 0xF0, 0x00, |
| + 0x00, 0x00); |
| + TestRegAddrBase(cmp, ebp, esi, 0xF0, i16, 7, 0x66, 0x3B, 0xaE, 0xF0, 0x00, |
| + 0x00, 0x00); |
| + TestRegAddrBase(cmp, esi, edi, 0xF0, i16, 7, 0x66, 0x3B, 0xb7, 0xF0, 0x00, |
| + 0x00, 0x00); |
| + TestRegAddrBase(cmp, edi, eax, 0xF0, i16, 7, 0x66, 0x3B, 0xb8, 0xF0, 0x00, |
| + 0x00, 0x00); |
| + |
| + TestRegAddrBase(cmp, eax, ecx, 0xF0, i8, 6, 0x3A, 0x81, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, ecx, edx, 0xF0, i8, 6, 0x3A, 0x8A, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, edx, ebx, 0xF0, i8, 6, 0x3A, 0x93, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, ebx, esp, 0xF0, i8, 7, 0x3A, 0x9C, 0x24, 0xF0, 0x00, |
| + 0x00, 0x00); |
| + TestRegAddrBase(cmp, esp, ebp, 0xF0, i8, 6, 0x3A, 0xA5, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, ebp, esi, 0xF0, i8, 6, 0x3A, 0xAE, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, esi, edi, 0xF0, i8, 6, 0x3A, 0xB7, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + TestRegAddrBase(cmp, edi, eax, 0xF0, i8, 6, 0x3A, 0xB8, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + |
| + /* cmp GPR, Imm(,Index,Scale) */ |
| + TestRegAddrScaledIndex(cmp, eax, ecx, 1, 0, i32, 7, 0x3B, 0x04, 0x0D, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, ecx, edx, 2, 0, i32, 7, 0x3B, 0x0C, 0x55, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, edx, ebx, 4, 0, i32, 7, 0x3B, 0x14, 0x9D, 0x00, |
| + 0x00, 0x00, 0x00); |
| + // esp cannot be an scaled index. |
| + TestRegAddrScaledIndex(cmp, esp, ebp, 8, 0, i32, 7, 0x3B, 0x24, 0xED, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, ebp, esi, 1, 0, i32, 7, 0x3B, 0x2C, 0x35, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, esi, edi, 2, 0, i32, 7, 0x3B, 0x34, 0x7D, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, edi, eax, 4, 0, i32, 7, 0x3B, 0x3C, 0x85, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, ebx, ecx, 8, 0, i32, 7, 0x3B, 0x1C, 0xCD, 0x00, |
| + 0x00, 0x00, 0x00); |
| + |
| + TestRegAddrScaledIndex(cmp, eax, ecx, 8, 0, i16, 8, 0x66, 0x3B, 0x04, 0xCD, |
| + 0x00, 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, ecx, edx, 1, 0, i16, 8, 0x66, 0x3B, 0x0C, 0x15, |
| + 0x00, 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, edx, ebx, 2, 0, i16, 8, 0x66, 0x3B, 0x14, 0x5D, |
| + 0x00, 0x00, 0x00, 0x00); |
| + // esp cannot be an scaled index. |
| + TestRegAddrScaledIndex(cmp, esp, ebp, 4, 0, i16, 8, 0x66, 0x3B, 0x24, 0xAD, |
| + 0x00, 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, ebp, esi, 8, 0, i16, 8, 0x66, 0x3B, 0x2C, 0xF5, |
| + 0x00, 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, esi, edi, 1, 0, i16, 8, 0x66, 0x3B, 0x34, 0x3D, |
| + 0x00, 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, edi, eax, 2, 0, i16, 8, 0x66, 0x3B, 0x3C, 0x45, |
| + 0x00, 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, ebx, ecx, 8, 0, i16, 8, 0x66, 0x3B, 0x1C, 0xCD, |
| + 0x00, 0x00, 0x00, 0x00); |
| + |
| + TestRegAddrScaledIndex(cmp, eax, ecx, 4, 0, i8, 7, 0x3A, 0x04, 0x8D, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, ecx, edx, 8, 0, i8, 7, 0x3A, 0x0C, 0xD5, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, edx, ebx, 1, 0, i8, 7, 0x3A, 0x14, 0x1D, 0x00, |
| + 0x00, 0x00, 0x00); |
| + // esp cannot be an scaled index. |
| + TestRegAddrScaledIndex(cmp, esp, ebp, 2, 0, i8, 7, 0x3A, 0x24, 0x6D, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, ebp, esi, 4, 0, i8, 7, 0x3A, 0x2C, 0xB5, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, esi, edi, 8, 0, i8, 7, 0x3A, 0x34, 0xFD, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, edi, eax, 1, 0, i8, 7, 0x3A, 0x3C, 0x05, 0x00, |
| + 0x00, 0x00, 0x00); |
| + TestRegAddrScaledIndex(cmp, ebx, ecx, 8, 0, i8, 7, 0x3a, 0x1C, 0xCD, 0x00, |
| + 0x00, 0x00, 0x00); |
| - const size_t CmpRegRegBytes = 2; |
| - const size_t ByteCount = 6 * CmpRegRegBytes; |
| + /* cmp GPR, 0(Base,Index,Scale) */ |
| + TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0, i32, 3, 0x3B, 0x04, |
| + 0x11); |
| + TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0, i32, 3, 0x3B, 0x0C, |
| + 0x5A); |
| + // esp cannot be an scaled index. |
| + TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0, i32, 3, 0x3B, 0x1C, |
| + 0xAC); |
| + TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0, i32, 4, 0x3B, 0x64, 0xF5, |
| + 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0, i32, 3, 0x3B, 0x2C, |
| + 0x3E); |
| + TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0, i32, 3, 0x3B, 0x34, |
| + 0x47); |
| + TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0, i32, 3, 0x3B, 0x3C, |
| + 0x98); |
| + TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0, i32, 3, 0x3B, 0x1C, |
| + 0xD1); |
| + |
| + TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0, i16, 4, 0x66, 0x3B, 0x04, |
| + 0x11); |
| + TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0, i16, 4, 0x66, 0x3B, 0x0C, |
| + 0x5A); |
| + // esp cannot be an scaled index. |
| + TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0, i16, 4, 0x66, 0x3B, 0x1C, |
| + 0xAC); |
| + TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0, i16, 5, 0x66, 0x3B, 0x64, |
| + 0xF5, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0, i16, 4, 0x66, 0x3B, 0x2C, |
| + 0x3E); |
| + TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0, i16, 4, 0x66, 0x3B, 0x34, |
| + 0x47); |
| + TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0, i16, 4, 0x66, 0x3B, 0x3C, |
| + 0x98); |
| + TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0, i16, 4, 0x66, 0x3B, 0x1C, |
| + 0xD1); |
| + |
| + TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0, i8, 3, 0x3A, 0x04, 0x11); |
| + TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0, i8, 3, 0x3A, 0x0C, 0x5A); |
| + // esp cannot be an scaled index. |
| + TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0, i8, 3, 0x3A, 0x1C, 0xAC); |
| + TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0, i8, 4, 0x3A, 0x64, 0xF5, |
| + 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0, i8, 3, 0x3A, 0x2C, 0x3E); |
| + TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0, i8, 3, 0x3A, 0x34, 0x47); |
| + TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0, i8, 3, 0x3A, 0x3C, 0x98); |
| + TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0, i8, 3, 0x3A, 0x1C, 0xD1); |
| + |
| + /* cmp GPR, Imm8(Base,Index,Scale) */ |
| + TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0x40, i32, 4, 0x3B, 0x44, |
| + 0x11, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0x40, i32, 4, 0x3B, 0x4C, |
| + 0x5A, 0x40); |
| + // esp cannot be an scaled index. |
| + TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0x40, i32, 4, 0x3B, 0x5C, |
| + 0xAC, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0x40, i32, 4, 0x3B, 0x64, |
| + 0xF5, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0x40, i32, 4, 0x3B, 0x6C, |
| + 0x3E, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0x40, i32, 4, 0x3B, 0x74, |
| + 0x47, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0x40, i32, 4, 0x3B, 0x7C, |
| + 0x98, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0x40, i32, 4, 0x3B, 0x5C, |
| + 0xD1, 0x40); |
| + |
| + TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0x40, i16, 5, 0x66, 0x3B, |
| + 0x44, 0x11, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0x40, i16, 5, 0x66, 0x3B, |
| + 0x4C, 0x5A, 0x40); |
| + // esp cannot be an scaled index. |
| + TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0x40, i16, 5, 0x66, 0x3B, |
| + 0x5C, 0xAC, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0x40, i16, 5, 0x66, 0x3B, |
| + 0x64, 0xF5, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0x40, i16, 5, 0x66, 0x3B, |
| + 0x6C, 0x3E, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0x40, i16, 5, 0x66, 0x3B, |
| + 0x74, 0x47, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0x40, i16, 5, 0x66, 0x3B, |
| + 0x7C, 0x98, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0x40, i16, 5, 0x66, 0x3B, |
| + 0x5C, 0xD1, 0x40); |
| + |
| + TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0x40, i8, 4, 0x3A, 0x44, |
| + 0x11, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0x40, i8, 4, 0x3A, 0x4C, |
| + 0x5A, 0x40); |
| + // esp cannot be an scaled index. |
| + TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0x40, i8, 4, 0x3A, 0x5C, |
| + 0xAC, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0x40, i8, 4, 0x3A, 0x64, |
| + 0xF5, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0x40, i8, 4, 0x3A, 0x6C, |
| + 0x3E, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0x40, i8, 4, 0x3A, 0x74, |
| + 0x47, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0x40, i8, 4, 0x3A, 0x7C, |
| + 0x98, 0x40); |
| + TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0x40, i8, 4, 0x3A, 0x5C, |
| + 0xD1, 0x40); |
| + |
| + /* cmp GPR, Imm32(Base,Index,Scale) */ |
| + TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0xF0, i32, 7, 0x3B, 0x84, |
| + 0x11, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0xF0, i32, 7, 0x3B, 0x8C, |
| + 0x5A, 0xF0, 0x00, 0x00, 0x00); |
| + // esp cannot be an scaled index. |
| + TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0xF0, i32, 7, 0x3B, 0x9C, |
| + 0xAC, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0xF0, i32, 7, 0x3B, 0xA4, |
| + 0xF5, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0xF0, i32, 7, 0x3B, 0xAC, |
| + 0x3E, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0xF0, i32, 7, 0x3B, 0xB4, |
| + 0x47, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0xF0, i32, 7, 0x3B, 0xBC, |
| + 0x98, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0xF0, i32, 7, 0x3B, 0x9C, |
| + 0xD1, 0xF0, 0x00, 0x00, 0x00); |
| + |
| + TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0xF0, i16, 8, 0x66, 0x3B, |
| + 0x84, 0x11, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0xF0, i16, 8, 0x66, 0x3B, |
| + 0x8C, 0x5A, 0xF0, 0x00, 0x00, 0x00); |
| + // esp cannot be an scaled index. |
| + TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0xF0, i16, 8, 0x66, 0x3B, |
| + 0x9C, 0xAC, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0xF0, i16, 8, 0x66, 0x3B, |
| + 0xA4, 0xF5, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0xF0, i16, 8, 0x66, 0x3B, |
| + 0xAC, 0x3E, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0xF0, i16, 8, 0x66, 0x3B, |
| + 0xB4, 0x47, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0xF0, i16, 8, 0x66, 0x3B, |
| + 0xBC, 0x98, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0xF0, i16, 8, 0x66, 0x3B, |
| + 0x9C, 0xD1, 0xF0, 0x00, 0x00, 0x00); |
| + |
| + TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0xF0, i8, 7, 0x3A, 0x84, |
| + 0x11, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0xF0, i8, 7, 0x3A, 0x8C, |
| + 0x5A, 0xF0, 0x00, 0x00, 0x00); |
| + // esp cannot be an scaled index. |
| + TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0xF0, i8, 7, 0x3A, 0x9C, |
| + 0xAC, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0xF0, i8, 7, 0x3A, 0xA4, |
| + 0xF5, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0xF0, i8, 7, 0x3A, 0xAC, |
| + 0x3E, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0xF0, i8, 7, 0x3A, 0xB4, |
| + 0x47, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0xF0, i8, 7, 0x3A, 0xBC, |
| + 0x98, 0xF0, 0x00, 0x00, 0x00); |
| + TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0xF0, i8, 7, 0x3A, 0x9C, |
| + 0xD1, 0xF0, 0x00, 0x00, 0x00); |
| + |
| + /* cmp Addr, Imm */ |
| + // Note: at this point we trust the assembler knows how to encode addresses, |
| + // so no more exhaustive addressing mode testing. |
| + TestAddrBaseScaledIndexImm(cmp, eax, ecx, 1, 0xF0, 0x12, i32, 8, 0x83, 0xBC, |
| + 0x08, 0xF0, 0x00, 0x00, 0x00, 0x12); |
| + TestAddrBaseScaledIndexImm(cmp, ecx, edx, 1, 0xF0, 0xF0, i32, 11, 0x81, 0xBC, |
| + 0x11, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, |
| + 0x00); |
| + |
| + TestAddrBaseScaledIndexImm(cmp, eax, ecx, 1, 0xF0, 0x12, i16, 9, 0x66, 0x83, |
| + 0xBC, 0x08, 0xF0, 0x00, 0x00, 0x00, 0x12); |
| + TestAddrBaseScaledIndexImm(cmp, ecx, edx, 1, 0xF0, 0xF0, i16, 10, 0x66, 0x81, |
| + 0xBC, 0x11, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0x00); |
| + |
| + TestAddrBaseScaledIndexImm(cmp, eax, ecx, 1, 0xF0, 0x12, i8, 8, 0x80, 0xBC, |
| + 0x08, 0xF0, 0x00, 0x00, 0x00, 0x12); |
| + |
| + /* cmp Addr, GPR */ |
| + TestAddrBaseScaledIndexReg(cmp, eax, ecx, 1, 0xF0, edx, i32, 7, 0x39, 0x94, |
| + 0x08, 0xF0, 0x00, 0x00, 0x00); |
| + |
| + TestAddrBaseScaledIndexReg(cmp, eax, ecx, 1, 0xF0, edx, i16, 8, 0x66, 0x39, |
| + 0x94, 0x08, 0xF0, 0x00, 0x00, 0x00); |
| + |
| + TestAddrBaseScaledIndexReg(cmp, eax, ecx, 1, 0xF0, edx, i8, 7, 0x38, 0x94, |
| + 0x08, 0xF0, 0x00, 0x00, 0x00); |
| + |
| +#undef TestAddrBaseScaledIndexReg |
| +#undef TestAddrBaseScaledIndexImm |
| +#undef TestRegAddrBaseScaledIndex |
| +#undef TestRegAddrScaledIndex |
| +#undef TestRegAddrBase |
| +#undef TestRegAbsoluteAddr |
| +#undef TestRegImm |
| +#undef TestRegReg |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Fld) { |
| + __ fld(IceType_f32, Address(GPRRegister::Encoded_Reg_ebp, 1)); |
| + __ fld(IceType_f64, Address(GPRRegister::Encoded_Reg_ebp, 0x10000)); |
| + |
| + constexpr size_t ByteCount = 9; |
| ASSERT_EQ(ByteCount, codeBytesSize()); |
| - constexpr size_t CmpOpcode = 0x3b; |
| - constexpr size_t ModRm = 0xC0 /* Register Addressing */; |
| - verifyBytes<ByteCount>( |
| - codeBytes(), CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_eax << 3) | |
| - GPRRegister::Encoded_Reg_ebx, |
| - CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_ebx << 3) | |
| - GPRRegister::Encoded_Reg_ecx, |
| - CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_ecx << 3) | |
| - GPRRegister::Encoded_Reg_edx, |
| - CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_edx << 3) | |
| - GPRRegister::Encoded_Reg_edi, |
| - CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_edi << 3) | |
| - GPRRegister::Encoded_Reg_esi, |
| - CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_esi << 3) | |
| - GPRRegister::Encoded_Reg_eax); |
| + constexpr uint8_t Fld32Opcode = 0xd9; |
| + constexpr uint8_t Fld32ModRM = (/*mod*/ 1 << 6) | (/*reg*/ 0 << 3) | |
| + (/*rm*/ GPRRegister::Encoded_Reg_ebp); |
| + constexpr uint8_t Fld64Opcode = 0xdd; |
| + constexpr uint8_t Fld64ModRM = (/*mod*/ 2 << 6) | (/*reg*/ 0 << 3) | |
| + (/*rm*/ GPRRegister::Encoded_Reg_ebp); |
| + verifyBytes<ByteCount>(codeBytes(), Fld32Opcode, Fld32ModRM, 0x01, |
| + Fld64Opcode, Fld64ModRM, 0x00, 0x00, 0x01, 0x00); |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, FstpAddr) { |
| + __ fstp(IceType_f32, Address(GPRRegister::Encoded_Reg_ebp, 1)); |
| + __ fstp(IceType_f64, Address(GPRRegister::Encoded_Reg_ebp, 0x10000)); |
| + |
| + constexpr size_t ByteCount = 9; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + |
| + constexpr uint8_t Fld32Opcode = 0xd9; |
| + constexpr uint8_t Fld32ModRM = (/*mod*/ 1 << 6) | (/*reg*/ 3 << 3) | |
| + (/*rm*/ GPRRegister::Encoded_Reg_ebp); |
| + constexpr uint8_t Fld64Opcode = 0xdd; |
| + constexpr uint8_t Fld64ModRM = (/*mod*/ 2 << 6) | (/*reg*/ 3 << 3) | |
| + (/*rm*/ GPRRegister::Encoded_Reg_ebp); |
| + verifyBytes<ByteCount>(codeBytes(), Fld32Opcode, Fld32ModRM, 0x01, |
| + Fld64Opcode, Fld64ModRM, 0x00, 0x00, 0x01, 0x00); |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Fincstp) { |
| + __ fincstp(); |
| + |
| + constexpr size_t ByteCount = 2; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + |
| + verifyBytes<ByteCount>(codeBytes(), 0xD9, 0XF7); |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, FnstcwAddr) { |
| + __ fnstcw(Address(GPRRegister::Encoded_Reg_ebp, 0x12345)); |
| + |
| + constexpr size_t ByteCount = 6; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + |
| + constexpr uint8_t Opcode = 0xd9; |
| + constexpr uint8_t ModRM = (/*mod*/ 2 << 6) | (/*reg*/ 7 << 3) | |
| + (/*rm*/ GPRRegister::Encoded_Reg_ebp); |
| + verifyBytes<ByteCount>(codeBytes(), Opcode, ModRM, 0x45, 0x23, 0x01, 0x00); |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, FldcwAddr) { |
| + __ fldcw(Address(GPRRegister::Encoded_Reg_ebp, 0x12345)); |
| + |
| + constexpr size_t ByteCount = 6; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + |
| + constexpr uint8_t Opcode = 0xd9; |
| + constexpr uint8_t ModRM = (/*mod*/ 2 << 6) | (/*reg*/ 5 << 3) | |
| + (/*rm*/ GPRRegister::Encoded_Reg_ebp); |
| + verifyBytes<ByteCount>(codeBytes(), Opcode, ModRM, 0x45, 0x23, 0x01, 0x00); |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, PushalPopal) { |
| + // These are invalid in x86-64, so we can't write tests which will execute |
| + // these instructions. |
| + __ pushal(); |
| + __ popal(); |
| + |
| + constexpr size_t ByteCount = 2; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + |
| + constexpr uint8_t Pushal = 0x60; |
| + constexpr uint8_t Popal = 0x61; |
| + |
| + verifyBytes<ByteCount>(codeBytes(), Pushal, Popal); |
| } |
| // After these tests we should have a sane environment; we know the following |
| @@ -282,6 +905,16 @@ TEST_F(AssemblerX8632LowLevelTest, CmpRegReg) { |
| // mov %edx, {12 + $ScratchpadOffset}(%ebp) |
| // mov %edi, {16 + $ScratchpadOffset}(%ebp) |
| // mov %esi, {20 + $ScratchpadOffset}(%ebp) |
| +// mov %ebp, {24 + $ScratchpadOffset}(%ebp) |
| +// mov %esp, {28 + $ScratchpadOffset}(%ebp) |
| +// movups %xmm0, {32 + $ScratchpadOffset}(%ebp) |
| +// movups %xmm1, {48 + $ScratchpadOffset}(%ebp) |
| +// movups %xmm2, {64 + $ScratchpadOffset}(%ebp) |
| +// movusp %xmm3, {80 + $ScratchpadOffset}(%ebp) |
| +// movusp %xmm4, {96 + $ScratchpadOffset}(%ebp) |
| +// movusp %xmm5, {112 + $ScratchpadOffset}(%ebp) |
| +// movusp %xmm6, {128 + $ScratchpadOffset}(%ebp) |
| +// movusp %xmm7, {144 + $ScratchpadOffset}(%ebp) |
| // |
| // pop %ebp |
| // pop %esi |
| @@ -301,33 +934,349 @@ TEST_F(AssemblerX8632LowLevelTest, CmpRegReg) { |
| // TODO(jpp): test the |
| // |
| // mov %reg, $Offset(%ebp) |
| +// movups %xmm, $Offset(%ebp) |
| // |
| // encodings using the low level assembler test ensuring that the register |
| // values can be written to the scratchpad area. |
| class AssemblerX8632Test : public AssemblerX8632TestBase { |
| protected: |
| + // Dqword is used to represent 128-bit data types. The Dqword's contents are |
| + // the same as the contents read from memory. Tests can then use the union |
| + // members to verify the tests' outputs. |
| + // |
| + // NOTE: We want sizeof(Dqword) == sizeof(uint64_t) * 2. In other words, we |
| + // want Dqword's contents to be **exactly** what the memory contents were so |
| + // that we can do, e.g., |
| + // |
| + // ... |
| + // float Ret[4]; |
| + // // populate Ret |
| + // return *reinterpret_cast<Dqword *>(&Ret); |
| + // |
| + // While being an ugly hack, this kind of return statements are used |
| + // extensively in the PackedArith (see below) class. |
| + union Dqword { |
| + template <typename T0, typename T1, typename T2, typename T3, |
| + typename = typename std::enable_if< |
| + std::is_floating_point<T0>::value>::type> |
| + Dqword(T0 F0, T1 F1, T2 F2, T3 F3) { |
| + F32[0] = F0; |
| + F32[1] = F1; |
| + F32[2] = F2; |
| + F32[3] = F3; |
| + } |
| + |
| + template <typename T> |
| + Dqword(typename std::enable_if<std::is_same<T, int32_t>::value, T>::type I0, |
| + T I1, T I2, T I3) { |
| + I32[0] = I0; |
| + I32[1] = I1; |
| + I32[2] = I2; |
| + I32[3] = I3; |
| + } |
| + |
| + template <typename T> |
| + Dqword(typename std::enable_if<std::is_same<T, uint64_t>::value, T>::type |
| + U64_0, |
| + T U64_1) { |
| + U64[0] = U64_0; |
| + U64[1] = U64_1; |
| + } |
| + |
| + template <typename T> |
| + Dqword(typename std::enable_if<std::is_same<T, double>::value, T>::type D0, |
| + T D1) { |
| + F64[0] = D0; |
| + F64[1] = D1; |
| + } |
| + |
| + bool operator==(const Dqword &Rhs) const { |
| + return std::memcmp(this, &Rhs, sizeof(*this)) == 0; |
| + } |
| + |
| + double F64[2]; |
| + uint64_t U64[2]; |
| + int64_t I64[2]; |
| + |
| + float F32[4]; |
| + uint32_t U32[4]; |
| + int32_t I32[4]; |
| + |
| + uint16_t U16[8]; |
| + int16_t I16[8]; |
| + |
| + uint8_t U8[16]; |
| + int8_t I8[16]; |
| + |
| + private: |
| + Dqword() = delete; |
| + }; |
| + |
| + // As stated, we want this condition to hold, so we assert. |
| + static_assert(sizeof(Dqword) == 2 * sizeof(uint64_t), |
| + "Dqword has the wrong size."); |
| + |
| + // PackedArith is an interface provider for Dqwords. PackedArith's C argument |
| + // is the undelying Dqword's type, which is then used so that we can define |
| + // operators in terms of C++ operators on the underlying elements' type. |
| + template <typename C> class PackedArith { |
| + public: |
| + static constexpr uint32_t N = sizeof(Dqword) / sizeof(C); |
| + static_assert(N * sizeof(C) == sizeof(Dqword), |
| + "Invalid template paramenter."); |
| + static_assert((N & 1) == 0, "N should be divisible by 2"); |
| + |
| +#define DefinePackedComparisonOperator(Op) \ |
| + template <typename Container = C, int Size = N> \ |
| + typename std::enable_if<std::is_floating_point<Container>::value, \ |
| + Dqword>::type \ |
| + operator Op(const Dqword &Rhs) const { \ |
| + using ElemType = \ |
| + typename std::conditional<std::is_same<float, Container>::value, \ |
| + int32_t, int64_t>::type; \ |
| + static_assert(sizeof(ElemType) == sizeof(Container), \ |
| + "Check ElemType definition."); \ |
| + const ElemType *const RhsPtr = \ |
| + reinterpret_cast<const ElemType *const>(&Rhs); \ |
| + const ElemType *const LhsPtr = \ |
| + reinterpret_cast<const ElemType *const>(&Lhs); \ |
| + ElemType Ret[N]; \ |
| + for (uint32_t i = 0; i < N; ++i) { \ |
| + Ret[i] = (LhsPtr[i] Op RhsPtr[i]) ? -1 : 0; \ |
| + } \ |
| + return *reinterpret_cast<Dqword *>(&Ret); \ |
| + } |
| + |
| + DefinePackedComparisonOperator(< ); |
| + DefinePackedComparisonOperator(<= ); |
| + DefinePackedComparisonOperator(> ); |
| + DefinePackedComparisonOperator(>= ); |
| + DefinePackedComparisonOperator(== ); |
| + DefinePackedComparisonOperator(!= ); |
| + |
| +#undef DefinePackedComparisonOperator |
| + |
| +#define DefinePackedOrdUnordComparisonOperator(Op, Ordered) \ |
| + template <typename Container = C, int Size = N> \ |
| + typename std::enable_if<std::is_floating_point<Container>::value, \ |
| + Dqword>::type \ |
| + Op(const Dqword &Rhs) const { \ |
| + using ElemType = \ |
| + typename std::conditional<std::is_same<float, Container>::value, \ |
| + int32_t, int64_t>::type; \ |
| + static_assert(sizeof(ElemType) == sizeof(Container), \ |
| + "Check ElemType definition."); \ |
| + const Container *const RhsPtr = \ |
| + reinterpret_cast<const Container *const>(&Rhs); \ |
| + const Container *const LhsPtr = \ |
| + reinterpret_cast<const Container *const>(&Lhs); \ |
| + ElemType Ret[N]; \ |
| + for (uint32_t i = 0; i < N; ++i) { \ |
| + Ret[i] = (!(LhsPtr[i] == LhsPtr[i]) || !(RhsPtr[i] == RhsPtr[i])) != \ |
| + (Ordered) \ |
| + ? -1 \ |
| + : 0; \ |
| + } \ |
| + return *reinterpret_cast<Dqword *>(&Ret); \ |
| + } |
| + |
| + DefinePackedOrdUnordComparisonOperator(ord, true); |
| + DefinePackedOrdUnordComparisonOperator(unord, false); |
| +#undef DefinePackedOrdUnordComparisonOperator |
| + |
| +#define DefinePackedArithOperator(Op, RhsIndexChanges, NeedsInt) \ |
| + template <typename Container = C, int Size = N> \ |
| + Dqword operator Op(const Dqword &Rhs) const { \ |
| + using ElemTypeForFp = typename std::conditional< \ |
| + !(NeedsInt), Container, \ |
| + typename std::conditional< \ |
| + std::is_same<Container, float>::value, uint32_t, \ |
| + typename std::conditional<std::is_same<Container, double>::value, \ |
| + uint64_t, void>::type>::type>::type; \ |
| + using ElemType = \ |
| + typename std::conditional<std::is_integral<Container>::value, \ |
| + Container, ElemTypeForFp>::type; \ |
| + static_assert(!std::is_same<void, ElemType>::value, \ |
| + "Check ElemType definition."); \ |
| + const ElemType *const RhsPtr = \ |
| + reinterpret_cast<const ElemType *const>(&Rhs); \ |
| + const ElemType *const LhsPtr = \ |
| + reinterpret_cast<const ElemType *const>(&Lhs); \ |
| + ElemType Ret[N]; \ |
| + for (uint32_t i = 0; i < N; ++i) { \ |
| + Ret[i] = LhsPtr[i] Op RhsPtr[(RhsIndexChanges) ? i : 0]; \ |
| + } \ |
| + return *reinterpret_cast<Dqword *>(&Ret); \ |
| + } |
| + |
| + DefinePackedArithOperator(>>, false, true); |
| + DefinePackedArithOperator(<<, false, true); |
| + DefinePackedArithOperator(+, true, false); |
| + DefinePackedArithOperator(-, true, false); |
| + DefinePackedArithOperator(/, true, false); |
| + DefinePackedArithOperator(&, true, true); |
| + DefinePackedArithOperator(|, true, true); |
| + DefinePackedArithOperator (^, true, true); |
| + |
| +#undef DefinePackedArithOperator |
| + |
| +#define DefinePackedArithShiftImm(Op) \ |
| + template <typename Container = C, int Size = N> \ |
| + Dqword operator Op(uint8_t imm) const { \ |
| + const Container *const LhsPtr = \ |
| + reinterpret_cast<const Container *const>(&Lhs); \ |
| + Container Ret[N]; \ |
| + for (uint32_t i = 0; i < N; ++i) { \ |
| + Ret[i] = LhsPtr[i] Op imm; \ |
| + } \ |
| + return *reinterpret_cast<Dqword *>(&Ret); \ |
| + } |
| + |
| + DefinePackedArithShiftImm(>> ); |
| + DefinePackedArithShiftImm(<< ); |
| + |
| +#undef DefinePackedArithShiftImm |
| + |
| + template <typename Container = C, int Size = N> |
| + typename std::enable_if<std::is_signed<Container>::value || |
| + std::is_floating_point<Container>::value, |
| + Dqword>::type |
| + operator*(const Dqword &Rhs) const { |
| + static_assert((std::is_integral<Container>::value && |
| + sizeof(Container) < sizeof(uint64_t)) || |
| + std::is_floating_point<Container>::value, |
| + "* is only defined for i(8|16|32), and fp types."); |
| + |
| + const Container *const RhsPtr = |
| + reinterpret_cast<const Container *const>(&Rhs); |
| + const Container *const LhsPtr = |
| + reinterpret_cast<const Container *const>(&Lhs); |
| + Container Ret[Size]; |
| + for (uint32_t i = 0; i < Size; ++i) { |
| + Ret[i] = LhsPtr[i] * RhsPtr[i]; |
| + } |
| + return *reinterpret_cast<Dqword *>(&Ret); |
| + } |
| + |
| + template <typename Container = C, int Size = N, |
| + typename = typename std::enable_if< |
| + !std::is_signed<Container>::value>::type> |
| + Dqword operator*(const Dqword &Rhs) const { |
| + static_assert(std::is_integral<Container>::value && |
| + sizeof(Container) < sizeof(uint64_t), |
| + "* is only defined for ui(8|16|32)"); |
| + using NextType = typename std::conditional< |
| + sizeof(Container) == 1, uint16_t, |
| + typename std::conditional<sizeof(Container) == 2, uint32_t, |
| + uint64_t>::type>::type; |
| + static_assert(sizeof(Container) * 2 == sizeof(NextType), |
| + "Unexpected size"); |
| + |
| + const Container *const RhsPtr = |
| + reinterpret_cast<const Container *const>(&Rhs); |
| + const Container *const LhsPtr = |
| + reinterpret_cast<const Container *const>(&Lhs); |
| + NextType Ret[Size / 2]; |
| + for (uint32_t i = 0; i < Size; i += 2) { |
| + Ret[i / 2] = |
| + static_cast<NextType>(LhsPtr[i]) * static_cast<NextType>(RhsPtr[i]); |
| + } |
| + return *reinterpret_cast<Dqword *>(&Ret); |
| + } |
| + |
| + template <typename Container = C, int Size = N> |
| + PackedArith<Container> operator~() const { |
| + const Container *const LhsPtr = |
| + reinterpret_cast<const Container *const>(&Lhs); |
| + Container Ret[Size]; |
| + for (uint32_t i = 0; i < Size; ++i) { |
| + Ret[i] = ~LhsPtr[i]; |
| + } |
| + return PackedArith<Container>(*reinterpret_cast<Dqword *>(&Ret)); |
| + } |
| + |
| +#define MinMaxOperations(Name, Suffix) \ |
| + template <typename Container = C, int Size = N> \ |
| + Dqword Name##Suffix(const Dqword &Rhs) const { \ |
| + static_assert(std::is_floating_point<Container>::value, \ |
| + #Name #Suffix "ps is only available for fp."); \ |
| + const Container *const RhsPtr = \ |
| + reinterpret_cast<const Container *const>(&Rhs); \ |
| + const Container *const LhsPtr = \ |
| + reinterpret_cast<const Container *const>(&Lhs); \ |
| + Container Ret[Size]; \ |
| + for (uint32_t i = 0; i < Size; ++i) { \ |
| + Ret[i] = std::Name(LhsPtr[i], RhsPtr[i]); \ |
| + } \ |
| + return *reinterpret_cast<Dqword *>(&Ret); \ |
| + } |
| + |
| + MinMaxOperations(max, ps); |
| + MinMaxOperations(max, pd); |
| + MinMaxOperations(min, ps); |
| + MinMaxOperations(min, pd); |
| +#undef MinMaxOperations |
| + |
| + template <typename Container = C, int Size = N> |
| + Dqword blendWith(const Dqword &Rhs, const Dqword &Mask) const { |
| + using MaskType = typename std::conditional< |
| + sizeof(Container) == 1, int8_t, |
| + typename std::conditional<sizeof(Container) == 2, int16_t, |
| + int32_t>::type>::type; |
| + static_assert(sizeof(MaskType) == sizeof(Container), |
| + "MaskType has the wrong size."); |
| + const Container *const RhsPtr = |
| + reinterpret_cast<const Container *const>(&Rhs); |
| + const Container *const LhsPtr = |
| + reinterpret_cast<const Container *const>(&Lhs); |
| + const MaskType *const MaskPtr = |
| + reinterpret_cast<const MaskType *const>(&Mask); |
| + Container Ret[Size]; |
| + for (int i = 0; i < Size; ++i) { |
| + Ret[i] = ((MaskPtr[i] < 0) ? RhsPtr : LhsPtr)[i]; |
| + } |
| + return *reinterpret_cast<Dqword *>(&Ret); |
| + } |
| + |
| + private: |
| + // The AssemblerX8632Test class needs to be a friend so that it can create |
| + // PackedArith objects (see below.) |
| + friend class AssemblerX8632Test; |
| + |
| + explicit PackedArith(const Dqword &MyLhs) : Lhs(MyLhs) {} |
| + |
| + // Lhs can't be a & because operator~ returns a temporary object that needs |
| + // access to its own Dqword. |
| + const Dqword Lhs; |
| + }; |
| + |
| + // Named constructor for PackedArith objects. |
| + template <typename C> static PackedArith<C> packedAs(const Dqword &D) { |
| + return PackedArith<C>(D); |
| + } |
| + |
| AssemblerX8632Test() { reset(); } |
| void reset() { |
| AssemblerX8632TestBase::reset(); |
| NeedsEpilogue = true; |
| - // 6 dwords are allocated for saving the GPR state after the jitted code |
| + // These dwords are allocated for saving the GPR state after the jitted code |
| // runs. |
| - NumAllocatedDwords = 6; |
| + NumAllocatedDwords = AssembledTest::ScratchpadSlots; |
| addPrologue(); |
| } |
| - // AssembledBuffer is a wrapper around a PROT_EXEC mmap'ed buffer. This buffer |
| + // AssembledTest is a wrapper around a PROT_EXEC mmap'ed buffer. This buffer |
| // contains both the test code as well as prologue/epilogue, and the |
| // scratchpad area that tests may use -- all tests use this scratchpad area |
| // for storing the processor's registers after the tests executed. This class |
| // also exposes helper methods for reading the register state after test |
| // execution, as well as for reading the scratchpad area. |
| - class AssembledBuffer { |
| - AssembledBuffer() = delete; |
| - AssembledBuffer(const AssembledBuffer &) = delete; |
| - AssembledBuffer &operator=(const AssembledBuffer &) = delete; |
| + class AssembledTest { |
| + AssembledTest() = delete; |
| + AssembledTest(const AssembledTest &) = delete; |
| + AssembledTest &operator=(const AssembledTest &) = delete; |
| public: |
| static constexpr uint32_t MaximumCodeSize = 1 << 20; |
| @@ -337,9 +1286,21 @@ protected: |
| static constexpr uint32_t EdxSlot = 3; |
| static constexpr uint32_t EdiSlot = 4; |
| static constexpr uint32_t EsiSlot = 5; |
| + static constexpr uint32_t EbpSlot = 6; |
| + static constexpr uint32_t EspSlot = 7; |
| + // save 4 dwords for each xmm registers. |
| + static constexpr uint32_t Xmm0Slot = 8; |
| + static constexpr uint32_t Xmm1Slot = 12; |
| + static constexpr uint32_t Xmm2Slot = 16; |
| + static constexpr uint32_t Xmm3Slot = 20; |
| + static constexpr uint32_t Xmm4Slot = 24; |
| + static constexpr uint32_t Xmm5Slot = 28; |
| + static constexpr uint32_t Xmm6Slot = 32; |
| + static constexpr uint32_t Xmm7Slot = 36; |
| + static constexpr uint32_t ScratchpadSlots = 40; |
| - AssembledBuffer(const uint8_t *Data, const size_t MySize, |
| - const size_t ExtraStorageDwords) |
| + AssembledTest(const uint8_t *Data, const size_t MySize, |
| + const size_t ExtraStorageDwords) |
| : Size(MaximumCodeSize + 4 * ExtraStorageDwords) { |
| // MaxCodeSize is needed because EXPECT_LT needs a symbol with a name -- |
| // probably a compiler bug? |
| @@ -353,15 +1314,15 @@ protected: |
| std::memcpy(ExecutableData, Data, MySize); |
| } |
| - // We allow AssembledBuffer to be moved so that we can return objects of |
| + // We allow AssembledTest to be moved so that we can return objects of |
| // this type. |
| - AssembledBuffer(AssembledBuffer &&Buffer) |
| + AssembledTest(AssembledTest &&Buffer) |
| : ExecutableData(Buffer.ExecutableData), Size(Buffer.Size) { |
| Buffer.ExecutableData = nullptr; |
| Buffer.Size = 0; |
| } |
| - AssembledBuffer &operator=(AssembledBuffer &&Buffer) { |
| + AssembledTest &operator=(AssembledTest &&Buffer) { |
| ExecutableData = Buffer.ExecutableData; |
| Buffer.ExecutableData = nullptr; |
| Size = Buffer.Size; |
| @@ -369,7 +1330,7 @@ protected: |
| return *this; |
| } |
| - ~AssembledBuffer() { |
| + ~AssembledTest() { |
| if (ExecutableData != nullptr) { |
| munmap(ExecutableData, Size); |
| ExecutableData = nullptr; |
| @@ -378,28 +1339,118 @@ protected: |
| void run() const { reinterpret_cast<void (*)()>(ExecutableData)(); } |
| - uint32_t eax() const { return contentsOfDword(AssembledBuffer::EaxSlot); } |
| + uint32_t eax() const { return contentsOfDword(AssembledTest::EaxSlot); } |
| + |
| + uint32_t ebx() const { return contentsOfDword(AssembledTest::EbxSlot); } |
| + |
| + uint32_t ecx() const { return contentsOfDword(AssembledTest::EcxSlot); } |
| + |
| + uint32_t edx() const { return contentsOfDword(AssembledTest::EdxSlot); } |
| + |
| + uint32_t edi() const { return contentsOfDword(AssembledTest::EdiSlot); } |
| + |
| + uint32_t esi() const { return contentsOfDword(AssembledTest::EsiSlot); } |
| + |
| + uint32_t ebp() const { return contentsOfDword(AssembledTest::EbpSlot); } |
| + |
| + uint32_t esp() const { return contentsOfDword(AssembledTest::EspSlot); } |
| + |
| + template <typename T> T xmm0() const { |
| + return xmm<T>(AssembledTest::Xmm0Slot); |
| + } |
| + |
| + template <typename T> T xmm1() const { |
| + return xmm<T>(AssembledTest::Xmm1Slot); |
| + } |
| + |
| + template <typename T> T xmm2() const { |
| + return xmm<T>(AssembledTest::Xmm2Slot); |
| + } |
| - uint32_t ebx() const { return contentsOfDword(AssembledBuffer::EbxSlot); } |
| + template <typename T> T xmm3() const { |
| + return xmm<T>(AssembledTest::Xmm3Slot); |
| + } |
| - uint32_t ecx() const { return contentsOfDword(AssembledBuffer::EcxSlot); } |
| + template <typename T> T xmm4() const { |
| + return xmm<T>(AssembledTest::Xmm4Slot); |
| + } |
| - uint32_t edx() const { return contentsOfDword(AssembledBuffer::EdxSlot); } |
| + template <typename T> T xmm5() const { |
| + return xmm<T>(AssembledTest::Xmm5Slot); |
| + } |
| - uint32_t edi() const { return contentsOfDword(AssembledBuffer::EdiSlot); } |
| + template <typename T> T xmm6() const { |
| + return xmm<T>(AssembledTest::Xmm6Slot); |
| + } |
| - uint32_t esi() const { return contentsOfDword(AssembledBuffer::EsiSlot); } |
| + template <typename T> T xmm7() const { |
| + return xmm<T>(AssembledTest::Xmm7Slot); |
| + } |
| // contentsOfDword is used for reading the values in the scratchpad area. |
| // Valid arguments are the dword ids returned by |
| // AssemblerX8632Test::allocateDword() -- other inputs are considered |
| // invalid, and are not guaranteed to work if the implementation changes. |
| - uint32_t contentsOfDword(uint32_t Dword) const { |
| - return *reinterpret_cast<uint32_t *>( |
| - static_cast<uint8_t *>(ExecutableData) + dwordOffset(Dword)); |
| + template <typename T = uint32_t, typename = typename std::enable_if< |
| + sizeof(T) == sizeof(uint32_t)>::type> |
| + T contentsOfDword(uint32_t Dword) const { |
| + return *reinterpret_cast<T *>(static_cast<uint8_t *>(ExecutableData) + |
| + dwordOffset(Dword)); |
| + } |
| + |
| + template <typename T = uint64_t, typename = typename std::enable_if< |
| + sizeof(T) == sizeof(uint64_t)>::type> |
| + T contentsOfQword(uint32_t InitialDword) const { |
| + return *reinterpret_cast<T *>(static_cast<uint8_t *>(ExecutableData) + |
| + dwordOffset(InitialDword)); |
| + } |
| + |
| + Dqword contentsOfDqword(uint32_t InitialDword) const { |
| + return *reinterpret_cast<Dqword *>( |
| + static_cast<uint8_t *>(ExecutableData) + |
| + dwordOffset(InitialDword)); |
| + } |
| + |
| + template <typename T = uint32_t, typename = typename std::enable_if< |
| + sizeof(T) == sizeof(uint32_t)>::type> |
| + void setDwordTo(uint32_t Dword, T value) { |
| + *reinterpret_cast<uint32_t *>(static_cast<uint8_t *>(ExecutableData) + |
| + dwordOffset(Dword)) = |
| + *reinterpret_cast<uint32_t *>(&value); |
| + } |
| + |
| + template <typename T = uint64_t, typename = typename std::enable_if< |
| + sizeof(T) == sizeof(uint64_t)>::type> |
| + void setQwordTo(uint32_t InitialDword, T value) { |
| + *reinterpret_cast<uint64_t *>(static_cast<uint8_t *>(ExecutableData) + |
| + dwordOffset(InitialDword)) = |
| + *reinterpret_cast<uint64_t *>(&value); |
| + } |
| + |
| + void setDqwordTo(uint32_t InitialDword, const Dqword &qdword) { |
| + setQwordTo(InitialDword, qdword.U64[0]); |
| + setQwordTo(InitialDword + 2, qdword.U64[1]); |
| } |
| private: |
| + template <typename T> |
| + typename std::enable_if<std::is_same<T, Dqword>::value, Dqword>::type |
| + xmm(uint8_t Slot) const { |
| + return contentsOfDqword(Slot); |
| + } |
| + |
| + template <typename T> |
| + typename std::enable_if<!std::is_same<T, Dqword>::value, T>::type |
| + xmm(uint8_t Slot) const { |
| + constexpr bool TIs64Bit = sizeof(T) == sizeof(uint64_t); |
| + using _64BitType = typename std::conditional<TIs64Bit, T, uint64_t>::type; |
| + using _32BitType = typename std::conditional<TIs64Bit, uint32_t, T>::type; |
| + if (TIs64Bit) { |
| + return contentsOfQword<_64BitType>(Slot); |
| + } |
| + return contentsOfDword<_32BitType>(Slot); |
| + } |
| + |
| static uint32_t dwordOffset(uint32_t Index) { |
| return MaximumCodeSize + (Index * 4); |
| } |
| @@ -408,21 +1459,35 @@ protected: |
| size_t Size; |
| }; |
| - // assemble created an AssembledBuffer with the jitted code. The first time |
| + // assemble created an AssembledTest with the jitted code. The first time |
| // assemble is executed it will add the epilogue to the jitted code (which is |
| // the reason why this method is not const qualified. |
| - AssembledBuffer assemble() { |
| + AssembledTest assemble() { |
| if (NeedsEpilogue) { |
| addEpilogue(); |
| } |
| NeedsEpilogue = false; |
| - return AssembledBuffer(codeBytes(), codeBytesSize(), NumAllocatedDwords); |
| + return AssembledTest(codeBytes(), codeBytesSize(), NumAllocatedDwords); |
| } |
| // Allocates a new dword slot in the test's scratchpad area. |
| uint32_t allocateDword() { return NumAllocatedDwords++; } |
| + // Allocates a new qword slot in the test's scratchpad area. |
| + uint32_t allocateQword() { |
| + uint32_t InitialDword = allocateDword(); |
| + allocateDword(); |
| + return InitialDword; |
| + } |
| + |
| + // Allocates a new dqword slot in the test's scratchpad area. |
| + uint32_t allocateDqword() { |
| + uint32_t InitialDword = allocateQword(); |
| + allocateQword(); |
| + return InitialDword; |
| + } |
| + |
| Address dwordAddress(uint32_t Dword) { |
| return Address(GPRRegister::Encoded_Reg_ebp, dwordDisp(Dword)); |
| } |
| @@ -433,12 +1498,22 @@ private: |
| // the specified register. These are all private for, when jitting the test |
| // code, tests should not tamper with these values. Besides, during the test |
| // execution these slots' contents are undefined and should not be accessed. |
| - Address eaxSlotAddress() { return dwordAddress(AssembledBuffer::EaxSlot); } |
| - Address ebxSlotAddress() { return dwordAddress(AssembledBuffer::EbxSlot); } |
| - Address ecxSlotAddress() { return dwordAddress(AssembledBuffer::EcxSlot); } |
| - Address edxSlotAddress() { return dwordAddress(AssembledBuffer::EdxSlot); } |
| - Address ediSlotAddress() { return dwordAddress(AssembledBuffer::EdiSlot); } |
| - Address esiSlotAddress() { return dwordAddress(AssembledBuffer::EsiSlot); } |
| + Address eaxSlotAddress() { return dwordAddress(AssembledTest::EaxSlot); } |
| + Address ebxSlotAddress() { return dwordAddress(AssembledTest::EbxSlot); } |
| + Address ecxSlotAddress() { return dwordAddress(AssembledTest::EcxSlot); } |
| + Address edxSlotAddress() { return dwordAddress(AssembledTest::EdxSlot); } |
| + Address ediSlotAddress() { return dwordAddress(AssembledTest::EdiSlot); } |
| + Address esiSlotAddress() { return dwordAddress(AssembledTest::EsiSlot); } |
| + Address ebpSlotAddress() { return dwordAddress(AssembledTest::EbpSlot); } |
| + Address espSlotAddress() { return dwordAddress(AssembledTest::EspSlot); } |
| + Address xmm0SlotAddress() { return dwordAddress(AssembledTest::Xmm0Slot); } |
| + Address xmm1SlotAddress() { return dwordAddress(AssembledTest::Xmm1Slot); } |
| + Address xmm2SlotAddress() { return dwordAddress(AssembledTest::Xmm2Slot); } |
| + Address xmm3SlotAddress() { return dwordAddress(AssembledTest::Xmm3Slot); } |
| + Address xmm4SlotAddress() { return dwordAddress(AssembledTest::Xmm4Slot); } |
| + Address xmm5SlotAddress() { return dwordAddress(AssembledTest::Xmm5Slot); } |
| + Address xmm6SlotAddress() { return dwordAddress(AssembledTest::Xmm6Slot); } |
| + Address xmm7SlotAddress() { return dwordAddress(AssembledTest::Xmm7Slot); } |
| // Returns the displacement that should be used when accessing the specified |
| // Dword in the scratchpad area. It needs to adjust for the initial |
| @@ -449,7 +1524,7 @@ private: |
| assert(Dword < NumAllocatedDwords); |
| static constexpr uint8_t PushBytes = 1; |
| static constexpr uint8_t CallImmBytes = 5; |
| - return AssembledBuffer::MaximumCodeSize + (Dword * 4) - |
| + return AssembledTest::MaximumCodeSize + (Dword * 4) - |
| (7 * PushBytes + CallImmBytes); |
| } |
| @@ -479,6 +1554,16 @@ private: |
| __ mov(IceType_i32, edxSlotAddress(), GPRRegister::Encoded_Reg_edx); |
| __ mov(IceType_i32, ediSlotAddress(), GPRRegister::Encoded_Reg_edi); |
| __ mov(IceType_i32, esiSlotAddress(), GPRRegister::Encoded_Reg_esi); |
| + __ mov(IceType_i32, ebpSlotAddress(), GPRRegister::Encoded_Reg_ebp); |
| + __ mov(IceType_i32, espSlotAddress(), GPRRegister::Encoded_Reg_esp); |
| + __ movups(xmm0SlotAddress(), XmmRegister::Encoded_Reg_xmm0); |
| + __ movups(xmm1SlotAddress(), XmmRegister::Encoded_Reg_xmm1); |
| + __ movups(xmm2SlotAddress(), XmmRegister::Encoded_Reg_xmm2); |
| + __ movups(xmm3SlotAddress(), XmmRegister::Encoded_Reg_xmm3); |
| + __ movups(xmm4SlotAddress(), XmmRegister::Encoded_Reg_xmm4); |
| + __ movups(xmm5SlotAddress(), XmmRegister::Encoded_Reg_xmm5); |
| + __ movups(xmm6SlotAddress(), XmmRegister::Encoded_Reg_xmm6); |
| + __ movups(xmm7SlotAddress(), XmmRegister::Encoded_Reg_xmm7); |
| __ popl(GPRRegister::Encoded_Reg_ebp); |
| __ popl(GPRRegister::Encoded_Reg_esi); |
| @@ -495,6 +1580,40 @@ private: |
| uint32_t NumAllocatedDwords; |
| }; |
| +TEST_F(AssemblerX8632Test, ScratchpadGettersAndSetters) { |
| + const uint32_t S0 = allocateDword(); |
| + const uint32_t S1 = allocateDword(); |
| + const uint32_t S2 = allocateDword(); |
| + const uint32_t S3 = allocateDword(); |
| + AssembledTest test = assemble(); |
| + test.setDwordTo(S0, 0xBEEF0000u); |
| + test.setDwordTo(S1, 0xDEADu); |
| + test.setDwordTo(S2, 0x20406080u); |
| + ASSERT_EQ(0xBEEF0000u, test.contentsOfDword(S0)); |
| + ASSERT_EQ(0xDEADu, test.contentsOfDword(S1)); |
| + ASSERT_EQ(0x20406080u, test.contentsOfDword(S2)); |
| + ASSERT_EQ(0xDEADBEEF0000ull, test.contentsOfQword(S0)); |
| + ASSERT_EQ(0x204060800000DEADull, test.contentsOfQword(S1)); |
| + |
| + test.setQwordTo(S1, 0x1234567890ABCDEFull); |
| + ASSERT_EQ(0x1234567890ABCDEFull, test.contentsOfQword(S1)); |
| + test.setDwordTo(S0, 0xBEEF0000u); |
| + ASSERT_EQ(0x90ABCDEFull, test.contentsOfDword(S1)); |
| + ASSERT_EQ(0x12345678ull, test.contentsOfDword(S2)); |
| + |
| + test.setDwordTo(S0, 1.0f); |
| + ASSERT_FLOAT_EQ(1.0f, test.contentsOfDword<float>(S0)); |
| + test.setQwordTo(S0, 3.14); |
| + ASSERT_DOUBLE_EQ(3.14, test.contentsOfQword<double>(S0)); |
| + |
| + test.setDqwordTo(S0, Dqword(1.0f, 2.0f, 3.0f, 4.0f)); |
| + ASSERT_EQ(Dqword(1.0f, 2.0f, 3.0f, 4.0f), test.contentsOfDqword(S0)); |
| + EXPECT_FLOAT_EQ(1.0f, test.contentsOfDword<float>(S0)); |
| + EXPECT_FLOAT_EQ(2.0f, test.contentsOfDword<float>(S1)); |
| + EXPECT_FLOAT_EQ(3.0f, test.contentsOfDword<float>(S2)); |
| + EXPECT_FLOAT_EQ(4.0f, test.contentsOfDword<float>(S3)); |
| +} |
| + |
| TEST_F(AssemblerX8632Test, MovRegImm) { |
| constexpr uint32_t ExpectedEax = 0x000000FFul; |
| constexpr uint32_t ExpectedEbx = 0x0000FF00ul; |
| @@ -510,7 +1629,7 @@ TEST_F(AssemblerX8632Test, MovRegImm) { |
| __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(ExpectedEdi)); |
| __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(ExpectedEsi)); |
| - AssembledBuffer test = assemble(); |
| + AssembledTest test = assemble(); |
| test.run(); |
| EXPECT_EQ(ExpectedEax, test.eax()); |
| EXPECT_EQ(ExpectedEbx, test.ebx()); |
| @@ -535,7 +1654,7 @@ TEST_F(AssemblerX8632Test, MovMemImm) { |
| __ mov(IceType_i32, dwordAddress(T2), Immediate(ExpectedT2)); |
| __ mov(IceType_i32, dwordAddress(T3), Immediate(ExpectedT3)); |
| - AssembledBuffer test = assemble(); |
| + AssembledTest test = assemble(); |
| test.run(); |
| EXPECT_EQ(0ul, test.eax()); |
| EXPECT_EQ(0ul, test.ebx()); |
| @@ -576,7 +1695,7 @@ TEST_F(AssemblerX8632Test, MovMemReg) { |
| __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(ExpectedT5)); |
| __ mov(IceType_i32, dwordAddress(T5), GPRRegister::Encoded_Reg_esi); |
| - AssembledBuffer test = assemble(); |
| + AssembledTest test = assemble(); |
| test.run(); |
| EXPECT_EQ(ExpectedT0, test.contentsOfDword(T0)); |
| EXPECT_EQ(ExpectedT1, test.contentsOfDword(T1)); |
| @@ -603,7 +1722,7 @@ TEST_F(AssemblerX8632Test, MovRegReg) { |
| __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, |
| GPRRegister::Encoded_Reg_esi); |
| - AssembledBuffer test = assemble(); |
| + AssembledTest test = assemble(); |
| test.run(); |
| EXPECT_EQ(0x55000000ul, test.eax()); |
| EXPECT_EQ(0x20ul, test.ebx()); |
| @@ -645,7 +1764,7 @@ TEST_F(AssemblerX8632Test, MovRegMem) { |
| __ mov(IceType_i32, dwordAddress(T5), Immediate(ExpectedT5)); |
| __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, dwordAddress(T5)); |
| - AssembledBuffer test = assemble(); |
| + AssembledTest test = assemble(); |
| test.run(); |
| EXPECT_EQ(ExpectedT0, test.eax()); |
| EXPECT_EQ(ExpectedT1, test.ebx()); |
| @@ -658,7 +1777,7 @@ TEST_F(AssemblerX8632Test, MovRegMem) { |
| TEST_F(AssemblerX8632Test, J) { |
| #define TestJ(C, Near, Src0, Value0, Src1, Value1, Dest) \ |
| do { \ |
| - const bool NearJmp = std::strcmp(#Near, "Near") == 0; \ |
| + const bool NearJmp = AssemblerX8632::k##Near##Jump; \ |
| Label ShouldBeTaken; \ |
| __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src0, Immediate(Value0)); \ |
| __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src1, Immediate(Value1)); \ |
| @@ -668,7 +1787,7 @@ TEST_F(AssemblerX8632Test, J) { |
| __ j(Cond::Br_##C, &ShouldBeTaken, NearJmp); \ |
| __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(0xC0FFEE)); \ |
| __ bind(&ShouldBeTaken); \ |
| - AssembledBuffer test = assemble(); \ |
| + AssembledTest test = assemble(); \ |
| test.run(); \ |
| EXPECT_EQ(Value0, test.Src0()) << "Br_" #C ", " #Near; \ |
| EXPECT_EQ(Value1, test.Src1()) << "Br_" #C ", " #Near; \ |
| @@ -712,6 +1831,5455 @@ TEST_F(AssemblerX8632Test, J) { |
| #undef TestJ |
| } |
| +TEST_F(AssemblerX8632Test, FstpSt) { |
| +#define TestFstpSt(Size, MemorySize, Type) \ |
| + do { \ |
| + const uint32_t T1 = allocate##MemorySize(); \ |
| + const Type OldValue1 = -1.0f; \ |
| + const uint32_t T2 = allocate##MemorySize(); \ |
| + const Type OldValue2 = -2.0f; \ |
| + const uint32_t T3 = allocate##MemorySize(); \ |
| + const Type OldValue3 = -3.0f; \ |
| + const uint32_t T4 = allocate##MemorySize(); \ |
| + const Type OldValue4 = -4.0f; \ |
| + const uint32_t T5 = allocate##MemorySize(); \ |
| + const Type OldValue5 = -5.0f; \ |
| + const uint32_t T6 = allocate##MemorySize(); \ |
| + const Type OldValue6 = -6.0f; \ |
| + const uint32_t T7 = allocate##MemorySize(); \ |
| + const Type OldValue7 = -7.0f; \ |
| + \ |
| + const uint32_t N7 = allocate##MemorySize(); \ |
| + constexpr Type NewValue7 = 777.77f; \ |
| + const uint32_t N6 = allocate##MemorySize(); \ |
| + constexpr Type NewValue6 = 666.66f; \ |
| + const uint32_t N5 = allocate##MemorySize(); \ |
| + constexpr Type NewValue5 = 555.55f; \ |
| + const uint32_t N4 = allocate##MemorySize(); \ |
| + constexpr Type NewValue4 = 444.44f; \ |
| + const uint32_t N3 = allocate##MemorySize(); \ |
| + constexpr Type NewValue3 = 333.33f; \ |
| + const uint32_t N2 = allocate##MemorySize(); \ |
| + constexpr Type NewValue2 = 222.22f; \ |
| + const uint32_t N1 = allocate##MemorySize(); \ |
| + constexpr Type NewValue1 = 111.11f; \ |
| + \ |
| + __ fincstp(); \ |
| + __ fincstp(); \ |
| + __ fincstp(); \ |
| + __ fincstp(); \ |
| + __ fincstp(); \ |
| + __ fincstp(); \ |
| + __ fincstp(); \ |
| + \ |
| + __ fld(IceType_f##Size, dwordAddress(N7)); \ |
| + __ fstp(X87STRegister::Encoded_X87ST_7); \ |
| + __ fld(IceType_f##Size, dwordAddress(N6)); \ |
| + __ fstp(X87STRegister::Encoded_X87ST_6); \ |
| + __ fld(IceType_f##Size, dwordAddress(N5)); \ |
| + __ fstp(X87STRegister::Encoded_X87ST_5); \ |
| + __ fld(IceType_f##Size, dwordAddress(N4)); \ |
| + __ fstp(X87STRegister::Encoded_X87ST_4); \ |
| + __ fld(IceType_f##Size, dwordAddress(N3)); \ |
| + __ fstp(X87STRegister::Encoded_X87ST_3); \ |
| + __ fld(IceType_f##Size, dwordAddress(N2)); \ |
| + __ fstp(X87STRegister::Encoded_X87ST_2); \ |
| + __ fld(IceType_f##Size, dwordAddress(N1)); \ |
| + __ fstp(X87STRegister::Encoded_X87ST_1); \ |
| + \ |
| + __ fstp(IceType_f##Size, dwordAddress(T1)); \ |
| + __ fstp(IceType_f##Size, dwordAddress(T2)); \ |
| + __ fstp(IceType_f##Size, dwordAddress(T3)); \ |
| + __ fstp(IceType_f##Size, dwordAddress(T4)); \ |
| + __ fstp(IceType_f##Size, dwordAddress(T5)); \ |
| + __ fstp(IceType_f##Size, dwordAddress(T6)); \ |
| + __ fstp(IceType_f##Size, dwordAddress(T7)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.set##MemorySize##To(T1, OldValue1); \ |
| + test.set##MemorySize##To(N1, NewValue1); \ |
| + test.set##MemorySize##To(T2, OldValue2); \ |
| + test.set##MemorySize##To(N2, NewValue2); \ |
| + test.set##MemorySize##To(T3, OldValue3); \ |
| + test.set##MemorySize##To(N3, NewValue3); \ |
| + test.set##MemorySize##To(T4, OldValue4); \ |
| + test.set##MemorySize##To(N4, NewValue4); \ |
| + test.set##MemorySize##To(T5, OldValue5); \ |
| + test.set##MemorySize##To(N5, NewValue5); \ |
| + test.set##MemorySize##To(T6, OldValue6); \ |
| + test.set##MemorySize##To(N6, NewValue6); \ |
| + test.set##MemorySize##To(T7, OldValue7); \ |
| + test.set##MemorySize##To(N7, NewValue7); \ |
| + \ |
| + test.run(); \ |
| + \ |
| + ASSERT_FLOAT_EQ(NewValue1, test.contentsOf##MemorySize<Type>(T1)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue1, test.contentsOf##MemorySize<Type>(N1)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue2, test.contentsOf##MemorySize<Type>(T2)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue2, test.contentsOf##MemorySize<Type>(N2)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue3, test.contentsOf##MemorySize<Type>(T3)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue3, test.contentsOf##MemorySize<Type>(N3)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue4, test.contentsOf##MemorySize<Type>(T4)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue4, test.contentsOf##MemorySize<Type>(N4)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue5, test.contentsOf##MemorySize<Type>(T5)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue5, test.contentsOf##MemorySize<Type>(N5)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue6, test.contentsOf##MemorySize<Type>(T6)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue6, test.contentsOf##MemorySize<Type>(N6)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue7, test.contentsOf##MemorySize<Type>(T7)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + ASSERT_FLOAT_EQ(NewValue7, test.contentsOf##MemorySize<Type>(N7)) \ |
| + << "(" #Size ", " #MemorySize ", " #Type ")"; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestFstpSt(32, Dword, float); |
| + TestFstpSt(64, Qword, double); |
| + |
| +#undef TestFstpSt |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Fild) { |
| +#define TestFild(OperandType, Size, MemorySize, FpType, IntType) \ |
| + do { \ |
| + const uint32_t T0 = allocate##MemorySize(); \ |
| + constexpr IntType V0 = 0x1234; \ |
| + \ |
| + __ fild##OperandType(dwordAddress(T0)); \ |
| + __ fstp(IceType_f##Size, dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + \ |
| + test.set##MemorySize##To(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_FLOAT_EQ(static_cast<FpType>(V0), \ |
| + test.contentsOf##MemorySize<FpType>(T0)) \ |
| + << "(" #OperandType ", " #Size ", " #MemorySize ", " #FpType \ |
| + ", " #IntType ")"; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestFild(s, 32, Dword, float, uint32_t); |
| + TestFild(l, 64, Qword, double, uint64_t); |
| +#undef TestFild |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Fistp) { |
| +#define TestFistp(OperandType, Size, MemorySize, FpType, IntType) \ |
| + do { \ |
| + const uint32_t T0 = allocate##MemorySize(); \ |
| + constexpr IntType V0 = 0x1234; \ |
| + const uint32_t T1 = allocate##MemorySize(); \ |
| + constexpr IntType V1 = 0xFFFF; \ |
| + \ |
| + __ fild##OperandType(dwordAddress(T0)); \ |
| + __ fistp##OperandType(dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + \ |
| + test.set##MemorySize##To(T0, V0); \ |
| + test.set##MemorySize##To(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(static_cast<IntType>(V0), \ |
| + test.contentsOf##MemorySize<IntType>(T0)) \ |
| + << "(" #OperandType ", " #Size ", " #MemorySize ", " #FpType \ |
| + ", " #IntType ")"; \ |
| + ASSERT_EQ(static_cast<IntType>(V0), \ |
| + test.contentsOf##MemorySize<IntType>(T1)) \ |
| + << "(" #OperandType ", " #Size ", " #MemorySize ", " #FpType \ |
| + ", " #IntType ")"; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestFistp(s, 32, Dword, float, uint32_t); |
| + TestFistp(l, 64, Qword, double, uint64_t); |
| +#undef TestFistp |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, PopAddr) { |
| + const uint32_t T0 = allocateDword(); |
| + constexpr uint32_t V0 = 0xEFAB; |
| + |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0xC0FFEE)); |
| + __ pushl(GPRRegister::Encoded_Reg_eax); |
| + __ popl(dwordAddress(T0)); |
| + |
| + AssembledTest test = assemble(); |
| + test.setDwordTo(T0, V0); |
| + |
| + test.run(); |
| + |
| + ASSERT_EQ(0xC0FFEEul, test.contentsOfDword(T0)); |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, SetCC) { |
| +#define TestSetCC(C, Src0, Value0, Src1, Value1, Dest, IsTrue) \ |
| + do { \ |
| + const uint32_t T0 = allocateDword(); \ |
| + constexpr uint32_t V0 = 0xF00F00; \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src0, Immediate(Value0)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src1, Immediate(Value1)); \ |
| + __ cmp(IceType_i32, GPRRegister::Encoded_Reg_##Src0, \ |
| + GPRRegister::Encoded_Reg_##Src1); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(0)); \ |
| + __ setcc(Cond::Br_##C, \ |
| + RegX8632::getEncodedByteReg(GPRRegister::Encoded_Reg_##Dest)); \ |
| + __ setcc(Cond::Br_##C, dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + \ |
| + test.run(); \ |
| + \ |
| + EXPECT_EQ(IsTrue, test.Dest()) \ |
| + << "(" #C ", " #Src0 ", " #Value0 ", " #Src1 ", " #Value1 ", " #Dest \ |
| + ", " #IsTrue ")"; \ |
| + EXPECT_EQ((0xF00F00 | IsTrue), test.contentsOfDword(T0)) \ |
| + << "(" #C ", " #Src0 ", " #Value0 ", " #Src1 ", " #Value1 ", " #Dest \ |
| + ", " #IsTrue ")"; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestSetCC(o, eax, 0x80000000u, ebx, 0x1u, ecx, 1u); |
| + TestSetCC(o, eax, 0x1u, ebx, 0x10000000u, ecx, 0u); |
| + |
| + TestSetCC(no, ebx, 0x1u, ecx, 0x10000000u, edx, 1u); |
| + TestSetCC(no, ebx, 0x80000000u, ecx, 0x1u, edx, 0u); |
| + |
| + TestSetCC(b, ecx, 0x1, edx, 0x80000000u, eax, 1u); |
| + TestSetCC(b, ecx, 0x80000000u, edx, 0x1u, eax, 0u); |
| + |
| + TestSetCC(ae, edx, 0x80000000u, edi, 0x1u, ebx, 1u); |
| + TestSetCC(ae, edx, 0x1u, edi, 0x80000000u, ebx, 0u); |
| + |
| + TestSetCC(e, edi, 0x1u, esi, 0x1u, ecx, 1u); |
| + TestSetCC(e, edi, 0x1u, esi, 0x11111u, ecx, 0u); |
| + |
| + TestSetCC(ne, esi, 0x80000000u, eax, 0x1u, edx, 1u); |
| + TestSetCC(ne, esi, 0x1u, eax, 0x1u, edx, 0u); |
| + |
| + TestSetCC(be, eax, 0x1u, ebx, 0x80000000u, eax, 1u); |
| + TestSetCC(be, eax, 0x80000000u, ebx, 0x1u, eax, 0u); |
| + |
| + TestSetCC(a, ebx, 0x80000000u, ecx, 0x1u, ebx, 1u); |
| + TestSetCC(a, ebx, 0x1u, ecx, 0x80000000u, ebx, 0u); |
| + |
| + TestSetCC(s, ecx, 0x1u, edx, 0x80000000u, ecx, 1u); |
| + TestSetCC(s, ecx, 0x80000000u, edx, 0x1u, ecx, 0u); |
| + |
| + TestSetCC(ns, edx, 0x80000000u, edi, 0x1u, ecx, 1u); |
| + TestSetCC(ns, edx, 0x1u, edi, 0x80000000u, ecx, 0u); |
| + |
| + TestSetCC(p, edi, 0x80000000u, esi, 0x1u, edx, 1u); |
| + TestSetCC(p, edi, 0x1u, esi, 0x80000000u, edx, 0u); |
| + |
| + TestSetCC(np, esi, 0x1u, edi, 0x80000000u, eax, 1u); |
| + TestSetCC(np, esi, 0x80000000u, edi, 0x1u, eax, 0u); |
| + |
| + TestSetCC(l, edi, 0x80000000u, eax, 0x1u, ebx, 1u); |
| + TestSetCC(l, edi, 0x1u, eax, 0x80000000u, ebx, 0u); |
| + |
| + TestSetCC(ge, eax, 0x1u, ebx, 0x80000000u, ecx, 1u); |
| + TestSetCC(ge, eax, 0x80000000u, ebx, 0x1u, ecx, 0u); |
| + |
| + TestSetCC(le, ebx, 0x80000000u, ecx, 0x1u, edx, 1u); |
| + TestSetCC(le, ebx, 0x1u, ecx, 0x80000000u, edx, 0u); |
| + |
| +#undef TestSetCC |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, CallImm) { |
| + __ call(Immediate(16)); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0xf00f)); |
| + __ popl(GPRRegister::Encoded_Reg_ebx); |
| + |
| + AssembledTest test = assemble(); |
| + |
| + test.run(); |
| + |
| + EXPECT_EQ(0xF00Fu, test.eax()); |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, CallReg) { |
| + __ call(Immediate(16)); |
| + __ popl(GPRRegister::Encoded_Reg_edx); |
| + __ pushl(GPRRegister::Encoded_Reg_edx); |
| + __ ret(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ popl(GPRRegister::Encoded_Reg_ebx); |
| + __ call(GPRRegister::Encoded_Reg_ebx); |
| + |
| + AssembledTest test = assemble(); |
| + |
| + test.run(); |
| + |
| + EXPECT_EQ(15u, test.edx() - test.ebx()); |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, CallAddr) { |
| + __ call(Immediate(16)); |
| + __ mov(IceType_i8, GPRRegister::Encoded_Reg_eax, Immediate(0xf4)); |
| + __ ret(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ hlt(); |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0xf1f2f300)); |
| + __ call(Address(GPRRegister::Encoded_Reg_esp, 0)); |
| + __ popl(GPRRegister::Encoded_Reg_edx); |
| + |
| + AssembledTest test = assemble(); |
| + |
| + test.run(); |
| + |
| + EXPECT_EQ(0xf1f2f3f4, test.eax()); |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Movzx) { |
| +#define TestMovzx8bitWithRegDest(Src, Dst, Imm) \ |
| + do { \ |
| + static_assert(((Imm)&0xFF) == (Imm), #Imm " is not an 8bit immediate"); \ |
| + __ mov(IceType_i8, GPRRegister::Encoded_Reg_##Src, Immediate(Imm)); \ |
| + __ movzx(IceType_i8, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + ASSERT_EQ(Imm, test.Dst()) << "(" #Src ", " #Dst ", " #Imm ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovzx16bitWithRegDest(Src, Dst, Imm) \ |
| + do { \ |
| + static_assert(((Imm)&0xFFFF) == (Imm), #Imm " is not a 16bit immediate"); \ |
| + __ mov(IceType_i16, GPRRegister::Encoded_Reg_##Src, Immediate(Imm)); \ |
| + __ movzx(IceType_i16, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + ASSERT_EQ(Imm, test.Dst()) << "(" #Src ", " #Dst ", " #Imm ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovzx8bitWithAddrSrc(Dst, Imm) \ |
| + do { \ |
| + static_assert(((Imm)&0xFF) == (Imm), #Imm " is not an 8bit immediate"); \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Imm; \ |
| + __ movzx(IceType_i8, GPRRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + ASSERT_EQ(Imm, test.Dst()) << "(Addr, " #Dst ", " #Imm ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovzx16bitWithAddrSrc(Dst, Imm) \ |
| + do { \ |
| + static_assert(((Imm)&0xFFFF) == (Imm), #Imm " is not a 16bit immediate"); \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Imm; \ |
| + __ movzx(IceType_i16, GPRRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + ASSERT_EQ(Imm, test.Dst()) << "(Addr, " #Dst ", " #Imm ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovzx(Dst) \ |
| + do { \ |
| + TestMovzx8bitWithRegDest(eax, Dst, 0x81u); \ |
| + TestMovzx8bitWithRegDest(ebx, Dst, 0x82u); \ |
| + TestMovzx8bitWithRegDest(ecx, Dst, 0x83u); \ |
| + TestMovzx8bitWithRegDest(edx, Dst, 0x84u); \ |
| + /* esi is encoded as dh */ \ |
| + TestMovzx8bitWithRegDest(esi, Dst, 0x85u); \ |
| + /* edi is encoded as bh */ \ |
| + TestMovzx8bitWithRegDest(edi, Dst, 0x86u); \ |
| + /* ebp is encoded as ch */ \ |
| + TestMovzx8bitWithRegDest(ebp, Dst, 0x87u); \ |
| + /* esp is encoded as ah */ \ |
| + TestMovzx8bitWithRegDest(esp, Dst, 0x88u); \ |
| + TestMovzx8bitWithAddrSrc(Dst, 0x8Fu); \ |
| + \ |
| + TestMovzx16bitWithRegDest(eax, Dst, 0x8118u); \ |
| + TestMovzx16bitWithRegDest(ebx, Dst, 0x8228u); \ |
| + TestMovzx16bitWithRegDest(ecx, Dst, 0x8338u); \ |
| + TestMovzx16bitWithRegDest(edx, Dst, 0x8448u); \ |
| + TestMovzx16bitWithAddrSrc(Dst, 0x8FF8u); \ |
| + } while (0) |
| + |
| + TestMovzx(eax); |
| + TestMovzx(ebx); |
| + TestMovzx(ecx); |
| + TestMovzx(edx); |
| + TestMovzx(esi); |
| + TestMovzx(edi); |
| + |
| +#undef TestMovzx |
| +#undef TestMovzx16bitWithAddrDest |
| +#undef TestMovzx8bitWithAddrDest |
| +#undef TestMovzx16bitWithRegDest |
| +#undef TestMovzx8bitWithRegDest |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Movsx) { |
| +#define TestMovsx8bitWithRegDest(Src, Dst, Imm) \ |
| + do { \ |
| + static_assert(((Imm)&0xFF) == (Imm), #Imm " is not an 8bit immediate"); \ |
| + __ mov(IceType_i8, GPRRegister::Encoded_Reg_##Src, Immediate(Imm)); \ |
| + __ movsx(IceType_i8, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + ASSERT_EQ((0xFFFFFF00 | (Imm)), test.Dst()) \ |
| + << "(" #Src ", " #Dst ", " #Imm ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovsx16bitWithRegDest(Src, Dst, Imm) \ |
| + do { \ |
| + static_assert(((Imm)&0xFFFF) == (Imm), #Imm " is not a 16bit immediate"); \ |
| + __ mov(IceType_i16, GPRRegister::Encoded_Reg_##Src, Immediate(Imm)); \ |
| + __ movsx(IceType_i16, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + ASSERT_EQ((0xFFFF0000 | (Imm)), test.Dst()) \ |
| + << "(" #Src ", " #Dst ", " #Imm ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovsx8bitWithAddrSrc(Dst, Imm) \ |
| + do { \ |
| + static_assert(((Imm)&0xFF) == (Imm), #Imm " is not an 8bit immediate"); \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Imm; \ |
| + __ movsx(IceType_i8, GPRRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + ASSERT_EQ((0xFFFFFF00 | (Imm)), test.Dst()) \ |
| + << "(Addr, " #Dst ", " #Imm ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovsx16bitWithAddrSrc(Dst, Imm) \ |
| + do { \ |
| + static_assert(((Imm)&0xFFFF) == (Imm), #Imm " is not a 16bit immediate"); \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Imm; \ |
| + __ movsx(IceType_i16, GPRRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + ASSERT_EQ((0xFFFF0000 | (Imm)), test.Dst()) \ |
| + << "(Addr, " #Dst ", " #Imm ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovsx(Dst) \ |
| + do { \ |
| + TestMovsx8bitWithRegDest(eax, Dst, 0x81u); \ |
| + TestMovsx8bitWithRegDest(ebx, Dst, 0x82u); \ |
| + TestMovsx8bitWithRegDest(ecx, Dst, 0x83u); \ |
| + TestMovsx8bitWithRegDest(edx, Dst, 0x84u); \ |
| + /* esi is encoded as dh */ \ |
| + TestMovsx8bitWithRegDest(esi, Dst, 0x85u); \ |
| + /* edi is encoded as bh */ \ |
| + TestMovsx8bitWithRegDest(edi, Dst, 0x86u); \ |
| + /* ebp is encoded as ch */ \ |
| + TestMovsx8bitWithRegDest(ebp, Dst, 0x87u); \ |
| + /* esp is encoded as ah */ \ |
| + TestMovsx8bitWithRegDest(esp, Dst, 0x88u); \ |
| + TestMovsx8bitWithAddrSrc(Dst, 0x8Fu); \ |
| + \ |
| + TestMovsx16bitWithRegDest(eax, Dst, 0x8118u); \ |
| + TestMovsx16bitWithRegDest(ebx, Dst, 0x8228u); \ |
| + TestMovsx16bitWithRegDest(ecx, Dst, 0x8338u); \ |
| + TestMovsx16bitWithRegDest(edx, Dst, 0x8448u); \ |
| + TestMovsx16bitWithAddrSrc(Dst, 0x8FF8u); \ |
| + } while (0) |
| + |
| + TestMovsx(eax); |
| + TestMovsx(ebx); |
| + TestMovsx(ecx); |
| + TestMovsx(edx); |
| + TestMovsx(esi); |
| + TestMovsx(edi); |
| + |
| +#undef TestMovsx |
| +#undef TestMovsx16bitWithAddrDest |
| +#undef TestMovsx8bitWithAddrDest |
| +#undef TestMovsx16bitWithRegDest |
| +#undef TestMovsx8bitWithRegDest |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Lea) { |
| +#define TestLeaBaseDisp(Base, BaseValue, Disp, Dst) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Base ", " #BaseValue ", " #Dst ")"; \ |
| + if (GPRRegister::Encoded_Reg_##Base != GPRRegister::Encoded_Reg_esp && \ |
| + GPRRegister::Encoded_Reg_##Base != GPRRegister::Encoded_Reg_ebp) { \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Base, \ |
| + Immediate(BaseValue)); \ |
| + } \ |
| + __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \ |
| + Address(GPRRegister::Encoded_Reg_##Base, Disp)); \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + ASSERT_EQ(test.Base() + (Disp), test.Dst()) << TestString << " with Disp " \ |
| + << Disp; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestLeaIndex32bitDisp(Index, IndexValue, Disp, Dst0, Dst1, Dst2, Dst3) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Index ", " #IndexValue ", " #Dst0 ", " #Dst1 ", " #Dst2 \ |
| + ", " #Dst3 ")"; \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Index, \ |
| + Immediate(IndexValue)); \ |
| + __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst0, \ |
| + Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_1, Disp)); \ |
| + __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst1, \ |
| + Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_2, Disp)); \ |
| + __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst2, \ |
| + Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_4, Disp)); \ |
| + __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst3, \ |
| + Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_8, Disp)); \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + ASSERT_EQ((test.Index() << Traits::TIMES_1) + (Disp), test.Dst0()) \ |
| + << TestString << " " << Disp; \ |
| + ASSERT_EQ((test.Index() << Traits::TIMES_2) + (Disp), test.Dst1()) \ |
| + << TestString << " " << Disp; \ |
| + ASSERT_EQ((test.Index() << Traits::TIMES_4) + (Disp), test.Dst2()) \ |
| + << TestString << " " << Disp; \ |
| + ASSERT_EQ((test.Index() << Traits::TIMES_8) + (Disp), test.Dst3()) \ |
| + << TestString << " " << Disp; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestLeaBaseIndexDisp(Base, BaseValue, Index, IndexValue, Disp, Dst0, \ |
| + Dst1, Dst2, Dst3) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Base ", " #BaseValue ", " #Index ", " #IndexValue ", " #Dst0 \ |
| + ", " #Dst1 ", " #Dst2 ", " #Dst3 ")"; \ |
| + if (GPRRegister::Encoded_Reg_##Base != GPRRegister::Encoded_Reg_esp && \ |
| + GPRRegister::Encoded_Reg_##Base != GPRRegister::Encoded_Reg_ebp) { \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Base, \ |
| + Immediate(BaseValue)); \ |
| + } \ |
| + /* esp is not a valid index register. */ \ |
| + if (GPRRegister::Encoded_Reg_##Index != GPRRegister::Encoded_Reg_ebp) { \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Index, \ |
| + Immediate(IndexValue)); \ |
| + } \ |
| + __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst0, \ |
| + Address(GPRRegister::Encoded_Reg_##Base, \ |
| + GPRRegister::Encoded_Reg_##Index, Traits::TIMES_1, Disp)); \ |
| + __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst1, \ |
| + Address(GPRRegister::Encoded_Reg_##Base, \ |
| + GPRRegister::Encoded_Reg_##Index, Traits::TIMES_2, Disp)); \ |
| + __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst2, \ |
| + Address(GPRRegister::Encoded_Reg_##Base, \ |
| + GPRRegister::Encoded_Reg_##Index, Traits::TIMES_4, Disp)); \ |
| + __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst3, \ |
| + Address(GPRRegister::Encoded_Reg_##Base, \ |
| + GPRRegister::Encoded_Reg_##Index, Traits::TIMES_8, Disp)); \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + uint32_t ExpectedIndexValue = test.Index(); \ |
| + if (GPRRegister::Encoded_Reg_##Index == GPRRegister::Encoded_Reg_esp) { \ |
| + ExpectedIndexValue = 0; \ |
| + } \ |
| + ASSERT_EQ(test.Base() + (ExpectedIndexValue << Traits::TIMES_1) + (Disp), \ |
| + test.Dst0()) \ |
| + << TestString << " " << Disp; \ |
| + ASSERT_EQ(test.Base() + (ExpectedIndexValue << Traits::TIMES_2) + (Disp), \ |
| + test.Dst1()) \ |
| + << TestString << " " << Disp; \ |
| + ASSERT_EQ(test.Base() + (ExpectedIndexValue << Traits::TIMES_4) + (Disp), \ |
| + test.Dst2()) \ |
| + << TestString << " " << Disp; \ |
| + ASSERT_EQ(test.Base() + (ExpectedIndexValue << Traits::TIMES_8) + (Disp), \ |
| + test.Dst3()) \ |
| + << TestString << " " << Disp; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + for (const int32_t Disp : |
| + {0x00, 0x06, -0x06, 0x0600, -0x6000, 0x6000000, -0x6000000}) { |
| + TestLeaBaseDisp(eax, 0x10000Fu, Disp, ebx); |
| + TestLeaBaseDisp(ebx, 0x20000Fu, Disp, ecx); |
| + TestLeaBaseDisp(ecx, 0x30000Fu, Disp, edx); |
| + TestLeaBaseDisp(edx, 0x40000Fu, Disp, esi); |
| + TestLeaBaseDisp(esi, 0x50000Fu, Disp, edi); |
| + TestLeaBaseDisp(edi, 0x60000Fu, Disp, eax); |
| + TestLeaBaseDisp(esp, 0x11000Fu, Disp, eax); |
| + TestLeaBaseDisp(ebp, 0x22000Fu, Disp, ecx); |
| + } |
| + |
| + // esp is not a valid index register. |
| + // ebp is not valid in this addressing mode (rm = 0). |
| + for (const int32_t Disp : |
| + {0x00, 0x06, -0x06, 0x0600, -0x6000, 0x6000000, -0x6000000}) { |
| + TestLeaIndex32bitDisp(eax, 0x2000u, Disp, ebx, ecx, edx, esi); |
| + TestLeaIndex32bitDisp(ebx, 0x4000u, Disp, ecx, edx, esi, edi); |
| + TestLeaIndex32bitDisp(ecx, 0x6000u, Disp, edx, esi, edi, eax); |
| + TestLeaIndex32bitDisp(edx, 0x8000u, Disp, esi, edi, eax, ebx); |
| + TestLeaIndex32bitDisp(esi, 0xA000u, Disp, edi, eax, ebx, ecx); |
| + TestLeaIndex32bitDisp(edi, 0xC000u, Disp, eax, ebx, ecx, edx); |
| + } |
| + |
| + for (const int32_t Disp : |
| + {0x00, 0x06, -0x06, 0x0600, -0x6000, 0x6000000, -0x6000000}) { |
| + TestLeaBaseIndexDisp(eax, 0x100000u, ebx, 0x600u, Disp, ecx, edx, esi, edi); |
| + TestLeaBaseIndexDisp(ebx, 0x200000u, ecx, 0x500u, Disp, edx, esi, edi, eax); |
| + TestLeaBaseIndexDisp(ecx, 0x300000u, edx, 0x400u, Disp, esi, edi, eax, ebx); |
| + TestLeaBaseIndexDisp(edx, 0x400000u, esi, 0x300u, Disp, edi, eax, ebx, ecx); |
| + TestLeaBaseIndexDisp(esi, 0x500000u, edi, 0x200u, Disp, eax, ebx, ecx, edx); |
| + TestLeaBaseIndexDisp(edi, 0x600000u, eax, 0x100u, Disp, ebx, ecx, edx, esi); |
| + |
| + /* Initializers are ignored when Src[01] is ebp/esp. */ |
| + TestLeaBaseIndexDisp(esp, 0, ebx, 0x6000u, Disp, ecx, edx, esi, edi); |
| + TestLeaBaseIndexDisp(esp, 0, ecx, 0x5000u, Disp, edx, esi, edi, eax); |
| + TestLeaBaseIndexDisp(esp, 0, edx, 0x4000u, Disp, esi, edi, eax, ebx); |
| + TestLeaBaseIndexDisp(esp, 0, esi, 0x3000u, Disp, edi, eax, ebx, ecx); |
| + TestLeaBaseIndexDisp(esp, 0, edi, 0x2000u, Disp, eax, ebx, ecx, edx); |
| + TestLeaBaseIndexDisp(esp, 0, eax, 0x1000u, Disp, ebx, ecx, edx, esi); |
| + |
| + TestLeaBaseIndexDisp(ebp, 0, ebx, 0x6000u, Disp, ecx, edx, esi, edi); |
| + TestLeaBaseIndexDisp(ebp, 0, ecx, 0x5000u, Disp, edx, esi, edi, eax); |
| + TestLeaBaseIndexDisp(ebp, 0, edx, 0x4000u, Disp, esi, edi, eax, ebx); |
| + TestLeaBaseIndexDisp(ebp, 0, esi, 0x3000u, Disp, edi, eax, ebx, ecx); |
| + TestLeaBaseIndexDisp(ebp, 0, edi, 0x2000u, Disp, eax, ebx, ecx, edx); |
| + TestLeaBaseIndexDisp(ebp, 0, eax, 0x1000u, Disp, ebx, ecx, edx, esi); |
| + |
| + TestLeaBaseIndexDisp(eax, 0x1000000u, ebp, 0, Disp, ecx, edx, esi, edi); |
| + TestLeaBaseIndexDisp(ebx, 0x2000000u, ebp, 0, Disp, edx, esi, edi, eax); |
| + TestLeaBaseIndexDisp(ecx, 0x3000000u, ebp, 0, Disp, esi, edi, eax, ebx); |
| + TestLeaBaseIndexDisp(edx, 0x4000000u, ebp, 0, Disp, edi, eax, ebx, ecx); |
| + TestLeaBaseIndexDisp(esi, 0x5000000u, ebp, 0, Disp, eax, ebx, ecx, edx); |
| + TestLeaBaseIndexDisp(edi, 0x6000000u, ebp, 0, Disp, ebx, ecx, edx, esi); |
| + |
| + TestLeaBaseIndexDisp(esp, 0, ebp, 0, Disp, ebx, ecx, edx, esi); |
| + } |
| + |
| +// Absolute addressing mode is tested in the Low Level tests. The encoding used |
| +// by the assembler has different meanings in x86-32 and x86-64. |
| +#undef TestLeaBaseIndexDisp |
| +#undef TestLeaScaled32bitDisp |
| +#undef TestLeaBaseDisp |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, LeaAbsolute) { |
| +#define TestLeaAbsolute(Dst, Value) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", " #Value ")"; \ |
| + __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \ |
| + Address(Address::ABSOLUTE, Value)); \ |
| + static constexpr uint32_t ByteCount = 6; \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \ |
| + static constexpr uint8_t Opcode = 0x8D; \ |
| + static constexpr uint8_t ModRM = \ |
| + /*mod=*/0x00 | /*reg*/ (GPRRegister::Encoded_Reg_##Dst << 3) | \ |
| + /*rm*/ GPRRegister::Encoded_Reg_ebp; \ |
| + verifyBytes<ByteCount>(codeBytes(), Opcode, ModRM, (Value)&0xFF, \ |
| + (Value >> 8) & 0xFF, (Value >> 16) & 0xFF, \ |
| + (Value >> 24) & 0xFF); \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestLeaAbsolute(eax, 0x11BEEF22); |
| + TestLeaAbsolute(ebx, 0x33BEEF44); |
| + TestLeaAbsolute(ecx, 0x55BEEF66); |
| + TestLeaAbsolute(edx, 0x77BEEF88); |
| + TestLeaAbsolute(esi, 0x99BEEFAA); |
| + TestLeaAbsolute(edi, 0xBBBEEFBB); |
| + |
| +#undef TesLeaAbsolute |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, CmovRegReg) { |
| +#define TestCmovRegReg(C, Src0, Value0, Src1, Value1, Dest, IsTrue) \ |
| + do { \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src0, Immediate(Value0)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src1, Immediate(Value1)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(Value0)); \ |
| + __ cmp(IceType_i32, GPRRegister::Encoded_Reg_##Src0, \ |
| + GPRRegister::Encoded_Reg_##Src1); \ |
| + __ cmov(IceType_i32, Cond::Br_##C, GPRRegister::Encoded_Reg_##Dest, \ |
| + GPRRegister::Encoded_Reg_##Src1); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + ASSERT_EQ((IsTrue) ? (Value1) : (Value0), test.Dest()) \ |
| + << "(" #C ", " #Src0 ", " #Value0 ", " #Src1 ", " #Value1 ", " #Dest \ |
| + ", " #IsTrue ")"; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestCmovRegReg(o, eax, 0x80000000u, ebx, 0x1u, ecx, 1u); |
| + TestCmovRegReg(o, eax, 0x1u, ebx, 0x10000000u, ecx, 0u); |
| + |
| + TestCmovRegReg(no, ebx, 0x1u, ecx, 0x10000000u, edx, 1u); |
| + TestCmovRegReg(no, ebx, 0x80000000u, ecx, 0x1u, edx, 0u); |
| + |
| + TestCmovRegReg(b, ecx, 0x1, edx, 0x80000000u, eax, 1u); |
| + TestCmovRegReg(b, ecx, 0x80000000u, edx, 0x1u, eax, 0u); |
| + |
| + TestCmovRegReg(ae, edx, 0x80000000u, edi, 0x1u, ebx, 1u); |
| + TestCmovRegReg(ae, edx, 0x1u, edi, 0x80000000u, ebx, 0u); |
| + |
| + TestCmovRegReg(e, edi, 0x1u, esi, 0x1u, ecx, 1u); |
| + TestCmovRegReg(e, edi, 0x1u, esi, 0x11111u, ecx, 0u); |
| + |
| + TestCmovRegReg(ne, esi, 0x80000000u, eax, 0x1u, edx, 1u); |
| + TestCmovRegReg(ne, esi, 0x1u, eax, 0x1u, edx, 0u); |
| + |
| + TestCmovRegReg(be, eax, 0x1u, ebx, 0x80000000u, eax, 1u); |
| + TestCmovRegReg(be, eax, 0x80000000u, ebx, 0x1u, eax, 0u); |
| + |
| + TestCmovRegReg(a, ebx, 0x80000000u, ecx, 0x1u, ebx, 1u); |
| + TestCmovRegReg(a, ebx, 0x1u, ecx, 0x80000000u, ebx, 0u); |
| + |
| + TestCmovRegReg(s, ecx, 0x1u, edx, 0x80000000u, ecx, 1u); |
| + TestCmovRegReg(s, ecx, 0x80000000u, edx, 0x1u, ecx, 0u); |
| + |
| + TestCmovRegReg(ns, edx, 0x80000000u, edi, 0x1u, ecx, 1u); |
| + TestCmovRegReg(ns, edx, 0x1u, edi, 0x80000000u, ecx, 0u); |
| + |
| + TestCmovRegReg(p, edi, 0x80000000u, esi, 0x1u, edx, 1u); |
| + TestCmovRegReg(p, edi, 0x1u, esi, 0x80000000u, edx, 0u); |
| + |
| + TestCmovRegReg(np, esi, 0x1u, edi, 0x80000000u, eax, 1u); |
| + TestCmovRegReg(np, esi, 0x80000000u, edi, 0x1u, eax, 0u); |
| + |
| + TestCmovRegReg(l, edi, 0x80000000u, eax, 0x1u, ebx, 1u); |
| + TestCmovRegReg(l, edi, 0x1u, eax, 0x80000000u, ebx, 0u); |
| + |
| + TestCmovRegReg(ge, eax, 0x1u, ebx, 0x80000000u, ecx, 1u); |
| + TestCmovRegReg(ge, eax, 0x80000000u, ebx, 0x1u, ecx, 0u); |
| + |
| + TestCmovRegReg(le, ebx, 0x80000000u, ecx, 0x1u, edx, 1u); |
| + TestCmovRegReg(le, ebx, 0x1u, ecx, 0x80000000u, edx, 0u); |
| + |
| +#undef TestCmovRegReg |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, CmovRegAddr) { |
| +#define TestCmovRegAddr(C, Src0, Value0, Value1, Dest, IsTrue) \ |
| + do { \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Value1; \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src0, Immediate(Value0)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(Value0)); \ |
| + __ cmp(IceType_i32, GPRRegister::Encoded_Reg_##Src0, dwordAddress(T0)); \ |
| + __ cmov(IceType_i32, Cond::Br_##C, GPRRegister::Encoded_Reg_##Dest, \ |
| + dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + ASSERT_EQ((IsTrue) ? (Value1) : (Value0), test.Dest()) \ |
| + << "(" #C ", " #Src0 ", " #Value0 ", " #Value1 ", " #Dest ", " #IsTrue \ |
| + ")"; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestCmovRegAddr(o, eax, 0x80000000u, 0x1u, ecx, 1u); |
| + TestCmovRegAddr(o, eax, 0x1u, 0x10000000u, ecx, 0u); |
| + |
| + TestCmovRegAddr(no, ebx, 0x1u, 0x10000000u, edx, 1u); |
| + TestCmovRegAddr(no, ebx, 0x80000000u, 0x1u, edx, 0u); |
| + |
| + TestCmovRegAddr(b, ecx, 0x1, 0x80000000u, eax, 1u); |
| + TestCmovRegAddr(b, ecx, 0x80000000u, 0x1u, eax, 0u); |
| + |
| + TestCmovRegAddr(ae, edx, 0x80000000u, 0x1u, ebx, 1u); |
| + TestCmovRegAddr(ae, edx, 0x1u, 0x80000000u, ebx, 0u); |
| + |
| + TestCmovRegAddr(e, edi, 0x1u, 0x1u, ecx, 1u); |
| + TestCmovRegAddr(e, edi, 0x1u, 0x11111u, ecx, 0u); |
| + |
| + TestCmovRegAddr(ne, esi, 0x80000000u, 0x1u, edx, 1u); |
| + TestCmovRegAddr(ne, esi, 0x1u, 0x1u, edx, 0u); |
| + |
| + TestCmovRegAddr(be, eax, 0x1u, 0x80000000u, eax, 1u); |
| + TestCmovRegAddr(be, eax, 0x80000000u, 0x1u, eax, 0u); |
| + |
| + TestCmovRegAddr(a, ebx, 0x80000000u, 0x1u, ebx, 1u); |
| + TestCmovRegAddr(a, ebx, 0x1u, 0x80000000u, ebx, 0u); |
| + |
| + TestCmovRegAddr(s, ecx, 0x1u, 0x80000000u, ecx, 1u); |
| + TestCmovRegAddr(s, ecx, 0x80000000u, 0x1u, ecx, 0u); |
| + |
| + TestCmovRegAddr(ns, edx, 0x80000000u, 0x1u, ecx, 1u); |
| + TestCmovRegAddr(ns, edx, 0x1u, 0x80000000u, ecx, 0u); |
| + |
| + TestCmovRegAddr(p, edi, 0x80000000u, 0x1u, edx, 1u); |
| + TestCmovRegAddr(p, edi, 0x1u, 0x80000000u, edx, 0u); |
| + |
| + TestCmovRegAddr(np, esi, 0x1u, 0x80000000u, eax, 1u); |
| + TestCmovRegAddr(np, esi, 0x80000000u, 0x1u, eax, 0u); |
| + |
| + TestCmovRegAddr(l, edi, 0x80000000u, 0x1u, ebx, 1u); |
| + TestCmovRegAddr(l, edi, 0x1u, 0x80000000u, ebx, 0u); |
| + |
| + TestCmovRegAddr(ge, eax, 0x1u, 0x80000000u, ecx, 1u); |
| + TestCmovRegAddr(ge, eax, 0x80000000u, 0x1u, ecx, 0u); |
| + |
| + TestCmovRegAddr(le, ebx, 0x80000000u, 0x1u, edx, 1u); |
| + TestCmovRegAddr(le, ebx, 0x1u, 0x80000000u, edx, 0u); |
| + |
| +#undef TestCmovRegAddr |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, RepMovsb) { |
| + __ rep_movsb(); |
| + |
| + static constexpr uint32_t ByteCount = 2; |
| + static constexpr uint8_t Prefix = 0xF3; |
| + static constexpr uint8_t Opcode = 0xA4; |
| + |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + verifyBytes<ByteCount>(codeBytes(), Prefix, Opcode); |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovssXmmAddr) { |
| +#define TestMovssXmmAddrFloatLength(FloatLength, Xmm, Value) \ |
| + do { \ |
| + static_assert((FloatLength) == 32 || (FloatLength) == 64, \ |
| + "Invalid fp length #FloatLength"); \ |
| + using Type = std::conditional<FloatLength == 32, float, double>::type; \ |
| + \ |
| + static constexpr char TestString[] = "(" #FloatLength ", " #Xmm ")"; \ |
| + static constexpr bool IsDouble = std::is_same<Type, double>::value; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + const Type V0 = Value; \ |
| + \ |
| + __ movss(IceType_f##FloatLength, XmmRegister::Encoded_Reg_##Xmm, \ |
| + dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + if (IsDouble) { \ |
| + test.setQwordTo(T0, static_cast<double>(V0)); \ |
| + } else { \ |
| + test.setDwordTo(T0, static_cast<float>(V0)); \ |
| + } \ |
| + test.run(); \ |
| + ASSERT_DOUBLE_EQ(Value, test.Xmm<Type>()) << TestString << " value is " \ |
| + << Value; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovssXmmAddr(FloatLength) \ |
| + do { \ |
| + using Type = std::conditional<FloatLength == 32, float, double>::type; \ |
| + for (const Type Value : {0.0, -0.0, 1.0, -1.0, 3.14, 99999.9999}) { \ |
| + TestMovssXmmAddrFloatLength(FloatLength, xmm0, Value); \ |
| + TestMovssXmmAddrFloatLength(FloatLength, xmm1, Value); \ |
| + TestMovssXmmAddrFloatLength(FloatLength, xmm2, Value); \ |
| + TestMovssXmmAddrFloatLength(FloatLength, xmm3, Value); \ |
| + TestMovssXmmAddrFloatLength(FloatLength, xmm4, Value); \ |
| + TestMovssXmmAddrFloatLength(FloatLength, xmm5, Value); \ |
| + TestMovssXmmAddrFloatLength(FloatLength, xmm6, Value); \ |
| + TestMovssXmmAddrFloatLength(FloatLength, xmm7, Value); \ |
| + } \ |
| + } while (0) |
| + |
| + TestMovssXmmAddr(32); |
| + TestMovssXmmAddr(64); |
| + |
| +#undef TestMovssXmmAddr |
| +#undef TestMovssXmmAddrType |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovssAddrXmm) { |
| +#define TestMovssAddrXmmFloatLength(FloatLength, Xmm, Value) \ |
| + do { \ |
| + static_assert((FloatLength) == 32 || (FloatLength) == 64, \ |
| + "Invalid fp length #FloatLength"); \ |
| + using Type = std::conditional<FloatLength == 32, float, double>::type; \ |
| + \ |
| + static constexpr char TestString[] = "(" #FloatLength ", " #Xmm ")"; \ |
| + static constexpr bool IsDouble = std::is_same<Type, double>::value; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + const Type V0 = Value; \ |
| + const uint32_t T1 = allocateQword(); \ |
| + static_assert(std::numeric_limits<Type>::has_quiet_NaN, \ |
| + "f" #FloatLength " does not have quiet nan."); \ |
| + const Type V1 = std::numeric_limits<Type>::quiet_NaN(); \ |
| + \ |
| + __ movss(IceType_f##FloatLength, XmmRegister::Encoded_Reg_##Xmm, \ |
| + dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + if (IsDouble) { \ |
| + test.setQwordTo(T0, static_cast<double>(V0)); \ |
| + test.setQwordTo(T1, static_cast<double>(V1)); \ |
| + } else { \ |
| + test.setDwordTo(T0, static_cast<float>(V0)); \ |
| + test.setDwordTo(T1, static_cast<float>(V1)); \ |
| + } \ |
| + test.run(); \ |
| + ASSERT_DOUBLE_EQ(Value, test.Xmm<Type>()) << TestString << " value is " \ |
| + << Value; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovssAddrXmm(FloatLength) \ |
| + do { \ |
| + using Type = std::conditional<FloatLength == 32, float, double>::type; \ |
| + for (const Type Value : {0.0, -0.0, 1.0, -1.0, 3.14, 99999.9999}) { \ |
| + TestMovssAddrXmmFloatLength(FloatLength, xmm0, Value); \ |
| + TestMovssAddrXmmFloatLength(FloatLength, xmm1, Value); \ |
| + TestMovssAddrXmmFloatLength(FloatLength, xmm2, Value); \ |
| + TestMovssAddrXmmFloatLength(FloatLength, xmm3, Value); \ |
| + TestMovssAddrXmmFloatLength(FloatLength, xmm4, Value); \ |
| + TestMovssAddrXmmFloatLength(FloatLength, xmm5, Value); \ |
| + TestMovssAddrXmmFloatLength(FloatLength, xmm6, Value); \ |
| + TestMovssAddrXmmFloatLength(FloatLength, xmm7, Value); \ |
| + } \ |
| + } while (0) |
| + |
| + TestMovssAddrXmm(32); |
| + TestMovssAddrXmm(64); |
| + |
| +#undef TestMovssAddrXmm |
| +#undef TestMovssAddrXmmType |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovssXmmXmm) { |
| +#define TestMovssXmmXmmFloatLength(FloatLength, Src, Dst, Value) \ |
| + do { \ |
| + static_assert((FloatLength) == 32 || (FloatLength) == 64, \ |
| + "Invalid fp length #FloatLength"); \ |
| + using Type = std::conditional<FloatLength == 32, float, double>::type; \ |
| + \ |
| + static constexpr char TestString[] = \ |
| + "(" #FloatLength ", " #Src ", " #Dst ")"; \ |
| + static constexpr bool IsDouble = std::is_same<Type, double>::value; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + const Type V0 = Value; \ |
| + const uint32_t T1 = allocateQword(); \ |
| + static_assert(std::numeric_limits<Type>::has_quiet_NaN, \ |
| + "f" #FloatLength " does not have quiet nan."); \ |
| + const Type V1 = std::numeric_limits<Type>::quiet_NaN(); \ |
| + \ |
| + __ movss(IceType_f##FloatLength, XmmRegister::Encoded_Reg_##Src, \ |
| + dwordAddress(T0)); \ |
| + __ movss(IceType_f##FloatLength, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T1)); \ |
| + __ movss(IceType_f##FloatLength, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + if (IsDouble) { \ |
| + test.setQwordTo(T0, static_cast<double>(V0)); \ |
| + test.setQwordTo(T1, static_cast<double>(V1)); \ |
| + } else { \ |
| + test.setDwordTo(T0, static_cast<float>(V0)); \ |
| + test.setDwordTo(T1, static_cast<float>(V1)); \ |
| + } \ |
| + test.run(); \ |
| + ASSERT_DOUBLE_EQ(Value, test.Dst<Type>()) << TestString << " value is " \ |
| + << Value; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovssXmmXmm(FloatLength) \ |
| + do { \ |
| + using Type = std::conditional<FloatLength == 32, float, double>::type; \ |
| + for (const Type Value : {0.0, -0.0, 1.0, -1.0, 3.14, 99999.9999}) { \ |
| + TestMovssXmmXmmFloatLength(FloatLength, xmm0, xmm1, Value); \ |
| + TestMovssXmmXmmFloatLength(FloatLength, xmm1, xmm2, Value); \ |
| + TestMovssXmmXmmFloatLength(FloatLength, xmm2, xmm3, Value); \ |
| + TestMovssXmmXmmFloatLength(FloatLength, xmm3, xmm4, Value); \ |
| + TestMovssXmmXmmFloatLength(FloatLength, xmm4, xmm5, Value); \ |
| + TestMovssXmmXmmFloatLength(FloatLength, xmm5, xmm6, Value); \ |
| + TestMovssXmmXmmFloatLength(FloatLength, xmm6, xmm7, Value); \ |
| + TestMovssXmmXmmFloatLength(FloatLength, xmm7, xmm0, Value); \ |
| + } \ |
| + } while (0) |
| + |
| + TestMovssXmmXmm(32); |
| + TestMovssXmmXmm(64); |
| + |
| +#undef TestMovssXmmXmm |
| +#undef TestMovssXmmXmmType |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovdToXmm) { |
| +#define TestMovdXmmReg(Src, Dst, Value) \ |
| + do { \ |
| + assert(((Value)&0xFFFFFFFF) == (Value)); \ |
| + static constexpr char TestString[] = "(" #Src ", " #Dst ")"; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + const uint64_t V0 = 0xFFFFFFFF00000000ull; \ |
| + \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src, Immediate(Value)); \ |
| + __ movss(IceType_f64, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movd(XmmRegister::Encoded_Reg_##Dst, GPRRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + \ |
| + test.setQwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is " \ |
| + << Value; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovdXmmAddr(Dst, Value) \ |
| + do { \ |
| + assert(((Value)&0xFFFFFFFF) == (Value)); \ |
| + static constexpr char TestString[] = "(" #Dst ", Addr)"; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + const uint32_t V0 = Value; \ |
| + const uint32_t T1 = allocateQword(); \ |
| + const uint64_t V1 = 0xFFFFFFFF00000000ull; \ |
| + \ |
| + __ movss(IceType_f64, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + __ movd(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + \ |
| + test.setDwordTo(T0, V0); \ |
| + test.setQwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is " \ |
| + << Value; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovd(Dst) \ |
| + do { \ |
| + for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) { \ |
| + TestMovdXmmReg(eax, Dst, Value); \ |
| + TestMovdXmmReg(ebx, Dst, Value); \ |
| + TestMovdXmmReg(ecx, Dst, Value); \ |
| + TestMovdXmmReg(edx, Dst, Value); \ |
| + TestMovdXmmReg(esi, Dst, Value); \ |
| + TestMovdXmmReg(edi, Dst, Value); \ |
| + TestMovdXmmAddr(Dst, Value); \ |
| + } \ |
| + } while (0) |
| + |
| + TestMovd(xmm0); |
| + TestMovd(xmm1); |
| + TestMovd(xmm2); |
| + TestMovd(xmm3); |
| + TestMovd(xmm4); |
| + TestMovd(xmm5); |
| + TestMovd(xmm6); |
| + TestMovd(xmm7); |
| + |
| +#undef TestMovdXmmAddr |
| +#undef TestMovdXmmReg |
| +#undef TestMovd |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovdFromXmm) { |
| +#define TestMovdRegXmm(Src, Dst, Value) \ |
| + do { \ |
| + assert(((Value)&0xFFFFFFFF) == (Value)); \ |
| + static constexpr char TestString[] = "(" #Src ", " #Dst ")"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Value; \ |
| + \ |
| + __ movss(IceType_f64, XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ movd(GPRRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Value, test.contentsOfDword(T0)) << TestString << " value is " \ |
| + << Value; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovdAddrXmm(Src, Value) \ |
| + do { \ |
| + assert(((Value)&0xFFFFFFFF) == (Value)); \ |
| + static constexpr char TestString[] = "(" #Src ", Addr)"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Value; \ |
| + const uint32_t T1 = allocateDword(); \ |
| + const uint32_t V1 = ~(Value); \ |
| + \ |
| + __ movss(IceType_f64, XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ movd(dwordAddress(T1), XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + \ |
| + test.setDwordTo(T0, V0); \ |
| + test.setDwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Value, test.contentsOfDword(T1)) << TestString << " value is " \ |
| + << Value; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovd(Src) \ |
| + do { \ |
| + for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) { \ |
| + TestMovdRegXmm(Src, eax, Value); \ |
| + TestMovdRegXmm(Src, ebx, Value); \ |
| + TestMovdRegXmm(Src, ecx, Value); \ |
| + TestMovdRegXmm(Src, edx, Value); \ |
| + TestMovdRegXmm(Src, esi, Value); \ |
| + TestMovdRegXmm(Src, edi, Value); \ |
| + TestMovdAddrXmm(Src, Value); \ |
| + } \ |
| + } while (0) |
| + |
| + TestMovd(xmm0); |
| + TestMovd(xmm1); |
| + TestMovd(xmm2); |
| + TestMovd(xmm3); |
| + TestMovd(xmm4); |
| + TestMovd(xmm5); |
| + TestMovd(xmm6); |
| + TestMovd(xmm7); |
| + |
| +#undef TestMovdAddrXmm |
| +#undef TestMovdRegXmm |
| +#undef TestMovd |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovqXmmAddr) { |
| +#define TestMovd(Dst, Value) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", Addr)"; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + const uint64_t V0 = Value; \ |
| + const uint32_t T1 = allocateQword(); \ |
| + const uint64_t V1 = ~(Value); \ |
| + \ |
| + __ movss(IceType_f64, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + __ movq(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + \ |
| + test.setQwordTo(T0, V0); \ |
| + test.setQwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is " \ |
| + << Value; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) { |
| + TestMovd(xmm0, Value); |
| + TestMovd(xmm1, Value); |
| + TestMovd(xmm2, Value); |
| + TestMovd(xmm3, Value); |
| + TestMovd(xmm4, Value); |
| + TestMovd(xmm5, Value); |
| + TestMovd(xmm6, Value); |
| + TestMovd(xmm7, Value); |
| + } |
| + |
| +#undef TestMovd |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovqAddrXmm) { |
| +#define TestMovd(Dst, Value) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", Addr)"; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + const uint64_t V0 = Value; \ |
| + const uint32_t T1 = allocateQword(); \ |
| + const uint64_t V1 = ~(Value); \ |
| + \ |
| + __ movq(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movq(dwordAddress(T1), XmmRegister::Encoded_Reg_##Dst); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + \ |
| + test.setQwordTo(T0, V0); \ |
| + test.setQwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is " \ |
| + << Value; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) { |
| + TestMovd(xmm0, Value); |
| + TestMovd(xmm1, Value); |
| + TestMovd(xmm2, Value); |
| + TestMovd(xmm3, Value); |
| + TestMovd(xmm4, Value); |
| + TestMovd(xmm5, Value); |
| + TestMovd(xmm6, Value); |
| + TestMovd(xmm7, Value); |
| + } |
| + |
| +#undef TestMovd |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovqXmmXmm) { |
| +#define TestMovd(Src, Dst, Value) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Src ", " #Dst ")"; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + const uint64_t V0 = Value; \ |
| + const uint32_t T1 = allocateQword(); \ |
| + const uint64_t V1 = ~(Value); \ |
| + \ |
| + __ movq(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ movq(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + __ movq(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + \ |
| + test.setQwordTo(T0, V0); \ |
| + test.setQwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is " \ |
| + << Value; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) { |
| + TestMovd(xmm0, xmm1, Value); |
| + TestMovd(xmm1, xmm2, Value); |
| + TestMovd(xmm2, xmm3, Value); |
| + TestMovd(xmm3, xmm4, Value); |
| + TestMovd(xmm4, xmm5, Value); |
| + TestMovd(xmm5, xmm6, Value); |
| + TestMovd(xmm6, xmm7, Value); |
| + TestMovd(xmm7, xmm0, Value); |
| + } |
| + |
| +#undef TestMovd |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, ArithSS) { |
| +#define TestArithSSXmmXmm(FloatSize, Src, Value0, Dst, Value1, Inst, Op) \ |
| + do { \ |
| + static_assert(FloatSize == 32 || FloatSize == 64, \ |
| + "Invalid fp size " #FloatSize); \ |
| + static constexpr char TestString[] = \ |
| + "(" #FloatSize ", " #Src ", " #Value0 ", " #Dst ", " #Value1 \ |
| + ", " #Inst ", " #Op ")"; \ |
| + static constexpr bool IsDouble = FloatSize == 64; \ |
| + using Type = std::conditional<IsDouble, double, float>::type; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + const Type V0 = Value0; \ |
| + const uint32_t T1 = allocateQword(); \ |
| + const Type V1 = Value1; \ |
| + \ |
| + __ movss(IceType_f##FloatSize, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T0)); \ |
| + __ movss(IceType_f##FloatSize, XmmRegister::Encoded_Reg_##Src, \ |
| + dwordAddress(T1)); \ |
| + __ Inst(IceType_f##FloatSize, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + if (IsDouble) { \ |
| + test.setQwordTo(T0, static_cast<double>(V0)); \ |
| + test.setQwordTo(T1, static_cast<double>(V1)); \ |
| + } else { \ |
| + test.setDwordTo(T0, static_cast<float>(V0)); \ |
| + test.setDwordTo(T1, static_cast<float>(V1)); \ |
| + } \ |
| + \ |
| + test.run(); \ |
| + \ |
| + ASSERT_DOUBLE_EQ(V0 Op V1, test.Dst<Type>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestArithSSXmmAddr(FloatSize, Value0, Dst, Value1, Inst, Op) \ |
| + do { \ |
| + static_assert(FloatSize == 32 || FloatSize == 64, \ |
| + "Invalid fp size " #FloatSize); \ |
| + static constexpr char TestString[] = \ |
| + "(" #FloatSize ", Addr, " #Value0 ", " #Dst ", " #Value1 ", " #Inst \ |
| + ", " #Op ")"; \ |
| + static constexpr bool IsDouble = FloatSize == 64; \ |
| + using Type = std::conditional<IsDouble, double, float>::type; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + const Type V0 = Value0; \ |
| + const uint32_t T1 = allocateQword(); \ |
| + const Type V1 = Value1; \ |
| + \ |
| + __ movss(IceType_f##FloatSize, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T0)); \ |
| + __ Inst(IceType_f##FloatSize, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + if (IsDouble) { \ |
| + test.setQwordTo(T0, static_cast<double>(V0)); \ |
| + test.setQwordTo(T1, static_cast<double>(V1)); \ |
| + } else { \ |
| + test.setDwordTo(T0, static_cast<float>(V0)); \ |
| + test.setDwordTo(T1, static_cast<float>(V1)); \ |
| + } \ |
| + \ |
| + test.run(); \ |
| + \ |
| + ASSERT_DOUBLE_EQ(V0 Op V1, test.Dst<Type>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestArithSS(FloatSize, Src, Dst0, Dst1) \ |
| + do { \ |
| + TestArithSSXmmXmm(FloatSize, Src, 1.0, Dst0, 10.0, addss, +); \ |
| + TestArithSSXmmAddr(FloatSize, 2.0, Dst1, 20.0, addss, +); \ |
| + TestArithSSXmmXmm(FloatSize, Src, 3.0, Dst0, 30.0, subss, -); \ |
| + TestArithSSXmmAddr(FloatSize, 4.0, Dst1, 40.0, subss, -); \ |
| + TestArithSSXmmXmm(FloatSize, Src, 5.0, Dst0, 50.0, mulss, *); \ |
| + TestArithSSXmmAddr(FloatSize, 6.0, Dst1, 60.0, mulss, *); \ |
| + TestArithSSXmmXmm(FloatSize, Src, 7.0, Dst0, 70.0, divss, / ); \ |
| + TestArithSSXmmAddr(FloatSize, 8.0, Dst1, 80.0, divss, / ); \ |
| + } while (0) |
| + |
| + TestArithSS(32, xmm0, xmm1, xmm2); |
| + TestArithSS(32, xmm1, xmm2, xmm3); |
| + TestArithSS(32, xmm2, xmm3, xmm4); |
| + TestArithSS(32, xmm3, xmm4, xmm5); |
| + TestArithSS(32, xmm4, xmm5, xmm6); |
| + TestArithSS(32, xmm5, xmm6, xmm7); |
| + TestArithSS(32, xmm6, xmm7, xmm0); |
| + TestArithSS(32, xmm7, xmm0, xmm1); |
| + |
| + TestArithSS(64, xmm0, xmm1, xmm2); |
| + TestArithSS(64, xmm1, xmm2, xmm3); |
| + TestArithSS(64, xmm2, xmm3, xmm4); |
| + TestArithSS(64, xmm3, xmm4, xmm5); |
| + TestArithSS(64, xmm4, xmm5, xmm6); |
| + TestArithSS(64, xmm5, xmm6, xmm7); |
| + TestArithSS(64, xmm6, xmm7, xmm0); |
| + TestArithSS(64, xmm7, xmm0, xmm1); |
| + |
| +#undef TestArithSS |
| +#undef TestArithSSXmmAddr |
| +#undef TestArithSSXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovupsXmmAddr) { |
| +#define TestMovups(Dst) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(), \ |
| + std::numeric_limits<float>::infinity()); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(V0, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestMovups(xmm0); |
| + TestMovups(xmm1); |
| + TestMovups(xmm2); |
| + TestMovups(xmm3); |
| + TestMovups(xmm4); |
| + TestMovups(xmm5); |
| + TestMovups(xmm6); |
| + TestMovups(xmm7); |
| + |
| +#undef TestMovups |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovupsAddrXmm) { |
| +#define TestMovups(Src) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Src ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(), \ |
| + std::numeric_limits<float>::infinity()); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1(0.0, 0.0, 0.0, 0.0); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ movups(dwordAddress(T1), XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(V0, test.contentsOfDqword(T1)) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestMovups(xmm0); |
| + TestMovups(xmm1); |
| + TestMovups(xmm2); |
| + TestMovups(xmm3); |
| + TestMovups(xmm4); |
| + TestMovups(xmm5); |
| + TestMovups(xmm6); |
| + TestMovups(xmm7); |
| + |
| +#undef TestMovups |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovupsXmmXmm) { |
| +#define TestMovups(Dst, Src) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", " #Src ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(), \ |
| + std::numeric_limits<float>::infinity()); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1(0.0, 0.0, 0.0, 0.0); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(V0, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestMovups(xmm0, xmm1); |
| + TestMovups(xmm1, xmm2); |
| + TestMovups(xmm2, xmm3); |
| + TestMovups(xmm3, xmm4); |
| + TestMovups(xmm4, xmm5); |
| + TestMovups(xmm5, xmm6); |
| + TestMovups(xmm6, xmm7); |
| + TestMovups(xmm7, xmm0); |
| + |
| +#undef TestMovups |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, MovapsXmmXmm) { |
| +#define TestMovaps(Dst, Src) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", " #Src ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(), \ |
| + std::numeric_limits<float>::infinity()); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1(0.0, 0.0, 0.0, 0.0); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + __ movaps(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(V0, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestMovaps(xmm0, xmm1); |
| + TestMovaps(xmm1, xmm2); |
| + TestMovaps(xmm2, xmm3); |
| + TestMovaps(xmm3, xmm4); |
| + TestMovaps(xmm4, xmm5); |
| + TestMovaps(xmm5, xmm6); |
| + TestMovaps(xmm6, xmm7); |
| + TestMovaps(xmm7, xmm0); |
| + |
| +#undef TestMovaps |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, PArith) { |
| +#define TestPArithXmmXmm(Dst, Value0, Src, Value1, Inst, Op, Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Inst ", " #Op \ |
| + ", " #Type ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ Inst(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<Type##Size##_t>(V0) Op V1, test.Dst<Dqword>()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPArithXmmAddr(Dst, Value0, Value1, Inst, Op, Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Inst ", " #Op \ |
| + ", " #Type ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ Inst(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<Type##Size##_t>(V0) Op V1, test.Dst<Dqword>()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPArithXmmImm(Dst, Value0, Imm, Inst, Op, Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Imm ", " #Inst ", " #Op ", " #Type \ |
| + ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ Inst(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, Immediate(Imm)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<Type##Size##_t>(V0) Op Imm, test.Dst<Dqword>()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPAndnXmmXmm(Dst, Value0, Src, Value1, Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", pandn, " #Type \ |
| + ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ pandn(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(~(packedAs<Type##Size##_t>(V0)) & V1, test.Dst<Dqword>()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPAndnXmmAddr(Dst, Value0, Value1, Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Addr, " #Value1 ", pandn, " #Type ", " #Size \ |
| + ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ pandn(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ((~packedAs<Type##Size##_t>(V0)) & V1, test.Dst<Dqword>()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPArithSize(Dst, Src, Size) \ |
| + do { \ |
| + static_assert(Size == 8 || Size == 16 || Size == 32, "Invalid size."); \ |
| + if (Size != 8) { \ |
| + TestPArithXmmXmm( \ |
| + Dst, \ |
| + (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(3u), uint64_t(0u)), psra, >>, int, Size); \ |
| + TestPArithXmmAddr(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(3u), uint64_t(0u)), psra, >>, int, Size); \ |
| + TestPArithXmmImm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + 3u, psra, >>, int, Size); \ |
| + TestPArithXmmXmm( \ |
| + Dst, \ |
| + (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(3u), uint64_t(0u)), psrl, >>, uint, Size); \ |
| + TestPArithXmmAddr(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(3u), uint64_t(0u)), psrl, >>, uint, Size); \ |
| + TestPArithXmmImm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + 3u, psrl, >>, uint, Size); \ |
| + TestPArithXmmXmm( \ |
| + Dst, \ |
| + (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(3u), uint64_t(0u)), psll, <<, uint, Size); \ |
| + TestPArithXmmAddr(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(3u), uint64_t(0u)), psll, <<, uint, Size); \ |
| + TestPArithXmmImm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + 3u, psll, <<, uint, Size); \ |
| + \ |
| + TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(0xFFFFFFFF00000000ull), \ |
| + uint64_t(0x0123456789ABCDEull)), \ |
| + pmull, *, int, Size); \ |
| + TestPArithXmmAddr( \ |
| + Dst, \ |
| + (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)), \ |
| + pmull, *, int, Size); \ |
| + if (Size != 16) { \ |
| + TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(0xFFFFFFFF00000000ull), \ |
| + uint64_t(0x0123456789ABCDEull)), \ |
| + pmuludq, *, uint, Size); \ |
| + TestPArithXmmAddr( \ |
| + Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)), \ |
| + pmuludq, *, uint, Size); \ |
| + } \ |
| + } \ |
| + TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(0xFFFFFFFF00000000ull), \ |
| + uint64_t(0x0123456789ABCDEull)), \ |
| + padd, +, int, Size); \ |
| + TestPArithXmmAddr( \ |
| + Dst, \ |
| + (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)), \ |
| + padd, +, int, Size); \ |
| + TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(0xFFFFFFFF00000000ull), \ |
| + uint64_t(0x0123456789ABCDEull)), \ |
| + psub, -, int, Size); \ |
| + TestPArithXmmAddr( \ |
| + Dst, \ |
| + (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)), \ |
| + psub, -, int, Size); \ |
| + TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(0xFFFFFFFF00000000ull), \ |
| + uint64_t(0x0123456789ABCDEull)), \ |
| + pand, &, int, Size); \ |
| + TestPArithXmmAddr( \ |
| + Dst, \ |
| + (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)), \ |
| + pand, &, int, Size); \ |
| + \ |
| + TestPAndnXmmXmm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(0xFFFFFFFF00000000ull), \ |
| + uint64_t(0x0123456789ABCDEull)), \ |
| + int, Size); \ |
| + TestPAndnXmmAddr( \ |
| + Dst, \ |
| + (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)), \ |
| + int, Size); \ |
| + \ |
| + TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(0xFFFFFFFF00000000ull), \ |
| + uint64_t(0x0123456789ABCDEull)), \ |
| + por, |, int, Size); \ |
| + TestPArithXmmAddr( \ |
| + Dst, \ |
| + (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)), \ |
| + por, |, int, Size); \ |
| + TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull), \ |
| + uint64_t(0x8080404002020101ull)), \ |
| + Src, (uint64_t(0xFFFFFFFF00000000ull), \ |
| + uint64_t(0x0123456789ABCDEull)), \ |
| + pxor, ^, int, Size); \ |
| + TestPArithXmmAddr( \ |
| + Dst, \ |
| + (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)), \ |
| + (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)), \ |
| + pxor, ^, int, Size); \ |
| + } while (0) |
| + |
| +#define TestPArith(Src, Dst) \ |
| + do { \ |
| + TestPArithSize(Src, Dst, 8); \ |
| + TestPArithSize(Src, Dst, 16); \ |
| + TestPArithSize(Src, Dst, 32); \ |
| + } while (0) |
| + |
| + TestPArith(xmm0, xmm1); |
| + TestPArith(xmm1, xmm2); |
| + TestPArith(xmm2, xmm3); |
| + TestPArith(xmm3, xmm4); |
| + TestPArith(xmm4, xmm5); |
| + TestPArith(xmm5, xmm6); |
| + TestPArith(xmm6, xmm7); |
| + TestPArith(xmm7, xmm0); |
| + |
| +#undef TestPArith |
| +#undef TestPArithSize |
| +#undef TestPAndnXmmAddr |
| +#undef TestPAndnXmmXmm |
| +#undef TestPArithXmmImm |
| +#undef TestPArithXmmAddr |
| +#undef TestPArithXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, ArithPS) { |
| +#define TestArithPSXmmXmm(Dst, Value0, Src, Value1, Inst, Op, Type) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Inst ", " #Op \ |
| + ", " #Type ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ Inst(IceType_f32, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<Type>(V0) Op V1, test.Dst<Dqword>()) << TestString; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestArithPSXmmXmmUntyped(Dst, Value0, Src, Value1, Inst, Op, Type) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Inst ", " #Op \ |
| + ", " #Type ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ Inst(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<Type>(V0) Op V1, test.Dst<Dqword>()) << TestString; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestArithPSXmmAddrUntyped(Dst, Value0, Value1, Inst, Op, Type) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Inst ", " #Op \ |
| + ", " #Type ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ Inst(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<Type>(V0) Op V1, test.Dst<Dqword>()) << TestString; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMinMaxPS(Dst, Value0, Src, Value1, Inst, Type) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Inst ", " #Type \ |
| + ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ Inst(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<Type>(V0).Inst(V1), test.Dst<Dqword>()) << TestString; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestArithPSXmmAddr(Dst, Value0, Value1, Inst, Op, Type) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Inst ", " #Op \ |
| + ", " #Type ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ Inst(IceType_f32, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<Type>(V0) Op V1, test.Dst<Dqword>()) << TestString; \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestArithPS(Src, Dst) \ |
| + do { \ |
| + TestArithPSXmmXmm(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \ |
| + (0.55, 0.43, 0.23, 1.21), addps, +, float); \ |
| + TestArithPSXmmAddr(Src, (1.0, 100.0, -1000.0, 20.0), \ |
| + (0.55, 0.43, 0.23, 1.21), addps, +, float); \ |
| + TestArithPSXmmXmm(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \ |
| + (0.55, 0.43, 0.23, 1.21), subps, -, float); \ |
| + TestArithPSXmmAddr(Src, (1.0, 100.0, -1000.0, 20.0), \ |
| + (0.55, 0.43, 0.23, 1.21), subps, -, float); \ |
| + TestArithPSXmmXmm(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \ |
| + (0.55, 0.43, 0.23, 1.21), mulps, *, float); \ |
| + TestArithPSXmmAddr(Src, (1.0, 100.0, -1000.0, 20.0), \ |
| + (0.55, 0.43, 0.23, 1.21), mulps, *, float); \ |
| + TestArithPSXmmXmmUntyped(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \ |
| + (0.55, 0.43, 0.23, 1.21), andps, &, float); \ |
| + TestArithPSXmmXmmUntyped(Src, (1.0, -1000.0), Dst, (0.55, 1.21), andpd, &, \ |
| + double); \ |
| + TestArithPSXmmAddrUntyped(Src, (1.0, -1000.0), (0.55, 1.21), andpd, &, \ |
| + double); \ |
| + TestArithPSXmmXmmUntyped(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \ |
| + (0.55, 0.43, 0.23, 1.21), orps, |, float); \ |
| + TestArithPSXmmXmmUntyped(Src, (1.0, -1000.0), Dst, (0.55, 1.21), orpd, |, \ |
| + double); \ |
| + TestMinMaxPS(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \ |
| + (0.55, 0.43, 0.23, 1.21), minps, float); \ |
| + TestMinMaxPS(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \ |
| + (0.55, 0.43, 0.23, 1.21), maxps, float); \ |
| + TestMinMaxPS(Src, (1.0, -1000.0), Dst, (0.55, 1.21), minpd, double); \ |
| + TestMinMaxPS(Src, (1.0, -1000.0), Dst, (0.55, 1.21), maxpd, double); \ |
| + TestArithPSXmmXmmUntyped(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \ |
| + (0.55, 0.43, 0.23, 1.21), xorps, ^, float); \ |
| + TestArithPSXmmAddrUntyped(Src, (1.0, 100.0, -1000.0, 20.0), \ |
| + (0.55, 0.43, 0.23, 1.21), xorps, ^, float); \ |
| + TestArithPSXmmXmmUntyped(Src, (1.0, -1000.0), Dst, (0.55, 1.21), xorpd, ^, \ |
| + double); \ |
| + TestArithPSXmmAddrUntyped(Src, (1.0, -1000.0), (0.55, 1.21), xorpd, ^, \ |
| + double); \ |
| + } while (0) |
| + |
| +#if 0 |
| + |
| +#endif |
| + |
| + TestArithPS(xmm0, xmm1); |
| + TestArithPS(xmm1, xmm2); |
| + TestArithPS(xmm2, xmm3); |
| + TestArithPS(xmm3, xmm4); |
| + TestArithPS(xmm4, xmm5); |
| + TestArithPS(xmm5, xmm6); |
| + TestArithPS(xmm6, xmm7); |
| + TestArithPS(xmm7, xmm0); |
| + |
| +#undef TestArithPs |
| +#undef TestMinMaxPS |
| +#undef TestArithPSXmmXmmUntyped |
| +#undef TestArithPSXmmAddr |
| +#undef TestArithPSXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Blending) { |
| + using f32 = float; |
| + using i8 = uint8_t; |
| + |
| +#define TestBlendingXmmXmm(Dst, Value0, Src, Value1, M /*ask*/, Inst, Type) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #M ", " #Inst \ |
| + ", " #Type ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + const uint32_t Mask = allocateDqword(); \ |
| + const Dqword MaskValue M; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_xmm0, dwordAddress(Mask)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ Inst(IceType_##Type, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.setDqwordTo(Mask, MaskValue); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<Type>(V0).blendWith(V1, MaskValue), test.Dst<Dqword>()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestBlendingXmmAddr(Dst, Value0, Value1, M /*ask*/, Inst, Type) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #M ", " #Inst ", " #Type \ |
| + ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + const uint32_t Mask = allocateDqword(); \ |
| + const Dqword MaskValue M; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_xmm0, dwordAddress(Mask)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ Inst(IceType_##Type, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.setDqwordTo(Mask, MaskValue); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<Type>(V0).blendWith(V1, MaskValue), test.Dst<Dqword>()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestBlending(Src, Dst) \ |
| + do { \ |
| + TestBlendingXmmXmm( \ |
| + Dst, (1.0, 2.0, 1.0, 2.0), Src, (-1.0, -2.0, -1.0, -2.0), \ |
| + (uint64_t(0x8000000000000000ull), uint64_t(0x0000000080000000ull)), \ |
| + blendvps, f32); \ |
| + TestBlendingXmmAddr( \ |
| + Dst, (1.0, 2.0, 1.0, 2.0), (-1.0, -2.0, -1.0, -2.0), \ |
| + (uint64_t(0x8000000000000000ull), uint64_t(0x0000000080000000ull)), \ |
| + blendvps, f32); \ |
| + TestBlendingXmmXmm( \ |
| + Dst, \ |
| + (uint64_t(0xFFFFFFFFFFFFFFFFull), uint64_t(0xBBBBBBBBBBBBBBBBull)), \ |
| + Src, \ |
| + (uint64_t(0xAAAAAAAAAAAAAAAAull), uint64_t(0xEEEEEEEEEEEEEEEEull)), \ |
| + (uint64_t(0x8000000000000080ull), uint64_t(0x8080808000000000ull)), \ |
| + pblendvb, i8); \ |
| + TestBlendingXmmAddr( \ |
| + Dst, \ |
| + (uint64_t(0xFFFFFFFFFFFFFFFFull), uint64_t(0xBBBBBBBBBBBBBBBBull)), \ |
| + (uint64_t(0xAAAAAAAAAAAAAAAAull), uint64_t(0xEEEEEEEEEEEEEEEEull)), \ |
| + (uint64_t(0x8000000000000080ull), uint64_t(0x8080808000000000ull)), \ |
| + pblendvb, i8); \ |
| + } while (0) |
| + |
| + /* xmm0 is taken. It is the implicit mask . */ |
| + TestBlending(xmm1, xmm2); |
| + TestBlending(xmm2, xmm3); |
| + TestBlending(xmm3, xmm4); |
| + TestBlending(xmm4, xmm5); |
| + TestBlending(xmm5, xmm6); |
| + TestBlending(xmm6, xmm7); |
| + TestBlending(xmm7, xmm1); |
| + |
| +#undef TestBlending |
| +#undef TestBlendingXmmAddr |
| +#undef TestBlendingXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Cmpps) { |
| +#define TestCmppsXmmXmm(Dst, Src, C, Op) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Src ", " #Dst ", " #C ", " #Op ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(-1.0, 1.0, 3.14, 1024.5); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1(-1.0, 1.0, 3.14, 1024.5); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ cmpps(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src, \ |
| + Cond::Cmpps_##C); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<float>(V0) Op V1, test.Dst<Dqword>()) << TestString; \ |
| + ; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestCmppsXmmAddr(Dst, C, Op) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", Addr, " #C ", " #Op ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(-1.0, 1.0, 3.14, 1024.5); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1(-1.0, 1.0, 3.14, 1024.5); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ cmpps(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1), \ |
| + Cond::Cmpps_##C); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<float>(V0) Op V1, test.Dst<Dqword>()) << TestString; \ |
| + ; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestCmppsOrdUnordXmmXmm(Dst, Src, C) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Src ", " #Dst ", " #C ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(1.0, 1.0, std::numeric_limits<float>::quiet_NaN(), \ |
| + std::numeric_limits<float>::quiet_NaN()); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1(1.0, std::numeric_limits<float>::quiet_NaN(), 1.0, \ |
| + std::numeric_limits<float>::quiet_NaN()); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ cmpps(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src, \ |
| + Cond::Cmpps_##C); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<float>(V0).C(V1), test.Dst<Dqword>()) << TestString; \ |
| + ; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestCmppsOrdUnordXmmAddr(Dst, C) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", " #C ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(1.0, 1.0, std::numeric_limits<float>::quiet_NaN(), \ |
| + std::numeric_limits<float>::quiet_NaN()); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1(1.0, std::numeric_limits<float>::quiet_NaN(), 1.0, \ |
| + std::numeric_limits<float>::quiet_NaN()); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ cmpps(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1), \ |
| + Cond::Cmpps_##C); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(packedAs<float>(V0).C(V1), test.Dst<Dqword>()) << TestString; \ |
| + ; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestCmpps(Dst, Src) \ |
| + do { \ |
| + TestCmppsXmmXmm(Dst, Src, eq, == ); \ |
| + TestCmppsXmmAddr(Dst, eq, == ); \ |
| + TestCmppsXmmXmm(Dst, Src, eq, == ); \ |
| + TestCmppsXmmAddr(Dst, eq, == ); \ |
| + TestCmppsXmmXmm(Dst, Src, eq, == ); \ |
| + TestCmppsXmmAddr(Dst, eq, == ); \ |
| + TestCmppsOrdUnordXmmXmm(Dst, Src, unord); \ |
| + TestCmppsOrdUnordXmmAddr(Dst, unord); \ |
| + TestCmppsXmmXmm(Dst, Src, eq, == ); \ |
| + TestCmppsXmmAddr(Dst, eq, == ); \ |
| + TestCmppsXmmXmm(Dst, Src, eq, == ); \ |
| + TestCmppsXmmAddr(Dst, eq, == ); \ |
| + TestCmppsXmmXmm(Dst, Src, eq, == ); \ |
| + TestCmppsXmmAddr(Dst, eq, == ); \ |
| + TestCmppsOrdUnordXmmXmm(Dst, Src, unord); \ |
| + TestCmppsOrdUnordXmmAddr(Dst, unord); \ |
| + } while (0) |
| + |
| + TestCmpps(xmm0, xmm1); |
| + TestCmpps(xmm1, xmm2); |
| + TestCmpps(xmm2, xmm3); |
| + TestCmpps(xmm3, xmm4); |
| + TestCmpps(xmm4, xmm5); |
| + TestCmpps(xmm5, xmm6); |
| + TestCmpps(xmm6, xmm7); |
| + TestCmpps(xmm7, xmm0); |
| + |
| +#undef TestCmpps |
| +#undef TestCmppsOrdUnordXmmAddr |
| +#undef TestCmppsOrdUnordXmmXmm |
| +#undef TestCmppsXmmAddr |
| +#undef TestCmppsXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Sqrtps_Rsqrtps_Reciprocalps_Sqrtpd) { |
| +#define TestImplSingle(Dst, Inst, Expect) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", " #Inst ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(1.0, 4.0, 20.0, 3.14); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ Inst(XmmRegister::Encoded_Reg_##Dst); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.run(); \ |
| + ASSERT_EQ(Dqword Expect, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst) \ |
| + do { \ |
| + TestImplSingle(Dst, sqrtps, (uint64_t(0x400000003F800000ull), \ |
| + uint64_t(0x3FE2D10B408F1BBDull))); \ |
| + TestImplSingle(Dst, rsqrtps, (uint64_t(0x3EFFF0003F7FF000ull), \ |
| + uint64_t(0x3F1078003E64F000ull))); \ |
| + TestImplSingle(Dst, reciprocalps, (uint64_t(0x3E7FF0003F7FF000ull), \ |
| + uint64_t(0x3EA310003D4CC000ull))); \ |
| + \ |
| + TestImplSingle(Dst, sqrtpd, (uint64_t(0x4036A09E9365F5F3ull), \ |
| + uint64_t(0x401C42FAE40282A8ull))); \ |
| + } while (0) |
| + |
| + TestImpl(xmm0); |
| + TestImpl(xmm1); |
| + TestImpl(xmm2); |
| + TestImpl(xmm3); |
| + TestImpl(xmm4); |
| + TestImpl(xmm5); |
| + TestImpl(xmm6); |
| + TestImpl(xmm7); |
| + |
| +#undef TestImpl |
| +#undef TestImplSingle |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Movhlps_Movlhps) { |
| +#define TestImplSingle(Dst, Src, Inst, Expect) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Inst ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(uint64_t(0xAAAAAAAABBBBBBBBull), \ |
| + uint64_t(0xCCCCCCCCDDDDDDDDull)); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1(uint64_t(0xEEEEEEEEFFFFFFFFull), \ |
| + uint64_t(0x9999999988888888ull)); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ Inst(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Dqword Expect, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst, Src) \ |
| + do { \ |
| + TestImplSingle(Dst, Src, movhlps, (uint64_t(0x9999999988888888ull), \ |
| + uint64_t(0xCCCCCCCCDDDDDDDDull))); \ |
| + TestImplSingle(Dst, Src, movlhps, (uint64_t(0xAAAAAAAABBBBBBBBull), \ |
| + uint64_t(0xEEEEEEEEFFFFFFFFull))); \ |
| + } while (0) |
| + |
| + TestImpl(xmm0, xmm1); |
| + TestImpl(xmm1, xmm2); |
| + TestImpl(xmm2, xmm3); |
| + TestImpl(xmm3, xmm4); |
| + TestImpl(xmm4, xmm5); |
| + TestImpl(xmm5, xmm6); |
| + TestImpl(xmm6, xmm7); |
| + TestImpl(xmm7, xmm0); |
| + |
| +#undef TestImpl |
| +#undef TestImplSingle |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Unpck) { |
| + const Dqword V0(uint64_t(0xAAAAAAAABBBBBBBBull), |
| + uint64_t(0xCCCCCCCCDDDDDDDDull)); |
| + const Dqword V1(uint64_t(0xEEEEEEEEFFFFFFFFull), |
| + uint64_t(0x9999999988888888ull)); |
| + |
| + const Dqword unpcklpsExpected(uint64_t(0xFFFFFFFFBBBBBBBBull), |
| + uint64_t(0xEEEEEEEEAAAAAAAAull)); |
| + const Dqword unpcklpdExpected(uint64_t(0xAAAAAAAABBBBBBBBull), |
| + uint64_t(0xEEEEEEEEFFFFFFFFull)); |
| + const Dqword unpckhpsExpected(uint64_t(0x88888888DDDDDDDDull), |
| + uint64_t(0x99999999CCCCCCCCull)); |
| + const Dqword unpckhpdExpected(uint64_t(0xCCCCCCCCDDDDDDDDull), |
| + uint64_t(0x9999999988888888ull)); |
| + |
| +#define TestImplSingle(Dst, Src, Inst) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Inst ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ Inst(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Inst##Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst, Src) \ |
| + do { \ |
| + TestImplSingle(Dst, Src, unpcklps); \ |
| + TestImplSingle(Dst, Src, unpcklpd); \ |
| + TestImplSingle(Dst, Src, unpckhps); \ |
| + TestImplSingle(Dst, Src, unpckhpd); \ |
| + } while (0) |
| + |
| + TestImpl(xmm0, xmm1); |
| + TestImpl(xmm1, xmm2); |
| + TestImpl(xmm2, xmm3); |
| + TestImpl(xmm3, xmm4); |
| + TestImpl(xmm4, xmm5); |
| + TestImpl(xmm5, xmm6); |
| + TestImpl(xmm6, xmm7); |
| + TestImpl(xmm7, xmm0); |
| + |
| +#undef TestImpl |
| +#undef TestImplSingle |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Shufp) { |
| + const Dqword V0(uint64_t(0x1111111122222222ull), |
| + uint64_t(0x5555555577777777ull)); |
| + const Dqword V1(uint64_t(0xAAAAAAAABBBBBBBBull), |
| + uint64_t(0xCCCCCCCCDDDDDDDDull)); |
| + |
| + const uint8_t pshufdImm = 0x63; |
| + const Dqword pshufdExpected(uint64_t(0xBBBBBBBBCCCCCCCCull), |
| + uint64_t(0xAAAAAAAADDDDDDDDull)); |
| + |
| + const uint8_t shufpsImm = 0xf9; |
| + const Dqword shufpsExpected(uint64_t(0x7777777711111111ull), |
| + uint64_t(0xCCCCCCCCCCCCCCCCull)); |
| + const Dqword shufpsUntypedExpected(uint64_t(0x7777777711111111ull), |
| + uint64_t(0xCCCCCCCCCCCCCCCCull)); |
| + |
| + const uint8_t shufpdImm = 0x02; |
| + const Dqword shufpdUntypedExpected(uint64_t(0x1111111122222222ull), |
| + uint64_t(0xCCCCCCCCDDDDDDDDull)); |
| + |
| +#define TestImplSingleXmmXmm(Dst, Src, Inst) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Inst ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ Inst(IceType_f32, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src, Immediate(Inst##Imm)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Inst##Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplSingleXmmAddr(Dst, Inst) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", Addr, " #Inst ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ Inst(IceType_f32, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1), \ |
| + Immediate(Inst##Imm)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Inst##Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplSingleXmmXmmUntyped(Dst, Src, Inst) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Src ", " #Inst ", Untyped)"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ Inst(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src, \ |
| + Immediate(Inst##Imm)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Inst##UntypedExpected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst, Src) \ |
| + do { \ |
| + TestImplSingleXmmXmm(Dst, Src, pshufd); \ |
| + TestImplSingleXmmAddr(Dst, pshufd); \ |
| + TestImplSingleXmmXmm(Dst, Src, shufps); \ |
| + TestImplSingleXmmAddr(Dst, shufps); \ |
| + TestImplSingleXmmXmmUntyped(Dst, Src, shufps); \ |
| + TestImplSingleXmmXmmUntyped(Dst, Src, shufpd); \ |
| + } while (0) |
| + |
| + TestImpl(xmm0, xmm1); |
| + TestImpl(xmm1, xmm2); |
| + TestImpl(xmm2, xmm3); |
| + TestImpl(xmm3, xmm4); |
| + TestImpl(xmm4, xmm5); |
| + TestImpl(xmm5, xmm6); |
| + TestImpl(xmm6, xmm7); |
| + TestImpl(xmm7, xmm0); |
| + |
| +#undef TestImpl |
| +#undef TestImplSingleXmmXmmUntyped |
| +#undef TestImplSingleXmmAddr |
| +#undef TestImplSingleXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Cvt) { |
| + const Dqword dq2ps32DstValue(-1.0f, -1.0f, -1.0f, -1.0f); |
| + const Dqword dq2ps32SrcValue(-5, 3, 100, 200); |
| + const Dqword dq2ps32Expected(-5.0f, 3.0f, 100.0, 200.0); |
| + |
| + const Dqword dq2ps64DstValue(0.0f, 0.0f, -1.0f, -1.0f); |
| + const Dqword dq2ps64SrcValue(-5, 3, 100, 200); |
| + const Dqword dq2ps64Expected(-5.0f, 3.0f, 100.0, 200.0); |
| + |
| + const Dqword tps2dq32DstValue(-1.0f, -1.0f, -1.0f, -1.0f); |
| + const Dqword tps2dq32SrcValue(-5.0f, 3.0f, 100.0, 200.0); |
| + const Dqword tps2dq32Expected(-5, 3, 100, 200); |
| + |
| + const Dqword tps2dq64DstValue(-1.0f, -1.0f, -1.0f, -1.0f); |
| + const Dqword tps2dq64SrcValue(-5.0f, 3.0f, 100.0, 200.0); |
| + const Dqword tps2dq64Expected(-5, 3, 100, 200); |
| + |
| + const Dqword si2ss32DstValue(-1.0f, -1.0f, -1.0f, -1.0f); |
| + const int32_t si2ss32SrcValue = 5; |
| + const Dqword si2ss32Expected(5.0f, -1.0f, -1.0f, -1.0f); |
| + |
| + const Dqword si2ss64DstValue(-1.0, -1.0); |
| + const int32_t si2ss64SrcValue = 5; |
| + const Dqword si2ss64Expected(5.0, -1.0); |
| + |
| + const int32_t tss2si32DstValue = 0xF00F0FF0; |
| + const Dqword tss2si32SrcValue(-5.0f, -1.0f, -1.0f, -1.0f); |
| + const int32_t tss2si32Expected = -5; |
| + |
| + const int32_t tss2si64DstValue = 0xF00F0FF0; |
| + const Dqword tss2si64SrcValue(-5.0, -1.0); |
| + const int32_t tss2si64Expected = -5; |
| + |
| + const Dqword float2float32DstValue(-1.0, -1.0); |
| + const Dqword float2float32SrcValue(-5.0, 3, 100, 200); |
| + const Dqword float2float32Expected(-5.0, -1.0); |
| + |
| + const Dqword float2float64DstValue(-1.0, -1.0, -1.0, -1.0); |
| + const Dqword float2float64SrcValue(-5.0, 3.0); |
| + const Dqword float2float64Expected(-5.0, -1.0, -1.0, -1.0); |
| + |
| +#define TestImplPXmmXmm(Dst, Src, Inst, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Src ", cvt" #Inst ", f" #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ cvt##Inst(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, Inst##Size##DstValue); \ |
| + test.setDqwordTo(T1, Inst##Size##SrcValue); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Inst##Size##Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplSXmmReg(Dst, GPR, Inst, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #GPR ", cvt" #Inst ", f" #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##GPR, \ |
| + Immediate(Inst##Size##SrcValue)); \ |
| + __ cvt##Inst(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##GPR); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, Inst##Size##DstValue); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Inst##Size##Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplSRegXmm(GPR, Src, Inst, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #GPR ", " #Src ", cvt" #Inst ", f" #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##GPR, \ |
| + Immediate(Inst##Size##DstValue)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ cvt##Inst(IceType_f##Size, GPRRegister::Encoded_Reg_##GPR, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, Inst##Size##SrcValue); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(static_cast<uint32_t>(Inst##Size##Expected), test.GPR()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplPXmmAddr(Dst, Inst, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", Addr, cvt" #Inst ", f" #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ cvt##Inst(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, Inst##Size##DstValue); \ |
| + test.setDqwordTo(T1, Inst##Size##SrcValue); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Inst##Size##Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplSXmmAddr(Dst, Inst, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", Addr, cvt" #Inst ", f" #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const uint32_t T1 = allocateDword(); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ cvt##Inst(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, Inst##Size##DstValue); \ |
| + test.setDwordTo(T1, Inst##Size##SrcValue); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Inst##Size##Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplSRegAddr(GPR, Inst, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #GPR ", Addr, cvt" #Inst ", f" #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##GPR, \ |
| + Immediate(Inst##Size##DstValue)); \ |
| + __ cvt##Inst(IceType_f##Size, GPRRegister::Encoded_Reg_##GPR, \ |
| + dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, Inst##Size##SrcValue); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(static_cast<uint32_t>(Inst##Size##Expected), test.GPR()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Dst, Src, GPR, Size) \ |
| + do { \ |
| + TestImplPXmmXmm(Dst, Src, dq2ps, Size); \ |
| + TestImplPXmmAddr(Src, dq2ps, Size); \ |
| + TestImplPXmmXmm(Dst, Src, tps2dq, Size); \ |
| + TestImplPXmmAddr(Src, tps2dq, Size); \ |
| + TestImplSXmmReg(Dst, GPR, si2ss, Size); \ |
| + TestImplSXmmAddr(Dst, si2ss, Size); \ |
| + TestImplSRegXmm(GPR, Src, tss2si, Size); \ |
| + TestImplSRegAddr(GPR, tss2si, Size); \ |
| + TestImplPXmmXmm(Dst, Src, float2float, Size); \ |
| + TestImplPXmmAddr(Src, float2float, Size); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst, Src, GPR) \ |
| + do { \ |
| + TestImplSize(Dst, Src, GPR, 32); \ |
| + TestImplSize(Dst, Src, GPR, 64); \ |
| + } while (0) |
| + |
| + TestImpl(xmm0, xmm1, eax); |
| + TestImpl(xmm1, xmm2, ebx); |
| + TestImpl(xmm2, xmm3, ecx); |
| + TestImpl(xmm3, xmm4, edx); |
| + TestImpl(xmm4, xmm5, esi); |
| + TestImpl(xmm5, xmm6, edi); |
| + TestImpl(xmm6, xmm7, eax); |
| + TestImpl(xmm7, xmm0, ebx); |
| + |
| +#undef TestImpl |
| +#undef TestImplSize |
| +#undef TestImplSRegAddr |
| +#undef TestImplSXmmAddr |
| +#undef TestImplPXmmAddr |
| +#undef TestImplSRegXmm |
| +#undef TestImplSXmmReg |
| +#undef TestImplPXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Ucomiss) { |
| + static constexpr float qnan32 = std::numeric_limits<float>::quiet_NaN(); |
| + static constexpr double qnan64 = std::numeric_limits<float>::quiet_NaN(); |
| + |
| + Dqword test32DstValue(0.0, qnan32, qnan32, qnan32); |
| + Dqword test32SrcValue(0.0, qnan32, qnan32, qnan32); |
| + |
| + Dqword test64DstValue(0.0, qnan64); |
| + Dqword test64SrcValue(0.0, qnan64); |
| + |
| +#define TestImplXmmXmm(Dst, Value0, Src, Value1, Size, CompType, BParity, \ |
| + BOther) \ |
| + do { \ |
| + static constexpr char NearBranch = AssemblerX8632::kNearJump; \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Size ", " #CompType \ |
| + ", " #BParity ", " #BOther ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + test##Size##DstValue.F##Size[0] = Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + test##Size##SrcValue.F##Size[0] = Value1; \ |
| + const uint32_t ImmIfTrue = 0xBEEF; \ |
| + const uint32_t ImmIfFalse = 0xC0FFE; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ImmIfFalse)); \ |
| + __ ucomiss(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + Label Done; \ |
| + __ j(Cond::Br_##BParity, &Done, NearBranch); \ |
| + __ j(Cond::Br_##BOther, &Done, NearBranch); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ImmIfTrue)); \ |
| + __ bind(&Done); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, test##Size##DstValue); \ |
| + test.setDqwordTo(T1, test##Size##SrcValue); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(ImmIfTrue, test.eax()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplXmmAddr(Dst, Value0, Value1, Size, CompType, BParity, BOther) \ |
| + do { \ |
| + static constexpr char NearBranch = AssemblerX8632::kNearJump; \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Size ", " #CompType \ |
| + ", " #BParity ", " #BOther ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + test##Size##DstValue.F##Size[0] = Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + test##Size##SrcValue.F##Size[0] = Value1; \ |
| + const uint32_t ImmIfTrue = 0xBEEF; \ |
| + const uint32_t ImmIfFalse = 0xC0FFE; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ImmIfFalse)); \ |
| + __ ucomiss(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T1)); \ |
| + Label Done; \ |
| + __ j(Cond::Br_##BParity, &Done, NearBranch); \ |
| + __ j(Cond::Br_##BOther, &Done, NearBranch); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ImmIfTrue)); \ |
| + __ bind(&Done); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, test##Size##DstValue); \ |
| + test.setDqwordTo(T1, test##Size##SrcValue); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(ImmIfTrue, test.eax()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplCond(Dst, Value0, Src, Value1, Size, CompType, BParity, \ |
| + BOther) \ |
| + do { \ |
| + TestImplXmmXmm(Dst, Value0, Src, Value1, Size, CompType, BParity, BOther); \ |
| + TestImplXmmAddr(Dst, Value0, Value1, Size, CompType, BParity, BOther); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Dst, Src, Size) \ |
| + do { \ |
| + TestImplCond(Dst, 1.0, Src, 1.0, Size, isEq, p, ne); \ |
| + TestImplCond(Dst, 1.0, Src, 2.0, Size, isNe, p, e); \ |
| + TestImplCond(Dst, 1.0, Src, 2.0, Size, isLe, p, a); \ |
| + TestImplCond(Dst, 1.0, Src, 1.0, Size, isLe, p, a); \ |
| + TestImplCond(Dst, 1.0, Src, 2.0, Size, isLt, p, ae); \ |
| + TestImplCond(Dst, 2.0, Src, 1.0, Size, isGe, p, b); \ |
| + TestImplCond(Dst, 1.0, Src, 1.0, Size, isGe, p, b); \ |
| + TestImplCond(Dst, 2.0, Src, 1.0, Size, isGt, p, be); \ |
| + TestImplCond(Dst, qnan##Size, Src, 1.0, Size, isUnord, np, o); \ |
| + TestImplCond(Dst, 1.0, Src, qnan##Size, Size, isUnord, np, s); \ |
| + TestImplCond(Dst, qnan##Size, Src, qnan##Size, Size, isUnord, np, s); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst, Src) \ |
| + do { \ |
| + TestImplSize(Dst, Src, 32); \ |
| + TestImplSize(Dst, Src, 64); \ |
| + } while (0) |
| + |
| + TestImpl(xmm0, xmm1); |
| + TestImpl(xmm1, xmm2); |
| + TestImpl(xmm2, xmm3); |
| + TestImpl(xmm3, xmm4); |
| + TestImpl(xmm4, xmm5); |
| + TestImpl(xmm5, xmm6); |
| + TestImpl(xmm6, xmm7); |
| + TestImpl(xmm7, xmm0); |
| + |
| +#undef TestImpl |
| +#undef TestImplSize |
| +#undef TestImplCond |
| +#undef TestImplXmmAddr |
| +#undef TestImplXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Movmsk) { |
| +#define TestMovmskGPRXmm(GPR, Src, Value1, Expected, Inst) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #GPR ", " #Src ", " #Value1 ", " #Expected ", " #Inst ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ Inst(GPRRegister::Encoded_Reg_##GPR, XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Expected, test.GPR()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestMovmsk(GPR, Src) \ |
| + do { \ |
| + TestMovmskGPRXmm(GPR, Src, (-1.0, 1.0, -1.0, 1.0), 0x05ul, movmskps); \ |
| + TestMovmskGPRXmm(GPR, Src, (1.0, -1.0), 0x02ul, movmskpd); \ |
| + } while (0) |
| + |
| + TestMovmsk(eax, xmm0); |
| + TestMovmsk(ebx, xmm1); |
| + TestMovmsk(ecx, xmm2); |
| + TestMovmsk(edx, xmm3); |
| + TestMovmsk(esi, xmm4); |
| + TestMovmsk(edi, xmm5); |
| + TestMovmsk(eax, xmm6); |
| + TestMovmsk(ebx, xmm7); |
| + |
| +#undef TestMovmskGPRXmm |
| +#undef TestMovmsk |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Sqrtss) { |
| + Dqword test32SrcValue(-100.0, -100.0, -100.0, -100.0); |
| + Dqword test32DstValue(-1.0, -1.0, -1.0, -1.0); |
| + |
| + Dqword test64SrcValue(-100.0, -100.0); |
| + Dqword test64DstValue(-1.0, -1.0); |
| + |
| +#define TestSqrtssXmmXmm(Dst, Src, Value1, Result, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Src ", " #Value1 ", " #Result ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + test##Size##SrcValue.F##Size[0] = Value1; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + __ sqrtss(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, test##Size##SrcValue); \ |
| + test.setDqwordTo(T1, test##Size##DstValue); \ |
| + test.run(); \ |
| + \ |
| + Dqword Expected = test##Size##DstValue; \ |
| + Expected.F##Size[0] = Result; \ |
| + ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestSqrtssXmmAddr(Dst, Value1, Result, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", Addr, " #Value1 ", " #Result ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + test##Size##SrcValue.F##Size[0] = Value1; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + __ sqrtss(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, test##Size##SrcValue); \ |
| + test.setDqwordTo(T1, test##Size##DstValue); \ |
| + test.run(); \ |
| + \ |
| + Dqword Expected = test##Size##DstValue; \ |
| + Expected.F##Size[0] = Result; \ |
| + ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestSqrtssSize(Dst, Src, Size) \ |
| + do { \ |
| + TestSqrtssXmmXmm(Dst, Src, 4.0, 2.0, Size); \ |
| + TestSqrtssXmmAddr(Dst, 4.0, 2.0, Size); \ |
| + TestSqrtssXmmXmm(Dst, Src, 9.0, 3.0, Size); \ |
| + TestSqrtssXmmAddr(Dst, 9.0, 3.0, Size); \ |
| + TestSqrtssXmmXmm(Dst, Src, 100.0, 10.0, Size); \ |
| + TestSqrtssXmmAddr(Dst, 100.0, 10.0, Size); \ |
| + } |
| + |
| +#define TestSqrtss(Dst, Src) \ |
| + do { \ |
| + TestSqrtssSizeS(Dst, Src, 32); \ |
| + TestSqrtssSizeS(Dst, Src, 64); \ |
| + } while (0) |
| + |
| +#undef TestSqrtss |
| +#undef TestSqrtssSize |
| +#undef TestSqrtssXmmAddr |
| +#undef TestSqrtssXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Insertps) { |
| +#define TestInsertpsXmmXmmImm(Dst, Value0, Src, Value1, Imm, Expected) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Imm ", " #Expected \ |
| + ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ insertps(IceType_v4f32, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src, Immediate(Imm)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Dqword Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestInsertpsXmmAddrImm(Dst, Value0, Value1, Imm, Expected) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Imm ", " #Expected ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ insertps(IceType_v4f32, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T1), Immediate(Imm)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Dqword Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestInsertps(Dst, Src) \ |
| + do { \ |
| + TestInsertpsXmmXmmImm( \ |
| + Dst, (uint64_t(-1), uint64_t(-1)), Src, \ |
| + (uint64_t(0xAAAAAAAABBBBBBBBull), uint64_t(0xCCCCCCCCDDDDDDDDull)), \ |
| + 0x99, \ |
| + (uint64_t(0xDDDDDDDD00000000ull), uint64_t(0x00000000FFFFFFFFull))); \ |
| + TestInsertpsXmmAddrImm( \ |
| + Dst, (uint64_t(-1), uint64_t(-1)), \ |
| + (uint64_t(0xAAAAAAAABBBBBBBBull), uint64_t(0xCCCCCCCCDDDDDDDDull)), \ |
| + 0x99, \ |
| + (uint64_t(0xBBBBBBBB00000000ull), uint64_t(0x00000000FFFFFFFFull))); \ |
| + TestInsertpsXmmXmmImm( \ |
| + Dst, (uint64_t(-1), uint64_t(-1)), Src, \ |
| + (uint64_t(0xAAAAAAAABBBBBBBBull), uint64_t(0xCCCCCCCCDDDDDDDDull)), \ |
| + 0x9D, \ |
| + (uint64_t(0xDDDDDDDD00000000ull), uint64_t(0x0000000000000000ull))); \ |
| + TestInsertpsXmmAddrImm( \ |
| + Dst, (uint64_t(-1), uint64_t(-1)), \ |
| + (uint64_t(0xAAAAAAAABBBBBBBBull), uint64_t(0xCCCCCCCCDDDDDDDDull)), \ |
| + 0x9D, \ |
| + (uint64_t(0xBBBBBBBB00000000ull), uint64_t(0x0000000000000000ull))); \ |
| + } while (0) |
| + |
| + TestInsertps(xmm0, xmm1); |
| + |
| +#undef TestInsertps |
| +#undef TestInsertpsXmmXmmAddr |
| +#undef TestInsertpsXmmXmmImm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Pinsr) { |
| + static constexpr uint8_t Mask32 = 0x03; |
| + static constexpr uint8_t Mask16 = 0x07; |
| + static constexpr uint8_t Mask8 = 0x0F; |
| + |
| +#define TestPinsrXmmGPRImm(Dst, Value0, GPR, Value1, Imm, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #GPR ", " #Value1 ", " #Imm ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##GPR, Immediate(Value1)); \ |
| + __ pinsr(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##GPR, Immediate(Imm)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + constexpr uint8_t sel = (Imm)&Mask##Size; \ |
| + Dqword Expected = V0; \ |
| + Expected.U##Size[sel] = Value1; \ |
| + ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPinsrXmmAddrImm(Dst, Value0, Value1, Imm, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Imm ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDword(); \ |
| + const uint32_t V1 = Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ pinsr(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T1), Immediate(Imm)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + constexpr uint8_t sel = (Imm)&Mask##Size; \ |
| + Dqword Expected = V0; \ |
| + Expected.U##Size[sel] = Value1; \ |
| + ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPinsrSize(Dst, GPR, Value1, Imm, Size) \ |
| + do { \ |
| + TestPinsrXmmGPRImm(Dst, (uint64_t(0xAAAAAAAABBBBBBBBull), \ |
| + uint64_t(0xFFFFFFFFDDDDDDDDull)), \ |
| + GPR, Value1, Imm, Size); \ |
| + TestPinsrXmmAddrImm(Dst, (uint64_t(0xAAAAAAAABBBBBBBBull), \ |
| + uint64_t(0xFFFFFFFFDDDDDDDDull)), \ |
| + Value1, Imm, Size); \ |
| + } while (0) |
| + |
| +#define TestPinsr(Src, Dst) \ |
| + do { \ |
| + TestPinsrSize(Src, Dst, 0xEE, 0x03, 8); \ |
| + TestPinsrSize(Src, Dst, 0xFFEE, 0x03, 16); \ |
| + TestPinsrSize(Src, Dst, 0xC0FFEE, 0x03, 32); \ |
| + } while (0) |
| + |
| + TestPinsr(xmm0, eax); |
| + TestPinsr(xmm1, ebx); |
| + TestPinsr(xmm2, ecx); |
| + TestPinsr(xmm3, edx); |
| + TestPinsr(xmm4, esi); |
| + TestPinsr(xmm5, edi); |
| + TestPinsr(xmm6, eax); |
| + TestPinsr(xmm7, ebx); |
| + |
| +#undef TestPinsr |
| +#undef TestPinsrSize |
| +#undef TestPinsrXmmAddrImm |
| +#undef TestPinsrXmmGPRImm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Pextr) { |
| + static constexpr uint8_t Mask32 = 0x03; |
| + static constexpr uint8_t Mask16 = 0x07; |
| + static constexpr uint8_t Mask8 = 0x0F; |
| + |
| +#define TestPextrGPRXmmImm(GPR, Src, Value1, Imm, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #GPR ", " #Src ", " #Value1 ", " #Imm ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ pextr(IceType_i##Size, GPRRegister::Encoded_Reg_##GPR, \ |
| + XmmRegister::Encoded_Reg_##Src, Immediate(Imm)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + constexpr uint8_t sel = (Imm)&Mask##Size; \ |
| + ASSERT_EQ(V0.U##Size[sel], test.GPR()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPextrSize(GPR, Src, Value1, Imm, Size) \ |
| + do { \ |
| + TestPextrGPRXmmImm(GPR, Src, (uint64_t(0xAAAAAAAABBBBBBBBull), \ |
| + uint64_t(0xFFFFFFFFDDDDDDDDull)), \ |
| + Imm, Size); \ |
| + } while (0) |
| + |
| +#define TestPextr(Src, Dst) \ |
| + do { \ |
| + TestPextrSize(Src, Dst, 0xEE, 0x03, 8); \ |
| + TestPextrSize(Src, Dst, 0xFFEE, 0x03, 16); \ |
| + TestPextrSize(Src, Dst, 0xC0FFEE, 0x03, 32); \ |
| + } while (0) |
| + |
| + TestPextr(eax, xmm0); |
| + TestPextr(ebx, xmm1); |
| + TestPextr(ecx, xmm2); |
| + TestPextr(edx, xmm3); |
| + TestPextr(esi, xmm4); |
| + TestPextr(edi, xmm5); |
| + TestPextr(eax, xmm6); |
| + TestPextr(ebx, xmm7); |
| + |
| +#undef TestPextr |
| +#undef TestPextrSize |
| +#undef TestPextrXmmGPRImm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Pmovsxdq) { |
| +#define TestPmovsxdqXmmXmm(Dst, Src, Value1) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Value1 ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value1; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1(uint64_t(0), uint64_t(0)); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \ |
| + __ pmovsxdq(XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + const Dqword Expected(uint64_t(V0.I32[0]), uint64_t(V0.I32[1])); \ |
| + ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPmovsxdq(Dst, Src) \ |
| + do { \ |
| + TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x700000007FFFFFFFull), \ |
| + uint64_t(0xAAAAAAAAEEEEEEEEull))); \ |
| + TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x800000007FFFFFFFull), \ |
| + uint64_t(0xAAAAAAAAEEEEEEEEull))); \ |
| + TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x70000000FFFFFFFFull), \ |
| + uint64_t(0xAAAAAAAAEEEEEEEEull))); \ |
| + TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x80000000FFFFFFFFull), \ |
| + uint64_t(0xAAAAAAAAEEEEEEEEull))); \ |
| + } while (0) |
| + |
| + TestPmovsxdq(xmm0, xmm1); |
| + TestPmovsxdq(xmm1, xmm2); |
| + TestPmovsxdq(xmm2, xmm3); |
| + TestPmovsxdq(xmm3, xmm4); |
| + TestPmovsxdq(xmm4, xmm5); |
| + TestPmovsxdq(xmm5, xmm6); |
| + TestPmovsxdq(xmm6, xmm7); |
| + TestPmovsxdq(xmm7, xmm0); |
| + |
| +#undef TestPmovsxdq |
| +#undef TestPmovsxdqXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Pcmpeq_Pcmpgt) { |
| +#define TestPcmpXmmXmm(Dst, Value0, Src, Value1, Size, Inst, Op) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Size ", " #Op ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ Inst(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + XmmRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + Dqword Expected(uint64_t(0), uint64_t(0)); \ |
| + static constexpr uint8_t ArraySize = \ |
| + sizeof(Dqword) / sizeof(uint##Size##_t); \ |
| + for (uint8_t i = 0; i < ArraySize; ++i) { \ |
| + Expected.I##Size[i] = (V1.I##Size[i] Op V0.I##Size[i]) ? -1 : 0; \ |
| + } \ |
| + ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPcmpXmmAddr(Dst, Value0, Value1, Size, Inst, Op) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Size ", " #Op ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0 Value0; \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1 Value1; \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ Inst(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + Dqword Expected(uint64_t(0), uint64_t(0)); \ |
| + static constexpr uint8_t ArraySize = \ |
| + sizeof(Dqword) / sizeof(uint##Size##_t); \ |
| + for (uint8_t i = 0; i < ArraySize; ++i) { \ |
| + Expected.I##Size[i] = (V1.I##Size[i] Op V0.I##Size[i]) ? -1 : 0; \ |
| + } \ |
| + ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestPcmpValues(Dst, Value0, Src, Value1, Size) \ |
| + do { \ |
| + TestPcmpXmmXmm(Dst, Value0, Src, Value1, Size, pcmpeq, == ); \ |
| + TestPcmpXmmAddr(Dst, Value0, Value1, Size, pcmpeq, == ); \ |
| + TestPcmpXmmXmm(Dst, Value0, Src, Value1, Size, pcmpgt, < ); \ |
| + TestPcmpXmmAddr(Dst, Value0, Value1, Size, pcmpgt, < ); \ |
| + } while (0) |
| + |
| +#define TestPcmpSize(Dst, Src, Size) \ |
| + do { \ |
| + TestPcmpValues(Dst, (uint64_t(0x8888888888888888ull), \ |
| + uint64_t(0x0000000000000000ull)), \ |
| + Src, (uint64_t(0x0000008800008800ull), \ |
| + uint64_t(0xFFFFFFFFFFFFFFFFull)), \ |
| + Size); \ |
| + TestPcmpValues(Dst, (uint64_t(0x123567ABAB55DE01ull), \ |
| + uint64_t(0x12345abcde12345Aull)), \ |
| + Src, (uint64_t(0x0000008800008800ull), \ |
| + uint64_t(0xAABBCCDD1234321Aull)), \ |
| + Size); \ |
| + } while (0) |
| + |
| +#define TestPcmp(Dst, Src) \ |
| + do { \ |
| + TestPcmpSize(xmm0, xmm1, 8); \ |
| + TestPcmpSize(xmm0, xmm1, 16); \ |
| + TestPcmpSize(xmm0, xmm1, 32); \ |
| + } while (0) |
| + |
| + TestPcmp(xmm0, xmm1); |
| + TestPcmp(xmm1, xmm2); |
| + TestPcmp(xmm2, xmm3); |
| + TestPcmp(xmm3, xmm4); |
| + TestPcmp(xmm4, xmm5); |
| + TestPcmp(xmm5, xmm6); |
| + TestPcmp(xmm6, xmm7); |
| + TestPcmp(xmm7, xmm0); |
| + |
| +#undef TestPcmp |
| +#undef TestPcmpSize |
| +#undef TestPcmpValues |
| +#undef TestPcmpXmmAddr |
| +#undef TestPcmpXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Roundsd) { |
| +#define TestRoundsdXmmXmm(Dst, Src, Mode, Input, RN) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Src ", " #Mode ", " #Input ", " #RN ")"; \ |
| + const uint32_t T0 = allocateDqword(); \ |
| + const Dqword V0(-3.0, -3.0); \ |
| + const uint32_t T1 = allocateDqword(); \ |
| + const Dqword V1(double(Input), -123.4); \ |
| + \ |
| + __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \ |
| + __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \ |
| + __ roundsd(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src, \ |
| + AssemblerX8632::k##Mode); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDqwordTo(T0, V0); \ |
| + test.setDqwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + const Dqword Expected(double(RN), -3.0); \ |
| + EXPECT_EQ(Expected, test.Dst<Dqword>()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestRoundsd(Dst, Src) \ |
| + do { \ |
| + TestRoundsdXmmXmm(Dst, Src, RoundToNearest, 5.51, 6); \ |
| + TestRoundsdXmmXmm(Dst, Src, RoundToNearest, 5.49, 5); \ |
| + TestRoundsdXmmXmm(Dst, Src, RoundDown, 5.51, 5); \ |
| + TestRoundsdXmmXmm(Dst, Src, RoundUp, 5.49, 6); \ |
| + TestRoundsdXmmXmm(Dst, Src, RoundToZero, 5.49, 5); \ |
| + TestRoundsdXmmXmm(Dst, Src, RoundToZero, 5.51, 5); \ |
| + } while (0) |
| + |
| + TestRoundsd(xmm0, xmm1); |
| + TestRoundsd(xmm1, xmm2); |
| + TestRoundsd(xmm2, xmm3); |
| + TestRoundsd(xmm3, xmm4); |
| + TestRoundsd(xmm4, xmm5); |
| + TestRoundsd(xmm5, xmm6); |
| + TestRoundsd(xmm6, xmm7); |
| + TestRoundsd(xmm7, xmm0); |
| + |
| +#undef TestRoundsd |
| +#undef TestRoundsdXmmXmm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Test) { |
| + static constexpr uint32_t Mask8 = 0xFF; |
| + static constexpr uint32_t Mask16 = 0xFFFF; |
| + static constexpr uint32_t Mask32 = 0xFFFFFFFF; |
| + |
| +#define TestImplRegReg(Dst, Value0, Src, Value1, Size) \ |
| + do { \ |
| + static constexpr bool NearJump = true; \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Size ")"; \ |
| + static constexpr uint32_t ValueIfTrue = 0xBEEFFEEB; \ |
| + static constexpr uint32_t ValueIfFalse = 0x11111111; \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate(Value0)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate(Value1)); \ |
| + __ test(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate(ValueIfFalse)); \ |
| + Label Done; \ |
| + __ j(Cond::Br_e, &Done, NearJump); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate(ValueIfTrue)); \ |
| + __ bind(&Done); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(((Value0)&Mask##Size) & ((Value1)&Mask##Size) ? ValueIfTrue \ |
| + : ValueIfFalse, \ |
| + test.Dst()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegImm(Dst, Value0, Imm, Size) \ |
| + do { \ |
| + static constexpr bool NearJump = true; \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Imm ", " #Size ")"; \ |
| + static constexpr uint32_t ValueIfTrue = 0xBEEFFEEB; \ |
| + static constexpr uint32_t ValueIfFalse = 0x11111111; \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate(Value0)); \ |
| + __ test(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate((Imm)&Mask##Size)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate(ValueIfFalse)); \ |
| + Label Done; \ |
| + __ j(Cond::Br_e, &Done, NearJump); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate(ValueIfTrue)); \ |
| + __ bind(&Done); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(((Value0)&Mask##Size) & ((Imm)&Mask##Size) ? ValueIfTrue \ |
| + : ValueIfFalse, \ |
| + test.Dst()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddrReg(Value0, Src, Value1, Size) \ |
| + do { \ |
| + static constexpr bool NearJump = true; \ |
| + static constexpr char TestString[] = \ |
| + "(Addr, " #Value0 ", " #Src ", " #Value1 ", " #Size ")"; \ |
| + static constexpr uint32_t ValueIfTrue = 0xBEEFFEEB; \ |
| + static constexpr uint32_t ValueIfFalse = 0x11111111; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate(Value1)); \ |
| + __ test(IceType_i##Size, dwordAddress(T0), \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + __ mov(IceType_i32, dwordAddress(T0), Immediate(ValueIfFalse)); \ |
| + Label Done; \ |
| + __ j(Cond::Br_e, &Done, NearJump); \ |
| + __ mov(IceType_i32, dwordAddress(T0), Immediate(ValueIfTrue)); \ |
| + __ bind(&Done); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, uint32_t(Value0)); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(((Value0)&Mask##Size) & ((Value1)&Mask##Size) ? ValueIfTrue \ |
| + : ValueIfFalse, \ |
| + test.contentsOfDword(T0)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddrImm(Value0, Value1, Size) \ |
| + do { \ |
| + static constexpr bool NearJump = true; \ |
| + static constexpr char TestString[] = \ |
| + "(Addr, " #Value0 ", " #Value1 ", " #Size ")"; \ |
| + static constexpr uint32_t ValueIfTrue = 0xBEEFFEEB; \ |
| + static constexpr uint32_t ValueIfFalse = 0x11111111; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + \ |
| + __ test(IceType_i##Size, dwordAddress(T0), \ |
| + Immediate((Value1)&Mask##Size)); \ |
| + __ mov(IceType_i32, dwordAddress(T0), Immediate(ValueIfFalse)); \ |
| + Label Done; \ |
| + __ j(Cond::Br_e, &Done, NearJump); \ |
| + __ mov(IceType_i32, dwordAddress(T0), Immediate(ValueIfTrue)); \ |
| + __ bind(&Done); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, uint32_t(Value0)); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(((Value0)&Mask##Size) & ((Value1)&Mask##Size) ? ValueIfTrue \ |
| + : ValueIfFalse, \ |
| + test.contentsOfDword(T0)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplValues(Dst, Value0, Src, Value1, Size) \ |
| + do { \ |
| + TestImplRegReg(Dst, Value0, Src, Value1, Size); \ |
| + TestImplRegImm(Dst, Value0, Value1, Size); \ |
| + TestImplAddrReg(Value0, Src, Value1, Size); \ |
| + TestImplAddrImm(Value0, Value1, Size); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Dst, Src, Size) \ |
| + do { \ |
| + TestImplValues(Dst, 0xF0F12101, Src, 0x00000000, Size); \ |
| + TestImplValues(Dst, 0xF0000000, Src, 0xF0000000, Size); \ |
| + TestImplValues(Dst, 0x0F00000F, Src, 0xF00000F0, Size); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst, Src) \ |
| + do { \ |
| + TestImplSize(Dst, Src, 8); \ |
| + TestImplSize(Dst, Src, 16); \ |
| + TestImplSize(Dst, Src, 32); \ |
| + } while (0) |
| + |
| + TestImpl(eax, ebx); |
| + TestImpl(ebx, ecx); |
| + TestImpl(ecx, edx); |
| + TestImpl(edx, esi); |
| + TestImpl(esi, edi); |
| + TestImpl(edi, eax); |
| + |
| +#undef TestImpl |
| +#undef TestImplSize |
| +#undef TestImplValues |
| +#undef TestImplAddrImm |
| +#undef TestImplAddrReg |
| +#undef TestImplRegImm |
| +#undef TestImplRegReg |
| +} |
| + |
| +// No mull/div because x86. |
| +// No shift because x86. |
| +TEST_F(AssemblerX8632Test, Arith_most) { |
| + static constexpr uint32_t Mask8 = 0xFF; |
| + static constexpr uint32_t Mask16 = 0xFFFF; |
| + static constexpr uint32_t Mask32 = 0xFFFFFFFF; |
| + |
| +#define TestImplRegReg(Inst, Dst, Value0, Src, Value1, Type, Size, Op) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Value0 ", " #Src ", " #Value1 \ |
| + ", " #Type #Size "_t, " #Op ")"; \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate(Value0)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate(Value1)); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Mask##Size &static_cast<uint32_t>( \ |
| + static_cast<Type##Size##_t>((Value0)&Mask##Size) \ |
| + Op static_cast<Type##Size##_t>((Value1)&Mask##Size)), \ |
| + Mask##Size &test.Dst()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegAddr(Inst, Dst, Value0, Value1, Type, Size, Op) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Value0 ", Addr, " #Value1 ", " #Type #Size \ |
| + "_t, " #Op ")"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Value1; \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate(Value0)); \ |
| + __ mov(IceType_i##Size, dwordAddress(T0), Immediate(Value1)); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Mask##Size &static_cast<uint32_t>( \ |
| + static_cast<Type##Size##_t>((Value0)&Mask##Size) \ |
| + Op static_cast<Type##Size##_t>((Value1)&Mask##Size)), \ |
| + Mask##Size &test.Dst()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegImm(Inst, Dst, Value0, Imm, Type, Size, Op) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Value0 ", Imm(" #Imm "), " #Type #Size \ |
| + "_t, " #Op ")"; \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate(Value0)); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate((Imm)&Mask##Size)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Mask##Size &static_cast<uint32_t>( \ |
| + static_cast<Type##Size##_t>((Value0)&Mask##Size) \ |
| + Op static_cast<Type##Size##_t>((Imm)&Mask##Size)), \ |
| + Mask##Size &test.Dst()) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddrReg(Inst, Value0, Src, Value1, Type, Size, Op) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", Addr, " #Value0 ", " #Src ", " #Value1 ", " #Type #Size \ |
| + "_t, " #Op ")"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Value0; \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate(Value1)); \ |
| + __ Inst(IceType_i##Size, dwordAddress(T0), \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Mask##Size &static_cast<uint32_t>( \ |
| + static_cast<Type##Size##_t>((Value0)&Mask##Size) \ |
| + Op static_cast<Type##Size##_t>((Value1)&Mask##Size)), \ |
| + Mask##Size &test.contentsOfDword(T0)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddrImm(Inst, Value0, Imm, Type, Size, Op) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", Addr, " #Value0 ", Imm, " #Imm ", " #Type #Size \ |
| + "_t, " #Op ")"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Value0; \ |
| + \ |
| + __ Inst(IceType_i##Size, dwordAddress(T0), Immediate((Imm)&Mask##Size)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Mask##Size &static_cast<uint32_t>( \ |
| + static_cast<Type##Size##_t>((Value0)&Mask##Size) \ |
| + Op static_cast<Type##Size##_t>((Imm)&Mask##Size)), \ |
| + Mask##Size &test.contentsOfDword(T0)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplOp(Inst, Dst, Value0, Src, Value1, Type, Size, Op) \ |
| + do { \ |
| + TestImplRegReg(Inst, Dst, Value0, Src, Value1, Type, Size, Op); \ |
| + TestImplRegAddr(Inst, Dst, Value0, Value1, Type, Size, Op); \ |
| + TestImplRegImm(Inst, Dst, Value0, Value1, Type, Size, Op); \ |
| + TestImplAddrReg(Inst, Value0, Src, Value1, Type, Size, Op); \ |
| + TestImplAddrImm(Inst, Value0, Value1, Type, Size, Op); \ |
| + } while (0) |
| + |
| +#define TestImplValues(Dst, Value0, Src, Value1, Size) \ |
| + do { \ |
| + TestImplOp(And, Dst, Value0, Src, Value1, int, Size, &); \ |
| + TestImplOp(And, Dst, Value0, Src, Value1, uint, Size, &); \ |
| + TestImplOp(Or, Dst, Value0, Src, Value1, int, Size, | ); \ |
| + TestImplOp(Or, Dst, Value0, Src, Value1, uint, Size, | ); \ |
| + TestImplOp(Xor, Dst, Value0, Src, Value1, int, Size, ^); \ |
| + TestImplOp(Xor, Dst, Value0, Src, Value1, uint, Size, ^); \ |
| + TestImplOp(add, Dst, Value0, Src, Value1, int, Size, +); \ |
| + TestImplOp(add, Dst, Value0, Src, Value1, uint, Size, +); \ |
| + TestImplOp(sub, Dst, Value0, Src, Value1, int, Size, -); \ |
| + TestImplOp(sub, Dst, Value0, Src, Value1, uint, Size, -); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Dst, Src, Size) \ |
| + do { \ |
| + TestImplValues(Dst, 0xF0F12101, Src, 0x00000000, Size); \ |
| + TestImplValues(Dst, 0xF0000000, Src, 0xF0000000, Size); \ |
| + TestImplValues(Dst, 0x0F00000F, Src, 0xF0000070, Size); \ |
| + TestImplValues(Dst, 0x0F00F00F, Src, 0xF000F070, Size); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst, Src) \ |
| + do { \ |
| + if (GPRRegister::Encoded_Reg_##Src <= 3 && \ |
| + GPRRegister::Encoded_Reg_##Dst <= 3) { \ |
| + TestImplSize(Dst, Src, 8); \ |
| + } \ |
| + TestImplSize(Dst, Src, 16); \ |
| + TestImplSize(Dst, Src, 32); \ |
| + } while (0) |
| + |
| + TestImpl(eax, ebx); |
| + TestImpl(ebx, ecx); |
| + TestImpl(ecx, edx); |
| + TestImpl(edx, esi); |
| + TestImpl(esi, edi); |
| + TestImpl(edi, eax); |
| + |
| +#undef TestImpl |
| +#undef TestImplSize |
| +#undef TestImplValues |
| +#undef TestImplOp |
| +#undef TestImplAddrImm |
| +#undef TestImplAddrReg |
| +#undef TestImplRegImm |
| +#undef TestImplRegAddr |
| +#undef TestImplRegReg |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Arith_BorrowNCarry) { |
| + const uint32_t Mask8 = 0x000000FF; |
| + const uint32_t Mask16 = 0x0000FFFF; |
| + const uint32_t Mask32 = 0xFFFFFFFF; |
| + |
| + const uint64_t ResultMask8 = 0x000000000000FFFFull; |
| + const uint64_t ResultMask16 = 0x00000000FFFFFFFFull; |
| + const uint64_t ResultMask32 = 0xFFFFFFFFFFFFFFFFull; |
| + |
| +#define TestImplRegReg(Inst0, Inst1, Dst0, Dst1, Value0, Src0, Src1, Value1, \ |
| + Op, Size) \ |
| + do { \ |
| + static_assert(Size == 8 || Size == 16 || Size == 32, \ |
| + "Invalid size " #Size); \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst0 ", " #Inst1 ", " #Dst0 ", " #Dst1 ", " #Value0 ", " #Src0 \ |
| + ", " #Src1 ", " #Value1 ", " #Op ", " #Size ")"; \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \ |
| + Immediate(uint64_t(Value0) & Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \ |
| + Immediate((uint64_t(Value0) >> Size) & Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src0, \ |
| + Immediate(uint64_t(Value1) & Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src1, \ |
| + Immediate((uint64_t(Value1) >> Size) & Mask##Size)); \ |
| + __ Inst0(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \ |
| + GPRRegister::Encoded_Reg_##Src0); \ |
| + __ Inst1(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \ |
| + GPRRegister::Encoded_Reg_##Src1); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + static constexpr uint64_t Result = \ |
| + (uint64_t(Value0) & ResultMask##Size)Op(uint64_t(Value1) & \ |
| + ResultMask##Size); \ |
| + static constexpr uint32_t Expected0 = Result & Mask##Size; \ |
| + static constexpr uint32_t Expected1 = (Result >> Size) & Mask##Size; \ |
| + ASSERT_EQ(Expected0, test.Dst0()) << TestString << ": 0"; \ |
| + ASSERT_EQ(Expected1, test.Dst1()) << TestString << ": 1"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegAddr(Inst0, Inst1, Dst0, Dst1, Value0, Value1, Op, Size) \ |
| + do { \ |
| + static_assert(Size == 8 || Size == 16 || Size == 32, \ |
| + "Invalid size " #Size); \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst0 ", " #Inst1 ", " #Dst0 ", " #Dst1 ", " #Value0 \ |
| + ", Addr, " #Value1 ", " #Op ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = uint64_t(Value1) & Mask##Size; \ |
| + const uint32_t T1 = allocateDword(); \ |
| + const uint32_t V1 = (uint64_t(Value1) >> Size) & Mask##Size; \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \ |
| + Immediate(uint64_t(Value0) & Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \ |
| + Immediate((uint64_t(Value0) >> Size) & Mask##Size)); \ |
| + __ Inst0(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \ |
| + dwordAddress(T0)); \ |
| + __ Inst1(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \ |
| + dwordAddress(T1)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.setDwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + static constexpr uint64_t Result = \ |
| + (uint64_t(Value0) & ResultMask##Size)Op(uint64_t(Value1) & \ |
| + ResultMask##Size); \ |
| + static constexpr uint32_t Expected0 = Result & Mask##Size; \ |
| + static constexpr uint32_t Expected1 = (Result >> Size) & Mask##Size; \ |
| + ASSERT_EQ(Expected0, test.Dst0()) << TestString << ": 0"; \ |
| + ASSERT_EQ(Expected1, test.Dst1()) << TestString << ": 1"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegImm(Inst0, Inst1, Dst0, Dst1, Value0, Imm, Op, Size) \ |
| + do { \ |
| + static_assert(Size == 8 || Size == 16 || Size == 32, \ |
| + "Invalid size " #Size); \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst0 ", " #Inst1 ", " #Dst0 ", " #Dst1 ", " #Value0 \ |
| + ", Imm(" #Imm "), " #Op ", " #Size ")"; \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \ |
| + Immediate(uint64_t(Value0) & Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \ |
| + Immediate((uint64_t(Value0) >> Size) & Mask##Size)); \ |
| + __ Inst0(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \ |
| + Immediate(uint64_t(Imm) & Mask##Size)); \ |
| + __ Inst1(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \ |
| + Immediate((uint64_t(Imm) >> Size) & Mask##Size)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + static constexpr uint64_t Result = \ |
| + (uint64_t(Value0) & ResultMask##Size)Op(uint64_t(Imm) & \ |
| + ResultMask##Size); \ |
| + static constexpr uint32_t Expected0 = Result & Mask##Size; \ |
| + static constexpr uint32_t Expected1 = (Result >> Size) & Mask##Size; \ |
| + ASSERT_EQ(Expected0, test.Dst0()) << TestString << ": 0"; \ |
| + ASSERT_EQ(Expected1, test.Dst1()) << TestString << ": 1"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddrReg(Inst0, Inst1, Value0, Src0, Src1, Value1, Op, Size) \ |
| + do { \ |
| + static_assert(Size == 8 || Size == 16 || Size == 32, \ |
| + "Invalid size " #Size); \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst0 ", " #Inst1 ", Addr, " #Value0 ", " #Src0 ", " #Src1 \ |
| + ", " #Value1 ", " #Op ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = uint64_t(Value0) & Mask##Size; \ |
| + const uint32_t T1 = allocateDword(); \ |
| + const uint32_t V1 = (uint64_t(Value0) >> Size) & Mask##Size; \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src0, \ |
| + Immediate(uint64_t(Value1) & Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src1, \ |
| + Immediate((uint64_t(Value1) >> Size) & Mask##Size)); \ |
| + __ Inst0(IceType_i##Size, dwordAddress(T0), \ |
| + GPRRegister::Encoded_Reg_##Src0); \ |
| + __ Inst1(IceType_i##Size, dwordAddress(T1), \ |
| + GPRRegister::Encoded_Reg_##Src1); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.setDwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + static constexpr uint64_t Result = \ |
| + (uint64_t(Value0) & ResultMask##Size)Op(uint64_t(Value1) & \ |
| + ResultMask##Size); \ |
| + static constexpr uint32_t Expected0 = Result & Mask##Size; \ |
| + static constexpr uint32_t Expected1 = (Result >> Size) & Mask##Size; \ |
| + ASSERT_EQ(Expected0, test.contentsOfDword(T0)) << TestString << ": 0"; \ |
| + ASSERT_EQ(Expected1, test.contentsOfDword(T1)) << TestString << ": 1"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddrImm(Inst0, Inst1, Value0, Imm, Op, Size) \ |
| + do { \ |
| + static_assert(Size == 8 || Size == 16 || Size == 32, \ |
| + "Invalid size " #Size); \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst0 ", " #Inst1 ", Addr, " #Value0 ", Imm(" #Imm "), " #Op \ |
| + ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = uint64_t(Value0) & Mask##Size; \ |
| + const uint32_t T1 = allocateDword(); \ |
| + const uint32_t V1 = (uint64_t(Value0) >> Size) & Mask##Size; \ |
| + __ Inst0(IceType_i##Size, dwordAddress(T0), \ |
| + Immediate(uint64_t(Imm) & Mask##Size)); \ |
| + __ Inst1(IceType_i##Size, dwordAddress(T1), \ |
| + Immediate((uint64_t(Imm) >> Size) & Mask##Size)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.setDwordTo(T1, V1); \ |
| + test.run(); \ |
| + \ |
| + static constexpr uint64_t Result = \ |
| + (uint64_t(Value0) & ResultMask##Size)Op(uint64_t(Imm) & \ |
| + ResultMask##Size); \ |
| + static constexpr uint32_t Expected0 = Result & Mask##Size; \ |
| + static constexpr uint32_t Expected1 = (Result >> Size) & Mask##Size; \ |
| + ASSERT_EQ(Expected0, test.contentsOfDword(T0)) << TestString << ": 0"; \ |
| + ASSERT_EQ(Expected1, test.contentsOfDword(T1)) << TestString << ": 1"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplOp(Inst0, Inst1, Dst0, Dst1, Value0, Src0, Src1, Value1, Op, \ |
| + Size) \ |
| + do { \ |
| + TestImplRegReg(Inst0, Inst1, Dst0, Dst1, Value0, Src0, Src1, Value1, Op, \ |
| + Size); \ |
| + TestImplRegAddr(Inst0, Inst1, Dst0, Dst1, Value0, Value1, Op, Size); \ |
| + TestImplRegImm(Inst0, Inst1, Dst0, Dst1, Value0, Value1, Op, Size); \ |
| + TestImplAddrReg(Inst0, Inst1, Value0, Src0, Src1, Value1, Op, Size); \ |
| + TestImplAddrImm(Inst0, Inst1, Value0, Value1, Op, Size); \ |
| + } while (0) |
| + |
| +#define TestImplValues(Dst0, Dst1, Value0, Src0, Src1, Value1, Size) \ |
| + do { \ |
| + TestImplOp(add, adc, Dst0, Dst1, Value0, Src0, Src1, Value1, +, Size); \ |
| + TestImplOp(sub, sbb, Dst0, Dst1, Value0, Src0, Src1, Value1, -, Size); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Dst0, Dst1, Src0, Src1, Size) \ |
| + do { \ |
| + TestImplValues(Dst0, Dst1, 0xFFFFFFFFFFFFFF00ull, Src0, Src1, \ |
| + 0xFFFFFFFF0000017Full, Size); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst0, Dst1, Src0, Src1) \ |
| + do { \ |
| + if (GPRRegister::Encoded_Reg_##Dst0 <= 3 && \ |
| + GPRRegister::Encoded_Reg_##Dst1 <= 3 && \ |
| + GPRRegister::Encoded_Reg_##Src0 <= 3 && \ |
| + GPRRegister::Encoded_Reg_##Src1 <= 3) { \ |
| + TestImplSize(Dst0, Dst1, Src0, Src1, 8); \ |
| + } \ |
| + TestImplSize(Dst0, Dst1, Src0, Src1, 16); \ |
| + TestImplSize(Dst0, Dst1, Src0, Src1, 32); \ |
| + } while (0) |
| + |
| + TestImpl(eax, ebx, ecx, edx); |
| + TestImpl(ebx, ecx, edx, esi); |
| + TestImpl(ecx, edx, esi, edi); |
| + TestImpl(edx, esi, edi, eax); |
| + TestImpl(esi, edi, eax, ebx); |
| + TestImpl(edi, eax, ebx, ecx); |
| + |
| +#undef TestImpl |
| +#undef TestImplSize |
| +#undef TestImplValues |
| +#undef TestImplOp |
| +#undef TestImplAddrImm |
| +#undef TestImplAddrReg |
| +#undef TestImplRegImm |
| +#undef TestImplRegAddr |
| +#undef TestImplRegReg |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Cbw_Cwd_Cdq) { |
| +#define TestImpl(Inst, BytesSize, ...) \ |
| + do { \ |
| + __ Inst(); \ |
| + ASSERT_EQ(BytesSize, codeBytesSize()) << #Inst; \ |
| + verifyBytes<BytesSize>(codeBytes(), __VA_ARGS__); \ |
|
jvoung (off chromium)
2015/07/23 17:59:36
Similar question about ASSERT_TRUE, except now it'
John
2015/07/27 20:35:58
Having the ASSERT_TRUE outside verifyFunction help
|
| + reset(); \ |
| + } while (0) |
| + |
| + TestImpl(cbw, 2u, 0x66, 0x98); |
| + TestImpl(cwd, 2u, 0x66, 0x99); |
| + TestImpl(cdq, 1u, 0x99); |
| + |
| +#undef TestImpl |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, SingleOperandMul) { |
| + static constexpr uint32_t Mask8 = 0x000000FF; |
| + static constexpr uint32_t Mask16 = 0x0000FFFF; |
| + static constexpr uint32_t Mask32 = 0xFFFFFFFF; |
| + |
| +#define TestImplReg(Inst, Value0, Src, Value1, Type, Size) \ |
| + do { \ |
| + static_assert(GPRRegister::Encoded_Reg_eax != \ |
| + GPRRegister::Encoded_Reg_##Src, \ |
| + "eax can not be src1."); \ |
| + \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Value0 ", " #Src ", " #Value1 ", " #Type ", " #Size \ |
| + ")"; \ |
| + static constexpr Type##64_t OperandEax = \ |
| + static_cast<Type##Size##_t>((Value0)&Mask##Size); \ |
| + static constexpr Type##64_t OperandOther = \ |
| + static_cast<Type##Size##_t>((Value1)&Mask##Size); \ |
| + static constexpr uint32_t ExpectedEax = \ |
| + Mask##Size & (OperandEax * OperandOther); \ |
| + static constexpr uint32_t ExpectedEdx = \ |
| + Mask##Size & ((OperandEax * OperandOther) >> Size); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \ |
| + Immediate((Value0)&Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate((Value1)&Mask##Size)); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Src); \ |
| + \ |
| + if (Size == 8) { \ |
| + /* mov %ah, %dl */ \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \ |
| + GPRRegister::Encoded_Reg_esp); \ |
| + __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \ |
| + if (GPRRegister::Encoded_Reg_##Src == GPRRegister::Encoded_Reg_esi) { \ |
| + /* src == dh; clear dx's upper 8 bits. */ \ |
| + __ And(IceType_i16, GPRRegister::Encoded_Reg_edx, Immediate(0x00FF)); \ |
| + } \ |
| + } \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(ExpectedEax, test.eax()) << TestString; \ |
| + ASSERT_EQ(ExpectedEdx, test.edx()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddr(Inst, Value0, Value1, Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Value0 ", Addr, " #Value1 ", " #Type ", " #Size ")"; \ |
| + static const uint32_t T0 = allocateDword(); \ |
| + static constexpr uint32_t V0 = Value1; \ |
| + static constexpr Type##64_t OperandEax = \ |
| + static_cast<Type##Size##_t>((Value0)&Mask##Size); \ |
| + static constexpr Type##64_t OperandOther = \ |
| + static_cast<Type##Size##_t>((Value1)&Mask##Size); \ |
| + static constexpr uint32_t ExpectedEax = \ |
| + Mask##Size & (OperandEax * OperandOther); \ |
| + static constexpr uint32_t ExpectedEdx = \ |
| + Mask##Size & ((OperandEax * OperandOther) >> Size); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \ |
| + Immediate((Value0)&Mask##Size)); \ |
| + __ Inst(IceType_i##Size, dwordAddress(T0)); \ |
| + \ |
| + if (Size == 8) { \ |
| + /* mov %ah, %dl */ \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \ |
| + GPRRegister::Encoded_Reg_esp); \ |
| + __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \ |
| + } \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(ExpectedEax, test.eax()) << TestString; \ |
| + ASSERT_EQ(ExpectedEdx, test.edx()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplOp(Inst, Value0, Src, Value1, Type, Size) \ |
| + do { \ |
| + TestImplReg(Inst, Value0, Src, Value1, Type, Size); \ |
| + TestImplAddr(Inst, Value0, Value1, Type, Size); \ |
| + } while (0) |
| + |
| +#define TestImplValue(Value0, Src, Value1, Size) \ |
| + do { \ |
| + TestImplOp(mul, Value0, Src, Value1, uint, Size); \ |
| + TestImplOp(imul, Value0, Src, Value1, int, Size); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Src, Size) \ |
| + do { \ |
| + TestImplValue(10, Src, 1, Size); \ |
| + TestImplValue(10, Src, -1, Size); \ |
| + TestImplValue(-10, Src, 37, Size); \ |
| + TestImplValue(-10, Src, -15, Size); \ |
| + } while (0) |
| + |
| +#define TestImpl(Src) \ |
| + do { \ |
| + TestImplSize(Src, 8); \ |
| + TestImplSize(Src, 16); \ |
| + TestImplSize(Src, 32); \ |
| + } while (0) |
| + |
| + TestImpl(ebx); |
| + TestImpl(ecx); |
| + TestImpl(edx); |
| + TestImpl(esi); |
| + TestImpl(edi); |
| + |
| +#undef TestImpl |
| +#undef TestImplSize |
| +#undef TestImplValue |
| +#undef TestImplOp |
| +#undef TestImplAddr |
| +#undef TestImplReg |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, TwoOperandImul) { |
| + static constexpr uint32_t Mask16 = 0x0000FFFF; |
| + static constexpr uint32_t Mask32 = 0xFFFFFFFF; |
| + |
| +#define TestImplRegReg(Dst, Value0, Src, Value1, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Size ")"; \ |
| + static constexpr int64_t Operand0 = \ |
| + static_cast<int##Size##_t>((Value0)&Mask##Size); \ |
| + static constexpr int64_t Operand1 = \ |
| + static_cast<int##Size##_t>((Value1)&Mask##Size); \ |
| + static constexpr uint32_t Expected = Mask##Size & (Operand0 * Operand1); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate((Value0)&Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate((Value1)&Mask##Size)); \ |
| + __ imul(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + \ |
| + if (Size == 8) { \ |
| + /* mov %ah, %dl */ \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \ |
| + GPRRegister::Encoded_Reg_esp); \ |
| + __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \ |
| + if (GPRRegister::Encoded_Reg_##Src == GPRRegister::Encoded_Reg_esi) { \ |
| + /* src == dh; clear dx's upper 8 bits. */ \ |
| + __ And(IceType_i16, GPRRegister::Encoded_Reg_edx, Immediate(0x00FF)); \ |
| + } \ |
| + } \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Expected, test.Dst()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegImm(Dst, Value0, Imm, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Imm(" #Imm "), " #Size ")"; \ |
| + static constexpr int64_t Operand0 = \ |
| + static_cast<int##Size##_t>((Value0)&Mask##Size); \ |
| + static constexpr int64_t Operand1 = \ |
| + static_cast<int##Size##_t>((Imm)&Mask##Size); \ |
| + static constexpr uint32_t Expected = Mask##Size & (Operand0 * Operand1); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate((Value0)&Mask##Size)); \ |
| + __ imul(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, Immediate(Imm)); \ |
| + \ |
| + if (Size == 8) { \ |
| + /* mov %ah, %dl */ \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \ |
| + GPRRegister::Encoded_Reg_esp); \ |
| + __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \ |
| + } \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Expected, test.Dst()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegAddr(Dst, Value0, Value1, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", Addr," #Value1 ", " #Size ")"; \ |
| + static constexpr int64_t Operand0 = \ |
| + static_cast<int##Size##_t>((Value0)&Mask##Size); \ |
| + static constexpr int64_t Operand1 = \ |
| + static_cast<int##Size##_t>((Value1)&Mask##Size); \ |
| + static constexpr uint32_t Expected = Mask##Size & (Operand0 * Operand1); \ |
| + const uint32_t T0 = allocateDword(); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate((Value0)&Mask##Size)); \ |
| + __ imul(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T0)); \ |
| + \ |
| + if (Size == 8) { \ |
| + /* mov %ah, %dl */ \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \ |
| + GPRRegister::Encoded_Reg_esp); \ |
| + __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \ |
| + } \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, static_cast<uint32_t>(Operand1)); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Expected, test.Dst()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplValue(Dst, Value0, Src, Value1, Size) \ |
| + do { \ |
| + TestImplRegReg(Dst, Value0, Src, Value1, Size); \ |
| + TestImplRegImm(Dst, Value0, Value1, Size); \ |
| + TestImplRegAddr(Dst, Value0, Value1, Size); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Dst, Src, Size) \ |
| + do { \ |
| + TestImplValue(Dst, 1, Src, 1, Size); \ |
| + TestImplValue(Dst, -10, Src, 0x4050AA20, Size); \ |
| + TestImplValue(Dst, -2, Src, -55, Size); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst, Src) \ |
| + do { \ |
| + TestImplSize(Dst, Src, 16); \ |
| + TestImplSize(Dst, Src, 32); \ |
| + } while (0) |
| + |
| + TestImpl(eax, ebx); |
| + TestImpl(ebx, ecx); |
| + TestImpl(ecx, edx); |
| + TestImpl(edx, esi); |
| + TestImpl(esi, edi); |
| + TestImpl(edi, eax); |
| + |
| +#undef TestImpl |
| +#undef TestImplSize |
| +#undef TestImplValue |
| +#undef TestImplRegAddr |
| +#undef TestImplRegImm |
| +#undef TestImplRegReg |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Div) { |
| + static constexpr uint32_t Mask8 = 0x000000FF; |
| + static constexpr uint32_t Mask16 = 0x0000FFFF; |
| + static constexpr uint32_t Mask32 = 0xFFFFFFFF; |
| + |
| + static constexpr uint64_t Operand0Mask8 = 0x00000000000000FFull; |
| + static constexpr uint64_t Operand0Mask16 = 0x00000000FFFFFFFFull; |
| + static constexpr uint64_t Operand0Mask32 = 0xFFFFFFFFFFFFFFFFull; |
| + |
| + using Operand0Type_int8 = int16_t; |
| + using Operand0Type_uint8 = uint16_t; |
| + using Operand0Type_int16 = int32_t; |
| + using Operand0Type_uint16 = uint32_t; |
| + using Operand0Type_int32 = int64_t; |
| + using Operand0Type_uint32 = uint64_t; |
| + |
| +#define TestImplReg(Inst, Value0, Src, Value1, Type, Size) \ |
| + do { \ |
| + static_assert(GPRRegister::Encoded_Reg_eax != \ |
| + GPRRegister::Encoded_Reg_##Src, \ |
| + "eax can not be src1."); \ |
| + static_assert(GPRRegister::Encoded_Reg_edx != \ |
| + GPRRegister::Encoded_Reg_##Src, \ |
| + "edx can not be src1."); \ |
| + \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Value0 ", " #Src ", " #Value1 ", " #Type ", " #Size \ |
| + ")"; \ |
| + static constexpr Operand0Type_##Type##Size Operand0 = \ |
| + static_cast<Type##64_t>(Value0) & Operand0Mask##Size; \ |
| + static constexpr Type##Size##_t Operand0Lo = Operand0 & Mask##Size; \ |
| + static constexpr Type##Size##_t Operand0Hi = \ |
| + (Operand0 >> Size) & Mask##Size; \ |
| + static constexpr Type##Size##_t Operand1 = \ |
| + static_cast<Type##Size##_t>(Value1) & Mask##Size; \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \ |
| + Immediate(Operand0Lo)); \ |
| + if (Size == 8) { \ |
| + /* mov Operand0Hi, %al */ \ |
| + __ mov(IceType_i8, GPRRegister::Encoded_Reg_esp, Immediate(Operand0Hi)); \ |
| + } else { \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \ |
| + Immediate(Operand0Hi)); \ |
| + } \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate(Operand1)); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Src); \ |
| + if (Size == 8) { \ |
| + /* mov %ah, %dl */ \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \ |
| + GPRRegister::Encoded_Reg_esp); \ |
| + __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \ |
| + if (GPRRegister::Encoded_Reg_##Src == GPRRegister::Encoded_Reg_esi) { \ |
| + __ And(IceType_i16, GPRRegister::Encoded_Reg_edx, Immediate(0x00FF)); \ |
| + } \ |
| + } \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + static constexpr uint32_t Quocient = (Operand0 / Operand1) & Mask##Size; \ |
| + static constexpr uint32_t Reminder = (Operand0 % Operand1) & Mask##Size; \ |
| + EXPECT_EQ(Quocient, test.eax()) << TestString; \ |
| + EXPECT_EQ(Reminder, test.edx()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddr(Inst, Value0, Value1, Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Value0 ", Addr, " #Value1 ", " #Type ", " #Size ")"; \ |
| + static constexpr Operand0Type_##Type##Size Operand0 = \ |
| + static_cast<Type##64_t>(Value0) & Operand0Mask##Size; \ |
| + static constexpr Type##Size##_t Operand0Lo = Operand0 & Mask##Size; \ |
| + static constexpr Type##Size##_t Operand0Hi = \ |
| + (Operand0 >> Size) & Mask##Size; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + static constexpr Type##Size##_t V0 = \ |
| + static_cast<Type##Size##_t>(Value1) & Mask##Size; \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \ |
| + Immediate(Operand0Lo)); \ |
| + if (Size == 8) { \ |
| + /* mov Operand0Hi, %al */ \ |
| + __ mov(IceType_i8, GPRRegister::Encoded_Reg_esp, Immediate(Operand0Hi)); \ |
| + } else { \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \ |
| + Immediate(Operand0Hi)); \ |
| + } \ |
| + __ Inst(IceType_i##Size, dwordAddress(T0)); \ |
| + if (Size == 8) { \ |
| + /* mov %ah, %dl */ \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \ |
| + GPRRegister::Encoded_Reg_esp); \ |
| + __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \ |
| + } \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, static_cast<uint32_t>(V0)); \ |
| + test.run(); \ |
| + \ |
| + static constexpr uint32_t Quocient = (Operand0 / V0) & Mask##Size; \ |
| + static constexpr uint32_t Reminder = (Operand0 % V0) & Mask##Size; \ |
| + EXPECT_EQ(Quocient, test.eax()) << TestString; \ |
| + EXPECT_EQ(Reminder, test.edx()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplOp(Inst, Value0, Src, Value1, Type, Size) \ |
| + do { \ |
| + TestImplReg(Inst, Value0, Src, Value1, Type, Size); \ |
| + TestImplAddr(Inst, Value0, Value1, Type, Size); \ |
| + } while (0) |
| + |
| +#define TestImplValue(Value0, Src, Value1, Size) \ |
| + do { \ |
| + TestImplOp(div, Value0, Src, Value1, uint, Size); \ |
| + TestImplOp(idiv, Value0, Src, Value1, int, Size); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Src, Size) \ |
| + do { \ |
| + TestImplValue(10, Src, 1, Size); \ |
| + TestImplValue(10, Src, -1, Size); \ |
| + } while (0) |
| + |
| +#define TestImpl(Src) \ |
| + do { \ |
| + TestImplSize(Src, 8); \ |
| + TestImplSize(Src, 16); \ |
| + TestImplSize(Src, 32); \ |
| + } while (0) |
| + |
| + TestImpl(ebx); |
| + TestImpl(ecx); |
| + TestImpl(esi); |
| + TestImpl(edi); |
| + |
| +#undef TestImpl |
| +#undef TestImplSize |
| +#undef TestImplValue |
| +#undef TestImplOp |
| +#undef TestImplAddr |
| +#undef TestImplReg |
| +} |
| + |
| +// This is not executable in x86-64 because the one byte inc/dec instructions |
| +// became the REX prefixes. Therefore, these are tested with the low-level test |
| +// infrastructure. |
| +TEST_F(AssemblerX8632LowLevelTest, Incl_Decl_Reg) { |
| +#define TestImpl(Inst, Dst, BaseOpcode) \ |
| + do { \ |
| + __ Inst(GPRRegister::Encoded_Reg_##Dst); \ |
| + static constexpr uint8_t ByteCount = 1; \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()); \ |
| + verifyBytes<ByteCount>(codeBytes(), \ |
| + BaseOpcode | GPRRegister::Encoded_Reg_##Dst); \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestInc(Dst) \ |
| + do { \ |
| + constexpr uint8_t InclOpcode = 0x40; \ |
| + TestImpl(incl, Dst, InclOpcode); \ |
| + } while (0) |
| + |
| +#define TestDec(Dst) \ |
| + do { \ |
| + constexpr uint8_t DeclOpcode = 0x48; \ |
| + TestImpl(decl, Dst, DeclOpcode); \ |
| + } while (0) |
| + |
| + TestInc(eax); |
| + TestInc(ecx); |
| + TestInc(edx); |
| + TestInc(ebx); |
| + TestInc(esp); |
| + TestInc(ebp); |
| + TestInc(esi); |
| + TestInc(esi); |
| + |
| + TestDec(eax); |
| + TestDec(ecx); |
| + TestDec(edx); |
| + TestDec(ebx); |
| + TestDec(esp); |
| + TestDec(ebp); |
| + TestDec(esi); |
| + TestDec(esi); |
| + |
| +#undef TestInc |
| +#undef TestDec |
| +#undef TestImpl |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Incl_Decl_Addr) { |
| +#define TestImpl(Inst, Value0) \ |
| + do { \ |
| + const bool IsInc = std::string(#Inst).find("incl") != std::string::npos; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Value0; \ |
| + \ |
| + __ Inst(dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(static_cast<uint32_t>(Value0 + (IsInc ? 1 : -1)), \ |
| + test.contentsOfDword(T0)); \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestInc(Value0) \ |
| + do { \ |
| + TestImpl(incl, Value0); \ |
| + } while (0) |
| + |
| +#define TestDec(Value0) \ |
| + do { \ |
| + TestImpl(decl, Value0); \ |
| + } while (0) |
| + |
| + TestInc(230); |
| + |
| + TestDec(30); |
| + |
| +#undef TestInc |
| +#undef TestDec |
| +#undef TestImpl |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Shifts) { |
| + static constexpr uint32_t Mask8 = 0x000000FF; |
| + static constexpr uint32_t Mask16 = 0x0000FFFF; |
| + static constexpr uint32_t Mask32 = 0xFFFFFFFF; |
| + |
| +#define TestImplRegImm(Inst, Dst, Value0, Imm, Op, Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Value0 ", Imm(" #Imm "), " #Op ", " #Type \ |
| + ", " #Size ")"; \ |
| + const bool IsRol = std::string(#Inst).find("rol") != std::string::npos; \ |
| + const uint##Size##_t Expected = \ |
| + Mask##Size & (static_cast<Type##Size##_t>(Value0) Op(Imm) | \ |
| + (!IsRol ? 0 : (Value0) >> (Size - Imm))); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate((Value0)&Mask##Size)); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate((Imm)&Mask##Size)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(static_cast<uint32_t>(Expected), test.Dst()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegRegImm(Inst, Dst, Value0, Src, Value1, Count, Op0, Op1, \ |
| + Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Value0 ", " #Src ", " #Value1 \ |
| + ", Imm(" #Count "), " #Op0 ", " #Op1 ", " #Type ", " #Size ")"; \ |
| + const uint##Size##_t Expected = \ |
| + Mask##Size & (static_cast<Type##Size##_t>(Value0) Op0(Count) | \ |
| + (static_cast<Type##64_t>(Value1) Op1(Size - Count))); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate((Value0)&Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate((Value1)&Mask##Size)); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src, Immediate(Count)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(static_cast<uint32_t>(Expected), test.Dst()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegCl(Inst, Dst, Value0, Count, Op, Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Value0 ", " #Count ", " #Op ", " #Type \ |
| + ", " #Size ")"; \ |
| + const bool IsRol = std::string(#Inst).find("rol") != std::string::npos; \ |
| + const uint##Size##_t Expected = \ |
| + Mask##Size & (static_cast<Type##Size##_t>(Value0) Op(Count) | \ |
| + (!IsRol ? 0 : Value0 >> (Size - Count))); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate((Value0)&Mask##Size)); \ |
| + __ mov(IceType_i8, GPRRegister::Encoded_Reg_ecx, \ |
| + Immediate((Count)&Mask##Size)); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_ecx); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(static_cast<uint32_t>(Expected), test.Dst()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegRegCl(Inst, Dst, Value0, Src, Value1, Count, Op0, Op1, \ |
| + Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Count \ |
| + ", " #Op0 ", " #Op1 ", " #Type ", " #Size ")"; \ |
| + const uint##Size##_t Expected = \ |
| + Mask##Size & (static_cast<Type##Size##_t>(Value0) Op0(Count) | \ |
| + (static_cast<Type##64_t>(Value1) Op1(Size - Count))); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate((Value0)&Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate((Value1)&Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_ecx, \ |
| + Immediate((Count)&0x7F)); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(static_cast<uint32_t>(Expected), test.Dst()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddrCl(Inst, Value0, Count, Op, Type, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", Addr, " #Value0 ", " #Count ", " #Op ", " #Type \ |
| + ", " #Size ")"; \ |
| + const bool IsRol = std::string(#Inst).find("rol") != std::string::npos; \ |
| + const uint##Size##_t Expected = \ |
| + Mask##Size & (static_cast<Type##Size##_t>(Value0) Op(Count) | \ |
| + (!IsRol ? 0 : Value0 >> (Size - Count))); \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = Value0; \ |
| + \ |
| + __ mov(IceType_i8, GPRRegister::Encoded_Reg_ecx, \ |
| + Immediate((Count)&Mask##Size)); \ |
| + __ Inst(IceType_i##Size, dwordAddress(T0), GPRRegister::Encoded_Reg_ecx); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(static_cast<uint32_t>(Expected), \ |
| + Mask##Size &test.contentsOfDword(T0)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddrRegCl(Inst, Value0, Src, Value1, Count, Op0, Op1, Type, \ |
| + Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", Addr, " #Value0 ", " #Src ", " #Value1 ", " #Count \ |
| + ", " #Op0 ", " #Op1 ", " #Type ", " #Size ")"; \ |
| + const uint##Size##_t Expected = \ |
| + Mask##Size & (static_cast<Type##Size##_t>(Value0) Op0(Count) | \ |
| + (static_cast<Type##64_t>(Value1) Op1(Size - Count))); \ |
| + const uint32_t T0 = allocateDword(); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate((Value1)&Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_ecx, \ |
| + Immediate((Count)&0x7F)); \ |
| + __ Inst(IceType_i##Size, dwordAddress(T0), \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, static_cast<uint32_t>(Value0)); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(static_cast<uint32_t>(Expected), test.contentsOfDword(T0)) \ |
| + << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplOp(Inst, Dst, Value0, Count, Op, Type, Size) \ |
| + do { \ |
| + static_assert(GPRRegister::Encoded_Reg_##Dst != \ |
| + GPRRegister::Encoded_Reg_ecx, \ |
| + "ecx should not be specified as Dst"); \ |
| + TestImplRegImm(Inst, Dst, Value0, Count, Op, Type, Size); \ |
| + TestImplRegImm(Inst, ecx, Value0, Count, Op, Type, Size); \ |
| + TestImplRegCl(Inst, Dst, Value0, Count, Op, Type, Size); \ |
| + TestImplAddrCl(Inst, Value0, Count, Op, Type, Size); \ |
| + } while (0) |
| + |
| +#define TestImplThreeOperandOp(Inst, Dst, Value0, Src, Value1, Count, Op0, \ |
| + Op1, Type, Size) \ |
| + do { \ |
| + static_assert(GPRRegister::Encoded_Reg_##Dst != \ |
| + GPRRegister::Encoded_Reg_ecx, \ |
| + "ecx should not be specified as Dst"); \ |
| + static_assert(GPRRegister::Encoded_Reg_##Src != \ |
| + GPRRegister::Encoded_Reg_ecx, \ |
| + "ecx should not be specified as Src"); \ |
| + TestImplRegRegImm(Inst, Dst, Value0, Src, Value1, Count, Op0, Op1, Type, \ |
| + Size); \ |
| + TestImplRegRegCl(Inst, Dst, Value0, Src, Value1, Count, Op0, Op1, Type, \ |
| + Size); \ |
| + TestImplAddrRegCl(Inst, Value0, Src, Value1, Count, Op0, Op1, Type, Size); \ |
| + } while (0) |
| + |
| +#define TestImplValue(Dst, Value0, Count, Size) \ |
| + do { \ |
| + TestImplOp(rol, Dst, Value0, Count, <<, uint, Size); \ |
| + TestImplOp(shl, Dst, Value0, Count, <<, uint, Size); \ |
| + TestImplOp(shr, Dst, Value0, Count, >>, uint, Size); \ |
| + TestImplOp(sar, Dst, Value0, Count, >>, int, Size); \ |
| + } while (0) |
| + |
| +#define TestImplThreeOperandValue(Dst, Value0, Src, Value1, Count, Size) \ |
| + do { \ |
| + TestImplThreeOperandOp(shld, Dst, Value0, Src, Value1, Count, <<, >>, \ |
| + uint, Size); \ |
| + TestImplThreeOperandOp(shrd, Dst, Value0, Src, Value1, Count, >>, <<, \ |
| + uint, Size); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Dst, Size) \ |
| + do { \ |
| + TestImplValue(Dst, 0x8F, 3, Size); \ |
| + TestImplValue(Dst, 0x8FFF, 7, Size); \ |
| + TestImplValue(Dst, 0x8FFFF, 7, Size); \ |
| + } while (0) |
| + |
| +#define TestImplThreeOperandSize(Dst, Src, Size) \ |
| + do { \ |
| + TestImplThreeOperandValue(Dst, 0xFFF3, Src, 0xA000, 8, Size); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst, Src) \ |
| + do { \ |
| + if (GPRRegister::Encoded_Reg_##Dst < 4) { \ |
| + TestImplSize(Dst, 8); \ |
| + } \ |
| + TestImplSize(Dst, 16); \ |
| + TestImplThreeOperandSize(Dst, Src, 16); \ |
| + TestImplSize(Dst, 32); \ |
| + TestImplThreeOperandSize(Dst, Src, 32); \ |
| + } while (0) |
| + |
| + TestImpl(eax, ebx); |
| + TestImpl(ebx, edx); |
| + TestImpl(edx, esi); |
| + TestImpl(esi, edi); |
| + TestImpl(edi, eax); |
| + |
| +#undef TestImpl |
| +#undef TestImplThreeOperandSize |
| +#undef TestImplSize |
| +#undef TestImplValue |
| +#undef TestImplThreeOperandValue |
| +#undef TestImplOp |
| +#undef TestImplThreeOperandOp |
| +#undef TestImplAddrCl |
| +#undef TestImplRegRegCl |
| +#undef TestImplRegCl |
| +#undef TestImplRegRegImm |
| +#undef TestImplRegImm |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Neg) { |
| + static constexpr uint32_t Mask8 = 0x000000ff; |
| + static constexpr uint32_t Mask16 = 0x0000ffff; |
| + static constexpr uint32_t Mask32 = 0xffffffff; |
| + |
| +#define TestImplReg(Dst, Size) \ |
| + do { \ |
| + static constexpr int32_t Value = 0xFF00A543; \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + Immediate(static_cast<int##Size##_t>(Value) & Mask##Size)); \ |
| + __ neg(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \ |
| + GPRRegister::Encoded_Reg_##Dst); \ |
| + __ And(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(Mask##Size)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(1 + (~static_cast<int##Size##_t>(Value) & Mask##Size), \ |
| + test.eax()) \ |
| + << "(" #Dst ", " #Size ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplAddr(Size) \ |
| + do { \ |
| + static constexpr int32_t Value = 0xFF00A543; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + __ neg(IceType_i##Size, dwordAddress(T0)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, Value &Mask##Size); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(1 + (~static_cast<int##Size##_t>(Value) & Mask##Size), \ |
| + test.contentsOfDword(T0)) \ |
| + << "(Addr, " #Size ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImpl(Size) \ |
| + do { \ |
| + TestImplAddr(Size); \ |
| + TestImplReg(eax, Size); \ |
| + TestImplReg(ebx, Size); \ |
| + TestImplReg(ecx, Size); \ |
| + TestImplReg(edx, Size); \ |
| + TestImplReg(esi, Size); \ |
| + TestImplReg(edi, Size); \ |
| + } while (0) |
| + |
| + TestImpl(8); |
| + TestImpl(16); |
| + TestImpl(32); |
| + |
| +#undef TestImpl |
| +#undef TestImplAddr |
| +#undef TestImplReg |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Not) { |
| +#define TestImpl(Dst) \ |
| + do { \ |
| + static constexpr uint32_t Value = 0xFF00A543; \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, Immediate(Value)); \ |
| + __ notl(GPRRegister::Encoded_Reg_##Dst); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(~Value, test.Dst()) << "(" #Dst ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestImpl(eax); |
| + TestImpl(ebx); |
| + TestImpl(ecx); |
| + TestImpl(edx); |
| + TestImpl(esi); |
| + TestImpl(edi); |
| + |
| +#undef TestImpl |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Bswap) { |
| +#define TestImpl(Dst) \ |
| + do { \ |
| + static constexpr uint32_t Value = 0xFF00A543; \ |
| + static constexpr uint32_t Expected = 0x43A500FF; \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, Immediate(Value)); \ |
| + __ bswap(IceType_i32, GPRRegister::Encoded_Reg_##Dst); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Expected, test.Dst()) << "(" #Dst ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestImpl(eax); |
| + TestImpl(ebx); |
| + TestImpl(ecx); |
| + TestImpl(edx); |
| + TestImpl(esi); |
| + TestImpl(edi); |
| + |
| +#undef TestImpl |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Bt) { |
| +#define TestImpl(Dst, Value0, Src, Value1) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ")"; \ |
| + static constexpr uint32_t Expected = ((Value0) & (1u << (Value1))) != 0; \ |
| + \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, Immediate(Value0)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src, Immediate(Value1)); \ |
| + __ bt(GPRRegister::Encoded_Reg_##Dst, GPRRegister::Encoded_Reg_##Src); \ |
| + __ setcc(Cond::Br_b, ByteRegister::Encoded_Reg_al); \ |
| + __ And(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0xFFu)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(Expected, test.eax()) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestImpl(eax, 0x08000000, ebx, 27u); |
| + TestImpl(ebx, 0x08000000, ecx, 23u); |
| + TestImpl(ecx, 0x00000000, edx, 1u); |
| + TestImpl(edx, 0x08000300, esi, 9u); |
| + TestImpl(esi, 0x08000300, edi, 10u); |
| + TestImpl(edi, 0x7FFFEFFF, eax, 13u); |
| + |
| +#undef TestImpl |
| +} |
| + |
| +template <uint32_t Value, uint32_t Bits> class BitScanHelper { |
| + BitScanHelper() = delete; |
| + |
| +public: |
| + static_assert(Bits == 16 || Bits == 32, "Bits must be 16 or 32"); |
| + using ValueType = |
| + typename std::conditional<Bits == 16, uint16_t, uint32_t>::type; |
| + |
| +private: |
| + static constexpr ValueType BitIndex(bool Forward, ValueType Index) { |
| + return (Value == 0) |
| + ? BitScanHelper<Value, Bits>::NoBitSet |
| + : (Value & (1u << Index) |
| + ? Index |
| + : BitIndex(Forward, (Forward ? Index + 1 : Index - 1))); |
| + } |
| + |
| +public: |
| + static constexpr ValueType NoBitSet = static_cast<ValueType>(-1); |
| + static constexpr ValueType bsf = BitIndex(/*Forward*/ true, /*Index=*/0); |
| + static constexpr ValueType bsr = |
| + BitIndex(/*Forward*/ false, /*Index=*/Bits - 1); |
| +}; |
| + |
| +TEST_F(AssemblerX8632Test, BitScanOperations) { |
| +#define TestImplRegReg(Inst, Dst, Src, Value1, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", " #Src ", " #Value1 ", " #Size ")"; \ |
| + static constexpr uint32_t Expected = BitScanHelper<Value1, Size>::Inst; \ |
| + const uint32_t ZeroFlag = allocateDword(); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate(Value1)); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + GPRRegister::Encoded_Reg_##Src); \ |
| + __ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(ZeroFlag, 0u); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ((Expected == BitScanHelper<Value1, Size>::NoBitSet), \ |
| + test.contentsOfDword(ZeroFlag)) \ |
| + << TestString; \ |
| + if ((Expected != BitScanHelper<Value1, Size>::NoBitSet)) { \ |
| + ASSERT_EQ(Expected, test.Dst()) << TestString; \ |
| + } \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplRegAddr(Inst, Dst, Value1, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Inst ", " #Dst ", Addr, " #Value1 ", " #Size ")"; \ |
| + static constexpr uint32_t Expected = BitScanHelper<Value1, Size>::Inst; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t ZeroFlag = allocateDword(); \ |
| + __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \ |
| + dwordAddress(T0)); \ |
| + __ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, Value1); \ |
| + test.setDwordTo(ZeroFlag, 0u); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ((Expected == BitScanHelper<Value1, Size>::NoBitSet), \ |
| + test.contentsOfDword(ZeroFlag)) \ |
| + << TestString; \ |
| + if (Expected != BitScanHelper<Value1, Size>::NoBitSet) { \ |
| + ASSERT_EQ(Expected, test.Dst()) << TestString; \ |
| + } \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Dst, Src, Value1, Size) \ |
| + do { \ |
| + TestImplRegReg(bsf, Dst, Src, Value1, Size); \ |
| + TestImplRegAddr(bsf, Dst, Value1, Size); \ |
| + TestImplRegReg(bsr, Dst, Src, Value1, Size); \ |
| + TestImplRegAddr(bsf, Dst, Value1, Size); \ |
| + } while (0) |
| + |
| +#define TestImplValue(Dst, Src, Value1) \ |
| + do { \ |
| + TestImplSize(Dst, Src, Value1, 16); \ |
| + TestImplSize(Dst, Src, Value1, 32); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst, Src) \ |
| + do { \ |
| + TestImplValue(Dst, Src, 0x80000001); \ |
| + TestImplValue(Dst, Src, 0x00000000); \ |
| + TestImplValue(Dst, Src, 0x80001000); \ |
| + TestImplValue(Dst, Src, 0x00FFFF00); \ |
| + } while (0) |
| + |
| + TestImpl(eax, ebx); |
| + TestImpl(ebx, ecx); |
| + TestImpl(ecx, edx); |
| + TestImpl(edx, esi); |
| + TestImpl(esi, edi); |
| + TestImpl(edi, eax); |
| + |
| +#undef TestImpl |
| +#undef TestImplValue |
| +#undef TestImplSize |
| +#undef TestImplRegAddr |
| +#undef TestImplRegReg |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Nop) { |
| +#define TestImpl(Size, ...) \ |
| + do { \ |
| + static constexpr char TestString[] = "(" #Size ", " #__VA_ARGS__ ")"; \ |
| + __ nop(Size); \ |
| + ASSERT_EQ(Size##u, codeBytesSize()) << TestString; \ |
| + ASSERT_TRUE(verifyBytes<Size>(codeBytes(), __VA_ARGS__)) << TestString; \ |
| + reset(); \ |
| + } while (0); |
| + |
| + TestImpl(1, 0x90); |
| + TestImpl(2, 0x66, 0x90); |
| + TestImpl(3, 0x0F, 0x1F, 0x00); |
| + TestImpl(4, 0x0F, 0x1F, 0x40, 0x00); |
| + TestImpl(5, 0x0F, 0x1F, 0x44, 0x00, 0x00); |
| + TestImpl(6, 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00); |
| + TestImpl(7, 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00); |
| + TestImpl(8, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00); |
| + |
| +#undef TestImpl |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Int3) { |
| + __ int3(); |
| + static constexpr uint32_t ByteCount = 1; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + verifyBytes<ByteCount>(codeBytes(), 0xCC); |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Hlt) { |
| + __ hlt(); |
| + static constexpr uint32_t ByteCount = 1; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + verifyBytes<ByteCount>(codeBytes(), 0xF4); |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Ud2) { |
| + __ ud2(); |
| + static constexpr uint32_t ByteCount = 2; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + verifyBytes<ByteCount>(codeBytes(), 0x0F, 0x0B); |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Jmp) { |
| +// TestImplReg uses jmp(Label), so jmp(Label) needs to be tested before it. |
| +#define TestImplAddr(Near) \ |
| + do { \ |
| + Label ForwardJmp; \ |
| + Label BackwardJmp; \ |
| + Label Done; \ |
| + \ |
| + __ jmp(&ForwardJmp, AssemblerX8632::k##Near##Jump); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ bind(&BackwardJmp); \ |
| + __ jmp(&Done, AssemblerX8632::k##Near##Jump); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ bind(&ForwardJmp); \ |
| + __ jmp(&BackwardJmp, AssemblerX8632::k##NearJump); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ bind(&Done); \ |
| + } while (0) |
| + |
| +#define TestImplReg(Dst) \ |
| + do { \ |
| + __ call(Immediate(16)); \ |
| + Label Done; \ |
| + __ jmp(&Done, AssemblerX8632::kNearJump); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ popl(GPRRegister::Encoded_Reg_##Dst); \ |
| + __ jmp(GPRRegister::Encoded_Reg_##Dst); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ hlt(); \ |
| + __ bind(&Done); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestImplAddr(Near); |
| + TestImplAddr(Far); |
| + |
| + TestImplReg(eax); |
| + TestImplReg(ebx); |
| + TestImplReg(ecx); |
| + TestImplReg(edx); |
| + TestImplReg(esi); |
| + TestImplReg(edi); |
| + |
| +#undef TestImplReg |
| +#undef TestImplAddr |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Mfence) { |
| + __ mfence(); |
| + |
| + static constexpr uint8_t ByteCount = 3; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + verifyBytes<ByteCount>(codeBytes(), 0x0F, 0xAE, 0xF0); |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Lock) { |
| + __ lock(); |
| + |
| + static constexpr uint8_t ByteCount = 1; |
| + ASSERT_EQ(ByteCount, codeBytesSize()); |
| + verifyBytes<ByteCount>(codeBytes(), 0xF0); |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Xchg) { |
| + static constexpr uint32_t Mask8 = 0x000000FF; |
| + static constexpr uint32_t Mask16 = 0x0000FFFF; |
| + static constexpr uint32_t Mask32 = 0xFFFFFFFF; |
| + |
| +#define TestImplAddrReg(Value0, Dst1, Value1, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Value0 ", " #Dst1 ", " #Value1 ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = (Value0)&Mask##Size; \ |
| + const uint32_t V1 = (Value1)&Mask##Size; \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \ |
| + Immediate(Value1)); \ |
| + __ xchg(IceType_i##Size, dwordAddress(T0), \ |
| + GPRRegister::Encoded_Reg_##Dst1); \ |
| + __ And(IceType_i32, GPRRegister::Encoded_Reg_##Dst1, \ |
| + Immediate(Mask##Size)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(V0, test.Dst1()) << TestString; \ |
| + ASSERT_EQ(V1, test.contentsOfDword(T0)) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Dst1, Size) \ |
| + do { \ |
| + TestImplAddrReg(0xa2b34567, Dst1, 0x0507ddee, Size); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst1) \ |
| + do { \ |
| + if (GPRRegister::Encoded_Reg_##Dst1 < 4) { \ |
| + TestImplSize(Dst1, 8); \ |
| + } \ |
| + TestImplSize(Dst1, 16); \ |
| + TestImplSize(Dst1, 32); \ |
| + } while (0) |
| + |
| + TestImpl(eax); |
| + TestImpl(ebx); |
| + TestImpl(ecx); |
| + TestImpl(edx); |
| + TestImpl(esi); |
| + TestImpl(edi); |
| + |
| +#undef TestImpl |
| +#undef TestImplSize |
| +#undef TestImplAddrReg |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Xadd) { |
| + static constexpr bool NotLocked = false; |
| + static constexpr bool Locked = true; |
| + |
| + static constexpr uint32_t Mask8 = 0x000000FF; |
| + static constexpr uint32_t Mask16 = 0x0000FFFF; |
| + static constexpr uint32_t Mask32 = 0xFFFFFFFF; |
| + |
| +#define TestImplAddrReg(Value0, Dst1, Value1, LockedOrNot, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Value0 ", " #Dst1 ", " #Value1 ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + const uint32_t V0 = (Value0)&Mask##Size; \ |
| + const uint32_t V1 = (Value1)&Mask##Size; \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \ |
| + Immediate(Value1)); \ |
| + __ xadd(IceType_i##Size, dwordAddress(T0), \ |
| + GPRRegister::Encoded_Reg_##Dst1, LockedOrNot); \ |
| + __ And(IceType_i32, GPRRegister::Encoded_Reg_##Dst1, \ |
| + Immediate(Mask##Size)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.run(); \ |
| + \ |
| + ASSERT_EQ(V0, test.Dst1()) << TestString; \ |
| + ASSERT_EQ(Mask##Size &(V1 + V0), test.contentsOfDword(T0)) << TestString; \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplSize(Dst1, Size) \ |
| + do { \ |
| + TestImplAddrReg(0xa2b34567, Dst1, 0x0507ddee, NotLocked, Size); \ |
| + TestImplAddrReg(0xa2b34567, Dst1, 0x0507ddee, Locked, Size); \ |
| + } while (0) |
| + |
| +#define TestImpl(Dst1) \ |
| + do { \ |
| + if (GPRRegister::Encoded_Reg_##Dst1 < 4) { \ |
| + TestImplSize(Dst1, 8); \ |
| + } \ |
| + TestImplSize(Dst1, 16); \ |
| + TestImplSize(Dst1, 32); \ |
| + } while (0) |
| + |
| + TestImpl(eax); |
| + TestImpl(ebx); |
| + TestImpl(ecx); |
| + TestImpl(edx); |
| + TestImpl(esi); |
| + TestImpl(edi); |
| + |
| +#undef TestImpl |
| +#undef TestImplSize |
| +#undef TestImplAddrReg |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Xadd) { |
| + static constexpr bool NotLocked = false; |
| + static constexpr bool Locked = true; |
| + |
| + // Ensures that xadd emits a lock prefix accordingly. |
| + { |
| + __ xadd(IceType_i8, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, NotLocked); |
| + static constexpr uint8_t ByteCountNotLocked8 = 7; |
| + ASSERT_EQ(ByteCountNotLocked8, codeBytesSize()); |
| + verifyBytes<ByteCountNotLocked8>(codeBytes(), 0x0F, 0xC0, 0x35, 0x00, 0xFF, |
| + 0x01, 0x00); |
| + reset(); |
| + |
| + __ xadd(IceType_i8, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, Locked); |
| + static constexpr uint8_t ByteCountLocked8 = 1 + ByteCountNotLocked8; |
| + ASSERT_EQ(ByteCountLocked8, codeBytesSize()); |
| + verifyBytes<ByteCountLocked8>(codeBytes(), 0xF0, 0x0F, 0xC0, 0x35, 0x00, |
| + 0xFF, 0x01, 0x00); |
| + reset(); |
| + } |
| + |
| + { |
| + __ xadd(IceType_i16, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, NotLocked); |
| + static constexpr uint8_t ByteCountNotLocked16 = 8; |
| + ASSERT_EQ(ByteCountNotLocked16, codeBytesSize()); |
| + verifyBytes<ByteCountNotLocked16>(codeBytes(), 0x66, 0x0F, 0xC1, 0x35, 0x00, |
| + 0xFF, 0x01, 0x00); |
| + reset(); |
| + |
| + __ xadd(IceType_i16, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, Locked); |
| + static constexpr uint8_t ByteCountLocked16 = 1 + ByteCountNotLocked16; |
| + ASSERT_EQ(ByteCountLocked16, codeBytesSize()); |
| + verifyBytes<ByteCountLocked16>(codeBytes(), 0x66, 0xF0, 0x0F, 0xC1, 0x35, |
| + 0x00, 0xFF, 0x01, 0x00); |
| + reset(); |
| + } |
| + |
| + { |
| + __ xadd(IceType_i32, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, NotLocked); |
| + static constexpr uint8_t ByteCountNotLocked32 = 7; |
| + ASSERT_EQ(ByteCountNotLocked32, codeBytesSize()); |
| + verifyBytes<ByteCountNotLocked32>(codeBytes(), 0x0F, 0xC1, 0x35, 0x00, 0xFF, |
| + 0x01, 0x00); |
| + reset(); |
| + |
| + __ xadd(IceType_i32, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, Locked); |
| + static constexpr uint8_t ByteCountLocked32 = 1 + ByteCountNotLocked32; |
| + ASSERT_EQ(ByteCountLocked32, codeBytesSize()); |
| + verifyBytes<ByteCountLocked32>(codeBytes(), 0xF0, 0x0F, 0xC1, 0x35, 0x00, |
| + 0xFF, 0x01, 0x00); |
| + reset(); |
| + } |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, EmitSegmentOverride) { |
| +#define TestImpl(Prefix) \ |
| + do { \ |
| + static constexpr uint8_t ByteCount = 1; \ |
| + __ emitSegmentOverride(Prefix); \ |
| + ASSERT_EQ(ByteCount, codeBytesSize()) << Prefix; \ |
| + verifyBytes<ByteCount>(codeBytes(), Prefix); \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestImpl(0x26); |
| + TestImpl(0x2E); |
| + TestImpl(0x36); |
| + TestImpl(0x3E); |
| + TestImpl(0x64); |
| + TestImpl(0x65); |
| + TestImpl(0x66); |
| + TestImpl(0x67); |
| + |
| +#undef TestImpl |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Cmpxchg8b) { |
| + static constexpr bool NotLocked = false; |
| + static constexpr bool Locked = true; |
| + |
| +#define TestImpl(Value0, Value1, ValueMem, LockedOrNot) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Value0 ", " #Value1 ", " #ValueMem ", " #LockedOrNot ")"; \ |
| + const uint32_t T0 = allocateQword(); \ |
| + static constexpr uint64_t V0 = ValueMem; \ |
| + const uint32_t ZeroFlag = allocateDword(); \ |
| + \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, \ |
| + Immediate(uint64_t(Value0) & 0xFFFFFFFF)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, \ |
| + Immediate(uint64_t(Value0) >> 32)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, \ |
| + Immediate(uint64_t(Value1) & 0xFFFFFFFF)); \ |
| + __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, \ |
| + Immediate(uint64_t(Value1) >> 32)); \ |
| + __ cmpxchg8b(dwordAddress(T0), LockedOrNot); \ |
| + __ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setQwordTo(T0, V0); \ |
| + test.setDwordTo(ZeroFlag, uint32_t(0xFF)); \ |
| + test.run(); \ |
| + \ |
| + if (V0 == (Value0)) { \ |
| + ASSERT_EQ(uint64_t(Value1), test.contentsOfQword(T0)) << TestString; \ |
| + ASSERT_EQ(1u, test.contentsOfDword(ZeroFlag)) << TestString; \ |
| + } else { \ |
| + ASSERT_EQ(uint64_t(ValueMem) & 0xFFFFFFFF, test.eax()) << TestString; \ |
| + ASSERT_EQ((uint64_t(ValueMem) >> 32) & 0xFFFFFFFF, test.edx()) \ |
| + << TestString; \ |
| + ASSERT_EQ(0u, test.contentsOfDword(ZeroFlag)) << TestString; \ |
| + } \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestImpl(0x98987676543210ull, 0x1, 0x98987676543210ull, NotLocked); |
| + TestImpl(0x98987676543210ull, 0x1, 0x98987676543210ull, Locked); |
| + TestImpl(0x98987676543210ull, 0x1, 0x98987676543211ull, NotLocked); |
| + TestImpl(0x98987676543210ull, 0x1, 0x98987676543211ull, Locked); |
| + |
| +#undef TestImpl |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Cmpxchg8b) { |
| + static constexpr bool NotLocked = false; |
| + static constexpr bool Locked = true; |
| + |
| + // Ensures that cmpxchg8b emits a lock prefix accordingly. |
| + __ cmpxchg8b(Address::Absolute(0x1FF00), NotLocked); |
| + static constexpr uint8_t ByteCountNotLocked = 7; |
| + ASSERT_EQ(ByteCountNotLocked, codeBytesSize()); |
| + verifyBytes<ByteCountNotLocked>(codeBytes(), 0x0F, 0xC7, 0x0D, 0x00, 0xFF, |
| + 0x01, 0x00); |
| + reset(); |
| + |
| + __ cmpxchg8b(Address::Absolute(0x1FF00), Locked); |
| + static constexpr uint8_t ByteCountLocked = 1 + ByteCountNotLocked; |
| + ASSERT_EQ(ByteCountLocked, codeBytesSize()); |
| + verifyBytes<ByteCountLocked>(codeBytes(), 0xF0, 0x0F, 0xC7, 0x0D, 0x00, 0xFF, |
| + 0x01, 0x00); |
| + reset(); |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Cmpxchg) { |
| + static constexpr bool NotLocked = false; |
| + static constexpr bool Locked = true; |
| + |
| + static constexpr uint32_t Mask8 = 0x000000FF; |
| + static constexpr uint32_t Mask16 = 0x0000FFFF; |
| + static constexpr uint32_t Mask32 = 0xFFFFFFFF; |
| + |
| +#define TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, Size) \ |
| + do { \ |
| + static constexpr char TestString[] = \ |
| + "(" #Value0 ", " #Src ", " #Value1 ", " #ValueMem ", " #LockedOrNot \ |
| + ", " #Size ")"; \ |
| + const uint32_t T0 = allocateDword(); \ |
| + static constexpr uint32_t V0 = (ValueMem)&Mask##Size; \ |
| + const uint32_t ZeroFlag = allocateDword(); \ |
| + \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \ |
| + Immediate((Value0)&Mask##Size)); \ |
| + __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate((Value1)&Mask##Size)); \ |
| + __ cmpxchg(IceType_i##Size, dwordAddress(T0), \ |
| + GPRRegister::Encoded_Reg_##Src, LockedOrNot); \ |
| + __ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.setDwordTo(T0, V0); \ |
| + test.setDwordTo(ZeroFlag, uint32_t(0xFF)); \ |
| + test.run(); \ |
| + \ |
| + if (V0 == (Mask##Size & (Value0))) { \ |
| + ASSERT_EQ(uint32_t((Value1)&Mask##Size), test.contentsOfDword(T0)) \ |
| + << TestString; \ |
| + ASSERT_EQ(1u, test.contentsOfDword(ZeroFlag)) << TestString; \ |
| + } else { \ |
| + ASSERT_EQ(uint32_t((ValueMem)&Mask##Size), test.eax()) << TestString; \ |
| + ASSERT_EQ(0u, test.contentsOfDword(ZeroFlag)) << TestString; \ |
| + } \ |
| + reset(); \ |
| + } while (0) |
| + |
| +#define TestImplValue(Value0, Src, Value1, ValueMem, LockedOrNot) \ |
| + do { \ |
| + if (GPRRegister::Encoded_Reg_##Src < 4) { \ |
| + TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, 8); \ |
| + } \ |
| + TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, 16); \ |
| + TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, 32); \ |
| + } while (0) |
| + |
| +#define TestImpl(Src, LockedOrNot) \ |
| + do { \ |
| + TestImplValue(0xFFFFFFFF, Src, 0x1, 0xFFFFFFFF, LockedOrNot); \ |
| + TestImplValue(0x0FFF0F0F, Src, 0x1, 0xFFFFFFFF, LockedOrNot); \ |
| + } while (0) |
| + |
| + TestImpl(ebx, Locked); |
| + TestImpl(edx, NotLocked); |
| + TestImpl(ecx, Locked); |
| + TestImpl(ecx, NotLocked); |
| + TestImpl(edx, Locked); |
| + TestImpl(edx, NotLocked); |
| + TestImpl(esi, Locked); |
| + TestImpl(esi, NotLocked); |
| + TestImpl(edi, Locked); |
| + TestImpl(edi, NotLocked); |
| + |
| +#undef TestImpl |
| +#undef TestImplValue |
| +#undef TestImplAddrReg |
| +} |
| + |
| +TEST_F(AssemblerX8632LowLevelTest, Cmpxchg) { |
| + static constexpr bool NotLocked = false; |
| + static constexpr bool Locked = true; |
| + |
| + // Ensures that cmpxchg emits a lock prefix accordingly. |
| + { |
| + __ cmpxchg(IceType_i8, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, NotLocked); |
| + static constexpr uint8_t ByteCountNotLocked8 = 7; |
| + ASSERT_EQ(ByteCountNotLocked8, codeBytesSize()); |
| + verifyBytes<ByteCountNotLocked8>(codeBytes(), 0x0F, 0xB0, 0x35, 0x00, 0xFF, |
| + 0x01, 0x00); |
| + reset(); |
| + |
| + __ cmpxchg(IceType_i8, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, Locked); |
| + static constexpr uint8_t ByteCountLocked8 = 1 + ByteCountNotLocked8; |
| + ASSERT_EQ(ByteCountLocked8, codeBytesSize()); |
| + verifyBytes<ByteCountLocked8>(codeBytes(), 0xF0, 0x0F, 0xB0, 0x35, 0x00, |
| + 0xFF, 0x01, 0x00); |
| + reset(); |
| + } |
| + |
| + { |
| + __ cmpxchg(IceType_i16, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, NotLocked); |
| + static constexpr uint8_t ByteCountNotLocked16 = 8; |
| + ASSERT_EQ(ByteCountNotLocked16, codeBytesSize()); |
| + verifyBytes<ByteCountNotLocked16>(codeBytes(), 0x66, 0x0F, 0xB1, 0x35, 0x00, |
| + 0xFF, 0x01, 0x00); |
| + reset(); |
| + |
| + __ cmpxchg(IceType_i16, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, Locked); |
| + static constexpr uint8_t ByteCountLocked16 = 1 + ByteCountNotLocked16; |
| + ASSERT_EQ(ByteCountLocked16, codeBytesSize()); |
| + verifyBytes<ByteCountLocked16>(codeBytes(), 0x66, 0xF0, 0x0F, 0xB1, 0x35, |
| + 0x00, 0xFF, 0x01, 0x00); |
| + reset(); |
| + } |
| + |
| + { |
| + __ cmpxchg(IceType_i32, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, NotLocked); |
| + static constexpr uint8_t ByteCountNotLocked32 = 7; |
| + ASSERT_EQ(ByteCountNotLocked32, codeBytesSize()); |
| + verifyBytes<ByteCountNotLocked32>(codeBytes(), 0x0F, 0xB1, 0x35, 0x00, 0xFF, |
| + 0x01, 0x00); |
| + reset(); |
| + |
| + __ cmpxchg(IceType_i32, Address::Absolute(0x1FF00), |
| + GPRRegister::Encoded_Reg_esi, Locked); |
| + static constexpr uint8_t ByteCountLocked32 = 1 + ByteCountNotLocked32; |
| + ASSERT_EQ(ByteCountLocked32, codeBytesSize()); |
| + verifyBytes<ByteCountLocked32>(codeBytes(), 0xF0, 0x0F, 0xB1, 0x35, 0x00, |
| + 0xFF, 0x01, 0x00); |
| + reset(); |
| + } |
| +} |
| + |
| +TEST_F(AssemblerX8632Test, Set1ps) { |
| +#define TestImpl(Xmm, Src, Imm) \ |
| + do { \ |
| + __ set1ps(XmmRegister::Encoded_Reg_##Xmm, GPRRegister::Encoded_Reg_##Src, \ |
| + Immediate(Imm)); \ |
| + \ |
| + AssembledTest test = assemble(); \ |
| + test.run(); \ |
| + \ |
| + const Dqword Expected((uint64_t(Imm) << 32) | uint32_t(Imm), \ |
| + (uint64_t(Imm) << 32) | uint32_t(Imm)); \ |
| + ASSERT_EQ(Expected, test.Xmm<Dqword>()) \ |
| + << "(" #Xmm ", " #Src ", " #Imm ")"; \ |
| + reset(); \ |
| + } while (0) |
| + |
| + TestImpl(xmm0, ebx, 1); |
| + TestImpl(xmm1, ecx, 2); |
| + TestImpl(xmm2, edx, 3); |
| + TestImpl(xmm3, esi, 4); |
| + TestImpl(xmm4, edi, 5); |
| + TestImpl(xmm5, eax, 6); |
| + TestImpl(xmm6, ebx, 7); |
| + TestImpl(xmm7, ecx, 8); |
| + |
| +#undef TestImpl |
| +} |
| + |
|
jvoung (off chromium)
2015/07/23 17:59:36
Uh... haven't read all this. I'm assuming this is
John
2015/07/27 20:35:58
Well, these are just dumb tests -- I went over the
|
| #undef __ |
| } // end of anonymous namespace |