//===- subzero/unittest/AssemblerX8664/XmmArith.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, ArithSS) {
#define TestArithSSXmmXmm(FloatSize, Src, Value0, Dst, Value1, Inst, Op)       \
  do {                                                                         \
    static_assert(FloatSize == 32 || FloatSize == 64,                          \
                  "Invalid fp size " #FloatSize);                              \
    static constexpr char TestString[] =                                       \
        "(" #FloatSize ", " #Src ", " #Value0 ", " #Dst ", " #Value1           \
        ", " #Inst ", " #Op ")";                                               \
    static constexpr bool IsDouble = FloatSize == 64;                          \
    using Type = std::conditional<IsDouble, double, float>::type;              \
    const uint32_t T0 = allocateQword();                                       \
    const Type V0 = Value0;                                                    \
    const uint32_t T1 = allocateQword();                                       \
    const Type V1 = Value1;                                                    \
                                                                               \
    __ movss(IceType_f##FloatSize, Encoded_Xmm_##Dst(), dwordAddress(T0));     \
    __ movss(IceType_f##FloatSize, Encoded_Xmm_##Src(), dwordAddress(T1));     \
    __ Inst(IceType_f##FloatSize, 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(V0 Op V1, test.Dst<Type>()) << TestString;                \
    reset();                                                                   \
  } while (0)

#define TestArithSSXmmAddr(FloatSize, Value0, Dst, Value1, Inst, Op)           \
  do {                                                                         \
    static_assert(FloatSize == 32 || FloatSize == 64,                          \
                  "Invalid fp size " #FloatSize);                              \
    static constexpr char TestString[] =                                       \
        "(" #FloatSize ", Addr, " #Value0 ", " #Dst ", " #Value1 ", " #Inst    \
        ", " #Op ")";                                                          \
    static constexpr bool IsDouble = FloatSize == 64;                          \
    using Type = std::conditional<IsDouble, double, float>::type;              \
    const uint32_t T0 = allocateQword();                                       \
    const Type V0 = Value0;                                                    \
    const uint32_t T1 = allocateQword();                                       \
    const Type V1 = Value1;                                                    \
                                                                               \
    __ movss(IceType_f##FloatSize, Encoded_Xmm_##Dst(), dwordAddress(T0));     \
    __ Inst(IceType_f##FloatSize, Encoded_Xmm_##Dst(), dwordAddress(T1));      \
                                                                               \
    AssembledTest test = assemble();                                           \
    if (IsDouble) {                                                            \
      test.setQwordTo(T0, static_cast<double>(V0));                            \
      test.setQwordTo(T1, static_cast<double>(V1));                            \
    } else {                                                                   \
      test.setDwordTo(T0, static_cast<float>(V0));                             \
      test.setDwordTo(T1, static_cast<float>(V1));                             \
    }                                                                          \
                                                                               \
    test.run();                                                                \
                                                                               \
    ASSERT_DOUBLE_EQ(V0 Op V1, test.Dst<Type>()) << TestString;                \
    reset();                                                                   \
  } while (0)

#define TestArithSS(FloatSize, Src, Dst0, Dst1)                                \
  do {                                                                         \
    TestArithSSXmmXmm(FloatSize, Src, 1.0, Dst0, 10.0, addss, +);              \
    TestArithSSXmmAddr(FloatSize, 2.0, Dst1, 20.0, addss, +);                  \
    TestArithSSXmmXmm(FloatSize, Src, 3.0, Dst0, 30.0, subss, -);              \
    TestArithSSXmmAddr(FloatSize, 4.0, Dst1, 40.0, subss, -);                  \
    TestArithSSXmmXmm(FloatSize, Src, 5.0, Dst0, 50.0, mulss, *);              \
    TestArithSSXmmAddr(FloatSize, 6.0, Dst1, 60.0, mulss, *);                  \
    TestArithSSXmmXmm(FloatSize, Src, 7.0, Dst0, 70.0, divss, / );             \
    TestArithSSXmmAddr(FloatSize, 8.0, Dst1, 80.0, divss, / );                 \
  } while (0)

#define TestImpl(Src, Dst0, Dst1)                                              \
  do {                                                                         \
    TestArithSS(32, Src, Dst0, Dst1);                                          \
    TestArithSS(64, Src, Dst0, Dst1);                                          \
  } while (0)

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

#undef TestImpl
#undef TestArithSS
#undef TestArithSSXmmAddr
#undef TestArithSSXmmXmm
}

TEST_F(AssemblerX8664Test, PArith) {
#define TestPArithXmmXmm(Dst, Value0, Src, Value1, Inst, Op, Type, Size)       \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Inst ", " #Op       \
        ", " #Type ", " #Size ")";                                             \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
                                                                               \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ Inst(IceType_i##Size, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());        \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type##Size##_t>(V0) Op V1, test.Dst<Dqword>())          \
        << TestString;                                                         \
    reset();                                                                   \
  } while (0)

#define TestPArithXmmAddr(Dst, Value0, Value1, Inst, Op, Type, Size)           \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Inst ", " #Op           \
        ", " #Type ", " #Size ")";                                             \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
                                                                               \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ Inst(IceType_i##Size, Encoded_Xmm_##Dst(), dwordAddress(T1));           \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type##Size##_t>(V0) Op V1, test.Dst<Dqword>())          \
        << TestString;                                                         \
    reset();                                                                   \
  } while (0)

#define TestPArithXmmImm(Dst, Value0, Imm, Inst, Op, Type, Size)               \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #Imm ", " #Inst ", " #Op ", " #Type         \
        ", " #Size ")";                                                        \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ Inst(IceType_i##Size, Encoded_Xmm_##Dst(), Immediate(Imm));             \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type##Size##_t>(V0) Op Imm, test.Dst<Dqword>())         \
        << TestString;                                                         \
    reset();                                                                   \
  } while (0)

#define TestPAndnXmmXmm(Dst, Value0, Src, Value1, Type, Size)                  \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", pandn, " #Type         \
        ", " #Size ")";                                                        \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
                                                                               \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ pandn(IceType_i##Size, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());       \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(~(packedAs<Type##Size##_t>(V0)) & V1, test.Dst<Dqword>())        \
        << TestString;                                                         \
    reset();                                                                   \
  } while (0)

#define TestPAndnXmmAddr(Dst, Value0, Value1, Type, Size)                      \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", Addr, " #Value1 ", pandn, " #Type ", " #Size  \
        ")";                                                                   \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
                                                                               \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ pandn(IceType_i##Size, Encoded_Xmm_##Dst(), dwordAddress(T1));          \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ((~packedAs<Type##Size##_t>(V0)) & V1, test.Dst<Dqword>())        \
        << TestString;                                                         \
    reset();                                                                   \
  } while (0)

#define TestPArithSize(Dst, Src, Size)                                         \
  do {                                                                         \
    static_assert(Size == 8 || Size == 16 || Size == 32, "Invalid size.");     \
    if (Size != 8) {                                                           \
      TestPArithXmmXmm(                                                        \
          Dst,                                                                 \
          (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)),  \
          Src, (uint64_t(3u), uint64_t(0u)), psra, >>, int, Size);             \
      TestPArithXmmAddr(Dst, (uint64_t(0x8040201008040201ull),                 \
                              uint64_t(0x8080404002020101ull)),                \
                        (uint64_t(3u), uint64_t(0u)), psra, >>, int, Size);    \
      TestPArithXmmImm(Dst, (uint64_t(0x8040201008040201ull),                  \
                             uint64_t(0x8080404002020101ull)),                 \
                       3u, psra, >>, int, Size);                               \
      TestPArithXmmXmm(                                                        \
          Dst,                                                                 \
          (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)),  \
          Src, (uint64_t(3u), uint64_t(0u)), psrl, >>, uint, Size);            \
      TestPArithXmmAddr(Dst, (uint64_t(0x8040201008040201ull),                 \
                              uint64_t(0x8080404002020101ull)),                \
                        (uint64_t(3u), uint64_t(0u)), psrl, >>, uint, Size);   \
      TestPArithXmmImm(Dst, (uint64_t(0x8040201008040201ull),                  \
                             uint64_t(0x8080404002020101ull)),                 \
                       3u, psrl, >>, uint, Size);                              \
      TestPArithXmmXmm(                                                        \
          Dst,                                                                 \
          (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)),  \
          Src, (uint64_t(3u), uint64_t(0u)), psll, <<, uint, Size);            \
      TestPArithXmmAddr(Dst, (uint64_t(0x8040201008040201ull),                 \
                              uint64_t(0x8080404002020101ull)),                \
                        (uint64_t(3u), uint64_t(0u)), psll, <<, uint, Size);   \
      TestPArithXmmImm(Dst, (uint64_t(0x8040201008040201ull),                  \
                             uint64_t(0x8080404002020101ull)),                 \
                       3u, psll, <<, uint, Size);                              \
                                                                               \
      TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull),                  \
                             uint64_t(0x8080404002020101ull)),                 \
                       Src, (uint64_t(0xFFFFFFFF00000000ull),                  \
                             uint64_t(0x0123456789ABCDEull)),                  \
                       pmull, *, int, Size);                                   \
      TestPArithXmmAddr(                                                       \
          Dst,                                                                 \
          (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)),  \
          (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)),   \
          pmull, *, int, Size);                                                \
      if (Size != 16) {                                                        \
        TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull),                \
                               uint64_t(0x8080404002020101ull)),               \
                         Src, (uint64_t(0xFFFFFFFF00000000ull),                \
                               uint64_t(0x0123456789ABCDEull)),                \
                         pmuludq, *, uint, Size);                              \
        TestPArithXmmAddr(                                                     \
            Dst, (uint64_t(0x8040201008040201ull),                             \
                  uint64_t(0x8080404002020101ull)),                            \
            (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)), \
            pmuludq, *, uint, Size);                                           \
      }                                                                        \
    }                                                                          \
    TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull),                    \
                           uint64_t(0x8080404002020101ull)),                   \
                     Src, (uint64_t(0xFFFFFFFF00000000ull),                    \
                           uint64_t(0x0123456789ABCDEull)),                    \
                     padd, +, int, Size);                                      \
    TestPArithXmmAddr(                                                         \
        Dst,                                                                   \
        (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)),    \
        (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)),     \
        padd, +, int, Size);                                                   \
    TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull),                    \
                           uint64_t(0x8080404002020101ull)),                   \
                     Src, (uint64_t(0xFFFFFFFF00000000ull),                    \
                           uint64_t(0x0123456789ABCDEull)),                    \
                     psub, -, int, Size);                                      \
    TestPArithXmmAddr(                                                         \
        Dst,                                                                   \
        (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)),    \
        (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)),     \
        psub, -, int, Size);                                                   \
    TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull),                    \
                           uint64_t(0x8080404002020101ull)),                   \
                     Src, (uint64_t(0xFFFFFFFF00000000ull),                    \
                           uint64_t(0x0123456789ABCDEull)),                    \
                     pand, &, int, Size);                                      \
    TestPArithXmmAddr(                                                         \
        Dst,                                                                   \
        (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)),    \
        (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)),     \
        pand, &, int, Size);                                                   \
                                                                               \
    TestPAndnXmmXmm(Dst, (uint64_t(0x8040201008040201ull),                     \
                          uint64_t(0x8080404002020101ull)),                    \
                    Src, (uint64_t(0xFFFFFFFF00000000ull),                     \
                          uint64_t(0x0123456789ABCDEull)),                     \
                    int, Size);                                                \
    TestPAndnXmmAddr(                                                          \
        Dst,                                                                   \
        (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)),    \
        (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)),     \
        int, Size);                                                            \
                                                                               \
    TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull),                    \
                           uint64_t(0x8080404002020101ull)),                   \
                     Src, (uint64_t(0xFFFFFFFF00000000ull),                    \
                           uint64_t(0x0123456789ABCDEull)),                    \
                     por, |, int, Size);                                       \
    TestPArithXmmAddr(                                                         \
        Dst,                                                                   \
        (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)),    \
        (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)),     \
        por, |, int, Size);                                                    \
    TestPArithXmmXmm(Dst, (uint64_t(0x8040201008040201ull),                    \
                           uint64_t(0x8080404002020101ull)),                   \
                     Src, (uint64_t(0xFFFFFFFF00000000ull),                    \
                           uint64_t(0x0123456789ABCDEull)),                    \
                     pxor, ^, int, Size);                                      \
    TestPArithXmmAddr(                                                         \
        Dst,                                                                   \
        (uint64_t(0x8040201008040201ull), uint64_t(0x8080404002020101ull)),    \
        (uint64_t(0xFFFFFFFF00000000ull), uint64_t(0x0123456789ABCDEull)),     \
        pxor, ^, int, Size);                                                   \
  } while (0)

