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

namespace Ice {
namespace X8632 {
namespace Test {
namespace {

TEST_F(AssemblerX8632LowLevelTest, Fld) {
  __ fld(IceType_f32,
         Address(GPRRegister::Encoded_Reg_ebp, 1, AssemblerFixup::NoFixup));
  __ fld(IceType_f64, Address(GPRRegister::Encoded_Reg_ebp, 0x10000,
                              AssemblerFixup::NoFixup));

  constexpr size_t ByteCount = 9;
  ASSERT_EQ(ByteCount, codeBytesSize());

  constexpr uint8_t Fld32Opcode = 0xd9;
  constexpr uint8_t Fld32ModRM = (/*mod*/ 1 << 6) | (/*reg*/ 0 << 3) |
                                 (/*rm*/ GPRRegister::Encoded_Reg_ebp);
  constexpr uint8_t Fld64Opcode = 0xdd;
  constexpr uint8_t Fld64ModRM = (/*mod*/ 2 << 6) | (/*reg*/ 0 << 3) |
                                 (/*rm*/ GPRRegister::Encoded_Reg_ebp);
  verifyBytes<ByteCount>(codeBytes(), Fld32Opcode, Fld32ModRM, 0x01,
                         Fld64Opcode, Fld64ModRM, 0x00, 0x00, 0x01, 0x00);
}

TEST_F(AssemblerX8632LowLevelTest, FstpAddr) {
  __ fstp(IceType_f32,
          Address(GPRRegister::Encoded_Reg_ebp, 1, AssemblerFixup::NoFixup));
  __ fstp(IceType_f64, Address(GPRRegister::Encoded_Reg_ebp, 0x10000,
                               AssemblerFixup::NoFixup));

  constexpr size_t ByteCount = 9;
  ASSERT_EQ(ByteCount, codeBytesSize());

  constexpr uint8_t Fld32Opcode = 0xd9;
  constexpr uint8_t Fld32ModRM = (/*mod*/ 1 << 6) | (/*reg*/ 3 << 3) |
                                 (/*rm*/ GPRRegister::Encoded_Reg_ebp);
  constexpr uint8_t Fld64Opcode = 0xdd;
  constexpr uint8_t Fld64ModRM = (/*mod*/ 2 << 6) | (/*reg*/ 3 << 3) |
                                 (/*rm*/ GPRRegister::Encoded_Reg_ebp);
  verifyBytes<ByteCount>(codeBytes(), Fld32Opcode, Fld32ModRM, 0x01,
                         Fld64Opcode, Fld64ModRM, 0x00, 0x00, 0x01, 0x00);
}

TEST_F(AssemblerX8632LowLevelTest, Fincstp) {
  __ fincstp();

  constexpr size_t ByteCount = 2;
  ASSERT_EQ(ByteCount, codeBytesSize());

  verifyBytes<ByteCount>(codeBytes(), 0xD9, 0XF7);
}

TEST_F(AssemblerX8632LowLevelTest, FnstcwAddr) {
  __ fnstcw(
      Address(GPRRegister::Encoded_Reg_ebp, 0x12345, AssemblerFixup::NoFixup));

  constexpr size_t ByteCount = 6;
  ASSERT_EQ(ByteCount, codeBytesSize());

  constexpr uint8_t Opcode = 0xd9;
  constexpr uint8_t ModRM = (/*mod*/ 2 << 6) | (/*reg*/ 7 << 3) |
                            (/*rm*/ GPRRegister::Encoded_Reg_ebp);
  verifyBytes<ByteCount>(codeBytes(), Opcode, ModRM, 0x45, 0x23, 0x01, 0x00);
}

TEST_F(AssemblerX8632LowLevelTest, FldcwAddr) {
  __ fldcw(
      Address(GPRRegister::Encoded_Reg_ebp, 0x12345, AssemblerFixup::NoFixup));

  constexpr size_t ByteCount = 6;
  ASSERT_EQ(ByteCount, codeBytesSize());

  constexpr uint8_t Opcode = 0xd9;
  constexpr uint8_t ModRM = (/*mod*/ 2 << 6) | (/*reg*/ 5 << 3) |
                            (/*rm*/ GPRRegister::Encoded_Reg_ebp);
  verifyBytes<ByteCount>(codeBytes(), Opcode, ModRM, 0x45, 0x23, 0x01, 0x00);
}

TEST_F(AssemblerX8632Test, FstpSt) {
#define TestFstpSt(Size, MemorySize, Type)                                     \
  do {                                                                         \
    const uint32_t T1 = allocate##MemorySize();                                \
    const Type OldValue1 = -1.0f;                                              \
    const uint32_t T2 = allocate##MemorySize();                                \
    const Type OldValue2 = -2.0f;                                              \
    const uint32_t T3 = allocate##MemorySize();                                \
    const Type OldValue3 = -3.0f;                                              \
    const uint32_t T4 = allocate##MemorySize();                                \
    const Type OldValue4 = -4.0f;                                              \
    const uint32_t T5 = allocate##MemorySize();                                \
    const Type OldValue5 = -5.0f;                                              \
    const uint32_t T6 = allocate##MemorySize();                                \
    const Type OldValue6 = -6.0f;                                              \
    const uint32_t T7 = allocate##MemorySize();                                \
    const Type OldValue7 = -7.0f;                                              \
                                                                               \
    const uint32_t N7 = allocate##MemorySize();                                \
    constexpr Type NewValue7 = 777.77f;                                        \
    const uint32_t N6 = allocate##MemorySize();                                \
    constexpr Type NewValue6 = 666.66f;                                        \
    const uint32_t N5 = allocate##MemorySize();                                \
    constexpr Type NewValue5 = 555.55f;                                        \
    const uint32_t N4 = allocate##MemorySize();                                \
    constexpr Type NewValue4 = 444.44f;                                        \
    const uint32_t N3 = allocate##MemorySize();                                \
    constexpr Type NewValue3 = 333.33f;                                        \
    const uint32_t N2 = allocate##MemorySize();                                \
    constexpr Type NewValue2 = 222.22f;                                        \
    const uint32_t N1 = allocate##MemorySize();                                \
    constexpr Type NewValue1 = 111.11f;                                        \
                                                                               \
    __ fincstp();                                                              \
    __ fincstp();                                                              \
    __ fincstp();                                                              \
    __ fincstp();                                                              \
    __ fincstp();                                                              \
    __ fincstp();                                                              \
    __ fincstp();                                                              \
                                                                               \
    __ fld(IceType_f##Size, dwordAddress(N7));                                 \
    __ fstp(X87STRegister::Encoded_X87ST_7);                                   \
    __ fld(IceType_f##Size, dwordAddress(N6));                                 \
    __ fstp(X87STRegister::Encoded_X87ST_6);                                   \
    __ fld(IceType_f##Size, dwordAddress(N5));                                 \
    __ fstp(X87STRegister::Encoded_X87ST_5);                                   \
    __ fld(IceType_f##Size, dwordAddress(N4));                                 \
    __ fstp(X87STRegister::Encoded_X87ST_4);                                   \
    __ fld(IceType_f##Size, dwordAddress(N3));                                 \
    __ fstp(X87STRegister::Encoded_X87ST_3);                                   \
    __ fld(IceType_f##Size, dwordAddress(N2));                                 \
    __ fstp(X87STRegister::Encoded_X87ST_2);                                   \
    __ fld(IceType_f##Size, dwordAddress(N1));                                 \
    __ fstp(X87STRegister::Encoded_X87ST_1);                                   \
                                                                               \
    __ fstp(IceType_f##Size, dwordAddress(T1));                                \
    __ fstp(IceType_f##Size, dwordAddress(T2));                                \
    __ fstp(IceType_f##Size, dwordAddress(T3));                                \
    __ fstp(IceType_f##Size, dwordAddress(T4));                                \
    __ fstp(IceType_f##Size, dwordAddress(T5));                                \
    __ fstp(IceType_f##Size, dwordAddress(T6));                                \
    __ fstp(IceType_f##Size, dwordAddress(T7));                                \
                                                                               \
    AssembledTest test = assemble();                                           \
    test.set##MemorySize##To(T1, OldValue1);                                   \
    test.set##MemorySize##To(N1, NewValue1);                                   \
    test.set##MemorySize##To(T2, OldValue2);                                   \
    test.set##MemorySize##To(N2, NewValue2);                                   \
    test.set##MemorySize##To(T3, OldValue3);                                   \
    test.set##MemorySize##To(N3, NewValue3);                                   \
    test.set##MemorySize##To(T4, OldValue4);                                   \
    test.set##MemorySize##To(N4, NewValue4);                                   \
    test.set##MemorySize##To(T5, OldValue5);                                   \
    test.set##MemorySize##To(N5, NewValue5);                                   \
    test.set##MemorySize##To(T6, OldValue6);                                   \
    test.set##MemorySize##To(N6, NewValue6);                                   \
    test.set##MemorySize##To(T7, OldValue7);                                   \
    test.set##MemorySize##To(N7, NewValue7);                                   \
                                                                               \
    test.run();                                                                \
                                                                               \
    ASSERT_FLOAT_EQ(NewValue1, test.contentsOf##MemorySize<Type>(T1))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue1, test.contentsOf##MemorySize<Type>(N1))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue2, test.contentsOf##MemorySize<Type>(T2))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue2, test.contentsOf##MemorySize<Type>(N2))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue3, test.contentsOf##MemorySize<Type>(T3))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue3, test.contentsOf##MemorySize<Type>(N3))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue4, test.contentsOf##MemorySize<Type>(T4))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue4, test.contentsOf##MemorySize<Type>(N4))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue5, test.contentsOf##MemorySize<Type>(T5))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue5, test.contentsOf##MemorySize<Type>(N5))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue6, test.contentsOf##MemorySize<Type>(T6))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue6, test.contentsOf##MemorySize<Type>(N6))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue7, test.contentsOf##MemorySize<Type>(T7))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
    ASSERT_FLOAT_EQ(NewValue7, test.contentsOf##MemorySize<Type>(N7))          \
        << "(" #Size ", " #MemorySize ", " #Type ")";                          \
                                                                               \
    reset();                                                                   \
  } while (0)

  TestFstpSt(32, Dword, float);
  TestFstpSt(64, Qword, double);

#undef TestFstpSt
}

TEST_F(AssemblerX8632Test, Fild) {
#define TestFild(OperandType, Size, MemorySize, FpType, IntType)               \
  do {                                                                         \
    const uint32_t T0 = allocate##MemorySize();                                \
    constexpr IntType V0 = 0x1234;                                             \
                                                                               \
    __ fild##OperandType(dwordAddress(T0));                                    \
    __ fstp(IceType_f##Size, dwordAddress(T0));                                \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.set##MemorySize##To(T0, V0);                                          \
    test.run();                                                                \
                                                                               \
    ASSERT_FLOAT_EQ(static_cast<FpType>(V0),                                   \
                    test.contentsOf##MemorySize<FpType>(T0))                   \
        << "(" #OperandType ", " #Size ", " #MemorySize ", " #FpType           \
           ", " #IntType ")";                                                  \
                                                                               \
    reset();                                                                   \
  } while (0)

  TestFild(s, 32, Dword, float, uint32_t);
  TestFild(l, 64, Qword, double, uint64_t);
#undef TestFild
}

TEST_F(AssemblerX8632Test, Fistp) {
#define TestFistp(OperandType, Size, MemorySize, FpType, IntType)              \
  do {                                                                         \
    const uint32_t T0 = allocate##MemorySize();                                \
    constexpr IntType V0 = 0x1234;                                             \
    const uint32_t T1 = allocate##MemorySize();                                \
    constexpr IntType V1 = 0xFFFF;                                             \
                                                                               \
    __ fild##OperandType(dwordAddress(T0));                                    \
    __ fistp##OperandType(dwordAddress(T1));                                   \
                                                                               \
    AssembledTest test = assemble();                                           \
                                                                               \
    test.set##MemorySize##To(T0, V0);                                          \
    test.set##MemorySize##To(T1, V1);                                          \
    test.run();                                                                \
                                                                               \
    ASSERT_EQ(static_cast<IntType>(V0),                                        \
              test.contentsOf##MemorySize<IntType>(T0))                        \
        << "(" #OperandType ", " #Size ", " #MemorySize ", " #FpType           \
           ", " #IntType ")";                                                  \
    ASSERT_EQ(static_cast<IntType>(V0),                                        \
              test.contentsOf##MemorySize<IntType>(T1))                        \
        << "(" #OperandType ", " #Size ", " #MemorySize ", " #FpType           \
           ", " #IntType ")";                                                  \
                                                                               \
    reset();                                                                   \
  } while (0)

  TestFistp(s, 32, Dword, float, uint32_t);
  TestFistp(l, 64, Qword, double, uint64_t);
#undef TestFistp
}

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