//===- 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, ByteRegister(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,                      \
                   AssemblerFixup::NoFixup));                                  \
    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,    \
                   AssemblerFixup::NoFixup));                                  \
    __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst1,                       \
           Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_2, Disp,    \
                   AssemblerFixup::NoFixup));                                  \
    __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst2,                       \
           Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_4, Disp,    \
                   AssemblerFixup::NoFixup));                                  \
    __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst3,                       \
           Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_8, Disp,    \
                   AssemblerFixup::NoFixup));                                  \
    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,    \
                   AssemblerFixup::NoFixup));                                  \
    __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst1,                       \
           Address(GPRRegister::Encoded_Reg_##Base,                            \
                   GPRRegister::Encoded_Reg_##Index, Traits::TIMES_2, Disp,    \
                   AssemblerFixup::NoFixup));                                  \
    __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst2,                       \
           Address(GPRRegister::Encoded_Reg_##Base,                            \
                   GPRRegister::Encoded_Reg_##Index, Traits::TIMES_4, Disp,    \
                   AssemblerFixup::NoFixup));                                  \
    __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst3,                       \
           Address(GPRRegister::Encoded_Reg_##Base,                            \
                   GPRRegister::Encoded_Reg_##Index, Traits::TIMES_8, Disp,    \
                   AssemblerFixup::NoFixup));                                  \
    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(Value, AssemblerFixup::NoFixup));                           \
    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_8_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