#define TestPArith(Src, Dst)                                                   \
  do {                                                                         \
    TestPArithSize(Src, Dst, 8);                                               \
    TestPArithSize(Src, Dst, 16);                                              \
    TestPArithSize(Src, Dst, 32);                                              \
  } while (0)

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

#undef TestPArith
#undef TestPArithSize
#undef TestPAndnXmmAddr
#undef TestPAndnXmmXmm
#undef TestPArithXmmImm
#undef TestPArithXmmAddr
#undef TestPArithXmmXmm
}

TEST_F(AssemblerX8664Test, ArithPS) {
#define TestArithPSXmmXmm(FloatSize, Dst, Value0, Src, Value1, Inst, Op, Type) \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Inst ", " #Op       \
        ", " #Type ")";                                                        \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ Inst(IceType_f32, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());            \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type>(V0) Op V1, test.Dst<Dqword>()) << TestString;     \
                                                                               \
    reset();                                                                   \
  } while (0)

#define TestArithPSXmmXmmUntyped(Dst, Value0, Src, Value1, Inst, Op, Type)     \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Inst ", " #Op       \
        ", " #Type ")";                                                        \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(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(packedAs<Type>(V0) Op V1, test.Dst<Dqword>()) << TestString;     \
                                                                               \
    reset();                                                                   \
  } while (0)

#define TestArithPSXmmAddrUntyped(Dst, Value0, Value1, Inst, Op, Type)         \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Inst ", " #Op           \
        ", " #Type ")";                                                        \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ Inst(Encoded_Xmm_##Dst(), dwordAddress(T1));                            \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type>(V0) Op V1, test.Dst<Dqword>()) << TestString;     \
                                                                               \
    reset();                                                                   \
  } while (0)

#define TestMinMaxPS(FloatSize, Dst, Value0, Src, Value1, Inst, Type)          \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Inst ", " #Type     \
        ")";                                                                   \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ Inst(IceType_f##FloatSize, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type>(V0).Inst(V1), test.Dst<Dqword>()) << TestString;  \
                                                                               \
    reset();                                                                   \
  } while (0)

#define TestArithPSXmmAddr(FloatSize, Dst, Value0, Value1, Inst, Op, Type)     \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Inst ", " #Op           \
        ", " #Type ")";                                                        \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ Inst(IceType_f##FloatSize, Encoded_Xmm_##Dst(), dwordAddress(T1));      \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type>(V0) Op V1, test.Dst<Dqword>()) << TestString;     \
                                                                               \
    reset();                                                                   \
  } while (0)

#define TestArithPS(Dst, Src)                                                  \
  do {                                                                         \
    TestArithPSXmmXmm(32, Dst, (1.0, 100.0, -1000.0, 20.0), Src,               \
                      (0.55, 0.43, 0.23, 1.21), addps, +, float);              \
    TestArithPSXmmAddr(32, Dst, (1.0, 100.0, -1000.0, 20.0),                   \
                       (0.55, 0.43, 0.23, 1.21), addps, +, float);             \
    TestArithPSXmmXmm(32, Dst, (1.0, 100.0, -1000.0, 20.0), Src,               \
                      (0.55, 0.43, 0.23, 1.21), subps, -, float);              \
    TestArithPSXmmAddr(32, Dst, (1.0, 100.0, -1000.0, 20.0),                   \
                       (0.55, 0.43, 0.23, 1.21), subps, -, float);             \
    TestArithPSXmmXmm(32, Dst, (1.0, 100.0, -1000.0, 20.0), Src,               \
                      (0.55, 0.43, 0.23, 1.21), mulps, *, float);              \
    TestArithPSXmmAddr(32, Dst, (1.0, 100.0, -1000.0, 20.0),                   \
                       (0.55, 0.43, 0.23, 1.21), mulps, *, float);             \
    TestArithPSXmmXmm(32, Dst, (1.0, 100.0, -1000.0, 20.0), Src,               \
                      (0.55, 0.43, 0.23, 1.21), divps, /, float);              \
    TestArithPSXmmAddr(32, Dst, (1.0, 100.0, -1000.0, 20.0),                   \
                       (0.55, 0.43, 0.23, 1.21), divps, /, float);             \
    TestArithPSXmmXmm(32, Dst, (1.0, 100.0, -1000.0, 20.0), Src,               \
                      (0.55, 0.43, 0.23, 1.21), andps, &, float);              \
    TestArithPSXmmAddr(32, Dst, (1.0, 100.0, -1000.0, 20.0),                   \
                       (0.55, 0.43, 0.23, 1.21), andps, &, float);             \
    TestArithPSXmmXmm(64, Dst, (1.0, -1000.0), Src, (0.55, 1.21), andps, &,    \
                      double);                                                 \
    TestArithPSXmmAddr(64, Dst, (1.0, -1000.0), (0.55, 1.21), andps, &,        \
                       double);                                                \
    TestArithPSXmmXmm(32, Dst, (1.0, 100.0, -1000.0, 20.0), Src,               \
                      (0.55, 0.43, 0.23, 1.21), orps, |, float);               \
    TestArithPSXmmXmm(64, Dst, (1.0, -1000.0), Src, (0.55, 1.21), orps, |,     \
                      double);                                                 \
    TestMinMaxPS(32, Dst, (1.0, 100.0, -1000.0, 20.0), Src,                    \
                 (0.55, 0.43, 0.23, 1.21), minps, float);                      \
    TestMinMaxPS(32, Dst, (1.0, 100.0, -1000.0, 20.0), Src,                    \
                 (0.55, 0.43, 0.23, 1.21), maxps, float);                      \
    TestMinMaxPS(64, Dst, (1.0, -1000.0), Src, (0.55, 1.21), minps, double);   \
    TestMinMaxPS(64, Dst, (1.0, -1000.0), Src, (0.55, 1.21), maxps, double);   \
    TestArithPSXmmXmm(32, Dst, (1.0, 100.0, -1000.0, 20.0), Src,               \
                      (0.55, 0.43, 0.23, 1.21), xorps, ^, float);              \
    TestArithPSXmmAddr(32, Dst, (1.0, 100.0, -1000.0, 20.0),                   \
                       (0.55, 0.43, 0.23, 1.21), xorps, ^, float);             \
    TestArithPSXmmXmm(64, Dst, (1.0, -1000.0), Src, (0.55, 1.21), xorps, ^,    \
                      double);                                                 \
    TestArithPSXmmAddr(64, Dst, (1.0, -1000.0), (0.55, 1.21), xorps, ^,        \
                       double);                                                \
  } while (0)

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

