| Index: unittest/AssemblerX8632/GPRArith.cpp
|
| diff --git a/unittest/AssemblerX8632/GPRArith.cpp b/unittest/AssemblerX8632/GPRArith.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6ca27ace641c25b6a89f35c8dc8db997016cc0fd
|
| --- /dev/null
|
| +++ b/unittest/AssemblerX8632/GPRArith.cpp
|
| @@ -0,0 +1,1884 @@
|
| +//===- subzero/unittest/AssemblerX8632/GPRArith.cpp -----------------------===//
|
| +//
|
| +// The Subzero Code Generator
|
| +//
|
| +// This file is distributed under the University of Illinois Open Source
|
| +// License. See LICENSE.TXT for details.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +#include "AssemblerX8632/TestUtil.h"
|
| +
|
| +namespace Ice {
|
| +namespace X8632 {
|
| +namespace Test {
|
| +namespace {
|
| +
|
| +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);
|
| +}
|
| +
|
| +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, 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, 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__); \
|
| + 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, %ah */ \
|
| + __ 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, %ah */ \
|
| + __ 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
|
| +}
|
| +
|
| +} // end of anonymous namespace
|
| +} // end of namespace Test
|
| +} // end of namespace X8632
|
| +} // end of namespace Ice
|
|
|