//===- subzero/unittest/AssemblerX8664/GPRArith.cpp -----------------------===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "AssemblerX8664/TestUtil.h" namespace Ice { namespace X8664 { namespace Test { namespace { TEST_F(AssemblerX8664Test, PopAddr) { const uint32_t T0 = allocateQword(); constexpr uint64_t V0 = 0x3AABBEFABBBAA3ull; __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0xC0FFEE)); __ pushl(GPRRegister::Encoded_Reg_eax); __ popl(dwordAddress(T0)); AssembledTest test = assemble(); test.setQwordTo(T0, V0); test.run(); ASSERT_EQ(0xC0FFEEul, test.contentsOfQword(T0)); } TEST_F(AssemblerX8664Test, SetCC) { #define TestSetCC(C, Dest, IsTrue, Src0, Value0, Src1, Value1) \ do { \ static constexpr char TestString[] = \ "(" #C ", " #Dest ", " #IsTrue ", " #Src0 ", " #Value0 ", " #Src1 \ ", " #Value1 ")"; \ const uint32_t T0 = allocateDword(); \ constexpr uint32_t V0 = 0xF00F00; \ __ mov(IceType_i32, Encoded_GPR_##Src0(), Immediate(Value0)); \ __ mov(IceType_i32, Encoded_GPR_##Src1(), Immediate(Value1)); \ __ cmp(IceType_i32, Encoded_GPR_##Src0(), Encoded_GPR_##Src1()); \ __ mov(IceType_i32, Encoded_GPR_##Dest(), Immediate(0)); \ __ setcc(Cond::Br_##C, Encoded_Bytereg_##Dest()); \ __ setcc(Cond::Br_##C, dwordAddress(T0)); \ \ AssembledTest test = assemble(); \ test.setDwordTo(T0, V0); \ \ test.run(); \ \ ASSERT_EQ(IsTrue, test.Dest()) << TestString; \ ASSERT_EQ((0xF00F00 | IsTrue), test.contentsOfDword(T0)) << TestString; \ reset(); \ } while (0) #define TestImpl(Dest, Src0, Src1) \ do { \ TestSetCC(o, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(o, Dest, 0u, Src0, 0x1u, Src1, 0x10000000u); \ TestSetCC(no, Dest, 1u, Src0, 0x1u, Src1, 0x10000000u); \ TestSetCC(no, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(b, Dest, 1u, Src0, 0x1, Src1, 0x80000000u); \ TestSetCC(b, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(ae, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(ae, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u); \ TestSetCC(e, Dest, 1u, Src0, 0x1u, Src1, 0x1u); \ TestSetCC(e, Dest, 0u, Src0, 0x1u, Src1, 0x11111u); \ TestSetCC(ne, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(ne, Dest, 0u, Src0, 0x1u, Src1, 0x1u); \ TestSetCC(be, Dest, 1u, Src0, 0x1u, Src1, 0x80000000u); \ TestSetCC(be, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(a, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(a, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u); \ TestSetCC(s, Dest, 1u, Src0, 0x1u, Src1, 0x80000000u); \ TestSetCC(s, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(ns, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(ns, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u); \ TestSetCC(p, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(p, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u); \ TestSetCC(np, Dest, 1u, Src0, 0x1u, Src1, 0x80000000u); \ TestSetCC(np, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(l, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(l, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u); \ TestSetCC(ge, Dest, 1u, Src0, 0x1u, Src1, 0x80000000u); \ TestSetCC(ge, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(le, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u); \ TestSetCC(le, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u); \ } while (0) TestImpl(r1, r2, r3); TestImpl(r2, r3, r4); TestImpl(r3, r4, r5); TestImpl(r4, r5, r6); TestImpl(r4, r6, r7); TestImpl(r5, r6, r7); TestImpl(r6, r7, r8); TestImpl(r7, r8, r10); TestImpl(r8, r10, r11); TestImpl(r10, r11, r12); TestImpl(r11, r12, r13); TestImpl(r12, r13, r14); TestImpl(r13, r14, r15); TestImpl(r14, r15, r1); TestImpl(r15, r1, r2); #undef TestImpl #undef TestSetCC } TEST_F(AssemblerX8664Test, Lea) { #define TestLeaBaseDisp(Base, BaseValue, Disp, Dst) \ do { \ static constexpr char TestString[] = \ "(" #Base ", " #BaseValue ", " #Dst ")"; \ if (Encoded_GPR_##Base() != Encoded_GPR_esp() && \ Encoded_GPR_##Base() != Encoded_GPR_r9()) { \ __ mov(IceType_i32, Encoded_GPR_##Base(), Immediate(BaseValue)); \ } \ __ lea(IceType_i32, Encoded_GPR_##Dst(), \ Address(Encoded_GPR_##Base(), Disp, AssemblerFixup::NoFixup)); \ AssembledTest test = assemble(); \ test.run(); \ ASSERT_EQ(test.Base##d() + (Disp), test.Dst##d()) \ << 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 ")"; \ if (Encoded_GPR_##Index() != Encoded_GPR_r9()) { \ __ mov(IceType_i32, Encoded_GPR_##Index(), Immediate(IndexValue)); \ } \ __ lea(IceType_i32, Encoded_GPR_##Dst0(), \ Address(Encoded_GPR_##Index(), Traits::TIMES_1, Disp, \ AssemblerFixup::NoFixup)); \ __ lea(IceType_i32, Encoded_GPR_##Dst1(), \ Address(Encoded_GPR_##Index(), Traits::TIMES_2, Disp, \ AssemblerFixup::NoFixup)); \ __ lea(IceType_i32, Encoded_GPR_##Dst2(), \ Address(Encoded_GPR_##Index(), Traits::TIMES_4, Disp, \ AssemblerFixup::NoFixup)); \ __ lea(IceType_i32, Encoded_GPR_##Dst3(), \ Address(Encoded_GPR_##Index(), Traits::TIMES_8, Disp, \ AssemblerFixup::NoFixup)); \ AssembledTest test = assemble(); \ test.run(); \ ASSERT_EQ((test.Index##d() << Traits::TIMES_1) + (Disp), test.Dst0##d()) \ << TestString << " " << Disp; \ ASSERT_EQ((test.Index##d() << Traits::TIMES_2) + (Disp), test.Dst1##d()) \ << TestString << " " << Disp; \ ASSERT_EQ((test.Index##d() << Traits::TIMES_4) + (Disp), test.Dst2##d()) \ << TestString << " " << Disp; \ ASSERT_EQ((test.Index##d() << Traits::TIMES_8) + (Disp), test.Dst3##d()) \ << 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 (Encoded_GPR_##Base() != Encoded_GPR_esp() && \ Encoded_GPR_##Base() != Encoded_GPR_r9()) { \ __ mov(IceType_i32, Encoded_GPR_##Base(), Immediate(BaseValue)); \ } \ \ if (Encoded_GPR_##Index() != Encoded_GPR_r9()) { \ __ mov(IceType_i32, Encoded_GPR_##Index(), Immediate(IndexValue)); \ } \ \ __ lea(IceType_i32, Encoded_GPR_##Dst0(), \ Address(Encoded_GPR_##Base(), Encoded_GPR_##Index(), \ Traits::TIMES_1, Disp, AssemblerFixup::NoFixup)); \ __ lea(IceType_i32, Encoded_GPR_##Dst1(), \ Address(Encoded_GPR_##Base(), Encoded_GPR_##Index(), \ Traits::TIMES_2, Disp, AssemblerFixup::NoFixup)); \ __ lea(IceType_i32, Encoded_GPR_##Dst2(), \ Address(Encoded_GPR_##Base(), Encoded_GPR_##Index(), \ Traits::TIMES_4, Disp, AssemblerFixup::NoFixup)); \ __ lea(IceType_i32, Encoded_GPR_##Dst3(), \ Address(Encoded_GPR_##Base(), Encoded_GPR_##Index(), \ Traits::TIMES_8, Disp, AssemblerFixup::NoFixup)); \ AssembledTest test = assemble(); \ test.run(); \ uint32_t ExpectedIndexValue = test.Index(); \ if (Encoded_GPR_##Index() == Encoded_GPR_esp()) { \ ExpectedIndexValue = 0; \ } \ ASSERT_EQ(test.Base##d() + (ExpectedIndexValue << Traits::TIMES_1) + \ (Disp), \ test.Dst0##d()) \ << TestString << " " << Disp; \ ASSERT_EQ(test.Base##d() + (ExpectedIndexValue << Traits::TIMES_2) + \ (Disp), \ test.Dst1##d()) \ << TestString << " " << Disp; \ ASSERT_EQ(test.Base##d() + (ExpectedIndexValue << Traits::TIMES_4) + \ (Disp), \ test.Dst2##d()) \ << TestString << " " << Disp; \ ASSERT_EQ(test.Base##d() + (ExpectedIndexValue << Traits::TIMES_8) + \ (Disp), \ test.Dst3##d()) \ << TestString << " " << Disp; \ reset(); \ } while (0) for (const int32_t Disp : {0x00, 0x06, -0x06, 0x0600, -0x6000, 0x6000000, -0x6000000}) { TestLeaBaseDisp(r0, 0x22080Fu, Disp, r1); TestLeaBaseDisp(r1, 0x10000Fu, Disp, r2); TestLeaBaseDisp(r2, 0x20000Fu, Disp, r3); TestLeaBaseDisp(r3, 0x30000Fu, Disp, r4); TestLeaBaseDisp(r4, 0x40000Fu, Disp, r5); TestLeaBaseDisp(r5, 0x50000Fu, Disp, r6); TestLeaBaseDisp(r6, 0x60000Fu, Disp, r7); TestLeaBaseDisp(r7, 0x11000Fu, Disp, r8); TestLeaBaseDisp(r8, 0x11200Fu, Disp, r10); TestLeaBaseDisp(r9, 0x220400u, Disp, r10); TestLeaBaseDisp(r10, 0x22000Fu, Disp, r11); TestLeaBaseDisp(r11, 0x22030Fu, Disp, r12); TestLeaBaseDisp(r12, 0x22040Fu, Disp, r13); TestLeaBaseDisp(r13, 0x22050Fu, Disp, r14); TestLeaBaseDisp(r14, 0x22060Fu, Disp, r15); TestLeaBaseDisp(r15, 0x22070Fu, Disp, r1); } // 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(r1, 0x2000u, Disp, r2, r3, r4, r6); TestLeaIndex32bitDisp(r2, 0x4010u, Disp, r3, r4, r6, r7); TestLeaIndex32bitDisp(r3, 0x6020u, Disp, r4, r6, r7, r5); TestLeaIndex32bitDisp(r4, 0x8030u, Disp, r6, r7, r5, r10); TestLeaIndex32bitDisp(r6, 0xA040u, Disp, r7, r5, r10, r1); TestLeaIndex32bitDisp(r7, 0xC050u, Disp, r5, r10, r1, r11); TestLeaIndex32bitDisp(r8, 0xC060u, Disp, r10, r1, r11, r12); TestLeaIndex32bitDisp(r9, 0xC100u, Disp, r1, r11, r12, r13); TestLeaIndex32bitDisp(r10, 0xC008u, Disp, r11, r12, r13, r14); TestLeaIndex32bitDisp(r11, 0xC009u, Disp, r12, r13, r14, r15); TestLeaIndex32bitDisp(r12, 0xC00Au, Disp, r13, r14, r15, r1); TestLeaIndex32bitDisp(r13, 0xC00Bu, Disp, r14, r15, r1, r2); TestLeaIndex32bitDisp(r14, 0xC00Cu, Disp, r15, r1, r2, r3); TestLeaIndex32bitDisp(r15, 0xC00Du, Disp, r1, r2, r3, r4); } for (const int32_t Disp : {0x00, 0x06, -0x06, 0x0600, -0x6000, 0x6000000, -0x6000000}) { TestLeaBaseIndexDisp(r1, 0x100000u, r2, 0x600u, Disp, r3, r4, r6, r7); TestLeaBaseIndexDisp(r2, 0x200000u, r3, 0x500u, Disp, r4, r6, r7, r8); TestLeaBaseIndexDisp(r3, 0x300000u, r4, 0x400u, Disp, r6, r7, r8, r5); TestLeaBaseIndexDisp(r4, 0x400000u, r6, 0x300u, Disp, r7, r8, r5, r10); TestLeaBaseIndexDisp(r6, 0x500000u, r7, 0x200u, Disp, r8, r5, r10, r11); TestLeaBaseIndexDisp(r7, 0x600000u, r8, 0x100u, Disp, r5, r10, r11, r12); TestLeaBaseIndexDisp(r8, 0x600000u, r9, 0x1A0u, Disp, r10, r11, r12, r13); TestLeaBaseIndexDisp(r9, 0x600050u, r10, 0x1B0u, Disp, r11, r12, r13, r14); TestLeaBaseIndexDisp(r10, 0x602000u, r11, 0x1C0u, Disp, r12, r13, r14, r15); TestLeaBaseIndexDisp(r11, 0x603000u, r12, 0x1D0u, Disp, r13, r14, r15, r1); TestLeaBaseIndexDisp(r12, 0x604000u, r13, 0x1E0u, Disp, r14, r15, r1, r2); TestLeaBaseIndexDisp(r13, 0x605000u, r14, 0x1F0u, Disp, r15, r1, r2, r3); TestLeaBaseIndexDisp(r14, 0x606000u, r15, 0x10Au, Disp, r1, r2, r3, r4); TestLeaBaseIndexDisp(r15, 0x607000u, r1, 0x10Bu, Disp, r2, r3, r4, r6); TestLeaBaseIndexDisp(r0, 0, r2, 0x600u, Disp, r3, r4, r6, r7); TestLeaBaseIndexDisp(r0, 0, r3, 0x500u, Disp, r4, r6, r7, r8); TestLeaBaseIndexDisp(r0, 0, r4, 0x400u, Disp, r6, r7, r8, r5); TestLeaBaseIndexDisp(r0, 0, r6, 0x300u, Disp, r7, r8, r5, r10); TestLeaBaseIndexDisp(r0, 0, r7, 0x200u, Disp, r8, r5, r10, r11); TestLeaBaseIndexDisp(r0, 0, r8, 0x100u, Disp, r5, r10, r11, r12); TestLeaBaseIndexDisp(r0, 0, r9, 0x1000u, Disp, r10, r11, r12, r13); TestLeaBaseIndexDisp(r0, 0, r10, 0x1B0u, Disp, r11, r12, r13, r14); TestLeaBaseIndexDisp(r0, 0, r11, 0x1C0u, Disp, r12, r13, r14, r15); TestLeaBaseIndexDisp(r0, 0, r12, 0x1D0u, Disp, r13, r14, r15, r1); TestLeaBaseIndexDisp(r0, 0, r13, 0x1E0u, Disp, r14, r15, r1, r2); TestLeaBaseIndexDisp(r0, 0, r14, 0x1F0u, Disp, r15, r1, r2, r3); TestLeaBaseIndexDisp(r0, 0, r15, 0x10Au, Disp, r1, r2, r3, r4); TestLeaBaseIndexDisp(r0, 0, r1, 0x10Bu, Disp, r2, r3, r4, r6); TestLeaBaseIndexDisp(r5, 0x100000u, r2, 0x600u, Disp, r3, r4, r6, r7); TestLeaBaseIndexDisp(r5, 0x200000u, r3, 0x500u, Disp, r4, r6, r7, r8); TestLeaBaseIndexDisp(r5, 0x300000u, r4, 0x400u, Disp, r6, r7, r8, r1); TestLeaBaseIndexDisp(r5, 0x400000u, r6, 0x300u, Disp, r7, r8, r1, r10); TestLeaBaseIndexDisp(r5, 0x500000u, r7, 0x200u, Disp, r8, r1, r10, r11); TestLeaBaseIndexDisp(r5, 0x600000u, r8, 0x100u, Disp, r1, r10, r11, r12); TestLeaBaseIndexDisp(r5, 0x600000u, r9, 0x1A00u, Disp, r10, r11, r12, r13); TestLeaBaseIndexDisp(r5, 0x601000u, r10, 0x1B0u, Disp, r11, r12, r13, r14); TestLeaBaseIndexDisp(r5, 0x602000u, r11, 0x1C0u, Disp, r12, r13, r14, r15); TestLeaBaseIndexDisp(r5, 0x603000u, r12, 0x1D0u, Disp, r13, r14, r15, r1); TestLeaBaseIndexDisp(r5, 0x604000u, r13, 0x1E0u, Disp, r14, r15, r1, r2); TestLeaBaseIndexDisp(r5, 0x605000u, r14, 0x1F0u, Disp, r15, r1, r2, r3); TestLeaBaseIndexDisp(r5, 0x606000u, r15, 0x10Au, Disp, r1, r2, r3, r4); TestLeaBaseIndexDisp(r5, 0x607000u, r1, 0x10Bu, Disp, r2, r3, r4, r6); TestLeaBaseIndexDisp(r2, 0x100000u, r5, 0x600u, Disp, r3, r4, r6, r7); TestLeaBaseIndexDisp(r3, 0x200000u, r5, 0x500u, Disp, r4, r6, r7, r8); TestLeaBaseIndexDisp(r4, 0x300000u, r5, 0x400u, Disp, r6, r7, r8, r1); TestLeaBaseIndexDisp(r6, 0x400000u, r5, 0x300u, Disp, r7, r8, r1, r10); TestLeaBaseIndexDisp(r7, 0x500000u, r5, 0x200u, Disp, r8, r1, r10, r11); TestLeaBaseIndexDisp(r8, 0x600000u, r5, 0x100u, Disp, r1, r10, r11, r12); TestLeaBaseIndexDisp(r9, 0x660000u, r5, 0x1A0u, Disp, r10, r11, r12, r13); TestLeaBaseIndexDisp(r10, 0x601000u, r5, 0x1B0u, Disp, r11, r12, r13, r14); TestLeaBaseIndexDisp(r11, 0x602000u, r5, 0x1C0u, Disp, r12, r13, r14, r15); TestLeaBaseIndexDisp(r12, 0x603000u, r5, 0x1D0u, Disp, r13, r14, r15, r1); TestLeaBaseIndexDisp(r13, 0x604000u, r5, 0x1E0u, Disp, r14, r15, r1, r2); TestLeaBaseIndexDisp(r14, 0x605000u, r5, 0x1F0u, Disp, r15, r1, r2, r3); TestLeaBaseIndexDisp(r15, 0x606000u, r5, 0x10Au, Disp, r1, r2, r3, r4); TestLeaBaseIndexDisp(r1, 0x607000u, r5, 0x10Bu, Disp, r2, r3, r4, r6); TestLeaBaseIndexDisp(r0, 0, r5, 0xC0BEBEEF, Disp, r2, r3, r4, r6); } // 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(AssemblerX8664LowLevelTest, LeaAbsolute) { #define TestLeaAbsolute(Dst, Value) \ do { \ static constexpr char TestString[] = "(" #Dst ", " #Value ")"; \ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \ Address::Absolute(Value)); \ static constexpr uint32_t ByteCount = 8; \ 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_esp; \ static constexpr uint8_t SIB = \ /*Scale*/ 0x00 | /*Index*/ (GPRRegister::Encoded_Reg_esp << 3) | \ /*base*/ GPRRegister::Encoded_Reg_ebp; \ ASSERT_TRUE(verifyBytes( \ codeBytes(), 0x67, Opcode, ModRM, SIB, (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(AssemblerX8664Test, 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, Encoded_GPR_##Dst(), Immediate(Value0)); \ __ mov(IceType_i##Size, Encoded_GPR_##Src(), Immediate(Value1)); \ __ test(IceType_i##Size, Encoded_GPR_##Dst(), Encoded_GPR_##Src()); \ __ mov(IceType_i32, Encoded_GPR_##Dst(), Immediate(ValueIfFalse)); \ Label Done; \ __ j(Cond::Br_e, &Done, NearJump); \ __ mov(IceType_i32, Encoded_GPR_##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, Encoded_GPR_##Dst(), Immediate(Value0)); \ __ test(IceType_i##Size, Encoded_GPR_##Dst(), \ Immediate((Imm)&Mask##Size)); \ __ mov(IceType_i32, Encoded_GPR_##Dst(), Immediate(ValueIfFalse)); \ Label Done; \ __ j(Cond::Br_e, &Done, NearJump); \ __ mov(IceType_i32, Encoded_GPR_##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, Encoded_GPR_##Src(), Immediate(Value1)); \ __ test(IceType_i##Size, dwordAddress(T0), Encoded_GPR_##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(r1, r2); TestImpl(r2, r3); TestImpl(r3, r4); TestImpl(r4, r5); TestImpl(r5, r6); TestImpl(r6, r7); TestImpl(r7, r8); TestImpl(r8, r10); TestImpl(r10, r11); TestImpl(r11, r12); TestImpl(r12, r13); TestImpl(r13, r14); TestImpl(r14, r15); TestImpl(r15, r1); #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(AssemblerX8664Test, 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, Encoded_GPR_##Dst(), Immediate(Value0)); \ __ mov(IceType_i##Size, Encoded_GPR_##Src(), Immediate(Value1)); \ __ Inst(IceType_i##Size, Encoded_GPR_##Dst(), Encoded_GPR_##Src()); \ \ AssembledTest test = assemble(); \ test.run(); \ \ ASSERT_EQ(Mask##Size &static_cast( \ static_cast((Value0)&Mask##Size) \ Op static_cast((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, Encoded_GPR_##Dst(), Immediate(Value0)); \ __ mov(IceType_i##Size, dwordAddress(T0), Immediate(Value1)); \ __ Inst(IceType_i##Size, Encoded_GPR_##Dst(), dwordAddress(T0)); \ \ AssembledTest test = assemble(); \ test.setDwordTo(T0, V0); \ test.run(); \ \ ASSERT_EQ(Mask##Size &static_cast( \ static_cast((Value0)&Mask##Size) \ Op static_cast((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, Encoded_GPR_##Dst(), Immediate(Value0)); \ __ Inst(IceType_i##Size, Encoded_GPR_##Dst(), \ Immediate((Imm)&Mask##Size)); \ \ AssembledTest test = assemble(); \ test.run(); \ \ ASSERT_EQ(Mask##Size &static_cast( \ static_cast((Value0)&Mask##Size) \ Op static_cast((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, Encoded_GPR_##Src(), Immediate(Value1)); \ __ Inst(IceType_i##Size, dwordAddress(T0), Encoded_GPR_##Src()); \ \ AssembledTest test = assemble(); \ test.setDwordTo(T0, V0); \ test.run(); \ \ ASSERT_EQ(Mask##Size &static_cast( \ static_cast((Value0)&Mask##Size) \ Op static_cast((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( \ static_cast((Value0)&Mask##Size) \ Op static_cast((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 { \ TestImplSize(Dst, Src, 8); \ TestImplSize(Dst, Src, 16); \ TestImplSize(Dst, Src, 32); \ } while (0) TestImpl(r1, r2); TestImpl(r2, r3); TestImpl(r3, r4); TestImpl(r4, r5); TestImpl(r5, r6); TestImpl(r6, r7); TestImpl(r7, r8); TestImpl(r8, r10); TestImpl(r10, r11); TestImpl(r11, r12); TestImpl(r12, r13); TestImpl(r13, r14); TestImpl(r14, r15); TestImpl(r15, r1); #undef TestImpl #undef TestImplSize #undef TestImplValues #undef TestImplOp #undef TestImplAddrImm #undef TestImplAddrReg #undef TestImplRegImm #undef TestImplRegAddr #undef TestImplRegReg } TEST_F(AssemblerX8664Test, 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, Encoded_GPR_##Dst0(), \ Immediate(uint64_t(Value0) & Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_##Dst1(), \ Immediate((uint64_t(Value0) >> Size) & Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_##Src0(), \ Immediate(uint64_t(Value1) & Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_##Src1(), \ Immediate((uint64_t(Value1) >> Size) & Mask##Size)); \ __ Inst0(IceType_i##Size, Encoded_GPR_##Dst0(), Encoded_GPR_##Src0()); \ __ Inst1(IceType_i##Size, Encoded_GPR_##Dst1(), Encoded_GPR_##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, Encoded_GPR_##Dst0(), \ Immediate(uint64_t(Value0) & Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_##Dst1(), \ Immediate((uint64_t(Value0) >> Size) & Mask##Size)); \ __ Inst0(IceType_i##Size, Encoded_GPR_##Dst0(), dwordAddress(T0)); \ __ Inst1(IceType_i##Size, Encoded_GPR_##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, Encoded_GPR_##Dst0(), \ Immediate(uint64_t(Value0) & Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_##Dst1(), \ Immediate((uint64_t(Value0) >> Size) & Mask##Size)); \ __ Inst0(IceType_i##Size, Encoded_GPR_##Dst0(), \ Immediate(uint64_t(Imm) & Mask##Size)); \ __ Inst1(IceType_i##Size, Encoded_GPR_##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, Encoded_GPR_##Src0(), \ Immediate(uint64_t(Value1) & Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_##Src1(), \ Immediate((uint64_t(Value1) >> Size) & Mask##Size)); \ __ Inst0(IceType_i##Size, dwordAddress(T0), Encoded_GPR_##Src0()); \ __ Inst1(IceType_i##Size, dwordAddress(T1), Encoded_GPR_##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 { \ TestImplSize(Dst0, Dst1, Src0, Src1, 8); \ TestImplSize(Dst0, Dst1, Src0, Src1, 16); \ TestImplSize(Dst0, Dst1, Src0, Src1, 32); \ } while (0) TestImpl(r1, r2, r3, r5); TestImpl(r2, r3, r4, r6); TestImpl(r3, r4, r5, r7); TestImpl(r4, r5, r6, r8); TestImpl(r5, r6, r7, r10); TestImpl(r6, r7, r8, r11); TestImpl(r7, r8, r10, r12); TestImpl(r8, r10, r11, r13); TestImpl(r10, r11, r12, r14); TestImpl(r11, r12, r13, r15); TestImpl(r12, r13, r14, r1); TestImpl(r13, r14, r15, r2); TestImpl(r14, r15, r1, r3); TestImpl(r15, r1, r2, r4); #undef TestImpl #undef TestImplSize #undef TestImplValues #undef TestImplOp #undef TestImplAddrImm #undef TestImplAddrReg #undef TestImplRegImm #undef TestImplRegAddr #undef TestImplRegReg } TEST_F(AssemblerX8664LowLevelTest, Cbw_Cwd_Cdq) { #define TestImpl(Inst, BytesSize, ...) \ do { \ __ Inst(); \ ASSERT_EQ(BytesSize, codeBytesSize()) << #Inst; \ ASSERT_TRUE(verifyBytes(codeBytes(), __VA_ARGS__)); \ reset(); \ } while (0) TestImpl(cbw, 2u, 0x66, 0x98); TestImpl(cwd, 2u, 0x66, 0x99); TestImpl(cdq, 1u, 0x99); #undef TestImpl } TEST_F(AssemblerX8664Test, 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(Encoded_GPR_eax() != Encoded_GPR_##Src(), \ "eax can not be src1."); \ \ static constexpr char TestString[] = \ "(" #Inst ", " #Value0 ", " #Src ", " #Value1 ", " #Type ", " #Size \ ")"; \ static constexpr Type##64_t OperandEax = \ static_cast((Value0)&Mask##Size); \ static constexpr Type##64_t OperandOther = \ static_cast((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, Encoded_GPR_eax(), \ Immediate((Value0)&Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_##Src(), \ Immediate((Value1)&Mask##Size)); \ __ Inst(IceType_i##Size, Encoded_GPR_##Src()); \ \ if (Size == 8) { \ /* mov %ah, %dl */ \ __ mov(IceType_i16, Encoded_GPR_dx(), Encoded_GPR_ax()); \ __ shr(IceType_i32, Encoded_GPR_edx(), Immediate(8)); \ __ And(IceType_i16, Encoded_GPR_ax(), 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((Value0)&Mask##Size); \ static constexpr Type##64_t OperandOther = \ static_cast((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, Encoded_GPR_eax(), \ Immediate((Value0)&Mask##Size)); \ __ Inst(IceType_i##Size, dwordAddress(T0)); \ \ if (Size == 8) { \ /* mov %ah, %dl */ \ __ mov(IceType_i16, Encoded_GPR_dx(), Encoded_GPR_ax()); \ __ shr(IceType_i32, Encoded_GPR_edx(), Immediate(8)); \ __ And(IceType_i16, Encoded_GPR_ax(), 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(r2); TestImpl(r3); TestImpl(r4); TestImpl(r5); TestImpl(r6); TestImpl(r7); TestImpl(r8); TestImpl(r10); TestImpl(r11); TestImpl(r12); TestImpl(r13); TestImpl(r14); TestImpl(r15); #undef TestImpl #undef TestImplSize #undef TestImplValue #undef TestImplOp #undef TestImplAddr #undef TestImplReg } TEST_F(AssemblerX8664Test, 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((Value0)&Mask##Size); \ static constexpr int64_t Operand1 = \ static_cast((Value1)&Mask##Size); \ static constexpr uint32_t Expected = Mask##Size & (Operand0 * Operand1); \ \ __ mov(IceType_i##Size, Encoded_GPR_##Dst(), \ Immediate((Value0)&Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_##Src(), \ Immediate((Value1)&Mask##Size)); \ __ imul(IceType_i##Size, Encoded_GPR_##Dst(), Encoded_GPR_##Src()); \ \ if (Size == 8) { \ /* mov %ah, %dl */ \ __ mov(IceType_i16, Encoded_GPR_dx(), Encoded_GPR_ax()); \ __ shr(IceType_i32, Encoded_GPR_edx(), Immediate(8)); \ __ And(IceType_i16, Encoded_GPR_ax(), 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((Value0)&Mask##Size); \ static constexpr int64_t Operand1 = \ static_cast((Imm)&Mask##Size); \ static constexpr uint32_t Expected = Mask##Size & (Operand0 * Operand1); \ \ __ mov(IceType_i##Size, Encoded_GPR_##Dst(), \ Immediate((Value0)&Mask##Size)); \ __ imul(IceType_i##Size, Encoded_GPR_##Dst(), Immediate(Imm)); \ \ if (Size == 8) { \ /* mov %ah, %dl */ \ __ mov(IceType_i16, Encoded_GPR_dx(), Encoded_GPR_ax()); \ __ shr(IceType_i32, Encoded_GPR_edx(), Immediate(8)); \ __ And(IceType_i16, Encoded_GPR_ax(), 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((Value0)&Mask##Size); \ static constexpr int64_t Operand1 = \ static_cast((Value1)&Mask##Size); \ static constexpr uint32_t Expected = Mask##Size & (Operand0 * Operand1); \ const uint32_t T0 = allocateDword(); \ \ __ mov(IceType_i##Size, Encoded_GPR_##Dst(), \ Immediate((Value0)&Mask##Size)); \ __ imul(IceType_i##Size, Encoded_GPR_##Dst(), dwordAddress(T0)); \ \ if (Size == 8) { \ /* mov %ah, %dl */ \ __ mov(IceType_i16, Encoded_GPR_dx(), Encoded_GPR_ax()); \ __ shr(IceType_i32, Encoded_GPR_edx(), Immediate(8)); \ __ And(IceType_i16, Encoded_GPR_ax(), Immediate(0x00FF)); \ } \ \ AssembledTest test = assemble(); \ test.setDwordTo(T0, static_cast(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(r1, r2); TestImpl(r2, r3); TestImpl(r3, r4); TestImpl(r4, r5); TestImpl(r5, r6); TestImpl(r6, r7); TestImpl(r7, r8); TestImpl(r8, r10); TestImpl(r10, r11); TestImpl(r11, r12); TestImpl(r12, r13); TestImpl(r13, r14); TestImpl(r14, r15); TestImpl(r15, r1); #undef TestImpl #undef TestImplSize #undef TestImplValue #undef TestImplRegAddr #undef TestImplRegImm #undef TestImplRegReg } TEST_F(AssemblerX8664Test, 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(Encoded_GPR_eax() != Encoded_GPR_##Src(), \ "eax can not be src1."); \ static_assert(Encoded_GPR_edx() != Encoded_GPR_##Src(), \ "edx can not be src1."); \ \ static constexpr char TestString[] = \ "(" #Inst ", " #Value0 ", " #Src ", " #Value1 ", " #Type ", " #Size \ ")"; \ static constexpr Operand0Type_##Type##Size Operand0 = \ static_cast(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(Value1) & Mask##Size; \ if (Size == 8) { \ /* mov Operand0Hi|Operand0Lo, %ah|%al */ \ __ mov( \ IceType_i16, Encoded_GPR_eax(), \ Immediate((static_cast(Operand0Hi) << 8 | Operand0Lo))); \ } else { \ __ mov(IceType_i##Size, Encoded_GPR_eax(), Immediate(Operand0Lo)); \ __ mov(IceType_i##Size, Encoded_GPR_edx(), Immediate(Operand0Hi)); \ } \ __ mov(IceType_i##Size, Encoded_GPR_##Src(), Immediate(Operand1)); \ __ Inst(IceType_i##Size, Encoded_GPR_##Src()); \ if (Size == 8) { \ /* mov %ah, %dl */ \ __ mov(IceType_i16, Encoded_GPR_dx(), Encoded_GPR_ax()); \ __ shr(IceType_i32, Encoded_GPR_edx(), Immediate(8)); \ __ And(IceType_i16, Encoded_GPR_eax(), Immediate(0x00FF)); \ if (Encoded_GPR_##Src() == Encoded_GPR_esi()) { \ __ And(IceType_i16, Encoded_GPR_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; \ ASSERT_EQ(Quocient, test.eax()) << TestString; \ ASSERT_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(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(Value1) & Mask##Size; \ if (Size == 8) { \ /* mov Operand0Hi|Operand0Lo, %ah|%al */ \ __ mov( \ IceType_i16, Encoded_GPR_eax(), \ Immediate((static_cast(Operand0Hi) << 8 | Operand0Lo))); \ } else { \ __ mov(IceType_i##Size, Encoded_GPR_eax(), Immediate(Operand0Lo)); \ __ mov(IceType_i##Size, Encoded_GPR_edx(), Immediate(Operand0Hi)); \ } \ __ Inst(IceType_i##Size, dwordAddress(T0)); \ if (Size == 8) { \ /* mov %ah, %dl */ \ __ mov(IceType_i16, Encoded_GPR_dx(), Encoded_GPR_ax()); \ __ shr(IceType_i32, Encoded_GPR_edx(), Immediate(8)); \ __ And(IceType_i16, Encoded_GPR_eax(), Immediate(0x00FF)); \ } \ \ AssembledTest test = assemble(); \ test.setDwordTo(T0, static_cast(V0)); \ test.run(); \ \ static constexpr uint32_t Quocient = (Operand0 / V0) & Mask##Size; \ static constexpr uint32_t Reminder = (Operand0 % V0) & Mask##Size; \ ASSERT_EQ(Quocient, test.eax()) << TestString; \ ASSERT_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(r2); TestImpl(r3); TestImpl(r5); TestImpl(r6); TestImpl(r7); TestImpl(r8); TestImpl(r10); TestImpl(r11); TestImpl(r12); TestImpl(r13); TestImpl(r14); TestImpl(r15); #undef TestImpl #undef TestImplSize #undef TestImplValue #undef TestImplOp #undef TestImplAddr #undef TestImplReg } TEST_F(AssemblerX8664Test, 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(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(AssemblerX8664Test, 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(Value0) Op(Imm) | \ (!IsRol ? 0 : (Value0) >> (Size - Imm))); \ \ __ mov(IceType_i##Size, Encoded_GPR_##Dst(), \ Immediate((Value0)&Mask##Size)); \ __ Inst(IceType_i##Size, Encoded_GPR_##Dst(), \ Immediate((Imm)&Mask##Size)); \ \ AssembledTest test = assemble(); \ test.run(); \ \ ASSERT_EQ(static_cast(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(Value0) Op0(Count) | \ (static_cast(Value1) Op1(Size - Count))); \ \ __ mov(IceType_i##Size, Encoded_GPR_##Dst(), \ Immediate((Value0)&Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_##Src(), \ Immediate((Value1)&Mask##Size)); \ __ Inst(IceType_i##Size, Encoded_GPR_##Dst(), Encoded_GPR_##Src(), \ Immediate(Count)); \ \ AssembledTest test = assemble(); \ test.run(); \ \ ASSERT_EQ(static_cast(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(Value0) Op(Count) | \ (!IsRol ? 0 : Value0 >> (Size - Count))); \ \ __ mov(IceType_i##Size, Encoded_GPR_##Dst(), \ Immediate((Value0)&Mask##Size)); \ __ mov(IceType_i8, Encoded_GPR_ecx(), Immediate((Count)&Mask##Size)); \ __ Inst(IceType_i##Size, Encoded_GPR_##Dst(), Encoded_GPR_ecx()); \ \ AssembledTest test = assemble(); \ test.run(); \ \ ASSERT_EQ(static_cast(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(Value0) Op0(Count) | \ (static_cast(Value1) Op1(Size - Count))); \ \ __ mov(IceType_i##Size, Encoded_GPR_##Dst(), \ Immediate((Value0)&Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_##Src(), \ Immediate((Value1)&Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_ecx(), Immediate((Count)&0x7F)); \ __ Inst(IceType_i##Size, Encoded_GPR_##Dst(), Encoded_GPR_##Src()); \ \ AssembledTest test = assemble(); \ test.run(); \ \ ASSERT_EQ(static_cast(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(Value0) Op(Count) | \ (!IsRol ? 0 : Value0 >> (Size - Count))); \ const uint32_t T0 = allocateDword(); \ const uint32_t V0 = Value0; \ \ __ mov(IceType_i8, Encoded_GPR_ecx(), Immediate((Count)&Mask##Size)); \ __ Inst(IceType_i##Size, dwordAddress(T0), Encoded_GPR_ecx()); \ \ AssembledTest test = assemble(); \ test.setDwordTo(T0, V0); \ test.run(); \ \ ASSERT_EQ(static_cast(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(Value0) Op0(Count) | \ (static_cast(Value1) Op1(Size - Count))); \ const uint32_t T0 = allocateDword(); \ \ __ mov(IceType_i##Size, Encoded_GPR_##Src(), \ Immediate((Value1)&Mask##Size)); \ __ mov(IceType_i##Size, Encoded_GPR_ecx(), Immediate((Count)&0x7F)); \ __ Inst(IceType_i##Size, dwordAddress(T0), Encoded_GPR_##Src()); \ \ AssembledTest test = assemble(); \ test.setDwordTo(T0, static_cast(Value0)); \ test.run(); \ \ ASSERT_EQ(static_cast(Expected), test.contentsOfDword(T0)) \ << TestString; \ reset(); \ } while (0) #define TestImplOp(Inst, Dst, Value0, Count, Op, Type, Size) \ do { \ static_assert(Encoded_GPR_##Dst() != Encoded_GPR_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(Encoded_GPR_##Dst() != Encoded_GPR_ecx(), \ "ecx should not be specified as Dst"); \ static_assert(Encoded_GPR_##Src() != Encoded_GPR_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 { \ TestImplSize(Dst, 8); \ TestImplSize(Dst, 16); \ TestImplThreeOperandSize(Dst, Src, 16); \ TestImplSize(Dst, 32); \ TestImplThreeOperandSize(Dst, Src, 32); \ } while (0) TestImpl(r1, r2); TestImpl(r2, r4); TestImpl(r4, r5); TestImpl(r5, r6); TestImpl(r6, r7); TestImpl(r7, r8); TestImpl(r8, r10); TestImpl(r10, r11); TestImpl(r11, r12); TestImpl(r12, r13); TestImpl(r13, r14); TestImpl(r14, r15); TestImpl(r15, r1); #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(AssemblerX8664Test, 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, Encoded_GPR_##Dst(), \ Immediate(static_cast(Value) & Mask##Size)); \ __ neg(IceType_i##Size, Encoded_GPR_##Dst()); \ __ mov(IceType_i##Size, Encoded_GPR_eax(), Encoded_GPR_##Dst()); \ __ And(IceType_i32, Encoded_GPR_eax(), Immediate(Mask##Size)); \ \ AssembledTest test = assemble(); \ test.run(); \ \ ASSERT_EQ(1 + (~static_cast(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(Value) & Mask##Size), \ test.contentsOfDword(T0)) \ << "(Addr, " #Size ")"; \ reset(); \ } while (0) #define TestImpl(Size) \ do { \ TestImplAddr(Size); \ TestImplReg(r1, Size); \ TestImplReg(r2, Size); \ TestImplReg(r3, Size); \ TestImplReg(r4, Size); \ TestImplReg(r5, Size); \ TestImplReg(r6, Size); \ TestImplReg(r7, Size); \ TestImplReg(r8, Size); \ TestImplReg(r10, Size); \ TestImplReg(r11, Size); \ TestImplReg(r12, Size); \ TestImplReg(r13, Size); \ TestImplReg(r14, Size); \ TestImplReg(r15, Size); \ } while (0) TestImpl(8); TestImpl(16); TestImpl(32); #undef TestImpl #undef TestImplAddr #undef TestImplReg } TEST_F(AssemblerX8664Test, Not) { #define TestImpl(Dst) \ do { \ static constexpr uint32_t Value = 0xFF00A543; \ __ mov(IceType_i32, Encoded_GPR_##Dst(), Immediate(Value)); \ __ notl(Encoded_GPR_##Dst()); \ \ AssembledTest test = assemble(); \ test.run(); \ \ ASSERT_EQ(~Value, test.Dst()) << "(" #Dst ")"; \ reset(); \ } while (0) TestImpl(r1); TestImpl(r2); TestImpl(r3); TestImpl(r4); TestImpl(r5); TestImpl(r6); TestImpl(r7); TestImpl(r8); TestImpl(r10); TestImpl(r11); TestImpl(r12); TestImpl(r13); TestImpl(r14); TestImpl(r15); #undef TestImpl } TEST_F(AssemblerX8664Test, Bswap) { #define TestImpl(Dst) \ do { \ static constexpr uint32_t Value = 0xFF00A543; \ static constexpr uint32_t Expected = 0x43A500FF; \ __ mov(IceType_i32, Encoded_GPR_##Dst(), Immediate(Value)); \ __ bswap(IceType_i32, Encoded_GPR_##Dst()); \ \ AssembledTest test = assemble(); \ test.run(); \ \ ASSERT_EQ(Expected, test.Dst()) << "(" #Dst ")"; \ reset(); \ } while (0) TestImpl(r1); TestImpl(r2); TestImpl(r3); TestImpl(r4); TestImpl(r5); TestImpl(r6); TestImpl(r7); TestImpl(r8); TestImpl(r10); TestImpl(r11); TestImpl(r12); TestImpl(r13); TestImpl(r14); TestImpl(r15); #undef TestImpl } TEST_F(AssemblerX8664Test, 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, Encoded_GPR_##Dst(), Immediate(Value0)); \ __ mov(IceType_i32, Encoded_GPR_##Src(), Immediate(Value1)); \ __ bt(Encoded_GPR_##Dst(), Encoded_GPR_##Src()); \ __ setcc(Cond::Br_b, ByteRegister::Encoded_8_Reg_al); \ __ And(IceType_i32, Encoded_GPR_eax(), Immediate(0xFFu)); \ \ AssembledTest test = assemble(); \ test.run(); \ \ ASSERT_EQ(Expected, test.eax()) << TestString; \ reset(); \ } while (0) TestImpl(r1, 0x08000000, r2, 27u); TestImpl(r2, 0x08000000, r3, 23u); TestImpl(r3, 0x00000000, r4, 1u); TestImpl(r4, 0x08000300, r5, 9u); TestImpl(r5, 0x08000300, r6, 10u); TestImpl(r6, 0x7FFFEFFF, r7, 13u); TestImpl(r7, 0x08000000, r8, 27u); TestImpl(r8, 0x08000000, r10, 23u); TestImpl(r10, 0x00000000, r11, 1u); TestImpl(r11, 0x08000300, r12, 9u); TestImpl(r12, 0x08000300, r13, 10u); TestImpl(r13, 0x7FFFEFFF, r14, 13u); TestImpl(r14, 0x08000000, r15, 27u); TestImpl(r15, 0x08000000, r1, 23u); #undef TestImpl } template class BitScanHelper { BitScanHelper() = delete; public: static_assert(Bits == 16 || Bits == 32, "Bits must be 16 or 32"); using ValueType = typename std::conditional::type; private: static constexpr ValueType BitIndex(bool Forward, ValueType Index) { return (Value == 0) ? BitScanHelper::NoBitSet : (Value & (1u << Index) ? Index : BitIndex(Forward, (Forward ? Index + 1 : Index - 1))); } public: static constexpr ValueType NoBitSet = static_cast(-1); static constexpr ValueType bsf = BitIndex(/*Forward*/ true, /*Index=*/0); static constexpr ValueType bsr = BitIndex(/*Forward*/ false, /*Index=*/Bits - 1); }; TEST_F(AssemblerX8664Test, BitScanOperations) { #define TestImplRegReg(Inst, Dst, Src, Value1, Size) \ do { \ static constexpr char TestString[] = \ "(" #Inst ", " #Dst ", " #Src ", " #Value1 ", " #Size ")"; \ static constexpr uint32_t Expected = BitScanHelper::Inst; \ const uint32_t ZeroFlag = allocateDword(); \ __ mov(IceType_i##Size, Encoded_GPR_##Src(), Immediate(Value1)); \ __ Inst(IceType_i##Size, Encoded_GPR_##Dst(), Encoded_GPR_##Src()); \ __ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \ \ AssembledTest test = assemble(); \ test.setDwordTo(ZeroFlag, 0u); \ test.run(); \ \ ASSERT_EQ((Expected == BitScanHelper::NoBitSet), \ test.contentsOfDword(ZeroFlag)) \ << TestString; \ if ((Expected != BitScanHelper::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::Inst; \ const uint32_t T0 = allocateDword(); \ const uint32_t ZeroFlag = allocateDword(); \ __ Inst(IceType_i##Size, Encoded_GPR_##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::NoBitSet), \ test.contentsOfDword(ZeroFlag)) \ << TestString; \ if (Expected != BitScanHelper::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(r1, r2); TestImpl(r2, r3); TestImpl(r3, r4); TestImpl(r4, r5); TestImpl(r5, r6); TestImpl(r6, r7); TestImpl(r7, r8); TestImpl(r8, r10); TestImpl(r10, r11); TestImpl(r11, r12); TestImpl(r12, r13); TestImpl(r13, r14); TestImpl(r14, r15); TestImpl(r15, r1); #undef TestImpl #undef TestImplValue #undef TestImplSize #undef TestImplRegAddr #undef TestImplRegReg } } // end of anonymous namespace } // end of namespace Test } // end of namespace X8664 } // end of namespace Ice