#undef TestArithPs
#undef TestMinMaxPS
#undef TestArithPSXmmXmmUntyped
#undef TestArithPSXmmAddr
#undef TestArithPSXmmXmm
}

TEST_F(AssemblerX8664Test, Blending) {
  using f32 = float;
  using i8 = uint8_t;

#define TestBlendingXmmXmm(Dst, Value0, Src, Value1, M /*ask*/, Inst, Type)    \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #M ", " #Inst        \
        ", " #Type ")";                                                        \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
    const uint32_t Mask = allocateDqword();                                    \
    const Dqword MaskValue M;                                                  \
                                                                               \
    __ movups(Encoded_Xmm_xmm0(), dwordAddress(Mask));                         \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ Inst(IceType_##Type, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());         \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.setDqwordTo(Mask, MaskValue);                                         \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type>(V0).blendWith(V1, MaskValue), test.Dst<Dqword>()) \
        << TestString;                                                         \
    reset();                                                                   \
  } while (0)

#define TestBlendingXmmAddr(Dst, Value0, Value1, M /*ask*/, Inst, Type)        \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #M ", " #Inst ", " #Type \
        ")";                                                                   \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
    const uint32_t Mask = allocateDqword();                                    \
    const Dqword MaskValue M;                                                  \
                                                                               \
    __ movups(Encoded_Xmm_xmm0(), dwordAddress(Mask));                         \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ Inst(IceType_##Type, Encoded_Xmm_##Dst(), dwordAddress(T1));            \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.setDqwordTo(Mask, MaskValue);                                         \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type>(V0).blendWith(V1, MaskValue), test.Dst<Dqword>()) \
        << TestString;                                                         \
    reset();                                                                   \
  } while (0)

#define TestBlending(Src, Dst)                                                 \
  do {                                                                         \
    TestBlendingXmmXmm(                                                        \
        Dst, (1.0, 2.0, 1.0, 2.0), Src, (-1.0, -2.0, -1.0, -2.0),              \
        (uint64_t(0x8000000000000000ull), uint64_t(0x0000000080000000ull)),    \
        blendvps, f32);                                                        \
    TestBlendingXmmAddr(                                                       \
        Dst, (1.0, 2.0, 1.0, 2.0), (-1.0, -2.0, -1.0, -2.0),                   \
        (uint64_t(0x8000000000000000ull), uint64_t(0x0000000080000000ull)),    \
        blendvps, f32);                                                        \
    TestBlendingXmmXmm(                                                        \
        Dst,                                                                   \
        (uint64_t(0xFFFFFFFFFFFFFFFFull), uint64_t(0xBBBBBBBBBBBBBBBBull)),    \
        Src,                                                                   \
        (uint64_t(0xAAAAAAAAAAAAAAAAull), uint64_t(0xEEEEEEEEEEEEEEEEull)),    \
        (uint64_t(0x8000000000000080ull), uint64_t(0x8080808000000000ull)),    \
        pblendvb, i8);                                                         \
    TestBlendingXmmAddr(                                                       \
        Dst,                                                                   \
        (uint64_t(0xFFFFFFFFFFFFFFFFull), uint64_t(0xBBBBBBBBBBBBBBBBull)),    \
        (uint64_t(0xAAAAAAAAAAAAAAAAull), uint64_t(0xEEEEEEEEEEEEEEEEull)),    \
        (uint64_t(0x8000000000000080ull), uint64_t(0x8080808000000000ull)),    \
        pblendvb, i8);                                                         \
  } while (0)

  /* xmm0 is taken. It is the implicit mask . */
  TestBlending(xmm1, xmm2);
  TestBlending(xmm2, xmm3);
  TestBlending(xmm3, xmm4);
  TestBlending(xmm4, xmm5);
  TestBlending(xmm5, xmm6);
  TestBlending(xmm6, xmm7);
  TestBlending(xmm7, xmm8);
  TestBlending(xmm8, xmm9);
  TestBlending(xmm9, xmm10);
  TestBlending(xmm10, xmm11);
  TestBlending(xmm11, xmm12);
  TestBlending(xmm12, xmm13);
  TestBlending(xmm13, xmm14);
  TestBlending(xmm14, xmm15);
  TestBlending(xmm15, xmm1);

#undef TestBlending
#undef TestBlendingXmmAddr
#undef TestBlendingXmmXmm
}

TEST_F(AssemblerX8664Test, Cmpps) {
#define TestCmppsXmmXmm(FloatSize, Dst, Value0, Src, Value1, C, Op, Type)      \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Src ", " #Dst ", " #C ", " #Op ")";                               \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ cmpps(IceType_f##FloatSize, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src(),   \
             Cond::Cmpps_##C);                                                 \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type>(V0) Op V1, test.Dst<Dqword>()) << TestString;     \
    ;                                                                          \
    reset();                                                                   \
  } while (0)

#define TestCmppsXmmAddr(FloatSize, Dst, Value0, Value1, C, Op, Type)          \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", Addr, " #C ", " #Op ")";  \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ cmpps(IceType_f##FloatSize, Encoded_Xmm_##Dst(), dwordAddress(T1),      \
             Cond::Cmpps_##C);                                                 \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type>(V0) Op V1, test.Dst<Dqword>()) << TestString;     \
    ;                                                                          \
    reset();                                                                   \
  } while (0)

#define TestCmppsOrdUnordXmmXmm(FloatSize, Dst, Value0, Src, Value1, C, Type)  \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Src ", " #Dst ", " #C ")";       \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0(1.0, 1.0, std::numeric_limits<float>::quiet_NaN(),         \
                    std::numeric_limits<float>::quiet_NaN());                  \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1(1.0, std::numeric_limits<float>::quiet_NaN(), 1.0,         \
                    std::numeric_limits<float>::quiet_NaN());                  \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ cmpps(IceType_f##FloatSize, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src(),   \
             Cond::Cmpps_##C);                                                 \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type>(V0).C(V1), test.Dst<Dqword>()) << TestString;     \
    ;                                                                          \
    reset();                                                                   \
  } while (0)

#define TestCmppsOrdUnordXmmAddr(FloatSize, Dst, Value0, Value1, C, Type)      \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", " #C ")";                 \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0(1.0, 1.0, std::numeric_limits<float>::quiet_NaN(),         \
                    std::numeric_limits<float>::quiet_NaN());                  \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1(1.0, std::numeric_limits<float>::quiet_NaN(), 1.0,         \
                    std::numeric_limits<float>::quiet_NaN());                  \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ cmpps(IceType_f##FloatSize, Encoded_Xmm_##Dst(), dwordAddress(T1),      \
             Cond::Cmpps_##C);                                                 \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(packedAs<Type>(V0).C(V1), test.Dst<Dqword>()) << TestString;     \
    ;                                                                          \
    reset();                                                                   \
  } while (0)

#define TestCmpps(FloatSize, Dst, Value0, Src, Value1, Type)                   \
  do {                                                                         \
    TestCmppsXmmXmm(FloatSize, Dst, Value0, Src, Value1, eq, ==, Type);        \
    TestCmppsXmmAddr(FloatSize, Dst, Value0, Value1, eq, ==, Type);            \
    TestCmppsXmmXmm(FloatSize, Dst, Value0, Src, Value1, eq, ==, Type);        \
    TestCmppsXmmAddr(FloatSize, Dst, Value0, Value1, eq, ==, Type);            \
    TestCmppsXmmXmm(FloatSize, Dst, Value0, Src, Value1, eq, ==, Type);        \
    TestCmppsXmmAddr(FloatSize, Dst, Value0, Value1, eq, ==, Type);            \
    TestCmppsOrdUnordXmmXmm(FloatSize, Dst, Value0, Src, Value1, unord, Type); \
    TestCmppsOrdUnordXmmAddr(FloatSize, Dst, Value0, Value1, unord, Type);     \
    TestCmppsXmmXmm(FloatSize, Dst, Value0, Src, Value1, eq, ==, Type);        \
    TestCmppsXmmAddr(FloatSize, Dst, Value0, Value1, eq, ==, Type);            \
    TestCmppsXmmXmm(FloatSize, Dst, Value0, Src, Value1, eq, ==, Type);        \
    TestCmppsXmmAddr(FloatSize, Dst, Value0, Value1, eq, ==, Type);            \
    TestCmppsXmmXmm(FloatSize, Dst, Value0, Src, Value1, eq, ==, Type);        \
    TestCmppsXmmAddr(FloatSize, Dst, Value0, Value1, eq, ==, Type);            \
    if (FloatSize == 32) {                                                     \
      TestCmppsOrdUnordXmmXmm(                                                 \
          32, Dst, (1.0, 1.0, std::numeric_limits<float>::quiet_NaN(),         \
                    std::numeric_limits<float>::quiet_NaN()),                  \
          Src, (1.0, std::numeric_limits<float>::quiet_NaN(), 1.0,             \
                std::numeric_limits<float>::quiet_NaN()),                      \
          unord, Type);                                                        \
      TestCmppsOrdUnordXmmAddr(                                                \
          32, Dst, (1.0, 1.0, std::numeric_limits<float>::quiet_NaN(),         \
                    std::numeric_limits<float>::quiet_NaN()),                  \
          (1.0, std::numeric_limits<float>::quiet_NaN(), 1.0,                  \
           std::numeric_limits<float>::quiet_NaN()),                           \
          unord, Type);                                                        \
    } else {                                                                   \
      TestCmppsOrdUnordXmmXmm(64, Dst,                                         \
                              (1.0, std::numeric_limits<double>::quiet_NaN()), \
                              Src, (std::numeric_limits<double>::quiet_NaN(),  \
                                    std::numeric_limits<double>::quiet_NaN()), \
                              unord, Type);                                    \
      TestCmppsOrdUnordXmmXmm(64, Dst, (1.0, 1.0), Src,                        \
                              (1.0, std::numeric_limits<double>::quiet_NaN()), \
                              unord, Type);                                    \
      TestCmppsOrdUnordXmmAddr(                                                \
          64, Dst, (1.0, std::numeric_limits<double>::quiet_NaN()),            \
          (std::numeric_limits<double>::quiet_NaN(),                           \
           std::numeric_limits<double>::quiet_NaN()),                          \
          unord, Type);                                                        \
      TestCmppsOrdUnordXmmAddr(                                                \
          64, Dst, (1.0, 1.0),                                                 \
          (1.0, std::numeric_limits<double>::quiet_NaN()), unord, Type);       \
    }                                                                          \
  } while (0)

