//===- subzero/unittest/AssemblerX8664/DataMov.cpp ------------------------===//
//
//                        The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "AssemblerX8664/TestUtil.h"

namespace Ice {
namespace X8664 {
namespace Test {
namespace {

TEST_F(AssemblerX8664Test, MovRegImm) {
  static constexpr uint32_t Mask8 = 0x000000FF;
  static constexpr uint32_t Mask16 = 0x0000FFFF;
  static constexpr uint32_t Mask32 = 0xFFFFFFFF;

#define MovRegImm(Reg, Suffix, Size)                                           \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Reg ", " #Size ")";              \
    static constexpr uint32_t Value = (0xABCD7645) & Mask##Size;               \
    static constexpr uint32_t Marker = 0xBEEFFEEB;                             \
    __ mov(IceType_i32, Encoded_GPR_##Reg##q(), Immediate(Marker));            \
    __ mov(IceType_i##Size, Encoded_GPR_##Reg##Suffix(), Immediate(Value));    \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Value, test.Reg##Suffix()) << TestString;                        \
    ASSERT_EQ((Marker & ~Mask##Size) | Value, test.Reg##d()) << TestString;    \
    reset();                                                                   \
  } while (0)

#define TestImpl(Reg)                                                          \
  do {                                                                         \
    MovRegImm(Reg, l, 8);                                                      \
    MovRegImm(Reg, w, 16);                                                     \
    MovRegImm(Reg, d, 32);                                                     \
    /* MovRegImm64 not implemented */                                          \
  } while (0)

  TestImpl(r1);
  TestImpl(r2);
  TestImpl(r3);
  TestImpl(r4);
  TestImpl(r5);
  TestImpl(r6);
  TestImpl(r7);
  TestImpl(r8);
  TestImpl(r10);
  TestImpl(r11);
  TestImpl(r12);
  TestImpl(r13);
  TestImpl(r14);
  TestImpl(r15);

#undef TestImpl
#undef MovRegImm
}

TEST_F(AssemblerX8664Test, MovMemImm) {
  const uint32_t T0 = allocateDword();
  constexpr uint32_t ExpectedT0 = 0x00111100ul;
  const uint32_t T1 = allocateDword();
  constexpr uint32_t ExpectedT1 = 0x00222200ul;
  const uint32_t T2 = allocateDword();
  constexpr uint32_t ExpectedT2 = 0x03333000ul;
  const uint32_t T3 = allocateDword();
  constexpr uint32_t ExpectedT3 = 0x00444400ul;

  __ mov(IceType_i32, dwordAddress(T0), Immediate(ExpectedT0));
  __ mov(IceType_i16, dwordAddress(T1), Immediate(ExpectedT1));
  __ mov(IceType_i8, dwordAddress(T2), Immediate(ExpectedT2));
  __ mov(IceType_i32, dwordAddress(T3), Immediate(ExpectedT3));

  AssembledTest test = assemble();
  test.run();
  EXPECT_EQ(0ul, test.eax());
  EXPECT_EQ(0ul, test.ebx());
  EXPECT_EQ(0ul, test.ecx());
  EXPECT_EQ(0ul, test.edx());
  EXPECT_EQ(0ul, test.edi());
  EXPECT_EQ(0ul, test.esi());
  EXPECT_EQ(ExpectedT0, test.contentsOfDword(T0));
  EXPECT_EQ(ExpectedT1 & 0xFFFF, test.contentsOfDword(T1));
  EXPECT_EQ(ExpectedT2 & 0xFF, test.contentsOfDword(T2));
  EXPECT_EQ(ExpectedT3, test.contentsOfDword(T3));
}

TEST_F(AssemblerX8664Test, MovMemReg) {
  static constexpr uint64_t Mask8 = 0x00000000000000FF;
  static constexpr uint64_t Mask16 = 0x000000000000FFFF;
  static constexpr uint64_t Mask32 = 0x00000000FFFFFFFF;
  static constexpr uint64_t Mask64 = 0xFFFFFFFFFFFFFFFF;

#define TestMemReg(Src, Size)                                                  \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Src ", " #Size ")";              \
    static constexpr uint32_t Value = 0x1a4d567e & Mask##Size;                 \
    static constexpr uint64_t Marker = 0xD0DA33EEBEEFFEEB;                     \
    const uint32_t T0 = allocateQword();                                       \
                                                                               \
    __ mov(IceType_i32, Encoded_GPR_##Src(), Immediate(Value));                \
    __ mov(IceType_i##Size, dwordAddress(T0), Encoded_GPR_##Src());            \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setQwordTo(T0, Marker);                                               \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ((Marker & ~Mask##Size) | Value, test.contentsOfQword(T0))        \
        << TestString;                                                         \
    reset();                                                                   \
  } while (0)

#define TestImpl(Src)                                                          \
  do {                                                                         \
    TestMemReg(Src, 8);                                                        \
    TestMemReg(Src, 16);                                                       \
    TestMemReg(Src, 32);                                                       \
    TestMemReg(Src, 64);                                                       \
  } while (0)

  TestImpl(r1);
  TestImpl(r2);
  TestImpl(r3);
  TestImpl(r4);
  TestImpl(r5);
  TestImpl(r6);
  TestImpl(r7);
  TestImpl(r8);
  TestImpl(r10);
  TestImpl(r11);
  TestImpl(r12);
  TestImpl(r13);
  TestImpl(r14);
  TestImpl(r15);

#undef TestImpl
#undef TestMemReg
}

TEST_F(AssemblerX8664Test, MovRegReg) {
  static constexpr uint64_t Mask8 = 0x00000000000000FFull;
  static constexpr uint64_t Mask16 = 0x000000000000FFFFull;
  static constexpr uint64_t Mask32 = 0x00000000FFFFFFFFull;
  static constexpr uint64_t Mask64 = 0xFFFFFFFFFFFFFFFFull;

  static constexpr uint64_t MaskResult8 = 0x00000000000000FFull;
  static constexpr uint64_t MaskResult16 = 0x000000000000FFFFull;
  static constexpr uint64_t MaskResult32 = 0xFFFFFFFFFFFFFFFFull;
  static constexpr uint64_t MaskResult64 = 0xFFFFFFFFFFFFFFFFull;

#define TestRegReg(Dst, Src, Suffix, Size)                                     \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Src ", " #Suffix ", " #Size ")";                        \
    const uint8_t T0 = allocateQword();                                        \
    static constexpr uint64_t Value = 0xA4DD30Af86CCE321ull & Mask##Size;      \
    const uint8_t T1 = allocateQword();                                        \
    static constexpr uint64_t Marker = 0xC0FFEEA0BEEFFEEFull;                  \
                                                                               \
    __ mov(IceType_i64, Encoded_GPR_##Src(), dwordAddress(T0));                \
    __ mov(IceType_i64, Encoded_GPR_##Dst(), dwordAddress(T1));                \
    __ mov(IceType_i##Size, Encoded_GPR_##Dst(), Encoded_GPR_##Src());         \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setQwordTo(T0, Value);                                                \
    test.setQwordTo(T1, Marker);                                               \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ((Marker & ~MaskResult##Size) | Value, test.Dst()) << TestString; \
    ASSERT_EQ(Value, test.Dst##Suffix()) << TestString;                        \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestRegReg(Dst, Src, l, 8);                                                \
    TestRegReg(Dst, Src, w, 16);                                               \
    TestRegReg(Dst, Src, d, 32);                                               \
    TestRegReg(Dst, Src, q, 64);                                               \
  } while (0)

  TestImpl(r1, r2);
  TestImpl(r2, r3);
  TestImpl(r3, r4);
  TestImpl(r4, r5);
  TestImpl(r5, r6);
  TestImpl(r6, r7);
  TestImpl(r7, r8);
  TestImpl(r8, r10);
  TestImpl(r10, r11);
  TestImpl(r11, r12);
  TestImpl(r12, r13);
  TestImpl(r13, r14);
  TestImpl(r14, r15);
  TestImpl(r15, r1);

#undef TestImpl
#undef TestRegReg
}

TEST_F(AssemblerX8664Test, MovRegMem) {
  static constexpr uint64_t Mask8 = 0x00000000000000FFull;
  static constexpr uint64_t Mask16 = 0x000000000000FFFFull;
  static constexpr uint64_t Mask32 = 0x00000000FFFFFFFFull;
  static constexpr uint64_t Mask64 = 0xFFFFFFFFFFFFFFFFull;

  static constexpr uint64_t MaskResult8 = ~0x00000000000000FFull;
  static constexpr uint64_t MaskResult16 = ~0x000000000000FFFFull;
  static constexpr uint64_t MaskResult32 = ~0xFFFFFFFFFFFFFFFFull;
  static constexpr uint64_t MaskResult64 = ~0xFFFFFFFFFFFFFFFFull;

#define TestRegAddr(Dst, Suffix, Size)                                         \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", Addr, " #Suffix ", " #Size ")";                            \
    const uint8_t T0 = allocateQword();                                        \
    static constexpr uint64_t Value = 0xA4DD30Af86CCE321ull & Mask##Size;      \
    const uint8_t T1 = allocateQword();                                        \
    static constexpr uint64_t Marker = 0xC0FFEEA0BEEFFEEFull;                  \
                                                                               \
    __ mov(IceType_i64, Encoded_GPR_##Dst(), dwordAddress(T1));                \
    __ mov(IceType_i##Size, Encoded_GPR_##Dst(), dwordAddress(T0));            \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setQwordTo(T0, Value);                                                \
    test.setQwordTo(T1, Marker);                                               \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ((Marker & MaskResult##Size) | Value, test.Dst()) << TestString;  \
    ASSERT_EQ(Value, test.Dst##Suffix()) << TestString;                        \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst)                                                          \
  do {                                                                         \
    TestRegAddr(Dst, l, 8);                                                    \
    TestRegAddr(Dst, w, 16);                                                   \
    TestRegAddr(Dst, d, 32);                                                   \
    TestRegAddr(Dst, q, 64);                                                   \
  } while (0)

  TestImpl(r1);
  TestImpl(r2);
  TestImpl(r3);
  TestImpl(r4);
  TestImpl(r5);
  TestImpl(r6);
  TestImpl(r7);
  TestImpl(r8);
  TestImpl(r10);
  TestImpl(r11);
  TestImpl(r12);
  TestImpl(r13);
  TestImpl(r14);
  TestImpl(r15);

#undef TestImpl
#undef TestRegAddr
}

TEST_F(AssemblerX8664Test, Movabs) {
#define TestImplValue(Dst, Value)                                              \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", " #Value ")";             \
    uint64_t V = (Value);                                                      \
    __ movabs(Encoded_GPR_##Dst##q(), V);                                      \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(V, test.DST()) << TestString;                                    \
  } while (0)

#define TestImpl(Dst)                                                          \
  do {                                                                         \
    for (uint64_t V = {0, 1, 0xFFFFFFull, 0x80000000ull,                       \
                       0xFFFFFFFFFFFFFFFFull}) {                               \
      TestImpl(Dst, V);                                                        \
    }                                                                          \
  } while (0)

#undef TestImpl
#undef TestImplValue
}

TEST_F(AssemblerX8664Test, Movzx) {
  static constexpr uint32_t Mask8 = 0x000000FF;
  static constexpr uint32_t Mask16 = 0x0000FFFF;

#define TestImplRegReg(Dst, Src, Suffix, Size)                                 \
  do {                                                                         \
    const uint32_t T0 = allocateDqword();                                      \
    static constexpr uint64_t V0 = 0xAAAAAAAAAAAAAAAAull;                      \
    static constexpr uint32_t Value = (0xBEEF) & Mask##Size;                   \
    __ mov(IceType_i64, Encoded_GPR_##Dst##q(), dwordAddress(T0));             \
    __ mov(IceType_i##Size, Encoded_GPR_##Src##Suffix(), Immediate(Value));    \
    __ movzx(IceType_i##Size, Encoded_GPR_##Dst##d(),                          \
             Encoded_GPR_##Src##Suffix());                                     \
    AssembledTest test = assemble();                                           \
    test.setQwordTo(T0, V0);                                                   \
    test.run();                                                                \
    ASSERT_EQ(Value, test.Dst##q()) << "(" #Dst ", " #Src ", " #Size ")";      \
    reset();                                                                   \
  } while (0)

#define TestImplRegAddr(Dst, Suffix, Size)                                     \
  do {                                                                         \
    const uint32_t T0 = allocateDqword();                                      \
    static constexpr uint64_t V0 = 0xAAAAAAAAAAAAAAAAull;                      \
    static constexpr uint32_t Value = (0xBEEF) & Mask##Size;                   \
    __ movzx(IceType_i##Size, Encoded_GPR_##Dst##d(), dwordAddress(T0));       \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setQwordTo(T0, (V0 & ~Mask##Size) | Value);                           \
    test.run();                                                                \
    ASSERT_EQ(Value, test.Dst##q()) << "(" #Dst ", Addr, " #Size ")";          \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestImplRegReg(Dst, Src, l, 8);                                            \
    TestImplRegAddr(Dst, l, 8);                                                \
    TestImplRegReg(Dst, Src, w, 16);                                           \
    TestImplRegAddr(Dst, w, 16);                                               \
  } while (0)

  TestImpl(r1, r2);
  TestImpl(r2, r3);
  TestImpl(r3, r4);
  TestImpl(r4, r5);
  TestImpl(r5, r6);
  TestImpl(r6, r7);
  TestImpl(r7, r8);
  TestImpl(r8, r10);
  TestImpl(r10, r11);
  TestImpl(r11, r12);
  TestImpl(r12, r13);
  TestImpl(r13, r14);
  TestImpl(r14, r15);
  TestImpl(r15, r1);

#undef TestImpl
#undef TestImplRegAddr
#undef TestImplRegReg
}

TEST_F(AssemblerX8664Test, Movsx) {
  static constexpr uint64_t Mask8 = 0x000000FF;
  static constexpr uint64_t Mask16 = 0x0000FFFF;
  static constexpr uint64_t Mask32 = 0xFFFFFFFF;

#define TestImplRegReg(Dst, Src, Suffix, Size)                                 \
  do {                                                                         \
    const uint32_t T0 = allocateDqword();                                      \
    static constexpr uint64_t V0 = 0xAAAAAAAAAAAAAAAAull;                      \
    static constexpr uint64_t Value = (0xC0BEBEEF) & Mask##Size;               \
    __ mov(IceType_i64, Encoded_GPR_##Dst##q(), dwordAddress(T0));             \
    __ mov(IceType_i##Size, Encoded_GPR_##Src##Suffix(), Immediate(Value));    \
    __ movsx(IceType_i##Size, Encoded_GPR_##Dst##d(),                          \
             Encoded_GPR_##Src##Suffix());                                     \
    AssembledTest test = assemble();                                           \
    test.setQwordTo(T0, V0);                                                   \
    test.run();                                                                \
    ASSERT_EQ((uint64_t(-1) & ~Mask##Size) | Value, test.Dst##q())             \
        << "(" #Dst ", " #Src ", " #Size ")";                                  \
    reset();                                                                   \
  } while (0)

#define TestImplRegAddr(Dst, Suffix, Size)                                     \
  do {                                                                         \
    const uint32_t T0 = allocateDqword();                                      \
    static constexpr uint64_t V0 = 0xC0BEBEEF & Mask##Size;                    \
    static constexpr uint64_t Value = (0xC0BEBEEF) & Mask##Size;               \
    __ movsx(IceType_i##Size, Encoded_GPR_##Dst##d(), dwordAddress(T0));       \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setQwordTo(T0, V0);                                                   \
    test.run();                                                                \
    ASSERT_EQ((uint64_t(-1) & ~Mask##Size) | Value, test.Dst##q())             \
        << "(" #Dst ", Addr, " #Size ")";                                      \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestImplRegReg(Dst, Src, l, 8);                                            \
    TestImplRegAddr(Dst, l, 8);                                                \
    TestImplRegReg(Dst, Src, w, 16);                                           \
    TestImplRegAddr(Dst, w, 16);                                               \
    TestImplRegReg(Dst, Src, w, 32);                                           \
    TestImplRegAddr(Dst, w, 32);                                               \
  } while (0)

  TestImpl(r1, r2);
  TestImpl(r2, r3);
  TestImpl(r3, r4);
  TestImpl(r4, r5);
  TestImpl(r5, r6);
  TestImpl(r6, r7);
  TestImpl(r7, r8);
  TestImpl(r8, r10);
  TestImpl(r10, r11);
  TestImpl(r11, r12);
  TestImpl(r12, r13);
  TestImpl(r13, r14);
  TestImpl(r14, r15);
  TestImpl(r15, r1);

#undef TestImpl
#undef TestImplRegAddr
#undef TestImplRegReg
}

TEST_F(AssemblerX8664Test, Cmov) {
#define TestRegReg(C, Dest, IsTrue, Src0, Value0, Src1, Value1)                \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #C ", " #Dest ", " #IsTrue ", " #Src0 ", " #Value0 ", " #Src1      \
        ", " #Value1 ")";                                                      \
    __ mov(IceType_i32, Encoded_GPR_##Src0(), Immediate(Value0));              \
    __ mov(IceType_i32, Encoded_GPR_##Src1(), Immediate(Value1));              \
    __ mov(IceType_i32, Encoded_GPR_##Dest(), Immediate(Value0));              \
    __ cmp(IceType_i32, Encoded_GPR_##Src0(), Encoded_GPR_##Src1());           \
    __ cmov(IceType_i32, Cond::Br_##C, Encoded_GPR_##Dest(),                   \
            Encoded_GPR_##Src1());                                             \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.run();                                                                \
    ASSERT_EQ((IsTrue) ? (Value1) : (Value0), test.Dest()) << TestString;      \
                                                                               \
    reset();                                                                   \
  } while (0)

#define TestRegAddr(C, Dest, IsTrue, Src0, Value0, Value1)                     \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #C ", " #Dest ", " #IsTrue ", " #Src0 ", " #Value0                 \
        ", Addr, " #Value1 ")";                                                \
    const uint32_t T0 = allocateDword();                                       \
    const uint32_t V0 = Value1;                                                \
    __ mov(IceType_i32, Encoded_GPR_##Src0(), Immediate(Value0));              \
    __ mov(IceType_i32, Encoded_GPR_##Dest(), Immediate(Value0));              \
    __ cmp(IceType_i32, Encoded_GPR_##Src0(), dwordAddress(T0));               \
    __ cmov(IceType_i32, Cond::Br_##C, Encoded_GPR_##Dest(),                   \
            dwordAddress(T0));                                                 \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDwordTo(T0, V0);                                                   \
    test.run();                                                                \
    ASSERT_EQ((IsTrue) ? (Value1) : (Value0), test.Dest()) << TestString;      \
                                                                               \
    reset();                                                                   \
  } while (0)

#define TestValue(C, Dest, IsTrue, Src0, Value0, Src1, Value1)                 \
  do {                                                                         \
    TestRegReg(C, Dest, IsTrue, Src0, Value0, Src1, Value1);                   \
    TestRegAddr(C, Dest, IsTrue, Src0, Value0, Value1);                        \
  } while (0)

#define TestImpl(Dest, Src0, Src1)                                             \
  do {                                                                         \
    TestValue(o, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u);                     \
    TestValue(o, Dest, 0u, Src0, 0x1u, Src1, 0x10000000u);                     \
    TestValue(no, Dest, 1u, Src0, 0x1u, Src1, 0x10000000u);                    \
    TestValue(no, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u);                    \
    TestValue(b, Dest, 1u, Src0, 0x1, Src1, 0x80000000u);                      \
    TestValue(b, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u);                     \
    TestValue(ae, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u);                    \
    TestValue(ae, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u);                    \
    TestValue(e, Dest, 1u, Src0, 0x1u, Src1, 0x1u);                            \
    TestValue(e, Dest, 0u, Src0, 0x1u, Src1, 0x11111u);                        \
    TestValue(ne, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u);                    \
    TestValue(ne, Dest, 0u, Src0, 0x1u, Src1, 0x1u);                           \
    TestValue(be, Dest, 1u, Src0, 0x1u, Src1, 0x80000000u);                    \
    TestValue(be, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u);                    \
    TestValue(a, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u);                     \
    TestValue(a, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u);                     \
    TestValue(s, Dest, 1u, Src0, 0x1u, Src1, 0x80000000u);                     \
    TestValue(s, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u);                     \
    TestValue(ns, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u);                    \
    TestValue(ns, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u);                    \
    TestValue(p, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u);                     \
    TestValue(p, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u);                     \
    TestValue(np, Dest, 1u, Src0, 0x1u, Src1, 0x80000000u);                    \
    TestValue(np, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u);                    \
    TestValue(l, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u);                     \
    TestValue(l, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u);                     \
    TestValue(ge, Dest, 1u, Src0, 0x1u, Src1, 0x80000000u);                    \
    TestValue(ge, Dest, 0u, Src0, 0x80000000u, Src1, 0x1u);                    \
    TestValue(le, Dest, 1u, Src0, 0x80000000u, Src1, 0x1u);                    \
    TestValue(le, Dest, 0u, Src0, 0x1u, Src1, 0x80000000u);                    \
  } while (0)

  TestImpl(r1, r2, r3);

#undef TestImpl
#undef TestValue
#undef TestRegAddr
#undef TestRegReg
}

TEST_F(AssemblerX8664LowLevelTest, RepMovsb) {
  __ rep_movsb();

  static constexpr uint32_t ByteCount = 2;
  static constexpr uint8_t Prefix = 0xF3;
  static constexpr uint8_t Opcode = 0xA4;

  ASSERT_EQ(ByteCount, codeBytesSize());
  verifyBytes<ByteCount>(codeBytes(), Prefix, Opcode);
}

TEST_F(AssemblerX8664Test, MovssXmmAddr) {
#define TestMovssXmmAddrFloatLength(FloatLength, Xmm, Value)                   \
  do {                                                                         \
    static_assert((FloatLength) == 32 || (FloatLength) == 64,                  \
                  "Invalid fp length #FloatLength");                           \
    using Type = std::conditional<FloatLength == 32, float, double>::type;     \
                                                                               \
    static constexpr char TestString[] = "(" #FloatLength ", " #Xmm ")";       \
    static constexpr bool IsDouble = std::is_same<Type, double>::value;        \
    const uint32_t T0 = allocateQword();                                       \
    const Type V0 = Value;                                                     \
                                                                               \
    __ movss(IceType_f##FloatLength, Encoded_Xmm_##Xmm(), dwordAddress(T0));   \
                                                                               \
    AssembledTest test = assemble();                                           \
    if (IsDouble) {                                                            \
      test.setQwordTo(T0, static_cast<double>(V0));                            \
    } else {                                                                   \
      test.setDwordTo(T0, static_cast<float>(V0));                             \
    }                                                                          \
    test.run();                                                                \
    ASSERT_DOUBLE_EQ(Value, test.Xmm<Type>()) << TestString << " value is "    \
                                              << Value;                        \
    reset();                                                                   \
  } while (0)

#define TestMovssXmmAddr(FloatLength)                                          \
  do {                                                                         \
    using Type = std::conditional<FloatLength == 32, float, double>::type;     \
    for (const Type Value : {0.0, -0.0, 1.0, -1.0, 3.14, 99999.9999}) {        \
      TestMovssXmmAddrFloatLength(FloatLength, xmm0, Value);                   \
      TestMovssXmmAddrFloatLength(FloatLength, xmm1, Value);                   \
      TestMovssXmmAddrFloatLength(FloatLength, xmm2, Value);                   \
      TestMovssXmmAddrFloatLength(FloatLength, xmm3, Value);                   \
      TestMovssXmmAddrFloatLength(FloatLength, xmm4, Value);                   \
      TestMovssXmmAddrFloatLength(FloatLength, xmm5, Value);                   \
      TestMovssXmmAddrFloatLength(FloatLength, xmm6, Value);                   \
      TestMovssXmmAddrFloatLength(FloatLength, xmm7, Value);                   \
      TestMovssXmmAddrFloatLength(FloatLength, xmm8, Value);                   \
      TestMovssXmmAddrFloatLength(FloatLength, xmm9, Value);                   \
      TestMovssXmmAddrFloatLength(FloatLength, xmm10, Value);                  \
      TestMovssXmmAddrFloatLength(FloatLength, xmm11, Value);                  \
      TestMovssXmmAddrFloatLength(FloatLength, xmm12, Value);                  \
      TestMovssXmmAddrFloatLength(FloatLength, xmm13, Value);                  \
      TestMovssXmmAddrFloatLength(FloatLength, xmm14, Value);                  \
      TestMovssXmmAddrFloatLength(FloatLength, xmm15, Value);                  \
    }                                                                          \
  } while (0)

  TestMovssXmmAddr(32);
  TestMovssXmmAddr(64);

#undef TestMovssXmmAddr
#undef TestMovssXmmAddrType
}

TEST_F(AssemblerX8664Test, MovssAddrXmm) {
#define TestMovssAddrXmmFloatLength(FloatLength, Xmm, Value)                   \
  do {                                                                         \
    static_assert((FloatLength) == 32 || (FloatLength) == 64,                  \
                  "Invalid fp length #FloatLength");                           \
    using Type = std::conditional<FloatLength == 32, float, double>::type;     \
                                                                               \
    static constexpr char TestString[] = "(" #FloatLength ", " #Xmm ")";       \
    static constexpr bool IsDouble = std::is_same<Type, double>::value;        \
    const uint32_t T0 = allocateQword();                                       \
    const Type V0 = Value;                                                     \
    const uint32_t T1 = allocateQword();                                       \
    static_assert(std::numeric_limits<Type>::has_quiet_NaN,                    \
                  "f" #FloatLength " does not have quiet nan.");               \
    const Type V1 = std::numeric_limits<Type>::quiet_NaN();                    \
                                                                               \
    __ movss(IceType_f##FloatLength, Encoded_Xmm_##Xmm(), dwordAddress(T0));   \
                                                                               \
    AssembledTest test = assemble();                                           \
    if (IsDouble) {                                                            \
      test.setQwordTo(T0, static_cast<double>(V0));                            \
      test.setQwordTo(T1, static_cast<double>(V1));                            \
    } else {                                                                   \
      test.setDwordTo(T0, static_cast<float>(V0));                             \
      test.setDwordTo(T1, static_cast<float>(V1));                             \
    }                                                                          \
    test.run();                                                                \
    ASSERT_DOUBLE_EQ(Value, test.Xmm<Type>()) << TestString << " value is "    \
                                              << Value;                        \
    reset();                                                                   \
  } while (0)

#define TestMovssAddrXmm(FloatLength)                                          \
  do {                                                                         \
    using Type = std::conditional<FloatLength == 32, float, double>::type;     \
    for (const Type Value : {0.0, -0.0, 1.0, -1.0, 3.14, 99999.9999}) {        \
      TestMovssAddrXmmFloatLength(FloatLength, xmm0, Value);                   \
      TestMovssAddrXmmFloatLength(FloatLength, xmm1, Value);                   \
      TestMovssAddrXmmFloatLength(FloatLength, xmm2, Value);                   \
      TestMovssAddrXmmFloatLength(FloatLength, xmm3, Value);                   \
      TestMovssAddrXmmFloatLength(FloatLength, xmm4, Value);                   \
      TestMovssAddrXmmFloatLength(FloatLength, xmm5, Value);                   \
      TestMovssAddrXmmFloatLength(FloatLength, xmm6, Value);                   \
      TestMovssAddrXmmFloatLength(FloatLength, xmm7, Value);                   \
      TestMovssAddrXmmFloatLength(FloatLength, xmm8, Value);                   \
      TestMovssAddrXmmFloatLength(FloatLength, xmm9, Value);                   \
      TestMovssAddrXmmFloatLength(FloatLength, xmm10, Value);                  \
      TestMovssAddrXmmFloatLength(FloatLength, xmm11, Value);                  \
      TestMovssAddrXmmFloatLength(FloatLength, xmm12, Value);                  \
      TestMovssAddrXmmFloatLength(FloatLength, xmm13, Value);                  \
      TestMovssAddrXmmFloatLength(FloatLength, xmm14, Value);                  \
      TestMovssAddrXmmFloatLength(FloatLength, xmm15, Value);                  \
    }                                                                          \
  } while (0)

  TestMovssAddrXmm(32);
  TestMovssAddrXmm(64);

#undef TestMovssAddrXmm
#undef TestMovssAddrXmmType
}

TEST_F(AssemblerX8664Test, MovssXmmXmm) {
#define TestMovssXmmXmmFloatLength(FloatLength, Src, Dst, Value)               \
  do {                                                                         \
    static_assert((FloatLength) == 32 || (FloatLength) == 64,                  \
                  "Invalid fp length #FloatLength");                           \
    using Type = std::conditional<FloatLength == 32, float, double>::type;     \
                                                                               \
    static constexpr char TestString[] =                                       \
        "(" #FloatLength ", " #Src ", " #Dst ")";                              \
    static constexpr bool IsDouble = std::is_same<Type, double>::value;        \
    const uint32_t T0 = allocateQword();                                       \
    const Type V0 = Value;                                                     \
    const uint32_t T1 = allocateQword();                                       \
    static_assert(std::numeric_limits<Type>::has_quiet_NaN,                    \
                  "f" #FloatLength " does not have quiet nan.");               \
    const Type V1 = std::numeric_limits<Type>::quiet_NaN();                    \
                                                                               \
    __ movss(IceType_f##FloatLength, Encoded_Xmm_##Src(), dwordAddress(T0));   \
    __ movss(IceType_f##FloatLength, Encoded_Xmm_##Dst(), dwordAddress(T1));   \
    __ movss(IceType_f##FloatLength, Encoded_Xmm_##Dst(),                      \
             Encoded_Xmm_##Src());                                             \
                                                                               \
    AssembledTest test = assemble();                                           \
    if (IsDouble) {                                                            \
      test.setQwordTo(T0, static_cast<double>(V0));                            \
      test.setQwordTo(T1, static_cast<double>(V1));                            \
    } else {                                                                   \
      test.setDwordTo(T0, static_cast<float>(V0));                             \
      test.setDwordTo(T1, static_cast<float>(V1));                             \
    }                                                                          \
    test.run();                                                                \
    ASSERT_DOUBLE_EQ(Value, test.Dst<Type>()) << TestString << " value is "    \
                                              << Value;                        \
    reset();                                                                   \
  } while (0)

#define TestMovssXmmXmm(FloatLength)                                           \
  do {                                                                         \
    using Type = std::conditional<FloatLength == 32, float, double>::type;     \
    for (const Type Value : {0.0, -0.0, 1.0, -1.0, 3.14, 99999.9999}) {        \
      TestMovssXmmXmmFloatLength(FloatLength, xmm0, xmm1, Value);              \
      TestMovssXmmXmmFloatLength(FloatLength, xmm1, xmm2, Value);              \
      TestMovssXmmXmmFloatLength(FloatLength, xmm2, xmm3, Value);              \
      TestMovssXmmXmmFloatLength(FloatLength, xmm3, xmm4, Value);              \
      TestMovssXmmXmmFloatLength(FloatLength, xmm4, xmm5, Value);              \
      TestMovssXmmXmmFloatLength(FloatLength, xmm5, xmm6, Value);              \
      TestMovssXmmXmmFloatLength(FloatLength, xmm6, xmm7, Value);              \
      TestMovssXmmXmmFloatLength(FloatLength, xmm7, xmm8, Value);              \
      TestMovssXmmXmmFloatLength(FloatLength, xmm8, xmm9, Value);              \
      TestMovssXmmXmmFloatLength(FloatLength, xmm9, xmm10, Value);             \
      TestMovssXmmXmmFloatLength(FloatLength, xmm10, xmm11, Value);            \
      TestMovssXmmXmmFloatLength(FloatLength, xmm11, xmm12, Value);            \
      TestMovssXmmXmmFloatLength(FloatLength, xmm12, xmm13, Value);            \
      TestMovssXmmXmmFloatLength(FloatLength, xmm13, xmm14, Value);            \
      TestMovssXmmXmmFloatLength(FloatLength, xmm14, xmm15, Value);            \
      TestMovssXmmXmmFloatLength(FloatLength, xmm15, xmm0, Value);             \
    }                                                                          \
  } while (0)

  TestMovssXmmXmm(32);
  TestMovssXmmXmm(64);

#undef TestMovssXmmXmm
#undef TestMovssXmmXmmType
}

TEST_F(AssemblerX8664Test, MovdToXmm) {
#define TestMovdXmmReg32(Src, Dst, Value)                                      \
  do {                                                                         \
    assert(((Value)&0xFFFFFFFF) == (Value));                                   \
    static constexpr char TestString[] = "(" #Src ", " #Dst ")";               \
    const uint32_t T0 = allocateQword();                                       \
    const uint64_t V0 = 0xFFFFFFFF00000000ull;                                 \
                                                                               \
    __ mov(IceType_i32, Encoded_GPR_##Src(), Immediate(Value));                \
    __ movss(IceType_f64, Encoded_Xmm_##Dst(), dwordAddress(T0));              \
    __ movd(IceType_i32, Encoded_Xmm_##Dst(), Encoded_GPR_##Src());            \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setQwordTo(T0, V0);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is "       \
                                           << Value;                           \
    reset();                                                                   \
  } while (0)

#define TestMovdXmmReg64(Src, Dst, Value)                                      \
  do {                                                                         \
    assert(((Value)&0xFFFFFFFF) == (Value));                                   \
    static constexpr char TestString[] = "(" #Src ", " #Dst ")";               \
    const uint32_t T0 = allocateQword();                                       \
    const uint64_t V0 = 0xFFFFFFFF00000000ull;                                 \
    const uint64_t Expected = (static_cast<uint64_t>(Value) << 32) | (Value);  \
                                                                               \
    __ movabs(Encoded_GPR_##Src(), Expected);                                  \
    __ movss(IceType_f64, Encoded_Xmm_##Dst(), dwordAddress(T0));              \
    __ movd(IceType_i64, Encoded_Xmm_##Dst(), Encoded_GPR_##Src());            \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setQwordTo(T0, V0);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Expected, test.Dst<uint64_t>()) << TestString << " value is "    \
                                              << Value;                        \
    reset();                                                                   \
  } while (0)

#define TestMovdXmmReg(Src, Dst, Value)                                        \
  do {                                                                         \
    TestMovdXmmReg32(Src, Dst, Value);                                         \
    TestMovdXmmReg64(Src, Dst, Value);                                         \
  } while (0)

#define TestMovdXmmAddr32(Dst, Value)                                          \
  do {                                                                         \
    assert(((Value)&0xFFFFFFFF) == (Value));                                   \
    static constexpr char TestString[] = "(" #Dst ", Addr)";                   \
    const uint32_t T0 = allocateQword();                                       \
    const uint32_t V0 = Value;                                                 \
    const uint32_t T1 = allocateQword();                                       \
    const uint64_t V1 = 0xFFFFFFFF00000000ull;                                 \
                                                                               \
    __ movss(IceType_f64, Encoded_Xmm_##Dst(), dwordAddress(T1));              \
    __ movd(IceType_i32, Encoded_Xmm_##Dst(), dwordAddress(T0));               \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setDwordTo(T0, V0);                                                   \
    test.setQwordTo(T1, V1);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is "       \
                                           << Value;                           \
    reset();                                                                   \
  } while (0)

#define TestMovdXmmAddr64(Dst, Value)                                          \
  do {                                                                         \
    assert(((Value)&0xFFFFFFFF) == (Value));                                   \
    static constexpr char TestString[] = "(" #Dst ", Addr)";                   \
    const uint32_t T0 = allocateQword();                                       \
    const uint32_t V0 = (static_cast<uint64_t>(Value) << 32) | (Value);        \
    const uint32_t T1 = allocateQword();                                       \
    const uint64_t V1 = 0xFFFFFFFF00000000ull;                                 \
                                                                               \
    __ movss(IceType_f64, Encoded_Xmm_##Dst(), dwordAddress(T1));              \
    __ movd(IceType_i64, Encoded_Xmm_##Dst(), dwordAddress(T0));               \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setDwordTo(T0, V0);                                                   \
    test.setQwordTo(T1, V1);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is "       \
                                           << Value;                           \
    reset();                                                                   \
  } while (0)

#define TestMovdXmmAddr(Dst, Value)                                            \
  do {                                                                         \
    TestMovdXmmAddr32(Dst, Value);                                             \
    TestMovdXmmAddr64(Dst, Value);                                             \
  } while (0)

#define TestMovd(Dst)                                                          \
  do {                                                                         \
    for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) {   \
      TestMovdXmmReg(r1, Dst, Value);                                          \
      TestMovdXmmReg(r2, Dst, Value);                                          \
      TestMovdXmmReg(r3, Dst, Value);                                          \
      TestMovdXmmReg(r4, Dst, Value);                                          \
      TestMovdXmmReg(r5, Dst, Value);                                          \
      TestMovdXmmReg(r6, Dst, Value);                                          \
      TestMovdXmmReg(r7, Dst, Value);                                          \
      TestMovdXmmReg(r8, Dst, Value);                                          \
      TestMovdXmmReg(r10, Dst, Value);                                         \
      TestMovdXmmReg(r11, Dst, Value);                                         \
      TestMovdXmmReg(r12, Dst, Value);                                         \
      TestMovdXmmReg(r13, Dst, Value);                                         \
      TestMovdXmmReg(r14, Dst, Value);                                         \
      TestMovdXmmReg(r15, Dst, Value);                                         \
      TestMovdXmmAddr(Dst, Value);                                             \
    }                                                                          \
  } while (0)

  TestMovd(xmm0);
  TestMovd(xmm1);
  TestMovd(xmm2);
  TestMovd(xmm3);
  TestMovd(xmm4);
  TestMovd(xmm5);
  TestMovd(xmm6);
  TestMovd(xmm7);
  TestMovd(xmm8);
  TestMovd(xmm9);
  TestMovd(xmm10);
  TestMovd(xmm11);
  TestMovd(xmm12);
  TestMovd(xmm13);
  TestMovd(xmm14);
  TestMovd(xmm15);

#undef TestMovd
#undef TestMovdXmmAddr
#undef TestMovdXmmAddr64
#undef TestMovdXmmAddr32
#undef TestMovdXmmReg
#undef TestMovdXmmReg64
#undef TestMovdXmmReg32
}

TEST_F(AssemblerX8664Test, MovdFromXmm) {
#define TestMovdRegXmm32(Src, Dst, Value)                                      \
  do {                                                                         \
    assert(((Value)&0xFFFFFFFF) == (Value));                                   \
    static constexpr char TestString[] = "(" #Src ", " #Dst ")";               \
    const uint32_t T0 = allocateDword();                                       \
    const uint32_t V0 = Value;                                                 \
                                                                               \
    __ movss(IceType_f64, Encoded_Xmm_##Src(), dwordAddress(T0));              \
    __ movd(IceType_i32, Encoded_GPR_##Dst(), Encoded_Xmm_##Src());            \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setDwordTo(T0, V0);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Value, test.contentsOfDword(T0)) << TestString << " value is "   \
                                               << Value;                       \
    reset();                                                                   \
  } while (0)

#define TestMovdRegXmm64(Src, Dst, Value)                                      \
  do {                                                                         \
    assert(((Value)&0xFFFFFFFF) == (Value));                                   \
    static constexpr char TestString[] = "(" #Src ", " #Dst ")";               \
    const uint32_t T0 = allocateDword();                                       \
    const uint64_t V0 = (static_cast<uint64_t>(Value) << 32) | (Value);        \
                                                                               \
    __ movss(IceType_f64, Encoded_Xmm_##Src(), dwordAddress(T0));              \
    __ movd(IceType_i64, Encoded_GPR_##Dst(), Encoded_Xmm_##Src());            \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setQwordTo(T0, V0);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(V0, test.contentsOfQword(T0)) << TestString << " value is "      \
                                            << Value;                          \
    reset();                                                                   \
  } while (0)

#define TestMovdRegXmm(Src, Dst, Value)                                        \
  do {                                                                         \
    TestMovdRegXmm32(Src, Dst, Value);                                         \
    TestMovdRegXmm64(Src, Dst, Value);                                         \
  } while (0)

#define TestMovdAddrXmm32(Src, Value)                                          \
  do {                                                                         \
    assert(((Value)&0xFFFFFFFF) == (Value));                                   \
    static constexpr char TestString[] = "(" #Src ", Addr)";                   \
    const uint32_t T0 = allocateDword();                                       \
    const uint32_t V0 = Value;                                                 \
    const uint32_t T1 = allocateDword();                                       \
    const uint32_t V1 = ~(Value);                                              \
                                                                               \
    __ movss(IceType_f64, Encoded_Xmm_##Src(), dwordAddress(T0));              \
    __ movd(IceType_i32, dwordAddress(T1), Encoded_Xmm_##Src());               \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setDwordTo(T0, V0);                                                   \
    test.setDwordTo(T1, V1);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Value, test.contentsOfDword(T1)) << TestString << " value is "   \
                                               << Value;                       \
    reset();                                                                   \
  } while (0)

#define TestMovdAddrXmm64(Src, Value)                                          \
  do {                                                                         \
    assert(((Value)&0xFFFFFFFF) == (Value));                                   \
    static constexpr char TestString[] = "(" #Src ", Addr)";                   \
    const uint32_t T0 = allocateQword();                                       \
    const uint64_t V0 = (static_cast<uint64_t>(Value) << 32) | Value;          \
    const uint32_t T1 = allocateQword();                                       \
    const uint64_t V1 = ~V0;                                                   \
                                                                               \
    __ movss(IceType_f64, Encoded_Xmm_##Src(), dwordAddress(T0));              \
    __ movd(IceType_i64, dwordAddress(T1), Encoded_Xmm_##Src());               \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setQwordTo(T0, V0);                                                   \
    test.setQwordTo(T1, V1);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(V0, test.contentsOfQword(T1)) << TestString << " value is "      \
                                            << Value;                          \
    reset();                                                                   \
  } while (0)

#define TestMovdAddrXmm(Src, Value)                                            \
  do {                                                                         \
    TestMovdAddrXmm32(Src, Value);                                             \
    TestMovdAddrXmm64(Src, Value);                                             \
  } while (0)

#define TestMovd(Src)                                                          \
  do {                                                                         \
    for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) {   \
      TestMovdRegXmm(Src, r1, Value);                                          \
      TestMovdRegXmm(Src, r2, Value);                                          \
      TestMovdRegXmm(Src, r3, Value);                                          \
      TestMovdRegXmm(Src, r4, Value);                                          \
      TestMovdRegXmm(Src, r5, Value);                                          \
      TestMovdRegXmm(Src, r6, Value);                                          \
      TestMovdRegXmm(Src, r7, Value);                                          \
      TestMovdRegXmm(Src, r8, Value);                                          \
      TestMovdRegXmm(Src, r10, Value);                                         \
      TestMovdRegXmm(Src, r11, Value);                                         \
      TestMovdRegXmm(Src, r12, Value);                                         \
      TestMovdRegXmm(Src, r13, Value);                                         \
      TestMovdRegXmm(Src, r14, Value);                                         \
      TestMovdRegXmm(Src, r15, Value);                                         \
      TestMovdAddrXmm(Src, Value);                                             \
    }                                                                          \
  } while (0)

  TestMovd(xmm0);
  TestMovd(xmm1);
  TestMovd(xmm2);
  TestMovd(xmm3);
  TestMovd(xmm4);
  TestMovd(xmm5);
  TestMovd(xmm6);
  TestMovd(xmm7);
  TestMovd(xmm8);
  TestMovd(xmm9);
  TestMovd(xmm10);
  TestMovd(xmm11);
  TestMovd(xmm12);
  TestMovd(xmm13);
  TestMovd(xmm14);
  TestMovd(xmm15);

#undef TestMovd
#undef TestMovdAddrXmm
#undef TestMovdAddrXmm64
#undef TestMovdAddrXmm32
#undef TestMovdRegXmm
#undef TestMovdRegXmm64
#undef TestMovdRegXmm32
}

TEST_F(AssemblerX8664Test, MovqXmmAddr) {
#define TestMovd(Dst, Value)                                                   \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", Addr)";                   \
    const uint32_t T0 = allocateQword();                                       \
    const uint64_t V0 = Value;                                                 \
    const uint32_t T1 = allocateQword();                                       \
    const uint64_t V1 = ~(Value);                                              \
                                                                               \
    __ movss(IceType_f64, Encoded_Xmm_##Dst(), dwordAddress(T1));              \
    __ movq(Encoded_Xmm_##Dst(), dwordAddress(T0));                            \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setQwordTo(T0, V0);                                                   \
    test.setQwordTo(T1, V1);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is "       \
                                           << Value;                           \
    reset();                                                                   \
  } while (0)

  for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) {
    TestMovd(xmm0, Value);
    TestMovd(xmm1, Value);
    TestMovd(xmm2, Value);
    TestMovd(xmm3, Value);
    TestMovd(xmm4, Value);
    TestMovd(xmm5, Value);
    TestMovd(xmm6, Value);
    TestMovd(xmm7, Value);
    TestMovd(xmm8, Value);
    TestMovd(xmm9, Value);
    TestMovd(xmm10, Value);
    TestMovd(xmm11, Value);
    TestMovd(xmm12, Value);
    TestMovd(xmm13, Value);
    TestMovd(xmm14, Value);
    TestMovd(xmm15, Value);
  }

#undef TestMovd
}

TEST_F(AssemblerX8664Test, MovqAddrXmm) {
#define TestMovd(Dst, Value)                                                   \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", Addr)";                   \
    const uint32_t T0 = allocateQword();                                       \
    const uint64_t V0 = Value;                                                 \
    const uint32_t T1 = allocateQword();                                       \
    const uint64_t V1 = ~(Value);                                              \
                                                                               \
    __ movq(Encoded_Xmm_##Dst(), dwordAddress(T0));                            \
    __ movq(dwordAddress(T1), Encoded_Xmm_##Dst());                            \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setQwordTo(T0, V0);                                                   \
    test.setQwordTo(T1, V1);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is "       \
                                           << Value;                           \
    reset();                                                                   \
  } while (0)

  for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) {
    TestMovd(xmm0, Value);
    TestMovd(xmm1, Value);
    TestMovd(xmm2, Value);
    TestMovd(xmm3, Value);
    TestMovd(xmm4, Value);
    TestMovd(xmm5, Value);
    TestMovd(xmm6, Value);
    TestMovd(xmm7, Value);
    TestMovd(xmm8, Value);
    TestMovd(xmm9, Value);
    TestMovd(xmm10, Value);
    TestMovd(xmm11, Value);
    TestMovd(xmm12, Value);
    TestMovd(xmm13, Value);
    TestMovd(xmm14, Value);
    TestMovd(xmm15, Value);
  }

#undef TestMovd
}

TEST_F(AssemblerX8664Test, MovqXmmXmm) {
#define TestMovd(Src, Dst, Value)                                              \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Src ", " #Dst ")";               \
    const uint32_t T0 = allocateQword();                                       \
    const uint64_t V0 = Value;                                                 \
    const uint32_t T1 = allocateQword();                                       \
    const uint64_t V1 = ~(Value);                                              \
                                                                               \
    __ movq(Encoded_Xmm_##Src(), dwordAddress(T0));                            \
    __ movq(Encoded_Xmm_##Dst(), dwordAddress(T1));                            \
    __ movq(Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());                         \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.setQwordTo(T0, V0);                                                   \
    test.setQwordTo(T1, V1);                                                   \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is "       \
                                           << Value;                           \
    reset();                                                                   \
  } while (0)

  for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) {
    TestMovd(xmm0, xmm1, Value);
    TestMovd(xmm1, xmm2, Value);
    TestMovd(xmm2, xmm3, Value);
    TestMovd(xmm3, xmm4, Value);
    TestMovd(xmm4, xmm5, Value);
    TestMovd(xmm5, xmm6, Value);
    TestMovd(xmm6, xmm7, Value);
    TestMovd(xmm7, xmm8, Value);
    TestMovd(xmm8, xmm9, Value);
    TestMovd(xmm9, xmm10, Value);
    TestMovd(xmm10, xmm11, Value);
    TestMovd(xmm11, xmm12, Value);
    TestMovd(xmm12, xmm13, Value);
    TestMovd(xmm13, xmm14, Value);
    TestMovd(xmm14, xmm15, Value);
    TestMovd(xmm15, xmm0, Value);
  }

#undef TestMovd
}

TEST_F(AssemblerX8664Test, MovupsXmmAddr) {
#define TestMovups(Dst)                                                        \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ")";                         \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(),       \
                    std::numeric_limits<float>::infinity());                   \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(V0, test.Dst<Dqword>()) << TestString;                           \
    reset();                                                                   \
  } while (0)

  TestMovups(xmm0);
  TestMovups(xmm1);
  TestMovups(xmm2);
  TestMovups(xmm3);
  TestMovups(xmm4);
  TestMovups(xmm5);
  TestMovups(xmm6);
  TestMovups(xmm7);
  TestMovups(xmm8);
  TestMovups(xmm9);
  TestMovups(xmm10);
  TestMovups(xmm11);
  TestMovups(xmm12);
  TestMovups(xmm13);
  TestMovups(xmm14);
  TestMovups(xmm15);

#undef TestMovups
}

TEST_F(AssemblerX8664Test, MovupsAddrXmm) {
#define TestMovups(Src)                                                        \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Src ")";                         \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(),       \
                    std::numeric_limits<float>::infinity());                   \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1(0.0, 0.0, 0.0, 0.0);                                       \
                                                                               \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T0));                          \
    __ movups(dwordAddress(T1), Encoded_Xmm_##Src());                          \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(V0, test.contentsOfDqword(T1)) << TestString;                    \
    reset();                                                                   \
  } while (0)

  TestMovups(xmm0);
  TestMovups(xmm1);
  TestMovups(xmm2);
  TestMovups(xmm3);
  TestMovups(xmm4);
  TestMovups(xmm5);
  TestMovups(xmm6);
  TestMovups(xmm7);
  TestMovups(xmm8);
  TestMovups(xmm9);
  TestMovups(xmm10);
  TestMovups(xmm11);
  TestMovups(xmm12);
  TestMovups(xmm13);
  TestMovups(xmm14);
  TestMovups(xmm15);

#undef TestMovups
}

TEST_F(AssemblerX8664Test, MovupsXmmXmm) {
#define TestMovups(Dst, Src)                                                   \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", " #Src ")";               \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(),       \
                    std::numeric_limits<float>::infinity());                   \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1(0.0, 0.0, 0.0, 0.0);                                       \
                                                                               \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T1));                          \
    __ movups(Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());                       \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(V0, test.Dst<Dqword>()) << TestString;                           \
    reset();                                                                   \
  } while (0)

  TestMovups(xmm0, xmm1);
  TestMovups(xmm1, xmm2);
  TestMovups(xmm2, xmm3);
  TestMovups(xmm3, xmm4);
  TestMovups(xmm4, xmm5);
  TestMovups(xmm5, xmm6);
  TestMovups(xmm6, xmm7);
  TestMovups(xmm7, xmm8);
  TestMovups(xmm8, xmm9);
  TestMovups(xmm9, xmm10);
  TestMovups(xmm10, xmm11);
  TestMovups(xmm11, xmm12);
  TestMovups(xmm12, xmm13);
  TestMovups(xmm13, xmm14);
  TestMovups(xmm14, xmm15);
  TestMovups(xmm15, xmm0);

#undef TestMovups
}

TEST_F(AssemblerX8664Test, MovapsXmmXmm) {
#define TestMovaps(Dst, Src)                                                   \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", " #Src ")";               \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(),       \
                    std::numeric_limits<float>::infinity());                   \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1(0.0, 0.0, 0.0, 0.0);                                       \
                                                                               \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T1));                          \
    __ movaps(Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());                       \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(V0, test.Dst<Dqword>()) << TestString;                           \
    reset();                                                                   \
  } while (0)

  TestMovaps(xmm0, xmm1);
  TestMovaps(xmm1, xmm2);
  TestMovaps(xmm2, xmm3);
  TestMovaps(xmm3, xmm4);
  TestMovaps(xmm4, xmm5);
  TestMovaps(xmm5, xmm6);
  TestMovaps(xmm6, xmm7);
  TestMovaps(xmm7, xmm8);
  TestMovaps(xmm8, xmm9);
  TestMovaps(xmm9, xmm10);
  TestMovaps(xmm10, xmm11);
  TestMovaps(xmm11, xmm12);
  TestMovaps(xmm12, xmm13);
  TestMovaps(xmm13, xmm14);
  TestMovaps(xmm14, xmm15);
  TestMovaps(xmm15, xmm0);

#undef TestMovaps
}

TEST_F(AssemblerX8664Test, Movhlps_Movlhps) {
#define TestImplSingle(Dst, Src, Inst, Expect)                                 \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Inst ")";    \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0(uint64_t(0xAAAAAAAABBBBBBBBull),                           \
                    uint64_t(0xCCCCCCCCDDDDDDDDull));                          \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1(uint64_t(0xEEEEEEEEFFFFFFFFull),                           \
                    uint64_t(0x9999999988888888ull));                          \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ Inst(Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());                         \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Dqword Expect, test.Dst<Dqword>()) << TestString;                \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestImplSingle(Dst, Src, movhlps, (uint64_t(0x9999999988888888ull),        \
                                       uint64_t(0xCCCCCCCCDDDDDDDDull)));      \
    TestImplSingle(Dst, Src, movlhps, (uint64_t(0xAAAAAAAABBBBBBBBull),        \
                                       uint64_t(0xEEEEEEEEFFFFFFFFull)));      \
  } while (0)

  TestImpl(xmm0, xmm1);
  TestImpl(xmm1, xmm2);
  TestImpl(xmm2, xmm3);
  TestImpl(xmm3, xmm4);
  TestImpl(xmm4, xmm5);
  TestImpl(xmm5, xmm6);
  TestImpl(xmm6, xmm7);
  TestImpl(xmm7, xmm8);
  TestImpl(xmm8, xmm9);
  TestImpl(xmm9, xmm10);
  TestImpl(xmm10, xmm11);
  TestImpl(xmm11, xmm12);
  TestImpl(xmm12, xmm13);
  TestImpl(xmm13, xmm14);
  TestImpl(xmm14, xmm15);
  TestImpl(xmm15, xmm0);

#undef TestImpl
#undef TestImplSingle
}

TEST_F(AssemblerX8664Test, Movmsk) {
#define TestMovmskGPRXmm(GPR, Src, Value1, Expected, Inst)                     \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #GPR ", " #Src ", " #Value1 ", " #Expected ", " #Inst ")";         \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T0));                          \
    __ Inst(IceType_v4f32, Encoded_GPR_##GPR(), Encoded_Xmm_##Src());          \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Expected, test.GPR()) << TestString;                             \
    reset();                                                                   \
  } while (0)

#define TestMovmsk(GPR, Src)                                                   \
  do {                                                                         \
    TestMovmskGPRXmm(GPR, Src, (-1.0, 1.0, -1.0, 1.0), 0x05ul, movmsk);        \
  } while (0)

  TestMovmsk(r1, xmm0);
  TestMovmsk(r2, xmm1);
  TestMovmsk(r3, xmm2);
  TestMovmsk(r4, xmm3);
  TestMovmsk(r5, xmm4);
  TestMovmsk(r6, xmm5);
  TestMovmsk(r7, xmm6);
  TestMovmsk(r8, xmm7);
  TestMovmsk(r10, xmm8);
  TestMovmsk(r11, xmm9);
  TestMovmsk(r12, xmm10);
  TestMovmsk(r13, xmm11);
  TestMovmsk(r14, xmm12);
  TestMovmsk(r15, xmm13);
  TestMovmsk(r1, xmm14);
  TestMovmsk(r2, xmm15);

#undef TestMovmskGPRXmm
#undef TestMovmsk
}

TEST_F(AssemblerX8664Test, Pmovsxdq) {
#define TestPmovsxdqXmmXmm(Dst, Src, Value1)                                   \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Value1 ")";  \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value1;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1(uint64_t(0), uint64_t(0));                                 \
                                                                               \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T1));                          \
    __ pmovsxdq(Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());                     \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    const Dqword Expected(uint64_t(V0.I32[0]), uint64_t(V0.I32[1]));           \
    ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString;                     \
    reset();                                                                   \
  } while (0)

#define TestPmovsxdq(Dst, Src)                                                 \
  do {                                                                         \
    TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x700000007FFFFFFFull),             \
                                  uint64_t(0xAAAAAAAAEEEEEEEEull)));           \
    TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x800000007FFFFFFFull),             \
                                  uint64_t(0xAAAAAAAAEEEEEEEEull)));           \
    TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x70000000FFFFFFFFull),             \
                                  uint64_t(0xAAAAAAAAEEEEEEEEull)));           \
    TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x80000000FFFFFFFFull),             \
                                  uint64_t(0xAAAAAAAAEEEEEEEEull)));           \
  } while (0)

  TestPmovsxdq(xmm0, xmm1);
  TestPmovsxdq(xmm1, xmm2);
  TestPmovsxdq(xmm2, xmm3);
  TestPmovsxdq(xmm3, xmm4);
  TestPmovsxdq(xmm4, xmm5);
  TestPmovsxdq(xmm5, xmm6);
  TestPmovsxdq(xmm6, xmm7);
  TestPmovsxdq(xmm7, xmm8);
  TestPmovsxdq(xmm8, xmm9);
  TestPmovsxdq(xmm9, xmm10);
  TestPmovsxdq(xmm10, xmm11);
  TestPmovsxdq(xmm11, xmm12);
  TestPmovsxdq(xmm12, xmm13);
  TestPmovsxdq(xmm13, xmm14);
  TestPmovsxdq(xmm14, xmm15);
  TestPmovsxdq(xmm15, xmm0);

#undef TestPmovsxdq
#undef TestPmovsxdqXmmXmm
}

} // end of anonymous namespace
} // end of namespace Test
} // end of namespace X8664
} // end of namespace Ice