#define TestCmppsSize(FloatSize, Value0, Value1, Type)                         \
  do {                                                                         \
    TestCmpps(FloatSize, xmm0, Value0, xmm1, Value1, Type);                    \
    TestCmpps(FloatSize, xmm1, Value0, xmm2, Value1, Type);                    \
    TestCmpps(FloatSize, xmm2, Value0, xmm3, Value1, Type);                    \
    TestCmpps(FloatSize, xmm3, Value0, xmm4, Value1, Type);                    \
    TestCmpps(FloatSize, xmm4, Value0, xmm5, Value1, Type);                    \
    TestCmpps(FloatSize, xmm5, Value0, xmm6, Value1, Type);                    \
    TestCmpps(FloatSize, xmm6, Value0, xmm7, Value1, Type);                    \
    TestCmpps(FloatSize, xmm7, Value0, xmm8, Value1, Type);                    \
    TestCmpps(FloatSize, xmm8, Value0, xmm9, Value1, Type);                    \
    TestCmpps(FloatSize, xmm9, Value0, xmm10, Value1, Type);                   \
    TestCmpps(FloatSize, xmm10, Value0, xmm11, Value1, Type);                  \
    TestCmpps(FloatSize, xmm11, Value0, xmm12, Value1, Type);                  \
    TestCmpps(FloatSize, xmm12, Value0, xmm13, Value1, Type);                  \
    TestCmpps(FloatSize, xmm13, Value0, xmm14, Value1, Type);                  \
    TestCmpps(FloatSize, xmm14, Value0, xmm15, Value1, Type);                  \
    TestCmpps(FloatSize, xmm15, Value0, xmm0, Value1, Type);                   \
  } while (0)

  TestCmppsSize(32, (-1.0, 1.0, 3.14, 1024.5), (-1.0, 1.0, 3.14, 1024.5),
                float);
  TestCmppsSize(64, (1.0, -1000.0), (1.0, -1000.0), double);

#undef TestCmpps
#undef TestCmppsOrdUnordXmmAddr
#undef TestCmppsOrdUnordXmmXmm
#undef TestCmppsXmmAddr
#undef TestCmppsXmmXmm
}

TEST_F(AssemblerX8664Test, Sqrtps_Rsqrtps_Reciprocalps_Sqrtpd) {
#define TestImplSingle(Dst, Inst, Expect)                                      \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", " #Inst ")";              \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0(1.0, 4.0, 20.0, 3.14);                                     \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ Inst(Encoded_Xmm_##Dst());                                              \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.run();                                                                \
    ASSERT_EQ(Dqword Expect, test.Dst<Dqword>()) << TestString;                \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst)                                                          \
  do {                                                                         \
    TestImplSingle(Dst, sqrtps, (uint64_t(0x400000003F800000ull),              \
                                 uint64_t(0x3FE2D10B408F1BBDull)));            \
    TestImplSingle(Dst, rsqrtps, (uint64_t(0x3EFFF0003F7FF000ull),             \
                                  uint64_t(0x3F1078003E64F000ull)));           \
    TestImplSingle(Dst, reciprocalps, (uint64_t(0x3E7FF0003F7FF000ull),        \
                                       uint64_t(0x3EA310003D4CC000ull)));      \
                                                                               \
    TestImplSingle(Dst, sqrtpd, (uint64_t(0x4036A09E9365F5F3ull),              \
                                 uint64_t(0x401C42FAE40282A8ull)));            \
  } while (0)

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

#undef TestImpl
#undef TestImplSingle
}

TEST_F(AssemblerX8664Test, Unpck) {
  const Dqword V0(uint64_t(0xAAAAAAAABBBBBBBBull),
                  uint64_t(0xCCCCCCCCDDDDDDDDull));
  const Dqword V1(uint64_t(0xEEEEEEEEFFFFFFFFull),
                  uint64_t(0x9999999988888888ull));

  const Dqword unpcklpsExpected(uint64_t(0xFFFFFFFFBBBBBBBBull),
                                uint64_t(0xEEEEEEEEAAAAAAAAull));
  const Dqword unpcklpdExpected(uint64_t(0xAAAAAAAABBBBBBBBull),
                                uint64_t(0xEEEEEEEEFFFFFFFFull));
  const Dqword unpckhpsExpected(uint64_t(0x88888888DDDDDDDDull),
                                uint64_t(0x99999999CCCCCCCCull));
  const Dqword unpckhpdExpected(uint64_t(0xCCCCCCCCDDDDDDDDull),
                                uint64_t(0x9999999988888888ull));

#define TestImplSingle(Dst, Src, Inst)                                         \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Inst ")";    \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(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(Inst##Expected, test.Dst<Dqword>()) << TestString;               \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestImplSingle(Dst, Src, unpcklps);                                        \
    TestImplSingle(Dst, Src, unpcklpd);                                        \
    TestImplSingle(Dst, Src, unpckhps);                                        \
    TestImplSingle(Dst, Src, unpckhpd);                                        \
  } while (0)

  TestImpl(xmm0, xmm1);
  TestImpl(xmm1, xmm2);
  TestImpl(xmm2, xmm3);
  TestImpl(xmm3, xmm4);
  TestImpl(xmm4, xmm5);
  TestImpl(xmm5, xmm6);
  TestImpl(xmm6, xmm7);
  TestImpl(xmm7, 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, Shufp) {
  const Dqword V0(uint64_t(0x1111111122222222ull),
                  uint64_t(0x5555555577777777ull));
  const Dqword V1(uint64_t(0xAAAAAAAABBBBBBBBull),
                  uint64_t(0xCCCCCCCCDDDDDDDDull));

  const uint8_t pshufdImm = 0x63;
  const Dqword pshufdExpected(uint64_t(0xBBBBBBBBCCCCCCCCull),
                              uint64_t(0xAAAAAAAADDDDDDDDull));

  const uint8_t shufpsImm = 0xf9;
  const Dqword shufpsExpected(uint64_t(0x7777777711111111ull),
                              uint64_t(0xCCCCCCCCCCCCCCCCull));

#define TestImplSingleXmmXmm(Dst, Src, Inst)                                   \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Inst ")";    \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ Inst(IceType_f32, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src(),             \
            Immediate(Inst##Imm));                                             \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Inst##Expected, test.Dst<Dqword>()) << TestString;               \
    reset();                                                                   \
  } while (0)

#define TestImplSingleXmmAddr(Dst, Inst)                                       \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", Addr, " #Inst ")";        \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ Inst(IceType_f32, Encoded_Xmm_##Dst(), dwordAddress(T1),                \
            Immediate(Inst##Imm));                                             \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Inst##Expected, test.Dst<Dqword>()) << TestString;               \
    reset();                                                                   \
  } while (0)

#define TestImplSingleXmmXmmUntyped(Dst, Src, Inst)                            \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Src ", " #Inst ", Untyped)";                            \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ Inst(Encoded_Xmm_##Dst(), Encoded_Xmm_##Src(), Immediate(Inst##Imm));   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Inst##UntypedExpected, test.Dst<Dqword>()) << TestString;        \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestImplSingleXmmXmm(Dst, Src, pshufd);                                    \
    TestImplSingleXmmAddr(Dst, pshufd);                                        \
    TestImplSingleXmmXmm(Dst, Src, shufps);                                    \
    TestImplSingleXmmAddr(Dst, shufps);                                        \
  } 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 TestImplSingleXmmXmmUntyped
#undef TestImplSingleXmmAddr
#undef TestImplSingleXmmXmm
}

TEST_F(AssemblerX8664Test, Punpckl) {
  const Dqword V0_v4i32(uint64_t(0x1111111122222222ull),
                        uint64_t(0x5555555577777777ull));
  const Dqword V1_v4i32(uint64_t(0xAAAAAAAABBBBBBBBull),
                        uint64_t(0xCCCCCCCCDDDDDDDDull));
  const Dqword Expected_v4i32(uint64_t(0xBBBBBBBB22222222ull),
                              uint64_t(0xAAAAAAAA11111111ull));

  const Dqword V0_v8i16(uint64_t(0x1111222233334444ull),
                        uint64_t(0x5555666677778888ull));
  const Dqword V1_v8i16(uint64_t(0xAAAABBBBCCCCDDDDull),
                        uint64_t(0xEEEEFFFF00009999ull));
  const Dqword Expected_v8i16(uint64_t(0xCCCC3333DDDD4444ull),
                              uint64_t(0xAAAA1111BBBB2222ull));

  const Dqword V0_v16i8(uint64_t(0x1122334455667788ull),
                        uint64_t(0x99AABBCCDDEEFF00ull));
  const Dqword V1_v16i8(uint64_t(0xFFEEDDCCBBAA9900ull),
                        uint64_t(0xBAADF00DFEEDFACEull));
  const Dqword Expected_v16i8(uint64_t(0xBB55AA6699770088ull),
                              uint64_t(0xFF11EE22DD33CC44ull));

#define TestImplXmmXmm(Dst, Src, Inst, Ty)                                     \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Src ", " #Inst ", " #Ty ")";                            \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0));               \
    __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1));               \
    __ Inst(IceType_##Ty, XmmRegister::Encoded_Reg_##Dst,                      \
            XmmRegister::Encoded_Reg_##Src);                                   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0_##Ty);                                             \
    test.setDqwordTo(T1, V1_##Ty);                                             \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Expected_##Ty, test.Dst<Dqword>()) << TestString;                \
    reset();                                                                   \
  } while (0)

#define TestImplXmmAddr(Dst, Inst, Ty)                                         \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", Addr, " #Inst ", " #Ty ")";                                \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0));               \
    __ Inst(IceType_##Ty, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1));   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0_##Ty);                                             \
    test.setDqwordTo(T1, V1_##Ty);                                             \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Expected_##Ty, test.Dst<Dqword>()) << TestString;                \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestImplXmmXmm(Dst, Src, punpckl, v4i32);                                  \
    TestImplXmmAddr(Dst, punpckl, v4i32);                                      \
    TestImplXmmXmm(Dst, Src, punpckl, v8i16);                                  \
    TestImplXmmAddr(Dst, punpckl, v8i16);                                      \
    TestImplXmmXmm(Dst, Src, punpckl, v16i8);                                  \
    TestImplXmmAddr(Dst, punpckl, v16i8);                                      \
  } while (0)

  TestImpl(xmm0, xmm1);
  TestImpl(xmm1, xmm2);
  TestImpl(xmm2, xmm3);
  TestImpl(xmm3, xmm4);
  TestImpl(xmm4, xmm5);
  TestImpl(xmm5, xmm6);
  TestImpl(xmm6, xmm7);
  TestImpl(xmm7, xmm0);

#undef TestImpl
#undef TestImplXmmAddr
#undef TestImplXmmXmm
}

TEST_F(AssemblerX8664Test, Packss) {
  const Dqword V0_v4i32(uint64_t(0x0001000000001234ull),
                        uint64_t(0x7FFFFFFF80000000ull));
  const Dqword V1_v4i32(uint64_t(0xFFFFFFFEFFFFFFFFull),
                        uint64_t(0x0000800100007FFEull));
  const Dqword Expected_v4i32(uint64_t(0x7FFF80007FFF1234ull),
                              uint64_t(0x7FFF7FFEFFFEFFFFull));

  const Dqword V0_v8i16(uint64_t(0x0001000000120034ull),
                        uint64_t(0xFFFEFFFF7FFF8000ull));
  const Dqword V1_v8i16(uint64_t(0x00057FF80081007Eull),
                        uint64_t(0x0088007700660055ull));
  const Dqword Expected_v8i16(uint64_t(0xFEFF7F8001001234ull),
                              uint64_t(0x7F776655057F7F7Eull));

#define TestImplXmmXmm(Dst, Src, Inst, Ty)                                     \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Src ", " #Inst ", " #Ty ")";                            \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0));               \
    __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1));               \
    __ Inst(IceType_##Ty, XmmRegister::Encoded_Reg_##Dst,                      \
            XmmRegister::Encoded_Reg_##Src);                                   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0_##Ty);                                             \
    test.setDqwordTo(T1, V1_##Ty);                                             \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Expected_##Ty, test.Dst<Dqword>()) << TestString;                \
    reset();                                                                   \
  } while (0)

#define TestImplXmmAddr(Dst, Inst, Ty)                                         \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", Addr, " #Inst ", " #Ty ")";                                \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0));               \
    __ Inst(IceType_##Ty, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1));   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0_##Ty);                                             \
    test.setDqwordTo(T1, V1_##Ty);                                             \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Expected_##Ty, test.Dst<Dqword>()) << TestString;                \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestImplXmmXmm(Dst, Src, packss, v4i32);                                   \
    TestImplXmmAddr(Dst, packss, v4i32);                                       \
    TestImplXmmXmm(Dst, Src, packss, v8i16);                                   \
    TestImplXmmAddr(Dst, packss, v8i16);                                       \
  } while (0)

  TestImpl(xmm0, xmm1);
  TestImpl(xmm1, xmm2);
  TestImpl(xmm2, xmm3);
  TestImpl(xmm3, xmm4);
  TestImpl(xmm4, xmm5);
  TestImpl(xmm5, xmm6);
  TestImpl(xmm6, xmm7);
  TestImpl(xmm7, xmm0);

#undef TestImpl
#undef TestImplXmmAddr
#undef TestImplXmmXmm
}

TEST_F(AssemblerX8664Test, Packus) {
  const Dqword V0_v4i32(uint64_t(0x0001000000001234ull),
                        uint64_t(0x7FFFFFFF80000000ull));
  const Dqword V1_v4i32(uint64_t(0xFFFFFFFEFFFFFFFFull),
                        uint64_t(0x0000800100007FFEull));
  const Dqword Expected_v4i32(uint64_t(0xFFFF0000FFFF1234ull),
                              uint64_t(0x80017FFE00000000ull));

  const Dqword V0_v8i16(uint64_t(0x0001000000120034ull),
                        uint64_t(0xFFFEFFFF7FFF8000ull));
  const Dqword V1_v8i16(uint64_t(0x00057FF80081007Eull),
                        uint64_t(0x0088007700660055ull));
  const Dqword Expected_v8i16(uint64_t(0x0000FF0001001234ull),
                              uint64_t(0x8877665505FF817Eull));

#define TestImplXmmXmm(Dst, Src, Inst, Ty)                                     \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Src ", " #Inst ", " #Ty ")";                            \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0));               \
    __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1));               \
    __ Inst(IceType_##Ty, XmmRegister::Encoded_Reg_##Dst,                      \
            XmmRegister::Encoded_Reg_##Src);                                   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0_##Ty);                                             \
    test.setDqwordTo(T1, V1_##Ty);                                             \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Expected_##Ty, test.Dst<Dqword>()) << TestString;                \
    reset();                                                                   \
  } while (0)

#define TestImplXmmAddr(Dst, Inst, Ty)                                         \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", Addr, " #Inst ", " #Ty ")";                                \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0));               \
    __ Inst(IceType_##Ty, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1));   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0_##Ty);                                             \
    test.setDqwordTo(T1, V1_##Ty);                                             \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Expected_##Ty, test.Dst<Dqword>()) << TestString;                \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestImplXmmXmm(Dst, Src, packus, v4i32);                                   \
    TestImplXmmAddr(Dst, packus, v4i32);                                       \
    TestImplXmmXmm(Dst, Src, packus, v8i16);                                   \
    TestImplXmmAddr(Dst, packus, v8i16);                                       \
  } while (0)

  TestImpl(xmm0, xmm1);
  TestImpl(xmm1, xmm2);
  TestImpl(xmm2, xmm3);
  TestImpl(xmm3, xmm4);
  TestImpl(xmm4, xmm5);
  TestImpl(xmm5, xmm6);
  TestImpl(xmm6, xmm7);
  TestImpl(xmm7, xmm0);

#undef TestImpl
#undef TestImplXmmAddr
#undef TestImplXmmXmm
}

TEST_F(AssemblerX8664Test, Pshufb) {
  const Dqword V0(uint64_t(0x1122334455667788ull),
                  uint64_t(0x99aabbccddeeff32ull));
  const Dqword V1(uint64_t(0x0204050380060708ull),
                  uint64_t(0x010306080a8b0c0dull));

  const Dqword Expected(uint64_t(0x6644335500221132ull),
                        uint64_t(0x77552232ee00ccbbull));

#define TestImplXmmXmm(Dst, Src, Inst)                                         \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Inst ")";    \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0));               \
    __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1));               \
    __ Inst(IceType_void, XmmRegister::Encoded_Reg_##Dst,                      \
            XmmRegister::Encoded_Reg_##Src);                                   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString;                     \
    reset();                                                                   \
  } while (0)

#define TestImplXmmAddr(Dst, Inst)                                             \
  do {                                                                         \
    static constexpr char TestString[] = "(" #Dst ", Addr, " #Inst ")";        \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0));               \
    __ Inst(IceType_void, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1));   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString;                     \
    reset();                                                                   \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestImplXmmXmm(Dst, Src, pshufb);                                          \
    TestImplXmmAddr(Dst, pshufb);                                              \
  } 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 TestImplXmmAddr
#undef TestImplXmmXmm
}

TEST_F(AssemblerX8664Test, Cvt) {
  const Dqword dq2ps32DstValue(-1.0f, -1.0f, -1.0f, -1.0f);
  const Dqword dq2ps32SrcValue(-5, 3, 100, 200);
  const Dqword dq2ps32Expected(-5.0f, 3.0f, 100.0, 200.0);

  const Dqword dq2ps64DstValue(0.0f, 0.0f, -1.0f, -1.0f);
  const Dqword dq2ps64SrcValue(-5, 3, 100, 200);
  const Dqword dq2ps64Expected(-5.0f, 3.0f, 100.0, 200.0);

  const Dqword tps2dq32DstValue(-1.0f, -1.0f, -1.0f, -1.0f);
  const Dqword tps2dq32SrcValue(-5.0f, 3.0f, 100.0, 200.0);
  const Dqword tps2dq32Expected(-5, 3, 100, 200);

  const Dqword tps2dq64DstValue(-1.0f, -1.0f, -1.0f, -1.0f);
  const Dqword tps2dq64SrcValue(-5.0f, 3.0f, 100.0, 200.0);
  const Dqword tps2dq64Expected(-5, 3, 100, 200);

  const Dqword si2ss32DstValue(-1.0f, -1.0f, -1.0f, -1.0f);
  const int32_t si2ss32SrcValue = 5;
  const Dqword si2ss32Expected(5.0f, -1.0f, -1.0f, -1.0f);

  const Dqword si2ss64DstValue(-1.0, -1.0);
  const int32_t si2ss64SrcValue = 5;
  const Dqword si2ss64Expected(5.0, -1.0);

  const int32_t tss2si32DstValue = 0xF00F0FF0;
  const Dqword tss2si32SrcValue(-5.0f, -1.0f, -1.0f, -1.0f);
  const int32_t tss2si32Expected = -5;

  const int32_t tss2si64DstValue = 0xF00F0FF0;
  const Dqword tss2si64SrcValue(-5.0, -1.0);
  const int32_t tss2si64Expected = -5;

  const Dqword float2float32DstValue(-1.0, -1.0);
  const Dqword float2float32SrcValue(-5.0, 3, 100, 200);
  const Dqword float2float32Expected(-5.0, -1.0);

  const Dqword float2float64DstValue(-1.0, -1.0, -1.0, -1.0);
  const Dqword float2float64SrcValue(-5.0, 3.0);
  const Dqword float2float64Expected(-5.0, -1.0, -1.0, -1.0);

#define TestImplPXmmXmm(Dst, Src, Inst, Size)                                  \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Src ", cvt" #Inst ", f" #Size ")";                      \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ cvt##Inst(IceType_f##Size, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());   \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, Inst##Size##DstValue);                                \
    test.setDqwordTo(T1, Inst##Size##SrcValue);                                \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Inst##Size##Expected, test.Dst<Dqword>()) << TestString;         \
    reset();                                                                   \
  } while (0)

#define TestImplSXmmReg(Dst, GPR, Inst, Size, IntType)                         \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #GPR ", cvt" #Inst ", " #IntType ", f" #Size ")";        \
    const uint32_t T0 = allocateDqword();                                      \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ mov(IceType_i32, Encoded_GPR_##GPR(), Immediate(Inst##Size##SrcValue)); \
    __ cvt##Inst(IceType_f##Size, Encoded_Xmm_##Dst(), IntType,                \
                 Encoded_GPR_##GPR());                                         \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, Inst##Size##DstValue);                                \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Inst##Size##Expected, test.Dst<Dqword>()) << TestString;         \
    reset();                                                                   \
  } while (0)

#define TestImplSRegXmm(GPR, Src, Inst, IntSize, Size)                         \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #GPR ", " #Src ", cvt" #Inst ", " #IntSize ", f" #Size ")";        \
    const uint32_t T0 = allocateDqword();                                      \
                                                                               \
    __ mov(IceType_i32, Encoded_GPR_##GPR(), Immediate(Inst##Size##DstValue)); \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T0));                          \
    __ cvt##Inst(IceType_i##IntSize, Encoded_GPR_##GPR(), IceType_f##Size,     \
                 Encoded_Xmm_##Src());                                         \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, Inst##Size##SrcValue);                                \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(static_cast<uint##IntSize##_t>(Inst##Size##Expected),            \
              test.GPR())                                                      \
        << TestString;                                                         \
    reset();                                                                   \
  } while (0)

#define TestImplPXmmAddr(Dst, Inst, Size)                                      \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", Addr, cvt" #Inst ", f" #Size ")";                          \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ cvt##Inst(IceType_f##Size, Encoded_Xmm_##Dst(), dwordAddress(T1));      \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, Inst##Size##DstValue);                                \
    test.setDqwordTo(T1, Inst##Size##SrcValue);                                \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Inst##Size##Expected, test.Dst<Dqword>()) << TestString;         \
    reset();                                                                   \
  } while (0)

#define TestImplSXmmAddr(Dst, Inst, Size, IntType)                             \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", Addr, cvt" #Inst ", f" #Size ", " #IntType ")";            \
    const uint32_t T0 = allocateDqword();                                      \
    const uint32_t T1 = allocateDword();                                       \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ cvt##Inst(IceType_f##Size, Encoded_Xmm_##Dst(), IntType,                \
                 dwordAddress(T1));                                            \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, Inst##Size##DstValue);                                \
    test.setDwordTo(T1, Inst##Size##SrcValue);                                 \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Inst##Size##Expected, test.Dst<Dqword>()) << TestString;         \
    reset();                                                                   \
  } while (0)

#define TestImplSRegAddr(GPR, Inst, IntSize, Size)                             \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #GPR ", Addr, cvt" #Inst ", f" #Size ", " #IntSize ")";            \
    const uint32_t T0 = allocateDqword();                                      \
                                                                               \
    __ mov(IceType_i32, Encoded_GPR_##GPR(), Immediate(Inst##Size##DstValue)); \
    __ cvt##Inst(IceType_i##IntSize, Encoded_GPR_##GPR(), IceType_f##Size,     \
                 dwordAddress(T0));                                            \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, Inst##Size##SrcValue);                                \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(static_cast<uint##IntSize##_t>(Inst##Size##Expected),            \
              test.GPR())                                                      \
        << TestString;                                                         \
    reset();                                                                   \
  } while (0)

#define TestImplSize(Dst, Src, GPR, Size)                                      \
  do {                                                                         \
    TestImplPXmmXmm(Dst, Src, dq2ps, Size);                                    \
    TestImplPXmmAddr(Src, dq2ps, Size);                                        \
    TestImplPXmmXmm(Dst, Src, tps2dq, Size);                                   \
    TestImplPXmmAddr(Src, tps2dq, Size);                                       \
    TestImplSXmmReg(Dst, GPR, si2ss, Size, IceType_i32);                       \
    TestImplSXmmReg(Dst, GPR, si2ss, Size, IceType_i64);                       \
    TestImplSXmmAddr(Dst, si2ss, Size, IceType_i32);                           \
    TestImplSXmmAddr(Dst, si2ss, Size, IceType_i64);                           \
    TestImplSRegXmm(GPR, Src, tss2si, 32, Size);                               \
    TestImplSRegXmm(GPR, Src, tss2si, 64, Size);                               \
    TestImplSRegAddr(GPR, tss2si, 32, Size);                                   \
    TestImplSRegAddr(GPR, tss2si, 64, Size);                                   \
    TestImplPXmmXmm(Dst, Src, float2float, Size);                              \
    TestImplPXmmAddr(Src, float2float, Size);                                  \
  } while (0)

#define TestImpl(Dst, Src, GPR)                                                \
  do {                                                                         \
    TestImplSize(Dst, Src, GPR, 32);                                           \
    TestImplSize(Dst, Src, GPR, 64);                                           \
  } while (0)

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

#undef TestImpl
#undef TestImplSize
#undef TestImplSRegAddr
#undef TestImplSXmmAddr
#undef TestImplPXmmAddr
#undef TestImplSRegXmm
#undef TestImplSXmmReg
#undef TestImplPXmmXmm
}

TEST_F(AssemblerX8664Test, Ucomiss) {
  static constexpr float qnan32 = std::numeric_limits<float>::quiet_NaN();
  static constexpr double qnan64 = std::numeric_limits<float>::quiet_NaN();

  Dqword test32DstValue(0.0, qnan32, qnan32, qnan32);
  Dqword test32SrcValue(0.0, qnan32, qnan32, qnan32);

  Dqword test64DstValue(0.0, qnan64);
  Dqword test64SrcValue(0.0, qnan64);

#define TestImplXmmXmm(Dst, Value0, Src, Value1, Size, CompType, BParity,      \
                       BOther)                                                 \
  do {                                                                         \
    static constexpr char NearBranch = AssemblerX8664::kNearJump;              \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Size ", " #CompType \
        ", " #BParity ", " #BOther ")";                                        \
    const uint32_t T0 = allocateDqword();                                      \
    test##Size##DstValue.F##Size[0] = Value0;                                  \
    const uint32_t T1 = allocateDqword();                                      \
    test##Size##SrcValue.F##Size[0] = Value1;                                  \
    const uint32_t ImmIfTrue = 0xBEEF;                                         \
    const uint32_t ImmIfFalse = 0xC0FFE;                                       \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ImmIfFalse));  \
    __ ucomiss(IceType_f##Size, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());     \
    Label Done;                                                                \
    __ j(Cond::Br_##BParity, &Done, NearBranch);                               \
    __ j(Cond::Br_##BOther, &Done, NearBranch);                                \
    __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ImmIfTrue));   \
    __ bind(&Done);                                                            \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, test##Size##DstValue);                                \
    test.setDqwordTo(T1, test##Size##SrcValue);                                \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(ImmIfTrue, test.eax()) << TestString;                            \
    reset();                                                                   \
  } while (0)

#define TestImplXmmAddr(Dst, Value0, Value1, Size, CompType, BParity, BOther)  \
  do {                                                                         \
    static constexpr char NearBranch = AssemblerX8664::kNearJump;              \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Size ", " #CompType     \
        ", " #BParity ", " #BOther ")";                                        \
    const uint32_t T0 = allocateDqword();                                      \
    test##Size##DstValue.F##Size[0] = Value0;                                  \
    const uint32_t T1 = allocateDqword();                                      \
    test##Size##SrcValue.F##Size[0] = Value1;                                  \
    const uint32_t ImmIfTrue = 0xBEEF;                                         \
    const uint32_t ImmIfFalse = 0xC0FFE;                                       \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ImmIfFalse));  \
    __ ucomiss(IceType_f##Size, Encoded_Xmm_##Dst(), dwordAddress(T1));        \
    Label Done;                                                                \
    __ j(Cond::Br_##BParity, &Done, NearBranch);                               \
    __ j(Cond::Br_##BOther, &Done, NearBranch);                                \
    __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ImmIfTrue));   \
    __ bind(&Done);                                                            \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, test##Size##DstValue);                                \
    test.setDqwordTo(T1, test##Size##SrcValue);                                \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(ImmIfTrue, test.eax()) << TestString;                            \
    reset();                                                                   \
  } while (0)

#define TestImplCond(Dst, Value0, Src, Value1, Size, CompType, BParity,        \
                     BOther)                                                   \
  do {                                                                         \
    TestImplXmmXmm(Dst, Value0, Src, Value1, Size, CompType, BParity, BOther); \
    TestImplXmmAddr(Dst, Value0, Value1, Size, CompType, BParity, BOther);     \
  } while (0)

#define TestImplSize(Dst, Src, Size)                                           \
  do {                                                                         \
    TestImplCond(Dst, 1.0, Src, 1.0, Size, isEq, p, ne);                       \
    TestImplCond(Dst, 1.0, Src, 2.0, Size, isNe, p, e);                        \
    TestImplCond(Dst, 1.0, Src, 2.0, Size, isLe, p, a);                        \
    TestImplCond(Dst, 1.0, Src, 1.0, Size, isLe, p, a);                        \
    TestImplCond(Dst, 1.0, Src, 2.0, Size, isLt, p, ae);                       \
    TestImplCond(Dst, 2.0, Src, 1.0, Size, isGe, p, b);                        \
    TestImplCond(Dst, 1.0, Src, 1.0, Size, isGe, p, b);                        \
    TestImplCond(Dst, 2.0, Src, 1.0, Size, isGt, p, be);                       \
    TestImplCond(Dst, qnan##Size, Src, 1.0, Size, isUnord, np, o);             \
    TestImplCond(Dst, 1.0, Src, qnan##Size, Size, isUnord, np, s);             \
    TestImplCond(Dst, qnan##Size, Src, qnan##Size, Size, isUnord, np, s);      \
  } while (0)

#define TestImpl(Dst, Src)                                                     \
  do {                                                                         \
    TestImplSize(Dst, Src, 32);                                                \
    TestImplSize(Dst, Src, 64);                                                \
  } while (0)

  TestImpl(xmm0, xmm1);
  TestImpl(xmm1, xmm2);
  TestImpl(xmm2, xmm3);
  TestImpl(xmm3, xmm4);
  TestImpl(xmm4, xmm5);
  TestImpl(xmm5, xmm6);
  TestImpl(xmm6, xmm7);
  TestImpl(xmm7, 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 TestImplSize
#undef TestImplCond
#undef TestImplXmmAddr
#undef TestImplXmmXmm
}

TEST_F(AssemblerX8664Test, Sqrtss) {
  Dqword test32SrcValue(-100.0, -100.0, -100.0, -100.0);
  Dqword test32DstValue(-1.0, -1.0, -1.0, -1.0);

  Dqword test64SrcValue(-100.0, -100.0);
  Dqword test64DstValue(-1.0, -1.0);

#define TestSqrtssXmmXmm(Dst, Src, Value1, Result, Size)                       \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Src ", " #Value1 ", " #Result ", " #Size ")";           \
    const uint32_t T0 = allocateDqword();                                      \
    test##Size##SrcValue.F##Size[0] = Value1;                                  \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T1));                          \
    __ sqrt(IceType_f##Size, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());        \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, test##Size##SrcValue);                                \
    test.setDqwordTo(T1, test##Size##DstValue);                                \
    test.run();                                                                \
                                                                               \
    Dqword Expected = test##Size##DstValue;                                    \
    Expected.F##Size[0] = Result;                                              \
    ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString;                     \
    reset();                                                                   \
  } while (0)

#define TestSqrtssXmmAddr(Dst, Value1, Result, Size)                           \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", Addr, " #Value1 ", " #Result ", " #Size ")";               \
    const uint32_t T0 = allocateDqword();                                      \
    test##Size##SrcValue.F##Size[0] = Value1;                                  \
    const uint32_t T1 = allocateDqword();                                      \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T1));                          \
    __ sqrt(IceType_f##Size, Encoded_Xmm_##Dst(), dwordAddress(T0));           \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, test##Size##SrcValue);                                \
    test.setDqwordTo(T1, test##Size##DstValue);                                \
    test.run();                                                                \
                                                                               \
    Dqword Expected = test##Size##DstValue;                                    \
    Expected.F##Size[0] = Result;                                              \
    ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString;                     \
    reset();                                                                   \
  } while (0)

#define TestSqrtssSize(Dst, Src, Size)                                         \
  do {                                                                         \
    TestSqrtssXmmXmm(Dst, Src, 4.0, 2.0, Size);                                \
    TestSqrtssXmmAddr(Dst, 4.0, 2.0, Size);                                    \
    TestSqrtssXmmXmm(Dst, Src, 9.0, 3.0, Size);                                \
    TestSqrtssXmmAddr(Dst, 9.0, 3.0, Size);                                    \
    TestSqrtssXmmXmm(Dst, Src, 100.0, 10.0, Size);                             \
    TestSqrtssXmmAddr(Dst, 100.0, 10.0, Size);                                 \
  } while (0)

#define TestSqrtss(Dst, Src)                                                   \
  do {                                                                         \
    TestSqrtssSize(Dst, Src, 32);                                              \
    TestSqrtssSize(Dst, Src, 64);                                              \
  } while (0)

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

#undef TestSqrtss
#undef TestSqrtssSize
#undef TestSqrtssXmmAddr
#undef TestSqrtssXmmXmm
}

TEST_F(AssemblerX8664Test, Insertps) {
#define TestInsertpsXmmXmmImm(Dst, Value0, Src, Value1, Imm, Expected)         \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Imm ", " #Expected  \
        ")";                                                                   \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ insertps(IceType_v4f32, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src(),       \
                Immediate(Imm));                                               \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Dqword Expected, test.Dst<Dqword>()) << TestString;              \
    reset();                                                                   \
  } while (0)

#define TestInsertpsXmmAddrImm(Dst, Value0, Value1, Imm, Expected)             \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Imm ", " #Expected ")"; \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ insertps(IceType_v4f32, Encoded_Xmm_##Dst(), dwordAddress(T1),          \
                Immediate(Imm));                                               \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(Dqword Expected, test.Dst<Dqword>()) << TestString;              \
    reset();                                                                   \
  } while (0)

#define TestInsertps(Dst, Src)                                                 \
  do {                                                                         \
    TestInsertpsXmmXmmImm(                                                     \
        Dst, (uint64_t(-1), uint64_t(-1)), Src,                                \
        (uint64_t(0xAAAAAAAABBBBBBBBull), uint64_t(0xCCCCCCCCDDDDDDDDull)),    \
        0x99,                                                                  \
        (uint64_t(0xDDDDDDDD00000000ull), uint64_t(0x00000000FFFFFFFFull)));   \
    TestInsertpsXmmAddrImm(                                                    \
        Dst, (uint64_t(-1), uint64_t(-1)),                                     \
        (uint64_t(0xAAAAAAAABBBBBBBBull), uint64_t(0xCCCCCCCCDDDDDDDDull)),    \
        0x99,                                                                  \
        (uint64_t(0xBBBBBBBB00000000ull), uint64_t(0x00000000FFFFFFFFull)));   \
    TestInsertpsXmmXmmImm(                                                     \
        Dst, (uint64_t(-1), uint64_t(-1)), Src,                                \
        (uint64_t(0xAAAAAAAABBBBBBBBull), uint64_t(0xCCCCCCCCDDDDDDDDull)),    \
        0x9D,                                                                  \
        (uint64_t(0xDDDDDDDD00000000ull), uint64_t(0x0000000000000000ull)));   \
    TestInsertpsXmmAddrImm(                                                    \
        Dst, (uint64_t(-1), uint64_t(-1)),                                     \
        (uint64_t(0xAAAAAAAABBBBBBBBull), uint64_t(0xCCCCCCCCDDDDDDDDull)),    \
        0x9D,                                                                  \
        (uint64_t(0xBBBBBBBB00000000ull), uint64_t(0x0000000000000000ull)));   \
  } while (0)

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

#undef TestInsertps
#undef TestInsertpsXmmXmmAddr
#undef TestInsertpsXmmXmmImm
}

TEST_F(AssemblerX8664Test, Pinsr) {
  static constexpr uint8_t Mask32 = 0x03;
  static constexpr uint8_t Mask16 = 0x07;
  static constexpr uint8_t Mask8 = 0x0F;

#define TestPinsrXmmGPRImm(Dst, Value0, GPR, Value1, Imm, Size)                \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #GPR ", " #Value1 ", " #Imm ", " #Size ")"; \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ mov(IceType_i32, Encoded_GPR_##GPR(), Immediate(Value1));               \
    __ pinsr(IceType_i##Size, Encoded_Xmm_##Dst(), Encoded_GPR_##GPR(),        \
             Immediate(Imm));                                                  \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.run();                                                                \
                                                                               \
    constexpr uint8_t sel = (Imm)&Mask##Size;                                  \
    Dqword Expected = V0;                                                      \
    Expected.U##Size[sel] = Value1;                                            \
    ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString;                     \
    reset();                                                                   \
  } while (0)

#define TestPinsrXmmAddrImm(Dst, Value0, Value1, Imm, Size)                    \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Imm ", " #Size ")";     \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDword();                                       \
    const uint32_t V1 = Value1;                                                \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ pinsr(IceType_i##Size, Encoded_Xmm_##Dst(), dwordAddress(T1),           \
             Immediate(Imm));                                                  \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDwordTo(T1, V1);                                                   \
    test.run();                                                                \
                                                                               \
    constexpr uint8_t sel = (Imm)&Mask##Size;                                  \
    Dqword Expected = V0;                                                      \
    Expected.U##Size[sel] = Value1;                                            \
    ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString;                     \
    reset();                                                                   \
  } while (0)

#define TestPinsrSize(Dst, GPR, Value1, Imm, Size)                             \
  do {                                                                         \
    TestPinsrXmmGPRImm(Dst, (uint64_t(0xAAAAAAAABBBBBBBBull),                  \
                             uint64_t(0xFFFFFFFFDDDDDDDDull)),                 \
                       GPR, Value1, Imm, Size);                                \
    TestPinsrXmmAddrImm(Dst, (uint64_t(0xAAAAAAAABBBBBBBBull),                 \
                              uint64_t(0xFFFFFFFFDDDDDDDDull)),                \
                        Value1, Imm, Size);                                    \
  } while (0)

#define TestPinsr(Src, Dst)                                                    \
  do {                                                                         \
    TestPinsrSize(Src, Dst, 0xEE, 0x03, 8);                                    \
    TestPinsrSize(Src, Dst, 0xFFEE, 0x03, 16);                                 \
    TestPinsrSize(Src, Dst, 0xC0FFEE, 0x03, 32);                               \
  } while (0)

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

#undef TestPinsr
#undef TestPinsrSize
#undef TestPinsrXmmAddrImm
#undef TestPinsrXmmGPRImm
}

TEST_F(AssemblerX8664Test, Pextr) {
  static constexpr uint8_t Mask32 = 0x03;
  static constexpr uint8_t Mask16 = 0x07;
  static constexpr uint8_t Mask8 = 0x0F;

#define TestPextrGPRXmmImm(GPR, Src, Value1, Imm, Size)                        \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #GPR ", " #Src ", " #Value1 ", " #Imm ", " #Size ")";              \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T0));                          \
    __ pextr(IceType_i##Size, Encoded_GPR_##GPR(), Encoded_Xmm_##Src(),        \
             Immediate(Imm));                                                  \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.run();                                                                \
                                                                               \
    constexpr uint8_t sel = (Imm)&Mask##Size;                                  \
    ASSERT_EQ(V0.U##Size[sel], test.GPR()) << TestString;                      \
    reset();                                                                   \
  } while (0)

#define TestPextrSize(GPR, Src, Value1, Imm, Size)                             \
  do {                                                                         \
    TestPextrGPRXmmImm(GPR, Src, (uint64_t(0xAAAAAAAABBBBBBBBull),             \
                                  uint64_t(0xFFFFFFFFDDDDDDDDull)),            \
                       Imm, Size);                                             \
  } while (0)

#define TestPextr(Src, Dst)                                                    \
  do {                                                                         \
    TestPextrSize(Src, Dst, 0xEE, 0x03, 8);                                    \
    TestPextrSize(Src, Dst, 0xFFEE, 0x03, 16);                                 \
    TestPextrSize(Src, Dst, 0xC0FFEE, 0x03, 32);                               \
  } while (0)

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

#undef TestPextr
#undef TestPextrSize
#undef TestPextrXmmGPRImm
}

TEST_F(AssemblerX8664Test, Pcmpeq_Pcmpgt) {
#define TestPcmpXmmXmm(Dst, Value0, Src, Value1, Size, Inst, Op)               \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Size ", " #Op ")";  \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ Inst(IceType_i##Size, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src());        \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    Dqword Expected(uint64_t(0), uint64_t(0));                                 \
    static constexpr uint8_t ArraySize =                                       \
        sizeof(Dqword) / sizeof(uint##Size##_t);                               \
    for (uint8_t i = 0; i < ArraySize; ++i) {                                  \
      Expected.I##Size[i] = (V1.I##Size[i] Op V0.I##Size[i]) ? -1 : 0;         \
    }                                                                          \
    ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString;                     \
    reset();                                                                   \
  } while (0)

#define TestPcmpXmmAddr(Dst, Value0, Value1, Size, Inst, Op)                   \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Value0 ", Addr, " #Value1 ", " #Size ", " #Op ")";      \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0 Value0;                                                    \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1 Value1;                                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ Inst(IceType_i##Size, Encoded_Xmm_##Dst(), dwordAddress(T1));           \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    Dqword Expected(uint64_t(0), uint64_t(0));                                 \
    static constexpr uint8_t ArraySize =                                       \
        sizeof(Dqword) / sizeof(uint##Size##_t);                               \
    for (uint8_t i = 0; i < ArraySize; ++i) {                                  \
      Expected.I##Size[i] = (V1.I##Size[i] Op V0.I##Size[i]) ? -1 : 0;         \
    }                                                                          \
    ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString;                     \
    reset();                                                                   \
  } while (0)

#define TestPcmpValues(Dst, Value0, Src, Value1, Size)                         \
  do {                                                                         \
    TestPcmpXmmXmm(Dst, Value0, Src, Value1, Size, pcmpeq, == );               \
    TestPcmpXmmAddr(Dst, Value0, Value1, Size, pcmpeq, == );                   \
    TestPcmpXmmXmm(Dst, Value0, Src, Value1, Size, pcmpgt, < );                \
    TestPcmpXmmAddr(Dst, Value0, Value1, Size, pcmpgt, < );                    \
  } while (0)

#define TestPcmpSize(Dst, Src, Size)                                           \
  do {                                                                         \
    TestPcmpValues(Dst, (uint64_t(0x8888888888888888ull),                      \
                         uint64_t(0x0000000000000000ull)),                     \
                   Src, (uint64_t(0x0000008800008800ull),                      \
                         uint64_t(0xFFFFFFFFFFFFFFFFull)),                     \
                   Size);                                                      \
    TestPcmpValues(Dst, (uint64_t(0x123567ABAB55DE01ull),                      \
                         uint64_t(0x12345abcde12345Aull)),                     \
                   Src, (uint64_t(0x0000008800008800ull),                      \
                         uint64_t(0xAABBCCDD1234321Aull)),                     \
                   Size);                                                      \
  } while (0)

#define TestPcmp(Dst, Src)                                                     \
  do {                                                                         \
    TestPcmpSize(xmm0, xmm1, 8);                                               \
    TestPcmpSize(xmm0, xmm1, 16);                                              \
    TestPcmpSize(xmm0, xmm1, 32);                                              \
  } while (0)

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

#undef TestPcmp
#undef TestPcmpSize
#undef TestPcmpValues
#undef TestPcmpXmmAddr
#undef TestPcmpXmmXmm
}

TEST_F(AssemblerX8664Test, Roundsd) {
#define TestRoundsdXmmXmm(Dst, Src, Mode, Input, RN)                           \
  do {                                                                         \
    static constexpr char TestString[] =                                       \
        "(" #Dst ", " #Src ", " #Mode ", " #Input ", " #RN ")";                \
    const uint32_t T0 = allocateDqword();                                      \
    const Dqword V0(-3.0, -3.0);                                               \
    const uint32_t T1 = allocateDqword();                                      \
    const Dqword V1(double(Input), -123.4);                                    \
                                                                               \
    __ movups(Encoded_Xmm_##Dst(), dwordAddress(T0));                          \
    __ movups(Encoded_Xmm_##Src(), dwordAddress(T1));                          \
    __ round(IceType_f64, Encoded_Xmm_##Dst(), Encoded_Xmm_##Src(),            \
             Immediate(AssemblerX8664::k##Mode));                              \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.setDqwordTo(T0, V0);                                                  \
    test.setDqwordTo(T1, V1);                                                  \
    test.run();                                                                \
                                                                               \
    const Dqword Expected(double(RN), -3.0);                                   \
    EXPECT_EQ(Expected, test.Dst<Dqword>()) << TestString;                     \
    reset();                                                                   \
  } while (0)

#define TestRoundsd(Dst, Src)                                                  \
  do {                                                                         \
    TestRoundsdXmmXmm(Dst, Src, RoundToNearest, 5.51, 6);                      \
    TestRoundsdXmmXmm(Dst, Src, RoundToNearest, 5.49, 5);                      \
    TestRoundsdXmmXmm(Dst, Src, RoundDown, 5.51, 5);                           \
    TestRoundsdXmmXmm(Dst, Src, RoundUp, 5.49, 6);                             \
    TestRoundsdXmmXmm(Dst, Src, RoundToZero, 5.49, 5);                         \
    TestRoundsdXmmXmm(Dst, Src, RoundToZero, 5.51, 5);                         \
  } while (0)

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

#undef TestRoundsd
#undef TestRoundsdXmmXmm
}

TEST_F(AssemblerX8664Test, Set1ps) {
#define TestImpl(Xmm, Src, Imm)                                                \
  do {                                                                         \
    __ set1ps(Encoded_Xmm_##Xmm(), Encoded_GPR_##Src(), Immediate(Imm));       \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.run();                                                                \
                                                                               \
    const Dqword Expected((uint64_t(Imm) << 32) | uint32_t(Imm),               \
                          (uint64_t(Imm) << 32) | uint32_t(Imm));              \
    ASSERT_EQ(Expected, test.Xmm<Dqword>())                                    \
        << "(" #Xmm ", " #Src ", " #Imm ")";                                   \
    reset();                                                                   \
  } while (0)

  TestImpl(xmm0, r1, 1);
  TestImpl(xmm1, r2, 12);
  TestImpl(xmm2, r3, 22);
  TestImpl(xmm3, r4, 54);
  TestImpl(xmm4, r5, 80);
  TestImpl(xmm5, r6, 32);
  TestImpl(xmm6, r7, 55);
  TestImpl(xmm7, r8, 44);
  TestImpl(xmm8, r10, 10);
  TestImpl(xmm9, r11, 155);
  TestImpl(xmm10, r12, 165);
  TestImpl(xmm11, r13, 170);
  TestImpl(xmm12, r14, 200);
  TestImpl(xmm13, r15, 124);
  TestImpl(xmm14, r1, 101);
  TestImpl(xmm15, r2, 166);

#undef TestImpl
}

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