Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(370)

Unified Diff: unittest/IceAssemblerX8632Test.cpp

Issue 1224173006: Adds the x86-64 assembler. (Closed) Base URL: https://chromium.googlesource.com/native_client/pnacl-subzero.git@master
Patch Set: Introduces the X8664 assembler and tests. No REX support yet. Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: unittest/IceAssemblerX8632Test.cpp
diff --git a/unittest/IceAssemblerX8632Test.cpp b/unittest/IceAssemblerX8632Test.cpp
index 3e3d7f3da24101986dab67a426fe6448bec32b7a..1184c4e48bf31269e05a0cf71c0faab181daadc1 100644
--- a/unittest/IceAssemblerX8632Test.cpp
+++ b/unittest/IceAssemblerX8632Test.cpp
@@ -13,9 +13,11 @@
#include "gtest/gtest.h"
+#include <algorithm>
#include <cstring>
#include <errno.h>
#include <iostream>
+#include <limits>
#include <memory>
#include <sys/mman.h>
#include <type_traits>
@@ -23,12 +25,13 @@
namespace Ice {
namespace X8632 {
namespace {
-
class AssemblerX8632TestBase : public ::testing::Test {
protected:
using Address = AssemblerX8632::Traits::Address;
+ using ByteRegister = AssemblerX8632::Traits::ByteRegister;
using Cond = AssemblerX8632::Traits::Cond;
using GPRRegister = AssemblerX8632::Traits::GPRRegister;
+ using Traits = AssemblerX8632::Traits;
using XmmRegister = AssemblerX8632::Traits::XmmRegister;
using X87STRegister = AssemblerX8632::Traits::X87STRegister;
@@ -66,17 +69,17 @@ protected:
// verifyBytes is a template helper that takes a Buffer, and a variable number
// of bytes. As the name indicates, it is used to verify the bytes for an
// instruction encoding.
- template <int N, int I> static void verifyBytes(const uint8_t *) {
+ template <int N, int I> static bool verifyBytes(const uint8_t *) {
static_assert(I == N, "Invalid template instantiation.");
+ return true;
}
template <int N, int I = 0, typename... Args>
- static void verifyBytes(const uint8_t *Buffer, uint8_t Byte,
+ static bool verifyBytes(const uint8_t *Buffer, uint8_t Byte,
Args... OtherBytes) {
static_assert(I < N, "Invalid template instantiation.");
EXPECT_EQ(Byte, Buffer[I]) << "Byte " << (I + 1) << " of " << N;
- verifyBytes<N, I + 1>(Buffer, OtherBytes...);
- assert(Buffer[I] == Byte);
+ return verifyBytes<N, I + 1>(Buffer, OtherBytes...) && Buffer[I] == Byte;
}
};
@@ -89,6 +92,15 @@ TEST_F(AssemblerX8632LowLevelTest, Ret) {
verifyBytes<ByteCount>(codeBytes(), 0xc3);
jvoung (off chromium) 2015/07/23 17:59:36 Now that verifyBytes returns a bool, does this nee
John 2015/07/27 20:35:58 See the comment below.
}
+TEST_F(AssemblerX8632LowLevelTest, RetImm) {
+ __ ret(Immediate(0x20));
+
+ constexpr size_t ByteCount = 3;
+ ASSERT_EQ(ByteCount, codeBytesSize());
+
+ verifyBytes<ByteCount>(codeBytes(), 0xC2, 0x20, 0x00);
+}
+
TEST_F(AssemblerX8632LowLevelTest, CallImm4) {
__ call(Immediate(4));
@@ -164,39 +176,650 @@ TEST_F(AssemblerX8632LowLevelTest, MovRegisterZero) {
MovOpcode | GPRRegister::Encoded_Reg_esi, 0x00, 0x00, 0x00, 0x00);
}
-TEST_F(AssemblerX8632LowLevelTest, CmpRegReg) {
- __ cmp(IceType_i32, GPRRegister::Encoded_Reg_eax,
- GPRRegister::Encoded_Reg_ebx);
- __ cmp(IceType_i32, GPRRegister::Encoded_Reg_ebx,
- GPRRegister::Encoded_Reg_ecx);
- __ cmp(IceType_i32, GPRRegister::Encoded_Reg_ecx,
- GPRRegister::Encoded_Reg_edx);
- __ cmp(IceType_i32, GPRRegister::Encoded_Reg_edx,
- GPRRegister::Encoded_Reg_edi);
- __ cmp(IceType_i32, GPRRegister::Encoded_Reg_edi,
- GPRRegister::Encoded_Reg_esi);
- __ cmp(IceType_i32, GPRRegister::Encoded_Reg_esi,
- GPRRegister::Encoded_Reg_eax);
+TEST_F(AssemblerX8632LowLevelTest, Cmp) {
+#define TestRegReg(Inst, Dst, Src, OpType, ByteCountUntyped, ...) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Src ", " #OpType ", " #ByteCountUntyped \
+ ", " #__VA_ARGS__ ")"; \
+ static constexpr uint8_t ByteCount = ByteCountUntyped; \
+ __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src); \
+ ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \
+ ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestRegImm(Inst, Dst, Imm, OpType, ByteCountUntyped, ...) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Imm ", " #OpType ", " #ByteCountUntyped \
+ ", " #__VA_ARGS__ ")"; \
+ static constexpr uint8_t ByteCount = ByteCountUntyped; \
+ __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, Immediate(Imm)); \
+ ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \
+ ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestRegAbsoluteAddr(Inst, Dst, Disp, OpType, ByteCountUntyped, ...) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Disp ", " #OpType ", " #ByteCountUntyped \
+ ", " #__VA_ARGS__ ")"; \
+ static constexpr uint8_t ByteCount = ByteCountUntyped; \
+ __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, \
+ Address(Address::ABSOLUTE, Disp)); \
+ ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \
+ ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestRegAddrBase(Inst, Dst, Base, Disp, OpType, ByteCountUntyped, ...) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Base ", " #Disp ", " #OpType \
+ ", " #ByteCountUntyped ", " #__VA_ARGS__ ")"; \
+ static constexpr uint8_t ByteCount = ByteCountUntyped; \
+ __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, \
+ Address(GPRRegister::Encoded_Reg_##Base, Disp)); \
+ ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \
+ ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestRegAddrScaledIndex(Inst, Dst, Index, Scale, Disp, OpType, \
+ ByteCountUntyped, ...) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Index ", " #Scale ", " #Disp ", " #OpType \
+ ", " #ByteCountUntyped ", " #__VA_ARGS__ ")"; \
+ static constexpr uint8_t ByteCount = ByteCountUntyped; \
+ __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, \
+ Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_##Scale, \
+ Disp)); \
+ ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \
+ ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestRegAddrBaseScaledIndex(Inst, Dst, Base, Index, Scale, Disp, \
+ OpType, ByteCountUntyped, ...) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Base ", " #Index ", " #Scale ", " #Disp \
+ ", " #OpType ", " #ByteCountUntyped ", " #__VA_ARGS__ ")"; \
+ static constexpr uint8_t ByteCount = ByteCountUntyped; \
+ __ Inst(IceType_##OpType, GPRRegister::Encoded_Reg_##Dst, \
+ Address(GPRRegister::Encoded_Reg_##Base, \
+ GPRRegister::Encoded_Reg_##Index, Traits::TIMES_##Scale, \
+ Disp)); \
+ ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \
+ ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestAddrBaseScaledIndexImm(Inst, Base, Index, Scale, Disp, Imm, \
+ OpType, ByteCountUntyped, ...) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Base ", " #Index ", " #Scale ", " #Disp ", " #Imm \
+ ", " #OpType ", " #ByteCountUntyped ", " #__VA_ARGS__ ")"; \
+ static constexpr uint8_t ByteCount = ByteCountUntyped; \
+ __ Inst(IceType_##OpType, Address(GPRRegister::Encoded_Reg_##Base, \
+ GPRRegister::Encoded_Reg_##Index, \
+ Traits::TIMES_##Scale, Disp), \
+ Immediate(Imm)); \
+ ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \
+ ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestAddrBaseScaledIndexReg(Inst, Base, Index, Scale, Disp, Src, \
+ OpType, ByteCountUntyped, ...) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Base ", " #Index ", " #Scale ", " #Disp ", " #Src \
+ ", " #OpType ", " #ByteCountUntyped ", " #__VA_ARGS__ ")"; \
+ static constexpr uint8_t ByteCount = ByteCountUntyped; \
+ __ Inst(IceType_##OpType, Address(GPRRegister::Encoded_Reg_##Base, \
+ GPRRegister::Encoded_Reg_##Index, \
+ Traits::TIMES_##Scale, Disp), \
+ GPRRegister::Encoded_Reg_##Src); \
+ ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \
+ ASSERT_TRUE(verifyBytes<ByteCount>(codeBytes(), __VA_ARGS__)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+ /* cmp GPR, GPR */
+ TestRegReg(cmp, eax, ecx, i32, 2, 0x3B, 0xC1);
+ TestRegReg(cmp, ecx, edx, i32, 2, 0x3B, 0xCA);
+ TestRegReg(cmp, edx, ebx, i32, 2, 0x3B, 0xD3);
+ TestRegReg(cmp, ebx, esp, i32, 2, 0x3B, 0xDC);
+ TestRegReg(cmp, esp, ebp, i32, 2, 0x3B, 0xE5);
+ TestRegReg(cmp, ebp, esi, i32, 2, 0x3B, 0xEE);
+ TestRegReg(cmp, esi, edi, i32, 2, 0x3B, 0xF7);
+ TestRegReg(cmp, edi, eax, i32, 2, 0x3B, 0xF8);
+
+ TestRegReg(cmp, eax, ecx, i16, 3, 0x66, 0x3B, 0xC1);
+ TestRegReg(cmp, ecx, edx, i16, 3, 0x66, 0x3B, 0xCA);
+ TestRegReg(cmp, edx, ebx, i16, 3, 0x66, 0x3B, 0xD3);
+ TestRegReg(cmp, ebx, esp, i16, 3, 0x66, 0x3B, 0xDC);
+ TestRegReg(cmp, esp, ebp, i16, 3, 0x66, 0x3B, 0xE5);
+ TestRegReg(cmp, ebp, esi, i16, 3, 0x66, 0x3B, 0xEE);
+ TestRegReg(cmp, esi, edi, i16, 3, 0x66, 0x3B, 0xF7);
+ TestRegReg(cmp, edi, eax, i16, 3, 0x66, 0x3B, 0xF8);
+
+ TestRegReg(cmp, eax, ecx, i8, 2, 0x3A, 0xC1);
+ TestRegReg(cmp, ecx, edx, i8, 2, 0x3A, 0xCA);
+ TestRegReg(cmp, edx, ebx, i8, 2, 0x3A, 0xD3);
+ TestRegReg(cmp, ebx, esp, i8, 2, 0x3A, 0xDC);
+ TestRegReg(cmp, esp, ebp, i8, 2, 0x3A, 0xE5);
+ TestRegReg(cmp, ebp, esi, i8, 2, 0x3A, 0xEE);
+ TestRegReg(cmp, esi, edi, i8, 2, 0x3A, 0xF7);
+ TestRegReg(cmp, edi, eax, i8, 2, 0x3A, 0xF8);
+
+ /* cmp GPR, Imm8 */
+ TestRegImm(cmp, eax, 5, i32, 3, 0x83, 0xF8, 0x05);
+ TestRegImm(cmp, ecx, 5, i32, 3, 0x83, 0xF9, 0x05);
+ TestRegImm(cmp, edx, 5, i32, 3, 0x83, 0xFA, 0x05);
+ TestRegImm(cmp, ebx, 5, i32, 3, 0x83, 0xFB, 0x05);
+ TestRegImm(cmp, esp, 5, i32, 3, 0x83, 0xFC, 0x05);
+ TestRegImm(cmp, ebp, 5, i32, 3, 0x83, 0xFD, 0x05);
+ TestRegImm(cmp, esi, 5, i32, 3, 0x83, 0xFE, 0x05);
+ TestRegImm(cmp, edi, 5, i32, 3, 0x83, 0xFF, 0x05);
+
+ TestRegImm(cmp, eax, 5, i16, 4, 0x66, 0x83, 0xF8, 0x05);
+ TestRegImm(cmp, ecx, 5, i16, 4, 0x66, 0x83, 0xF9, 0x05);
+ TestRegImm(cmp, edx, 5, i16, 4, 0x66, 0x83, 0xFA, 0x05);
+ TestRegImm(cmp, ebx, 5, i16, 4, 0x66, 0x83, 0xFB, 0x05);
+ TestRegImm(cmp, esp, 5, i16, 4, 0x66, 0x83, 0xFC, 0x05);
+ TestRegImm(cmp, ebp, 5, i16, 4, 0x66, 0x83, 0xFD, 0x05);
+ TestRegImm(cmp, esi, 5, i16, 4, 0x66, 0x83, 0xFE, 0x05);
+ TestRegImm(cmp, edi, 5, i16, 4, 0x66, 0x83, 0xFF, 0x05);
+
+ TestRegImm(cmp, eax, 5, i8, 2, 0x3C, 0x05);
+ TestRegImm(cmp, ecx, 5, i8, 3, 0x80, 0xF9, 0x05);
+ TestRegImm(cmp, edx, 5, i8, 3, 0x80, 0xFA, 0x05);
+ TestRegImm(cmp, ebx, 5, i8, 3, 0x80, 0xFB, 0x05);
+ TestRegImm(cmp, esp, 5, i8, 3, 0x80, 0xFC, 0x05);
+ TestRegImm(cmp, ebp, 5, i8, 3, 0x80, 0xFD, 0x05);
+ TestRegImm(cmp, esi, 5, i8, 3, 0x80, 0xFE, 0x05);
+ TestRegImm(cmp, edi, 5, i8, 3, 0x80, 0xFF, 0x05);
+
+ /* cmp GPR, Imm16 */
+ TestRegImm(cmp, eax, 0x100, i32, 5, 0x3D, 0x00, 0x01, 0x00, 0x00);
+ TestRegImm(cmp, ecx, 0x100, i32, 6, 0x81, 0xF9, 0x00, 0x01, 0x00, 0x00);
+ TestRegImm(cmp, edx, 0x100, i32, 6, 0x81, 0xFA, 0x00, 0x01, 0x00, 0x00);
+ TestRegImm(cmp, ebx, 0x100, i32, 6, 0x81, 0xFB, 0x00, 0x01, 0x00, 0x00);
+ TestRegImm(cmp, esp, 0x100, i32, 6, 0x81, 0xFC, 0x00, 0x01, 0x00, 0x00);
+ TestRegImm(cmp, ebp, 0x100, i32, 6, 0x81, 0xFD, 0x00, 0x01, 0x00, 0x00);
+ TestRegImm(cmp, esi, 0x100, i32, 6, 0x81, 0xFE, 0x00, 0x01, 0x00, 0x00);
+ TestRegImm(cmp, edi, 0x100, i32, 6, 0x81, 0xFF, 0x00, 0x01, 0x00, 0x00);
+
+ TestRegImm(cmp, eax, 0x100, i16, 4, 0x66, 0x3D, 0x00, 0x01);
+ TestRegImm(cmp, ecx, 0x100, i16, 5, 0x66, 0x81, 0xF9, 0x00, 0x01);
+ TestRegImm(cmp, edx, 0x100, i16, 5, 0x66, 0x81, 0xFA, 0x00, 0x01);
+ TestRegImm(cmp, ebx, 0x100, i16, 5, 0x66, 0x81, 0xFB, 0x00, 0x01);
+ TestRegImm(cmp, esp, 0x100, i16, 5, 0x66, 0x81, 0xFC, 0x00, 0x01);
+ TestRegImm(cmp, ebp, 0x100, i16, 5, 0x66, 0x81, 0xFD, 0x00, 0x01);
+ TestRegImm(cmp, esi, 0x100, i16, 5, 0x66, 0x81, 0xFE, 0x00, 0x01);
+ TestRegImm(cmp, edi, 0x100, i16, 5, 0x66, 0x81, 0xFF, 0x00, 0x01);
+
+ /* cmp GPR, Absolute */
+ TestRegAbsoluteAddr(cmp, eax, 0xF00FBEEF, i32, 6, 0x3B, 0x05, 0xEF, 0xBE,
+ 0x0F, 0xF0);
+ TestRegAbsoluteAddr(cmp, eax, 0xF00FBEEF, i16, 7, 0x66, 0x3B, 0x05, 0xEF,
+ 0xBE, 0x0F, 0xF0);
+ TestRegAbsoluteAddr(cmp, eax, 0xF00FBEEF, i8, 6, 0x3A, 0x05, 0xEF, 0xBE, 0x0F,
+ 0xF0);
+
+ /* cmp GPR, 0(Base) */
+ TestRegAddrBase(cmp, eax, ecx, 0, i32, 2, 0x3B, 0x01);
+ TestRegAddrBase(cmp, ecx, edx, 0, i32, 2, 0x3B, 0x0A);
+ TestRegAddrBase(cmp, edx, ebx, 0, i32, 2, 0x3B, 0x13);
+ TestRegAddrBase(cmp, ebx, esp, 0, i32, 3, 0x3B, 0x1C, 0x24);
+ TestRegAddrBase(cmp, esp, ebp, 0, i32, 3, 0x3B, 0x65, 0x00);
+ TestRegAddrBase(cmp, ebp, esi, 0, i32, 2, 0x3B, 0x2E);
+ TestRegAddrBase(cmp, esi, edi, 0, i32, 2, 0x3B, 0x37);
+ TestRegAddrBase(cmp, edi, eax, 0, i32, 2, 0x3B, 0x38);
+
+ TestRegAddrBase(cmp, eax, ecx, 0, i16, 3, 0x66, 0x3B, 0x01);
+ TestRegAddrBase(cmp, ecx, edx, 0, i16, 3, 0x66, 0x3B, 0x0A);
+ TestRegAddrBase(cmp, edx, ebx, 0, i16, 3, 0x66, 0x3B, 0x13);
+ TestRegAddrBase(cmp, ebx, esp, 0, i16, 4, 0x66, 0x3B, 0x1C, 0x24);
+ TestRegAddrBase(cmp, esp, ebp, 0, i16, 4, 0x66, 0x3B, 0x65, 0x00);
+ TestRegAddrBase(cmp, ebp, esi, 0, i16, 3, 0x66, 0x3B, 0x2E);
+ TestRegAddrBase(cmp, esi, edi, 0, i16, 3, 0x66, 0x3B, 0x37);
+ TestRegAddrBase(cmp, edi, eax, 0, i16, 3, 0x66, 0x3B, 0x38);
+
+ TestRegAddrBase(cmp, eax, ecx, 0, i8, 2, 0x3A, 0x01);
+ TestRegAddrBase(cmp, ecx, edx, 0, i8, 2, 0x3A, 0x0A);
+ TestRegAddrBase(cmp, edx, ebx, 0, i8, 2, 0x3A, 0x13);
+ TestRegAddrBase(cmp, ebx, esp, 0, i8, 3, 0x3A, 0x1C, 0x24);
+ TestRegAddrBase(cmp, esp, ebp, 0, i8, 3, 0x3A, 0x65, 0x00);
+ TestRegAddrBase(cmp, ebp, esi, 0, i8, 2, 0x3A, 0x2E);
+ TestRegAddrBase(cmp, esi, edi, 0, i8, 2, 0x3A, 0x37);
+ TestRegAddrBase(cmp, edi, eax, 0, i8, 2, 0x3A, 0x38);
+
+ /* cmp GPR, Imm8(Base) */
+ TestRegAddrBase(cmp, eax, ecx, 0x40, i32, 3, 0x3B, 0x41, 0x40);
+ TestRegAddrBase(cmp, ecx, edx, 0x40, i32, 3, 0x3B, 0x4A, 0x40);
+ TestRegAddrBase(cmp, edx, ebx, 0x40, i32, 3, 0x3B, 0x53, 0x40);
+ TestRegAddrBase(cmp, ebx, esp, 0x40, i32, 4, 0x3B, 0x5C, 0x24, 0x40);
+ TestRegAddrBase(cmp, esp, ebp, 0x40, i32, 3, 0x3B, 0x65, 0x40);
+ TestRegAddrBase(cmp, ebp, esi, 0x40, i32, 3, 0x3B, 0x6E, 0x40);
+ TestRegAddrBase(cmp, esi, edi, 0x40, i32, 3, 0x3B, 0x77, 0x40);
+ TestRegAddrBase(cmp, edi, eax, 0x40, i32, 3, 0x3B, 0x78, 0x40);
+
+ TestRegAddrBase(cmp, eax, ecx, 0x40, i16, 4, 0x66, 0x3B, 0x41, 0x40);
+ TestRegAddrBase(cmp, ecx, edx, 0x40, i16, 4, 0x66, 0x3B, 0x4A, 0x40);
+ TestRegAddrBase(cmp, edx, ebx, 0x40, i16, 4, 0x66, 0x3B, 0x53, 0x40);
+ TestRegAddrBase(cmp, ebx, esp, 0x40, i16, 5, 0x66, 0x3B, 0x5C, 0x24, 0x40);
+ TestRegAddrBase(cmp, esp, ebp, 0x40, i16, 4, 0x66, 0x3B, 0x65, 0x40);
+ TestRegAddrBase(cmp, ebp, esi, 0x40, i16, 4, 0x66, 0x3B, 0x6E, 0x40);
+ TestRegAddrBase(cmp, esi, edi, 0x40, i16, 4, 0x66, 0x3B, 0x77, 0x40);
+ TestRegAddrBase(cmp, edi, eax, 0x40, i16, 4, 0x66, 0x3B, 0x78, 0x40);
+
+ TestRegAddrBase(cmp, eax, ecx, 0x40, i8, 3, 0x3A, 0x41, 0x40);
+ TestRegAddrBase(cmp, ecx, edx, 0x40, i8, 3, 0x3A, 0x4A, 0x40);
+ TestRegAddrBase(cmp, edx, ebx, 0x40, i8, 3, 0x3A, 0x53, 0x40);
+ TestRegAddrBase(cmp, ebx, esp, 0x40, i8, 4, 0x3A, 0x5C, 0x24, 0x40);
+ TestRegAddrBase(cmp, esp, ebp, 0x40, i8, 3, 0x3A, 0x65, 0x40);
+ TestRegAddrBase(cmp, ebp, esi, 0x40, i8, 3, 0x3A, 0x6E, 0x40);
+ TestRegAddrBase(cmp, esi, edi, 0x40, i8, 3, 0x3A, 0x77, 0x40);
+ TestRegAddrBase(cmp, edi, eax, 0x40, i8, 3, 0x3A, 0x78, 0x40);
+
+ /* cmp GPR, Imm32(Base) */
+ TestRegAddrBase(cmp, eax, ecx, 0xF0, i32, 6, 0x3B, 0x81, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, ecx, edx, 0xF0, i32, 6, 0x3B, 0x8A, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, edx, ebx, 0xF0, i32, 6, 0x3B, 0x93, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, ebx, esp, 0xF0, i32, 7, 0x3B, 0x9C, 0x24, 0xF0, 0x00,
+ 0x00, 0x00);
+ TestRegAddrBase(cmp, esp, ebp, 0xF0, i32, 6, 0x3B, 0xA5, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, ebp, esi, 0xF0, i32, 6, 0x3B, 0xAE, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, esi, edi, 0xF0, i32, 6, 0x3B, 0xB7, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, edi, eax, 0xF0, i32, 6, 0x3B, 0xB8, 0xF0, 0x00, 0x00,
+ 0x00);
+
+ TestRegAddrBase(cmp, eax, ecx, 0xF0, i16, 7, 0x66, 0x3B, 0x81, 0xF0, 0x00,
+ 0x00, 0x00);
+ TestRegAddrBase(cmp, ecx, edx, 0xF0, i16, 7, 0x66, 0x3B, 0x8A, 0xF0, 0x00,
+ 0x00, 0x00);
+ TestRegAddrBase(cmp, edx, ebx, 0xF0, i16, 7, 0x66, 0x3B, 0x93, 0xF0, 0x00,
+ 0x00, 0x00);
+ TestRegAddrBase(cmp, ebx, esp, 0xF0, i16, 8, 0x66, 0x3B, 0x9C, 0x24, 0xF0,
+ 0x00, 0x00, 0x00);
+ TestRegAddrBase(cmp, esp, ebp, 0xF0, i16, 7, 0x66, 0x3B, 0xa5, 0xF0, 0x00,
+ 0x00, 0x00);
+ TestRegAddrBase(cmp, ebp, esi, 0xF0, i16, 7, 0x66, 0x3B, 0xaE, 0xF0, 0x00,
+ 0x00, 0x00);
+ TestRegAddrBase(cmp, esi, edi, 0xF0, i16, 7, 0x66, 0x3B, 0xb7, 0xF0, 0x00,
+ 0x00, 0x00);
+ TestRegAddrBase(cmp, edi, eax, 0xF0, i16, 7, 0x66, 0x3B, 0xb8, 0xF0, 0x00,
+ 0x00, 0x00);
+
+ TestRegAddrBase(cmp, eax, ecx, 0xF0, i8, 6, 0x3A, 0x81, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, ecx, edx, 0xF0, i8, 6, 0x3A, 0x8A, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, edx, ebx, 0xF0, i8, 6, 0x3A, 0x93, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, ebx, esp, 0xF0, i8, 7, 0x3A, 0x9C, 0x24, 0xF0, 0x00,
+ 0x00, 0x00);
+ TestRegAddrBase(cmp, esp, ebp, 0xF0, i8, 6, 0x3A, 0xA5, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, ebp, esi, 0xF0, i8, 6, 0x3A, 0xAE, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, esi, edi, 0xF0, i8, 6, 0x3A, 0xB7, 0xF0, 0x00, 0x00,
+ 0x00);
+ TestRegAddrBase(cmp, edi, eax, 0xF0, i8, 6, 0x3A, 0xB8, 0xF0, 0x00, 0x00,
+ 0x00);
+
+ /* cmp GPR, Imm(,Index,Scale) */
+ TestRegAddrScaledIndex(cmp, eax, ecx, 1, 0, i32, 7, 0x3B, 0x04, 0x0D, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, ecx, edx, 2, 0, i32, 7, 0x3B, 0x0C, 0x55, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, edx, ebx, 4, 0, i32, 7, 0x3B, 0x14, 0x9D, 0x00,
+ 0x00, 0x00, 0x00);
+ // esp cannot be an scaled index.
+ TestRegAddrScaledIndex(cmp, esp, ebp, 8, 0, i32, 7, 0x3B, 0x24, 0xED, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, ebp, esi, 1, 0, i32, 7, 0x3B, 0x2C, 0x35, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, esi, edi, 2, 0, i32, 7, 0x3B, 0x34, 0x7D, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, edi, eax, 4, 0, i32, 7, 0x3B, 0x3C, 0x85, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, ebx, ecx, 8, 0, i32, 7, 0x3B, 0x1C, 0xCD, 0x00,
+ 0x00, 0x00, 0x00);
+
+ TestRegAddrScaledIndex(cmp, eax, ecx, 8, 0, i16, 8, 0x66, 0x3B, 0x04, 0xCD,
+ 0x00, 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, ecx, edx, 1, 0, i16, 8, 0x66, 0x3B, 0x0C, 0x15,
+ 0x00, 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, edx, ebx, 2, 0, i16, 8, 0x66, 0x3B, 0x14, 0x5D,
+ 0x00, 0x00, 0x00, 0x00);
+ // esp cannot be an scaled index.
+ TestRegAddrScaledIndex(cmp, esp, ebp, 4, 0, i16, 8, 0x66, 0x3B, 0x24, 0xAD,
+ 0x00, 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, ebp, esi, 8, 0, i16, 8, 0x66, 0x3B, 0x2C, 0xF5,
+ 0x00, 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, esi, edi, 1, 0, i16, 8, 0x66, 0x3B, 0x34, 0x3D,
+ 0x00, 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, edi, eax, 2, 0, i16, 8, 0x66, 0x3B, 0x3C, 0x45,
+ 0x00, 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, ebx, ecx, 8, 0, i16, 8, 0x66, 0x3B, 0x1C, 0xCD,
+ 0x00, 0x00, 0x00, 0x00);
+
+ TestRegAddrScaledIndex(cmp, eax, ecx, 4, 0, i8, 7, 0x3A, 0x04, 0x8D, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, ecx, edx, 8, 0, i8, 7, 0x3A, 0x0C, 0xD5, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, edx, ebx, 1, 0, i8, 7, 0x3A, 0x14, 0x1D, 0x00,
+ 0x00, 0x00, 0x00);
+ // esp cannot be an scaled index.
+ TestRegAddrScaledIndex(cmp, esp, ebp, 2, 0, i8, 7, 0x3A, 0x24, 0x6D, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, ebp, esi, 4, 0, i8, 7, 0x3A, 0x2C, 0xB5, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, esi, edi, 8, 0, i8, 7, 0x3A, 0x34, 0xFD, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, edi, eax, 1, 0, i8, 7, 0x3A, 0x3C, 0x05, 0x00,
+ 0x00, 0x00, 0x00);
+ TestRegAddrScaledIndex(cmp, ebx, ecx, 8, 0, i8, 7, 0x3a, 0x1C, 0xCD, 0x00,
+ 0x00, 0x00, 0x00);
- const size_t CmpRegRegBytes = 2;
- const size_t ByteCount = 6 * CmpRegRegBytes;
+ /* cmp GPR, 0(Base,Index,Scale) */
+ TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0, i32, 3, 0x3B, 0x04,
+ 0x11);
+ TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0, i32, 3, 0x3B, 0x0C,
+ 0x5A);
+ // esp cannot be an scaled index.
+ TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0, i32, 3, 0x3B, 0x1C,
+ 0xAC);
+ TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0, i32, 4, 0x3B, 0x64, 0xF5,
+ 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0, i32, 3, 0x3B, 0x2C,
+ 0x3E);
+ TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0, i32, 3, 0x3B, 0x34,
+ 0x47);
+ TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0, i32, 3, 0x3B, 0x3C,
+ 0x98);
+ TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0, i32, 3, 0x3B, 0x1C,
+ 0xD1);
+
+ TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0, i16, 4, 0x66, 0x3B, 0x04,
+ 0x11);
+ TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0, i16, 4, 0x66, 0x3B, 0x0C,
+ 0x5A);
+ // esp cannot be an scaled index.
+ TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0, i16, 4, 0x66, 0x3B, 0x1C,
+ 0xAC);
+ TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0, i16, 5, 0x66, 0x3B, 0x64,
+ 0xF5, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0, i16, 4, 0x66, 0x3B, 0x2C,
+ 0x3E);
+ TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0, i16, 4, 0x66, 0x3B, 0x34,
+ 0x47);
+ TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0, i16, 4, 0x66, 0x3B, 0x3C,
+ 0x98);
+ TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0, i16, 4, 0x66, 0x3B, 0x1C,
+ 0xD1);
+
+ TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0, i8, 3, 0x3A, 0x04, 0x11);
+ TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0, i8, 3, 0x3A, 0x0C, 0x5A);
+ // esp cannot be an scaled index.
+ TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0, i8, 3, 0x3A, 0x1C, 0xAC);
+ TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0, i8, 4, 0x3A, 0x64, 0xF5,
+ 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0, i8, 3, 0x3A, 0x2C, 0x3E);
+ TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0, i8, 3, 0x3A, 0x34, 0x47);
+ TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0, i8, 3, 0x3A, 0x3C, 0x98);
+ TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0, i8, 3, 0x3A, 0x1C, 0xD1);
+
+ /* cmp GPR, Imm8(Base,Index,Scale) */
+ TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0x40, i32, 4, 0x3B, 0x44,
+ 0x11, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0x40, i32, 4, 0x3B, 0x4C,
+ 0x5A, 0x40);
+ // esp cannot be an scaled index.
+ TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0x40, i32, 4, 0x3B, 0x5C,
+ 0xAC, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0x40, i32, 4, 0x3B, 0x64,
+ 0xF5, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0x40, i32, 4, 0x3B, 0x6C,
+ 0x3E, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0x40, i32, 4, 0x3B, 0x74,
+ 0x47, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0x40, i32, 4, 0x3B, 0x7C,
+ 0x98, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0x40, i32, 4, 0x3B, 0x5C,
+ 0xD1, 0x40);
+
+ TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0x40, i16, 5, 0x66, 0x3B,
+ 0x44, 0x11, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0x40, i16, 5, 0x66, 0x3B,
+ 0x4C, 0x5A, 0x40);
+ // esp cannot be an scaled index.
+ TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0x40, i16, 5, 0x66, 0x3B,
+ 0x5C, 0xAC, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0x40, i16, 5, 0x66, 0x3B,
+ 0x64, 0xF5, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0x40, i16, 5, 0x66, 0x3B,
+ 0x6C, 0x3E, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0x40, i16, 5, 0x66, 0x3B,
+ 0x74, 0x47, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0x40, i16, 5, 0x66, 0x3B,
+ 0x7C, 0x98, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0x40, i16, 5, 0x66, 0x3B,
+ 0x5C, 0xD1, 0x40);
+
+ TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0x40, i8, 4, 0x3A, 0x44,
+ 0x11, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0x40, i8, 4, 0x3A, 0x4C,
+ 0x5A, 0x40);
+ // esp cannot be an scaled index.
+ TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0x40, i8, 4, 0x3A, 0x5C,
+ 0xAC, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0x40, i8, 4, 0x3A, 0x64,
+ 0xF5, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0x40, i8, 4, 0x3A, 0x6C,
+ 0x3E, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0x40, i8, 4, 0x3A, 0x74,
+ 0x47, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0x40, i8, 4, 0x3A, 0x7C,
+ 0x98, 0x40);
+ TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0x40, i8, 4, 0x3A, 0x5C,
+ 0xD1, 0x40);
+
+ /* cmp GPR, Imm32(Base,Index,Scale) */
+ TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0xF0, i32, 7, 0x3B, 0x84,
+ 0x11, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0xF0, i32, 7, 0x3B, 0x8C,
+ 0x5A, 0xF0, 0x00, 0x00, 0x00);
+ // esp cannot be an scaled index.
+ TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0xF0, i32, 7, 0x3B, 0x9C,
+ 0xAC, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0xF0, i32, 7, 0x3B, 0xA4,
+ 0xF5, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0xF0, i32, 7, 0x3B, 0xAC,
+ 0x3E, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0xF0, i32, 7, 0x3B, 0xB4,
+ 0x47, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0xF0, i32, 7, 0x3B, 0xBC,
+ 0x98, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0xF0, i32, 7, 0x3B, 0x9C,
+ 0xD1, 0xF0, 0x00, 0x00, 0x00);
+
+ TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0xF0, i16, 8, 0x66, 0x3B,
+ 0x84, 0x11, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0xF0, i16, 8, 0x66, 0x3B,
+ 0x8C, 0x5A, 0xF0, 0x00, 0x00, 0x00);
+ // esp cannot be an scaled index.
+ TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0xF0, i16, 8, 0x66, 0x3B,
+ 0x9C, 0xAC, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0xF0, i16, 8, 0x66, 0x3B,
+ 0xA4, 0xF5, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0xF0, i16, 8, 0x66, 0x3B,
+ 0xAC, 0x3E, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0xF0, i16, 8, 0x66, 0x3B,
+ 0xB4, 0x47, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0xF0, i16, 8, 0x66, 0x3B,
+ 0xBC, 0x98, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0xF0, i16, 8, 0x66, 0x3B,
+ 0x9C, 0xD1, 0xF0, 0x00, 0x00, 0x00);
+
+ TestRegAddrBaseScaledIndex(cmp, eax, ecx, edx, 1, 0xF0, i8, 7, 0x3A, 0x84,
+ 0x11, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ecx, edx, ebx, 2, 0xF0, i8, 7, 0x3A, 0x8C,
+ 0x5A, 0xF0, 0x00, 0x00, 0x00);
+ // esp cannot be an scaled index.
+ TestRegAddrBaseScaledIndex(cmp, ebx, esp, ebp, 4, 0xF0, i8, 7, 0x3A, 0x9C,
+ 0xAC, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, esp, ebp, esi, 8, 0xF0, i8, 7, 0x3A, 0xA4,
+ 0xF5, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ebp, esi, edi, 1, 0xF0, i8, 7, 0x3A, 0xAC,
+ 0x3E, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, esi, edi, eax, 2, 0xF0, i8, 7, 0x3A, 0xB4,
+ 0x47, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, edi, eax, ebx, 4, 0xF0, i8, 7, 0x3A, 0xBC,
+ 0x98, 0xF0, 0x00, 0x00, 0x00);
+ TestRegAddrBaseScaledIndex(cmp, ebx, ecx, edx, 8, 0xF0, i8, 7, 0x3A, 0x9C,
+ 0xD1, 0xF0, 0x00, 0x00, 0x00);
+
+ /* cmp Addr, Imm */
+ // Note: at this point we trust the assembler knows how to encode addresses,
+ // so no more exhaustive addressing mode testing.
+ TestAddrBaseScaledIndexImm(cmp, eax, ecx, 1, 0xF0, 0x12, i32, 8, 0x83, 0xBC,
+ 0x08, 0xF0, 0x00, 0x00, 0x00, 0x12);
+ TestAddrBaseScaledIndexImm(cmp, ecx, edx, 1, 0xF0, 0xF0, i32, 11, 0x81, 0xBC,
+ 0x11, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00,
+ 0x00);
+
+ TestAddrBaseScaledIndexImm(cmp, eax, ecx, 1, 0xF0, 0x12, i16, 9, 0x66, 0x83,
+ 0xBC, 0x08, 0xF0, 0x00, 0x00, 0x00, 0x12);
+ TestAddrBaseScaledIndexImm(cmp, ecx, edx, 1, 0xF0, 0xF0, i16, 10, 0x66, 0x81,
+ 0xBC, 0x11, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0x00);
+
+ TestAddrBaseScaledIndexImm(cmp, eax, ecx, 1, 0xF0, 0x12, i8, 8, 0x80, 0xBC,
+ 0x08, 0xF0, 0x00, 0x00, 0x00, 0x12);
+
+ /* cmp Addr, GPR */
+ TestAddrBaseScaledIndexReg(cmp, eax, ecx, 1, 0xF0, edx, i32, 7, 0x39, 0x94,
+ 0x08, 0xF0, 0x00, 0x00, 0x00);
+
+ TestAddrBaseScaledIndexReg(cmp, eax, ecx, 1, 0xF0, edx, i16, 8, 0x66, 0x39,
+ 0x94, 0x08, 0xF0, 0x00, 0x00, 0x00);
+
+ TestAddrBaseScaledIndexReg(cmp, eax, ecx, 1, 0xF0, edx, i8, 7, 0x38, 0x94,
+ 0x08, 0xF0, 0x00, 0x00, 0x00);
+
+#undef TestAddrBaseScaledIndexReg
+#undef TestAddrBaseScaledIndexImm
+#undef TestRegAddrBaseScaledIndex
+#undef TestRegAddrScaledIndex
+#undef TestRegAddrBase
+#undef TestRegAbsoluteAddr
+#undef TestRegImm
+#undef TestRegReg
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Fld) {
+ __ fld(IceType_f32, Address(GPRRegister::Encoded_Reg_ebp, 1));
+ __ fld(IceType_f64, Address(GPRRegister::Encoded_Reg_ebp, 0x10000));
+
+ constexpr size_t ByteCount = 9;
ASSERT_EQ(ByteCount, codeBytesSize());
- constexpr size_t CmpOpcode = 0x3b;
- constexpr size_t ModRm = 0xC0 /* Register Addressing */;
- verifyBytes<ByteCount>(
- codeBytes(), CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_eax << 3) |
- GPRRegister::Encoded_Reg_ebx,
- CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_ebx << 3) |
- GPRRegister::Encoded_Reg_ecx,
- CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_ecx << 3) |
- GPRRegister::Encoded_Reg_edx,
- CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_edx << 3) |
- GPRRegister::Encoded_Reg_edi,
- CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_edi << 3) |
- GPRRegister::Encoded_Reg_esi,
- CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_esi << 3) |
- GPRRegister::Encoded_Reg_eax);
+ 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));
+ __ fstp(IceType_f64, Address(GPRRegister::Encoded_Reg_ebp, 0x10000));
+
+ 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));
+
+ 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));
+
+ 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(AssemblerX8632LowLevelTest, PushalPopal) {
+ // These are invalid in x86-64, so we can't write tests which will execute
+ // these instructions.
+ __ pushal();
+ __ popal();
+
+ constexpr size_t ByteCount = 2;
+ ASSERT_EQ(ByteCount, codeBytesSize());
+
+ constexpr uint8_t Pushal = 0x60;
+ constexpr uint8_t Popal = 0x61;
+
+ verifyBytes<ByteCount>(codeBytes(), Pushal, Popal);
}
// After these tests we should have a sane environment; we know the following
@@ -282,6 +905,16 @@ TEST_F(AssemblerX8632LowLevelTest, CmpRegReg) {
// mov %edx, {12 + $ScratchpadOffset}(%ebp)
// mov %edi, {16 + $ScratchpadOffset}(%ebp)
// mov %esi, {20 + $ScratchpadOffset}(%ebp)
+// mov %ebp, {24 + $ScratchpadOffset}(%ebp)
+// mov %esp, {28 + $ScratchpadOffset}(%ebp)
+// movups %xmm0, {32 + $ScratchpadOffset}(%ebp)
+// movups %xmm1, {48 + $ScratchpadOffset}(%ebp)
+// movups %xmm2, {64 + $ScratchpadOffset}(%ebp)
+// movusp %xmm3, {80 + $ScratchpadOffset}(%ebp)
+// movusp %xmm4, {96 + $ScratchpadOffset}(%ebp)
+// movusp %xmm5, {112 + $ScratchpadOffset}(%ebp)
+// movusp %xmm6, {128 + $ScratchpadOffset}(%ebp)
+// movusp %xmm7, {144 + $ScratchpadOffset}(%ebp)
//
// pop %ebp
// pop %esi
@@ -301,33 +934,349 @@ TEST_F(AssemblerX8632LowLevelTest, CmpRegReg) {
// TODO(jpp): test the
//
// mov %reg, $Offset(%ebp)
+// movups %xmm, $Offset(%ebp)
//
// encodings using the low level assembler test ensuring that the register
// values can be written to the scratchpad area.
class AssemblerX8632Test : public AssemblerX8632TestBase {
protected:
+ // Dqword is used to represent 128-bit data types. The Dqword's contents are
+ // the same as the contents read from memory. Tests can then use the union
+ // members to verify the tests' outputs.
+ //
+ // NOTE: We want sizeof(Dqword) == sizeof(uint64_t) * 2. In other words, we
+ // want Dqword's contents to be **exactly** what the memory contents were so
+ // that we can do, e.g.,
+ //
+ // ...
+ // float Ret[4];
+ // // populate Ret
+ // return *reinterpret_cast<Dqword *>(&Ret);
+ //
+ // While being an ugly hack, this kind of return statements are used
+ // extensively in the PackedArith (see below) class.
+ union Dqword {
+ template <typename T0, typename T1, typename T2, typename T3,
+ typename = typename std::enable_if<
+ std::is_floating_point<T0>::value>::type>
+ Dqword(T0 F0, T1 F1, T2 F2, T3 F3) {
+ F32[0] = F0;
+ F32[1] = F1;
+ F32[2] = F2;
+ F32[3] = F3;
+ }
+
+ template <typename T>
+ Dqword(typename std::enable_if<std::is_same<T, int32_t>::value, T>::type I0,
+ T I1, T I2, T I3) {
+ I32[0] = I0;
+ I32[1] = I1;
+ I32[2] = I2;
+ I32[3] = I3;
+ }
+
+ template <typename T>
+ Dqword(typename std::enable_if<std::is_same<T, uint64_t>::value, T>::type
+ U64_0,
+ T U64_1) {
+ U64[0] = U64_0;
+ U64[1] = U64_1;
+ }
+
+ template <typename T>
+ Dqword(typename std::enable_if<std::is_same<T, double>::value, T>::type D0,
+ T D1) {
+ F64[0] = D0;
+ F64[1] = D1;
+ }
+
+ bool operator==(const Dqword &Rhs) const {
+ return std::memcmp(this, &Rhs, sizeof(*this)) == 0;
+ }
+
+ double F64[2];
+ uint64_t U64[2];
+ int64_t I64[2];
+
+ float F32[4];
+ uint32_t U32[4];
+ int32_t I32[4];
+
+ uint16_t U16[8];
+ int16_t I16[8];
+
+ uint8_t U8[16];
+ int8_t I8[16];
+
+ private:
+ Dqword() = delete;
+ };
+
+ // As stated, we want this condition to hold, so we assert.
+ static_assert(sizeof(Dqword) == 2 * sizeof(uint64_t),
+ "Dqword has the wrong size.");
+
+ // PackedArith is an interface provider for Dqwords. PackedArith's C argument
+ // is the undelying Dqword's type, which is then used so that we can define
+ // operators in terms of C++ operators on the underlying elements' type.
+ template <typename C> class PackedArith {
+ public:
+ static constexpr uint32_t N = sizeof(Dqword) / sizeof(C);
+ static_assert(N * sizeof(C) == sizeof(Dqword),
+ "Invalid template paramenter.");
+ static_assert((N & 1) == 0, "N should be divisible by 2");
+
+#define DefinePackedComparisonOperator(Op) \
+ template <typename Container = C, int Size = N> \
+ typename std::enable_if<std::is_floating_point<Container>::value, \
+ Dqword>::type \
+ operator Op(const Dqword &Rhs) const { \
+ using ElemType = \
+ typename std::conditional<std::is_same<float, Container>::value, \
+ int32_t, int64_t>::type; \
+ static_assert(sizeof(ElemType) == sizeof(Container), \
+ "Check ElemType definition."); \
+ const ElemType *const RhsPtr = \
+ reinterpret_cast<const ElemType *const>(&Rhs); \
+ const ElemType *const LhsPtr = \
+ reinterpret_cast<const ElemType *const>(&Lhs); \
+ ElemType Ret[N]; \
+ for (uint32_t i = 0; i < N; ++i) { \
+ Ret[i] = (LhsPtr[i] Op RhsPtr[i]) ? -1 : 0; \
+ } \
+ return *reinterpret_cast<Dqword *>(&Ret); \
+ }
+
+ DefinePackedComparisonOperator(< );
+ DefinePackedComparisonOperator(<= );
+ DefinePackedComparisonOperator(> );
+ DefinePackedComparisonOperator(>= );
+ DefinePackedComparisonOperator(== );
+ DefinePackedComparisonOperator(!= );
+
+#undef DefinePackedComparisonOperator
+
+#define DefinePackedOrdUnordComparisonOperator(Op, Ordered) \
+ template <typename Container = C, int Size = N> \
+ typename std::enable_if<std::is_floating_point<Container>::value, \
+ Dqword>::type \
+ Op(const Dqword &Rhs) const { \
+ using ElemType = \
+ typename std::conditional<std::is_same<float, Container>::value, \
+ int32_t, int64_t>::type; \
+ static_assert(sizeof(ElemType) == sizeof(Container), \
+ "Check ElemType definition."); \
+ const Container *const RhsPtr = \
+ reinterpret_cast<const Container *const>(&Rhs); \
+ const Container *const LhsPtr = \
+ reinterpret_cast<const Container *const>(&Lhs); \
+ ElemType Ret[N]; \
+ for (uint32_t i = 0; i < N; ++i) { \
+ Ret[i] = (!(LhsPtr[i] == LhsPtr[i]) || !(RhsPtr[i] == RhsPtr[i])) != \
+ (Ordered) \
+ ? -1 \
+ : 0; \
+ } \
+ return *reinterpret_cast<Dqword *>(&Ret); \
+ }
+
+ DefinePackedOrdUnordComparisonOperator(ord, true);
+ DefinePackedOrdUnordComparisonOperator(unord, false);
+#undef DefinePackedOrdUnordComparisonOperator
+
+#define DefinePackedArithOperator(Op, RhsIndexChanges, NeedsInt) \
+ template <typename Container = C, int Size = N> \
+ Dqword operator Op(const Dqword &Rhs) const { \
+ using ElemTypeForFp = typename std::conditional< \
+ !(NeedsInt), Container, \
+ typename std::conditional< \
+ std::is_same<Container, float>::value, uint32_t, \
+ typename std::conditional<std::is_same<Container, double>::value, \
+ uint64_t, void>::type>::type>::type; \
+ using ElemType = \
+ typename std::conditional<std::is_integral<Container>::value, \
+ Container, ElemTypeForFp>::type; \
+ static_assert(!std::is_same<void, ElemType>::value, \
+ "Check ElemType definition."); \
+ const ElemType *const RhsPtr = \
+ reinterpret_cast<const ElemType *const>(&Rhs); \
+ const ElemType *const LhsPtr = \
+ reinterpret_cast<const ElemType *const>(&Lhs); \
+ ElemType Ret[N]; \
+ for (uint32_t i = 0; i < N; ++i) { \
+ Ret[i] = LhsPtr[i] Op RhsPtr[(RhsIndexChanges) ? i : 0]; \
+ } \
+ return *reinterpret_cast<Dqword *>(&Ret); \
+ }
+
+ DefinePackedArithOperator(>>, false, true);
+ DefinePackedArithOperator(<<, false, true);
+ DefinePackedArithOperator(+, true, false);
+ DefinePackedArithOperator(-, true, false);
+ DefinePackedArithOperator(/, true, false);
+ DefinePackedArithOperator(&, true, true);
+ DefinePackedArithOperator(|, true, true);
+ DefinePackedArithOperator (^, true, true);
+
+#undef DefinePackedArithOperator
+
+#define DefinePackedArithShiftImm(Op) \
+ template <typename Container = C, int Size = N> \
+ Dqword operator Op(uint8_t imm) const { \
+ const Container *const LhsPtr = \
+ reinterpret_cast<const Container *const>(&Lhs); \
+ Container Ret[N]; \
+ for (uint32_t i = 0; i < N; ++i) { \
+ Ret[i] = LhsPtr[i] Op imm; \
+ } \
+ return *reinterpret_cast<Dqword *>(&Ret); \
+ }
+
+ DefinePackedArithShiftImm(>> );
+ DefinePackedArithShiftImm(<< );
+
+#undef DefinePackedArithShiftImm
+
+ template <typename Container = C, int Size = N>
+ typename std::enable_if<std::is_signed<Container>::value ||
+ std::is_floating_point<Container>::value,
+ Dqword>::type
+ operator*(const Dqword &Rhs) const {
+ static_assert((std::is_integral<Container>::value &&
+ sizeof(Container) < sizeof(uint64_t)) ||
+ std::is_floating_point<Container>::value,
+ "* is only defined for i(8|16|32), and fp types.");
+
+ const Container *const RhsPtr =
+ reinterpret_cast<const Container *const>(&Rhs);
+ const Container *const LhsPtr =
+ reinterpret_cast<const Container *const>(&Lhs);
+ Container Ret[Size];
+ for (uint32_t i = 0; i < Size; ++i) {
+ Ret[i] = LhsPtr[i] * RhsPtr[i];
+ }
+ return *reinterpret_cast<Dqword *>(&Ret);
+ }
+
+ template <typename Container = C, int Size = N,
+ typename = typename std::enable_if<
+ !std::is_signed<Container>::value>::type>
+ Dqword operator*(const Dqword &Rhs) const {
+ static_assert(std::is_integral<Container>::value &&
+ sizeof(Container) < sizeof(uint64_t),
+ "* is only defined for ui(8|16|32)");
+ using NextType = typename std::conditional<
+ sizeof(Container) == 1, uint16_t,
+ typename std::conditional<sizeof(Container) == 2, uint32_t,
+ uint64_t>::type>::type;
+ static_assert(sizeof(Container) * 2 == sizeof(NextType),
+ "Unexpected size");
+
+ const Container *const RhsPtr =
+ reinterpret_cast<const Container *const>(&Rhs);
+ const Container *const LhsPtr =
+ reinterpret_cast<const Container *const>(&Lhs);
+ NextType Ret[Size / 2];
+ for (uint32_t i = 0; i < Size; i += 2) {
+ Ret[i / 2] =
+ static_cast<NextType>(LhsPtr[i]) * static_cast<NextType>(RhsPtr[i]);
+ }
+ return *reinterpret_cast<Dqword *>(&Ret);
+ }
+
+ template <typename Container = C, int Size = N>
+ PackedArith<Container> operator~() const {
+ const Container *const LhsPtr =
+ reinterpret_cast<const Container *const>(&Lhs);
+ Container Ret[Size];
+ for (uint32_t i = 0; i < Size; ++i) {
+ Ret[i] = ~LhsPtr[i];
+ }
+ return PackedArith<Container>(*reinterpret_cast<Dqword *>(&Ret));
+ }
+
+#define MinMaxOperations(Name, Suffix) \
+ template <typename Container = C, int Size = N> \
+ Dqword Name##Suffix(const Dqword &Rhs) const { \
+ static_assert(std::is_floating_point<Container>::value, \
+ #Name #Suffix "ps is only available for fp."); \
+ const Container *const RhsPtr = \
+ reinterpret_cast<const Container *const>(&Rhs); \
+ const Container *const LhsPtr = \
+ reinterpret_cast<const Container *const>(&Lhs); \
+ Container Ret[Size]; \
+ for (uint32_t i = 0; i < Size; ++i) { \
+ Ret[i] = std::Name(LhsPtr[i], RhsPtr[i]); \
+ } \
+ return *reinterpret_cast<Dqword *>(&Ret); \
+ }
+
+ MinMaxOperations(max, ps);
+ MinMaxOperations(max, pd);
+ MinMaxOperations(min, ps);
+ MinMaxOperations(min, pd);
+#undef MinMaxOperations
+
+ template <typename Container = C, int Size = N>
+ Dqword blendWith(const Dqword &Rhs, const Dqword &Mask) const {
+ using MaskType = typename std::conditional<
+ sizeof(Container) == 1, int8_t,
+ typename std::conditional<sizeof(Container) == 2, int16_t,
+ int32_t>::type>::type;
+ static_assert(sizeof(MaskType) == sizeof(Container),
+ "MaskType has the wrong size.");
+ const Container *const RhsPtr =
+ reinterpret_cast<const Container *const>(&Rhs);
+ const Container *const LhsPtr =
+ reinterpret_cast<const Container *const>(&Lhs);
+ const MaskType *const MaskPtr =
+ reinterpret_cast<const MaskType *const>(&Mask);
+ Container Ret[Size];
+ for (int i = 0; i < Size; ++i) {
+ Ret[i] = ((MaskPtr[i] < 0) ? RhsPtr : LhsPtr)[i];
+ }
+ return *reinterpret_cast<Dqword *>(&Ret);
+ }
+
+ private:
+ // The AssemblerX8632Test class needs to be a friend so that it can create
+ // PackedArith objects (see below.)
+ friend class AssemblerX8632Test;
+
+ explicit PackedArith(const Dqword &MyLhs) : Lhs(MyLhs) {}
+
+ // Lhs can't be a & because operator~ returns a temporary object that needs
+ // access to its own Dqword.
+ const Dqword Lhs;
+ };
+
+ // Named constructor for PackedArith objects.
+ template <typename C> static PackedArith<C> packedAs(const Dqword &D) {
+ return PackedArith<C>(D);
+ }
+
AssemblerX8632Test() { reset(); }
void reset() {
AssemblerX8632TestBase::reset();
NeedsEpilogue = true;
- // 6 dwords are allocated for saving the GPR state after the jitted code
+ // These dwords are allocated for saving the GPR state after the jitted code
// runs.
- NumAllocatedDwords = 6;
+ NumAllocatedDwords = AssembledTest::ScratchpadSlots;
addPrologue();
}
- // AssembledBuffer is a wrapper around a PROT_EXEC mmap'ed buffer. This buffer
+ // AssembledTest is a wrapper around a PROT_EXEC mmap'ed buffer. This buffer
// contains both the test code as well as prologue/epilogue, and the
// scratchpad area that tests may use -- all tests use this scratchpad area
// for storing the processor's registers after the tests executed. This class
// also exposes helper methods for reading the register state after test
// execution, as well as for reading the scratchpad area.
- class AssembledBuffer {
- AssembledBuffer() = delete;
- AssembledBuffer(const AssembledBuffer &) = delete;
- AssembledBuffer &operator=(const AssembledBuffer &) = delete;
+ class AssembledTest {
+ AssembledTest() = delete;
+ AssembledTest(const AssembledTest &) = delete;
+ AssembledTest &operator=(const AssembledTest &) = delete;
public:
static constexpr uint32_t MaximumCodeSize = 1 << 20;
@@ -337,9 +1286,21 @@ protected:
static constexpr uint32_t EdxSlot = 3;
static constexpr uint32_t EdiSlot = 4;
static constexpr uint32_t EsiSlot = 5;
+ static constexpr uint32_t EbpSlot = 6;
+ static constexpr uint32_t EspSlot = 7;
+ // save 4 dwords for each xmm registers.
+ static constexpr uint32_t Xmm0Slot = 8;
+ static constexpr uint32_t Xmm1Slot = 12;
+ static constexpr uint32_t Xmm2Slot = 16;
+ static constexpr uint32_t Xmm3Slot = 20;
+ static constexpr uint32_t Xmm4Slot = 24;
+ static constexpr uint32_t Xmm5Slot = 28;
+ static constexpr uint32_t Xmm6Slot = 32;
+ static constexpr uint32_t Xmm7Slot = 36;
+ static constexpr uint32_t ScratchpadSlots = 40;
- AssembledBuffer(const uint8_t *Data, const size_t MySize,
- const size_t ExtraStorageDwords)
+ AssembledTest(const uint8_t *Data, const size_t MySize,
+ const size_t ExtraStorageDwords)
: Size(MaximumCodeSize + 4 * ExtraStorageDwords) {
// MaxCodeSize is needed because EXPECT_LT needs a symbol with a name --
// probably a compiler bug?
@@ -353,15 +1314,15 @@ protected:
std::memcpy(ExecutableData, Data, MySize);
}
- // We allow AssembledBuffer to be moved so that we can return objects of
+ // We allow AssembledTest to be moved so that we can return objects of
// this type.
- AssembledBuffer(AssembledBuffer &&Buffer)
+ AssembledTest(AssembledTest &&Buffer)
: ExecutableData(Buffer.ExecutableData), Size(Buffer.Size) {
Buffer.ExecutableData = nullptr;
Buffer.Size = 0;
}
- AssembledBuffer &operator=(AssembledBuffer &&Buffer) {
+ AssembledTest &operator=(AssembledTest &&Buffer) {
ExecutableData = Buffer.ExecutableData;
Buffer.ExecutableData = nullptr;
Size = Buffer.Size;
@@ -369,7 +1330,7 @@ protected:
return *this;
}
- ~AssembledBuffer() {
+ ~AssembledTest() {
if (ExecutableData != nullptr) {
munmap(ExecutableData, Size);
ExecutableData = nullptr;
@@ -378,28 +1339,118 @@ protected:
void run() const { reinterpret_cast<void (*)()>(ExecutableData)(); }
- uint32_t eax() const { return contentsOfDword(AssembledBuffer::EaxSlot); }
+ uint32_t eax() const { return contentsOfDword(AssembledTest::EaxSlot); }
+
+ uint32_t ebx() const { return contentsOfDword(AssembledTest::EbxSlot); }
+
+ uint32_t ecx() const { return contentsOfDword(AssembledTest::EcxSlot); }
+
+ uint32_t edx() const { return contentsOfDword(AssembledTest::EdxSlot); }
+
+ uint32_t edi() const { return contentsOfDword(AssembledTest::EdiSlot); }
+
+ uint32_t esi() const { return contentsOfDword(AssembledTest::EsiSlot); }
+
+ uint32_t ebp() const { return contentsOfDword(AssembledTest::EbpSlot); }
+
+ uint32_t esp() const { return contentsOfDword(AssembledTest::EspSlot); }
+
+ template <typename T> T xmm0() const {
+ return xmm<T>(AssembledTest::Xmm0Slot);
+ }
+
+ template <typename T> T xmm1() const {
+ return xmm<T>(AssembledTest::Xmm1Slot);
+ }
+
+ template <typename T> T xmm2() const {
+ return xmm<T>(AssembledTest::Xmm2Slot);
+ }
- uint32_t ebx() const { return contentsOfDword(AssembledBuffer::EbxSlot); }
+ template <typename T> T xmm3() const {
+ return xmm<T>(AssembledTest::Xmm3Slot);
+ }
- uint32_t ecx() const { return contentsOfDword(AssembledBuffer::EcxSlot); }
+ template <typename T> T xmm4() const {
+ return xmm<T>(AssembledTest::Xmm4Slot);
+ }
- uint32_t edx() const { return contentsOfDword(AssembledBuffer::EdxSlot); }
+ template <typename T> T xmm5() const {
+ return xmm<T>(AssembledTest::Xmm5Slot);
+ }
- uint32_t edi() const { return contentsOfDword(AssembledBuffer::EdiSlot); }
+ template <typename T> T xmm6() const {
+ return xmm<T>(AssembledTest::Xmm6Slot);
+ }
- uint32_t esi() const { return contentsOfDword(AssembledBuffer::EsiSlot); }
+ template <typename T> T xmm7() const {
+ return xmm<T>(AssembledTest::Xmm7Slot);
+ }
// contentsOfDword is used for reading the values in the scratchpad area.
// Valid arguments are the dword ids returned by
// AssemblerX8632Test::allocateDword() -- other inputs are considered
// invalid, and are not guaranteed to work if the implementation changes.
- uint32_t contentsOfDword(uint32_t Dword) const {
- return *reinterpret_cast<uint32_t *>(
- static_cast<uint8_t *>(ExecutableData) + dwordOffset(Dword));
+ template <typename T = uint32_t, typename = typename std::enable_if<
+ sizeof(T) == sizeof(uint32_t)>::type>
+ T contentsOfDword(uint32_t Dword) const {
+ return *reinterpret_cast<T *>(static_cast<uint8_t *>(ExecutableData) +
+ dwordOffset(Dword));
+ }
+
+ template <typename T = uint64_t, typename = typename std::enable_if<
+ sizeof(T) == sizeof(uint64_t)>::type>
+ T contentsOfQword(uint32_t InitialDword) const {
+ return *reinterpret_cast<T *>(static_cast<uint8_t *>(ExecutableData) +
+ dwordOffset(InitialDword));
+ }
+
+ Dqword contentsOfDqword(uint32_t InitialDword) const {
+ return *reinterpret_cast<Dqword *>(
+ static_cast<uint8_t *>(ExecutableData) +
+ dwordOffset(InitialDword));
+ }
+
+ template <typename T = uint32_t, typename = typename std::enable_if<
+ sizeof(T) == sizeof(uint32_t)>::type>
+ void setDwordTo(uint32_t Dword, T value) {
+ *reinterpret_cast<uint32_t *>(static_cast<uint8_t *>(ExecutableData) +
+ dwordOffset(Dword)) =
+ *reinterpret_cast<uint32_t *>(&value);
+ }
+
+ template <typename T = uint64_t, typename = typename std::enable_if<
+ sizeof(T) == sizeof(uint64_t)>::type>
+ void setQwordTo(uint32_t InitialDword, T value) {
+ *reinterpret_cast<uint64_t *>(static_cast<uint8_t *>(ExecutableData) +
+ dwordOffset(InitialDword)) =
+ *reinterpret_cast<uint64_t *>(&value);
+ }
+
+ void setDqwordTo(uint32_t InitialDword, const Dqword &qdword) {
+ setQwordTo(InitialDword, qdword.U64[0]);
+ setQwordTo(InitialDword + 2, qdword.U64[1]);
}
private:
+ template <typename T>
+ typename std::enable_if<std::is_same<T, Dqword>::value, Dqword>::type
+ xmm(uint8_t Slot) const {
+ return contentsOfDqword(Slot);
+ }
+
+ template <typename T>
+ typename std::enable_if<!std::is_same<T, Dqword>::value, T>::type
+ xmm(uint8_t Slot) const {
+ constexpr bool TIs64Bit = sizeof(T) == sizeof(uint64_t);
+ using _64BitType = typename std::conditional<TIs64Bit, T, uint64_t>::type;
+ using _32BitType = typename std::conditional<TIs64Bit, uint32_t, T>::type;
+ if (TIs64Bit) {
+ return contentsOfQword<_64BitType>(Slot);
+ }
+ return contentsOfDword<_32BitType>(Slot);
+ }
+
static uint32_t dwordOffset(uint32_t Index) {
return MaximumCodeSize + (Index * 4);
}
@@ -408,21 +1459,35 @@ protected:
size_t Size;
};
- // assemble created an AssembledBuffer with the jitted code. The first time
+ // assemble created an AssembledTest with the jitted code. The first time
// assemble is executed it will add the epilogue to the jitted code (which is
// the reason why this method is not const qualified.
- AssembledBuffer assemble() {
+ AssembledTest assemble() {
if (NeedsEpilogue) {
addEpilogue();
}
NeedsEpilogue = false;
- return AssembledBuffer(codeBytes(), codeBytesSize(), NumAllocatedDwords);
+ return AssembledTest(codeBytes(), codeBytesSize(), NumAllocatedDwords);
}
// Allocates a new dword slot in the test's scratchpad area.
uint32_t allocateDword() { return NumAllocatedDwords++; }
+ // Allocates a new qword slot in the test's scratchpad area.
+ uint32_t allocateQword() {
+ uint32_t InitialDword = allocateDword();
+ allocateDword();
+ return InitialDword;
+ }
+
+ // Allocates a new dqword slot in the test's scratchpad area.
+ uint32_t allocateDqword() {
+ uint32_t InitialDword = allocateQword();
+ allocateQword();
+ return InitialDword;
+ }
+
Address dwordAddress(uint32_t Dword) {
return Address(GPRRegister::Encoded_Reg_ebp, dwordDisp(Dword));
}
@@ -433,12 +1498,22 @@ private:
// the specified register. These are all private for, when jitting the test
// code, tests should not tamper with these values. Besides, during the test
// execution these slots' contents are undefined and should not be accessed.
- Address eaxSlotAddress() { return dwordAddress(AssembledBuffer::EaxSlot); }
- Address ebxSlotAddress() { return dwordAddress(AssembledBuffer::EbxSlot); }
- Address ecxSlotAddress() { return dwordAddress(AssembledBuffer::EcxSlot); }
- Address edxSlotAddress() { return dwordAddress(AssembledBuffer::EdxSlot); }
- Address ediSlotAddress() { return dwordAddress(AssembledBuffer::EdiSlot); }
- Address esiSlotAddress() { return dwordAddress(AssembledBuffer::EsiSlot); }
+ Address eaxSlotAddress() { return dwordAddress(AssembledTest::EaxSlot); }
+ Address ebxSlotAddress() { return dwordAddress(AssembledTest::EbxSlot); }
+ Address ecxSlotAddress() { return dwordAddress(AssembledTest::EcxSlot); }
+ Address edxSlotAddress() { return dwordAddress(AssembledTest::EdxSlot); }
+ Address ediSlotAddress() { return dwordAddress(AssembledTest::EdiSlot); }
+ Address esiSlotAddress() { return dwordAddress(AssembledTest::EsiSlot); }
+ Address ebpSlotAddress() { return dwordAddress(AssembledTest::EbpSlot); }
+ Address espSlotAddress() { return dwordAddress(AssembledTest::EspSlot); }
+ Address xmm0SlotAddress() { return dwordAddress(AssembledTest::Xmm0Slot); }
+ Address xmm1SlotAddress() { return dwordAddress(AssembledTest::Xmm1Slot); }
+ Address xmm2SlotAddress() { return dwordAddress(AssembledTest::Xmm2Slot); }
+ Address xmm3SlotAddress() { return dwordAddress(AssembledTest::Xmm3Slot); }
+ Address xmm4SlotAddress() { return dwordAddress(AssembledTest::Xmm4Slot); }
+ Address xmm5SlotAddress() { return dwordAddress(AssembledTest::Xmm5Slot); }
+ Address xmm6SlotAddress() { return dwordAddress(AssembledTest::Xmm6Slot); }
+ Address xmm7SlotAddress() { return dwordAddress(AssembledTest::Xmm7Slot); }
// Returns the displacement that should be used when accessing the specified
// Dword in the scratchpad area. It needs to adjust for the initial
@@ -449,7 +1524,7 @@ private:
assert(Dword < NumAllocatedDwords);
static constexpr uint8_t PushBytes = 1;
static constexpr uint8_t CallImmBytes = 5;
- return AssembledBuffer::MaximumCodeSize + (Dword * 4) -
+ return AssembledTest::MaximumCodeSize + (Dword * 4) -
(7 * PushBytes + CallImmBytes);
}
@@ -479,6 +1554,16 @@ private:
__ mov(IceType_i32, edxSlotAddress(), GPRRegister::Encoded_Reg_edx);
__ mov(IceType_i32, ediSlotAddress(), GPRRegister::Encoded_Reg_edi);
__ mov(IceType_i32, esiSlotAddress(), GPRRegister::Encoded_Reg_esi);
+ __ mov(IceType_i32, ebpSlotAddress(), GPRRegister::Encoded_Reg_ebp);
+ __ mov(IceType_i32, espSlotAddress(), GPRRegister::Encoded_Reg_esp);
+ __ movups(xmm0SlotAddress(), XmmRegister::Encoded_Reg_xmm0);
+ __ movups(xmm1SlotAddress(), XmmRegister::Encoded_Reg_xmm1);
+ __ movups(xmm2SlotAddress(), XmmRegister::Encoded_Reg_xmm2);
+ __ movups(xmm3SlotAddress(), XmmRegister::Encoded_Reg_xmm3);
+ __ movups(xmm4SlotAddress(), XmmRegister::Encoded_Reg_xmm4);
+ __ movups(xmm5SlotAddress(), XmmRegister::Encoded_Reg_xmm5);
+ __ movups(xmm6SlotAddress(), XmmRegister::Encoded_Reg_xmm6);
+ __ movups(xmm7SlotAddress(), XmmRegister::Encoded_Reg_xmm7);
__ popl(GPRRegister::Encoded_Reg_ebp);
__ popl(GPRRegister::Encoded_Reg_esi);
@@ -495,6 +1580,40 @@ private:
uint32_t NumAllocatedDwords;
};
+TEST_F(AssemblerX8632Test, ScratchpadGettersAndSetters) {
+ const uint32_t S0 = allocateDword();
+ const uint32_t S1 = allocateDword();
+ const uint32_t S2 = allocateDword();
+ const uint32_t S3 = allocateDword();
+ AssembledTest test = assemble();
+ test.setDwordTo(S0, 0xBEEF0000u);
+ test.setDwordTo(S1, 0xDEADu);
+ test.setDwordTo(S2, 0x20406080u);
+ ASSERT_EQ(0xBEEF0000u, test.contentsOfDword(S0));
+ ASSERT_EQ(0xDEADu, test.contentsOfDword(S1));
+ ASSERT_EQ(0x20406080u, test.contentsOfDword(S2));
+ ASSERT_EQ(0xDEADBEEF0000ull, test.contentsOfQword(S0));
+ ASSERT_EQ(0x204060800000DEADull, test.contentsOfQword(S1));
+
+ test.setQwordTo(S1, 0x1234567890ABCDEFull);
+ ASSERT_EQ(0x1234567890ABCDEFull, test.contentsOfQword(S1));
+ test.setDwordTo(S0, 0xBEEF0000u);
+ ASSERT_EQ(0x90ABCDEFull, test.contentsOfDword(S1));
+ ASSERT_EQ(0x12345678ull, test.contentsOfDword(S2));
+
+ test.setDwordTo(S0, 1.0f);
+ ASSERT_FLOAT_EQ(1.0f, test.contentsOfDword<float>(S0));
+ test.setQwordTo(S0, 3.14);
+ ASSERT_DOUBLE_EQ(3.14, test.contentsOfQword<double>(S0));
+
+ test.setDqwordTo(S0, Dqword(1.0f, 2.0f, 3.0f, 4.0f));
+ ASSERT_EQ(Dqword(1.0f, 2.0f, 3.0f, 4.0f), test.contentsOfDqword(S0));
+ EXPECT_FLOAT_EQ(1.0f, test.contentsOfDword<float>(S0));
+ EXPECT_FLOAT_EQ(2.0f, test.contentsOfDword<float>(S1));
+ EXPECT_FLOAT_EQ(3.0f, test.contentsOfDword<float>(S2));
+ EXPECT_FLOAT_EQ(4.0f, test.contentsOfDword<float>(S3));
+}
+
TEST_F(AssemblerX8632Test, MovRegImm) {
constexpr uint32_t ExpectedEax = 0x000000FFul;
constexpr uint32_t ExpectedEbx = 0x0000FF00ul;
@@ -510,7 +1629,7 @@ TEST_F(AssemblerX8632Test, MovRegImm) {
__ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(ExpectedEdi));
__ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(ExpectedEsi));
- AssembledBuffer test = assemble();
+ AssembledTest test = assemble();
test.run();
EXPECT_EQ(ExpectedEax, test.eax());
EXPECT_EQ(ExpectedEbx, test.ebx());
@@ -535,7 +1654,7 @@ TEST_F(AssemblerX8632Test, MovMemImm) {
__ mov(IceType_i32, dwordAddress(T2), Immediate(ExpectedT2));
__ mov(IceType_i32, dwordAddress(T3), Immediate(ExpectedT3));
- AssembledBuffer test = assemble();
+ AssembledTest test = assemble();
test.run();
EXPECT_EQ(0ul, test.eax());
EXPECT_EQ(0ul, test.ebx());
@@ -576,7 +1695,7 @@ TEST_F(AssemblerX8632Test, MovMemReg) {
__ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(ExpectedT5));
__ mov(IceType_i32, dwordAddress(T5), GPRRegister::Encoded_Reg_esi);
- AssembledBuffer test = assemble();
+ AssembledTest test = assemble();
test.run();
EXPECT_EQ(ExpectedT0, test.contentsOfDword(T0));
EXPECT_EQ(ExpectedT1, test.contentsOfDword(T1));
@@ -603,7 +1722,7 @@ TEST_F(AssemblerX8632Test, MovRegReg) {
__ mov(IceType_i32, GPRRegister::Encoded_Reg_eax,
GPRRegister::Encoded_Reg_esi);
- AssembledBuffer test = assemble();
+ AssembledTest test = assemble();
test.run();
EXPECT_EQ(0x55000000ul, test.eax());
EXPECT_EQ(0x20ul, test.ebx());
@@ -645,7 +1764,7 @@ TEST_F(AssemblerX8632Test, MovRegMem) {
__ mov(IceType_i32, dwordAddress(T5), Immediate(ExpectedT5));
__ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, dwordAddress(T5));
- AssembledBuffer test = assemble();
+ AssembledTest test = assemble();
test.run();
EXPECT_EQ(ExpectedT0, test.eax());
EXPECT_EQ(ExpectedT1, test.ebx());
@@ -658,7 +1777,7 @@ TEST_F(AssemblerX8632Test, MovRegMem) {
TEST_F(AssemblerX8632Test, J) {
#define TestJ(C, Near, Src0, Value0, Src1, Value1, Dest) \
do { \
- const bool NearJmp = std::strcmp(#Near, "Near") == 0; \
+ const bool NearJmp = AssemblerX8632::k##Near##Jump; \
Label ShouldBeTaken; \
__ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src0, Immediate(Value0)); \
__ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src1, Immediate(Value1)); \
@@ -668,7 +1787,7 @@ TEST_F(AssemblerX8632Test, J) {
__ j(Cond::Br_##C, &ShouldBeTaken, NearJmp); \
__ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(0xC0FFEE)); \
__ bind(&ShouldBeTaken); \
- AssembledBuffer test = assemble(); \
+ AssembledTest test = assemble(); \
test.run(); \
EXPECT_EQ(Value0, test.Src0()) << "Br_" #C ", " #Near; \
EXPECT_EQ(Value1, test.Src1()) << "Br_" #C ", " #Near; \
@@ -712,6 +1831,5455 @@ TEST_F(AssemblerX8632Test, J) {
#undef TestJ
}
+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
+}
+
+TEST_F(AssemblerX8632Test, PopAddr) {
+ const uint32_t T0 = allocateDword();
+ constexpr uint32_t V0 = 0xEFAB;
+
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0xC0FFEE));
+ __ pushl(GPRRegister::Encoded_Reg_eax);
+ __ popl(dwordAddress(T0));
+
+ AssembledTest test = assemble();
+ test.setDwordTo(T0, V0);
+
+ test.run();
+
+ ASSERT_EQ(0xC0FFEEul, test.contentsOfDword(T0));
+}
+
+TEST_F(AssemblerX8632Test, SetCC) {
+#define TestSetCC(C, Src0, Value0, Src1, Value1, Dest, IsTrue) \
+ do { \
+ const uint32_t T0 = allocateDword(); \
+ constexpr uint32_t V0 = 0xF00F00; \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src0, Immediate(Value0)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src1, Immediate(Value1)); \
+ __ cmp(IceType_i32, GPRRegister::Encoded_Reg_##Src0, \
+ GPRRegister::Encoded_Reg_##Src1); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(0)); \
+ __ setcc(Cond::Br_##C, \
+ RegX8632::getEncodedByteReg(GPRRegister::Encoded_Reg_##Dest)); \
+ __ setcc(Cond::Br_##C, dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ \
+ test.run(); \
+ \
+ EXPECT_EQ(IsTrue, test.Dest()) \
+ << "(" #C ", " #Src0 ", " #Value0 ", " #Src1 ", " #Value1 ", " #Dest \
+ ", " #IsTrue ")"; \
+ EXPECT_EQ((0xF00F00 | IsTrue), test.contentsOfDword(T0)) \
+ << "(" #C ", " #Src0 ", " #Value0 ", " #Src1 ", " #Value1 ", " #Dest \
+ ", " #IsTrue ")"; \
+ \
+ reset(); \
+ } while (0)
+
+ TestSetCC(o, eax, 0x80000000u, ebx, 0x1u, ecx, 1u);
+ TestSetCC(o, eax, 0x1u, ebx, 0x10000000u, ecx, 0u);
+
+ TestSetCC(no, ebx, 0x1u, ecx, 0x10000000u, edx, 1u);
+ TestSetCC(no, ebx, 0x80000000u, ecx, 0x1u, edx, 0u);
+
+ TestSetCC(b, ecx, 0x1, edx, 0x80000000u, eax, 1u);
+ TestSetCC(b, ecx, 0x80000000u, edx, 0x1u, eax, 0u);
+
+ TestSetCC(ae, edx, 0x80000000u, edi, 0x1u, ebx, 1u);
+ TestSetCC(ae, edx, 0x1u, edi, 0x80000000u, ebx, 0u);
+
+ TestSetCC(e, edi, 0x1u, esi, 0x1u, ecx, 1u);
+ TestSetCC(e, edi, 0x1u, esi, 0x11111u, ecx, 0u);
+
+ TestSetCC(ne, esi, 0x80000000u, eax, 0x1u, edx, 1u);
+ TestSetCC(ne, esi, 0x1u, eax, 0x1u, edx, 0u);
+
+ TestSetCC(be, eax, 0x1u, ebx, 0x80000000u, eax, 1u);
+ TestSetCC(be, eax, 0x80000000u, ebx, 0x1u, eax, 0u);
+
+ TestSetCC(a, ebx, 0x80000000u, ecx, 0x1u, ebx, 1u);
+ TestSetCC(a, ebx, 0x1u, ecx, 0x80000000u, ebx, 0u);
+
+ TestSetCC(s, ecx, 0x1u, edx, 0x80000000u, ecx, 1u);
+ TestSetCC(s, ecx, 0x80000000u, edx, 0x1u, ecx, 0u);
+
+ TestSetCC(ns, edx, 0x80000000u, edi, 0x1u, ecx, 1u);
+ TestSetCC(ns, edx, 0x1u, edi, 0x80000000u, ecx, 0u);
+
+ TestSetCC(p, edi, 0x80000000u, esi, 0x1u, edx, 1u);
+ TestSetCC(p, edi, 0x1u, esi, 0x80000000u, edx, 0u);
+
+ TestSetCC(np, esi, 0x1u, edi, 0x80000000u, eax, 1u);
+ TestSetCC(np, esi, 0x80000000u, edi, 0x1u, eax, 0u);
+
+ TestSetCC(l, edi, 0x80000000u, eax, 0x1u, ebx, 1u);
+ TestSetCC(l, edi, 0x1u, eax, 0x80000000u, ebx, 0u);
+
+ TestSetCC(ge, eax, 0x1u, ebx, 0x80000000u, ecx, 1u);
+ TestSetCC(ge, eax, 0x80000000u, ebx, 0x1u, ecx, 0u);
+
+ TestSetCC(le, ebx, 0x80000000u, ecx, 0x1u, edx, 1u);
+ TestSetCC(le, ebx, 0x1u, ecx, 0x80000000u, edx, 0u);
+
+#undef TestSetCC
+}
+
+TEST_F(AssemblerX8632Test, CallImm) {
+ __ call(Immediate(16));
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0xf00f));
+ __ popl(GPRRegister::Encoded_Reg_ebx);
+
+ AssembledTest test = assemble();
+
+ test.run();
+
+ EXPECT_EQ(0xF00Fu, test.eax());
+}
+
+TEST_F(AssemblerX8632Test, CallReg) {
+ __ call(Immediate(16));
+ __ popl(GPRRegister::Encoded_Reg_edx);
+ __ pushl(GPRRegister::Encoded_Reg_edx);
+ __ ret();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ popl(GPRRegister::Encoded_Reg_ebx);
+ __ call(GPRRegister::Encoded_Reg_ebx);
+
+ AssembledTest test = assemble();
+
+ test.run();
+
+ EXPECT_EQ(15u, test.edx() - test.ebx());
+}
+
+TEST_F(AssemblerX8632Test, CallAddr) {
+ __ call(Immediate(16));
+ __ mov(IceType_i8, GPRRegister::Encoded_Reg_eax, Immediate(0xf4));
+ __ ret();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ hlt();
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0xf1f2f300));
+ __ call(Address(GPRRegister::Encoded_Reg_esp, 0));
+ __ popl(GPRRegister::Encoded_Reg_edx);
+
+ AssembledTest test = assemble();
+
+ test.run();
+
+ EXPECT_EQ(0xf1f2f3f4, test.eax());
+}
+
+TEST_F(AssemblerX8632Test, Movzx) {
+#define TestMovzx8bitWithRegDest(Src, Dst, Imm) \
+ do { \
+ static_assert(((Imm)&0xFF) == (Imm), #Imm " is not an 8bit immediate"); \
+ __ mov(IceType_i8, GPRRegister::Encoded_Reg_##Src, Immediate(Imm)); \
+ __ movzx(IceType_i8, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src); \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ ASSERT_EQ(Imm, test.Dst()) << "(" #Src ", " #Dst ", " #Imm ")"; \
+ reset(); \
+ } while (0)
+
+#define TestMovzx16bitWithRegDest(Src, Dst, Imm) \
+ do { \
+ static_assert(((Imm)&0xFFFF) == (Imm), #Imm " is not a 16bit immediate"); \
+ __ mov(IceType_i16, GPRRegister::Encoded_Reg_##Src, Immediate(Imm)); \
+ __ movzx(IceType_i16, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src); \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ ASSERT_EQ(Imm, test.Dst()) << "(" #Src ", " #Dst ", " #Imm ")"; \
+ reset(); \
+ } while (0)
+
+#define TestMovzx8bitWithAddrSrc(Dst, Imm) \
+ do { \
+ static_assert(((Imm)&0xFF) == (Imm), #Imm " is not an 8bit immediate"); \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Imm; \
+ __ movzx(IceType_i8, GPRRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ ASSERT_EQ(Imm, test.Dst()) << "(Addr, " #Dst ", " #Imm ")"; \
+ reset(); \
+ } while (0)
+
+#define TestMovzx16bitWithAddrSrc(Dst, Imm) \
+ do { \
+ static_assert(((Imm)&0xFFFF) == (Imm), #Imm " is not a 16bit immediate"); \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Imm; \
+ __ movzx(IceType_i16, GPRRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ ASSERT_EQ(Imm, test.Dst()) << "(Addr, " #Dst ", " #Imm ")"; \
+ reset(); \
+ } while (0)
+
+#define TestMovzx(Dst) \
+ do { \
+ TestMovzx8bitWithRegDest(eax, Dst, 0x81u); \
+ TestMovzx8bitWithRegDest(ebx, Dst, 0x82u); \
+ TestMovzx8bitWithRegDest(ecx, Dst, 0x83u); \
+ TestMovzx8bitWithRegDest(edx, Dst, 0x84u); \
+ /* esi is encoded as dh */ \
+ TestMovzx8bitWithRegDest(esi, Dst, 0x85u); \
+ /* edi is encoded as bh */ \
+ TestMovzx8bitWithRegDest(edi, Dst, 0x86u); \
+ /* ebp is encoded as ch */ \
+ TestMovzx8bitWithRegDest(ebp, Dst, 0x87u); \
+ /* esp is encoded as ah */ \
+ TestMovzx8bitWithRegDest(esp, Dst, 0x88u); \
+ TestMovzx8bitWithAddrSrc(Dst, 0x8Fu); \
+ \
+ TestMovzx16bitWithRegDest(eax, Dst, 0x8118u); \
+ TestMovzx16bitWithRegDest(ebx, Dst, 0x8228u); \
+ TestMovzx16bitWithRegDest(ecx, Dst, 0x8338u); \
+ TestMovzx16bitWithRegDest(edx, Dst, 0x8448u); \
+ TestMovzx16bitWithAddrSrc(Dst, 0x8FF8u); \
+ } while (0)
+
+ TestMovzx(eax);
+ TestMovzx(ebx);
+ TestMovzx(ecx);
+ TestMovzx(edx);
+ TestMovzx(esi);
+ TestMovzx(edi);
+
+#undef TestMovzx
+#undef TestMovzx16bitWithAddrDest
+#undef TestMovzx8bitWithAddrDest
+#undef TestMovzx16bitWithRegDest
+#undef TestMovzx8bitWithRegDest
+}
+
+TEST_F(AssemblerX8632Test, Movsx) {
+#define TestMovsx8bitWithRegDest(Src, Dst, Imm) \
+ do { \
+ static_assert(((Imm)&0xFF) == (Imm), #Imm " is not an 8bit immediate"); \
+ __ mov(IceType_i8, GPRRegister::Encoded_Reg_##Src, Immediate(Imm)); \
+ __ movsx(IceType_i8, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src); \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ ASSERT_EQ((0xFFFFFF00 | (Imm)), test.Dst()) \
+ << "(" #Src ", " #Dst ", " #Imm ")"; \
+ reset(); \
+ } while (0)
+
+#define TestMovsx16bitWithRegDest(Src, Dst, Imm) \
+ do { \
+ static_assert(((Imm)&0xFFFF) == (Imm), #Imm " is not a 16bit immediate"); \
+ __ mov(IceType_i16, GPRRegister::Encoded_Reg_##Src, Immediate(Imm)); \
+ __ movsx(IceType_i16, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src); \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ ASSERT_EQ((0xFFFF0000 | (Imm)), test.Dst()) \
+ << "(" #Src ", " #Dst ", " #Imm ")"; \
+ reset(); \
+ } while (0)
+
+#define TestMovsx8bitWithAddrSrc(Dst, Imm) \
+ do { \
+ static_assert(((Imm)&0xFF) == (Imm), #Imm " is not an 8bit immediate"); \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Imm; \
+ __ movsx(IceType_i8, GPRRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ ASSERT_EQ((0xFFFFFF00 | (Imm)), test.Dst()) \
+ << "(Addr, " #Dst ", " #Imm ")"; \
+ reset(); \
+ } while (0)
+
+#define TestMovsx16bitWithAddrSrc(Dst, Imm) \
+ do { \
+ static_assert(((Imm)&0xFFFF) == (Imm), #Imm " is not a 16bit immediate"); \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Imm; \
+ __ movsx(IceType_i16, GPRRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ ASSERT_EQ((0xFFFF0000 | (Imm)), test.Dst()) \
+ << "(Addr, " #Dst ", " #Imm ")"; \
+ reset(); \
+ } while (0)
+
+#define TestMovsx(Dst) \
+ do { \
+ TestMovsx8bitWithRegDest(eax, Dst, 0x81u); \
+ TestMovsx8bitWithRegDest(ebx, Dst, 0x82u); \
+ TestMovsx8bitWithRegDest(ecx, Dst, 0x83u); \
+ TestMovsx8bitWithRegDest(edx, Dst, 0x84u); \
+ /* esi is encoded as dh */ \
+ TestMovsx8bitWithRegDest(esi, Dst, 0x85u); \
+ /* edi is encoded as bh */ \
+ TestMovsx8bitWithRegDest(edi, Dst, 0x86u); \
+ /* ebp is encoded as ch */ \
+ TestMovsx8bitWithRegDest(ebp, Dst, 0x87u); \
+ /* esp is encoded as ah */ \
+ TestMovsx8bitWithRegDest(esp, Dst, 0x88u); \
+ TestMovsx8bitWithAddrSrc(Dst, 0x8Fu); \
+ \
+ TestMovsx16bitWithRegDest(eax, Dst, 0x8118u); \
+ TestMovsx16bitWithRegDest(ebx, Dst, 0x8228u); \
+ TestMovsx16bitWithRegDest(ecx, Dst, 0x8338u); \
+ TestMovsx16bitWithRegDest(edx, Dst, 0x8448u); \
+ TestMovsx16bitWithAddrSrc(Dst, 0x8FF8u); \
+ } while (0)
+
+ TestMovsx(eax);
+ TestMovsx(ebx);
+ TestMovsx(ecx);
+ TestMovsx(edx);
+ TestMovsx(esi);
+ TestMovsx(edi);
+
+#undef TestMovsx
+#undef TestMovsx16bitWithAddrDest
+#undef TestMovsx8bitWithAddrDest
+#undef TestMovsx16bitWithRegDest
+#undef TestMovsx8bitWithRegDest
+}
+
+TEST_F(AssemblerX8632Test, Lea) {
+#define TestLeaBaseDisp(Base, BaseValue, Disp, Dst) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Base ", " #BaseValue ", " #Dst ")"; \
+ if (GPRRegister::Encoded_Reg_##Base != GPRRegister::Encoded_Reg_esp && \
+ GPRRegister::Encoded_Reg_##Base != GPRRegister::Encoded_Reg_ebp) { \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Base, \
+ Immediate(BaseValue)); \
+ } \
+ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \
+ Address(GPRRegister::Encoded_Reg_##Base, Disp)); \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ ASSERT_EQ(test.Base() + (Disp), test.Dst()) << TestString << " with Disp " \
+ << Disp; \
+ reset(); \
+ } while (0)
+
+#define TestLeaIndex32bitDisp(Index, IndexValue, Disp, Dst0, Dst1, Dst2, Dst3) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Index ", " #IndexValue ", " #Dst0 ", " #Dst1 ", " #Dst2 \
+ ", " #Dst3 ")"; \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Index, \
+ Immediate(IndexValue)); \
+ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst0, \
+ Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_1, Disp)); \
+ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst1, \
+ Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_2, Disp)); \
+ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst2, \
+ Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_4, Disp)); \
+ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst3, \
+ Address(GPRRegister::Encoded_Reg_##Index, Traits::TIMES_8, Disp)); \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ ASSERT_EQ((test.Index() << Traits::TIMES_1) + (Disp), test.Dst0()) \
+ << TestString << " " << Disp; \
+ ASSERT_EQ((test.Index() << Traits::TIMES_2) + (Disp), test.Dst1()) \
+ << TestString << " " << Disp; \
+ ASSERT_EQ((test.Index() << Traits::TIMES_4) + (Disp), test.Dst2()) \
+ << TestString << " " << Disp; \
+ ASSERT_EQ((test.Index() << Traits::TIMES_8) + (Disp), test.Dst3()) \
+ << TestString << " " << Disp; \
+ reset(); \
+ } while (0)
+
+#define TestLeaBaseIndexDisp(Base, BaseValue, Index, IndexValue, Disp, Dst0, \
+ Dst1, Dst2, Dst3) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Base ", " #BaseValue ", " #Index ", " #IndexValue ", " #Dst0 \
+ ", " #Dst1 ", " #Dst2 ", " #Dst3 ")"; \
+ if (GPRRegister::Encoded_Reg_##Base != GPRRegister::Encoded_Reg_esp && \
+ GPRRegister::Encoded_Reg_##Base != GPRRegister::Encoded_Reg_ebp) { \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Base, \
+ Immediate(BaseValue)); \
+ } \
+ /* esp is not a valid index register. */ \
+ if (GPRRegister::Encoded_Reg_##Index != GPRRegister::Encoded_Reg_ebp) { \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Index, \
+ Immediate(IndexValue)); \
+ } \
+ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst0, \
+ Address(GPRRegister::Encoded_Reg_##Base, \
+ GPRRegister::Encoded_Reg_##Index, Traits::TIMES_1, Disp)); \
+ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst1, \
+ Address(GPRRegister::Encoded_Reg_##Base, \
+ GPRRegister::Encoded_Reg_##Index, Traits::TIMES_2, Disp)); \
+ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst2, \
+ Address(GPRRegister::Encoded_Reg_##Base, \
+ GPRRegister::Encoded_Reg_##Index, Traits::TIMES_4, Disp)); \
+ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst3, \
+ Address(GPRRegister::Encoded_Reg_##Base, \
+ GPRRegister::Encoded_Reg_##Index, Traits::TIMES_8, Disp)); \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ uint32_t ExpectedIndexValue = test.Index(); \
+ if (GPRRegister::Encoded_Reg_##Index == GPRRegister::Encoded_Reg_esp) { \
+ ExpectedIndexValue = 0; \
+ } \
+ ASSERT_EQ(test.Base() + (ExpectedIndexValue << Traits::TIMES_1) + (Disp), \
+ test.Dst0()) \
+ << TestString << " " << Disp; \
+ ASSERT_EQ(test.Base() + (ExpectedIndexValue << Traits::TIMES_2) + (Disp), \
+ test.Dst1()) \
+ << TestString << " " << Disp; \
+ ASSERT_EQ(test.Base() + (ExpectedIndexValue << Traits::TIMES_4) + (Disp), \
+ test.Dst2()) \
+ << TestString << " " << Disp; \
+ ASSERT_EQ(test.Base() + (ExpectedIndexValue << Traits::TIMES_8) + (Disp), \
+ test.Dst3()) \
+ << TestString << " " << Disp; \
+ reset(); \
+ } while (0)
+
+ for (const int32_t Disp :
+ {0x00, 0x06, -0x06, 0x0600, -0x6000, 0x6000000, -0x6000000}) {
+ TestLeaBaseDisp(eax, 0x10000Fu, Disp, ebx);
+ TestLeaBaseDisp(ebx, 0x20000Fu, Disp, ecx);
+ TestLeaBaseDisp(ecx, 0x30000Fu, Disp, edx);
+ TestLeaBaseDisp(edx, 0x40000Fu, Disp, esi);
+ TestLeaBaseDisp(esi, 0x50000Fu, Disp, edi);
+ TestLeaBaseDisp(edi, 0x60000Fu, Disp, eax);
+ TestLeaBaseDisp(esp, 0x11000Fu, Disp, eax);
+ TestLeaBaseDisp(ebp, 0x22000Fu, Disp, ecx);
+ }
+
+ // esp is not a valid index register.
+ // ebp is not valid in this addressing mode (rm = 0).
+ for (const int32_t Disp :
+ {0x00, 0x06, -0x06, 0x0600, -0x6000, 0x6000000, -0x6000000}) {
+ TestLeaIndex32bitDisp(eax, 0x2000u, Disp, ebx, ecx, edx, esi);
+ TestLeaIndex32bitDisp(ebx, 0x4000u, Disp, ecx, edx, esi, edi);
+ TestLeaIndex32bitDisp(ecx, 0x6000u, Disp, edx, esi, edi, eax);
+ TestLeaIndex32bitDisp(edx, 0x8000u, Disp, esi, edi, eax, ebx);
+ TestLeaIndex32bitDisp(esi, 0xA000u, Disp, edi, eax, ebx, ecx);
+ TestLeaIndex32bitDisp(edi, 0xC000u, Disp, eax, ebx, ecx, edx);
+ }
+
+ for (const int32_t Disp :
+ {0x00, 0x06, -0x06, 0x0600, -0x6000, 0x6000000, -0x6000000}) {
+ TestLeaBaseIndexDisp(eax, 0x100000u, ebx, 0x600u, Disp, ecx, edx, esi, edi);
+ TestLeaBaseIndexDisp(ebx, 0x200000u, ecx, 0x500u, Disp, edx, esi, edi, eax);
+ TestLeaBaseIndexDisp(ecx, 0x300000u, edx, 0x400u, Disp, esi, edi, eax, ebx);
+ TestLeaBaseIndexDisp(edx, 0x400000u, esi, 0x300u, Disp, edi, eax, ebx, ecx);
+ TestLeaBaseIndexDisp(esi, 0x500000u, edi, 0x200u, Disp, eax, ebx, ecx, edx);
+ TestLeaBaseIndexDisp(edi, 0x600000u, eax, 0x100u, Disp, ebx, ecx, edx, esi);
+
+ /* Initializers are ignored when Src[01] is ebp/esp. */
+ TestLeaBaseIndexDisp(esp, 0, ebx, 0x6000u, Disp, ecx, edx, esi, edi);
+ TestLeaBaseIndexDisp(esp, 0, ecx, 0x5000u, Disp, edx, esi, edi, eax);
+ TestLeaBaseIndexDisp(esp, 0, edx, 0x4000u, Disp, esi, edi, eax, ebx);
+ TestLeaBaseIndexDisp(esp, 0, esi, 0x3000u, Disp, edi, eax, ebx, ecx);
+ TestLeaBaseIndexDisp(esp, 0, edi, 0x2000u, Disp, eax, ebx, ecx, edx);
+ TestLeaBaseIndexDisp(esp, 0, eax, 0x1000u, Disp, ebx, ecx, edx, esi);
+
+ TestLeaBaseIndexDisp(ebp, 0, ebx, 0x6000u, Disp, ecx, edx, esi, edi);
+ TestLeaBaseIndexDisp(ebp, 0, ecx, 0x5000u, Disp, edx, esi, edi, eax);
+ TestLeaBaseIndexDisp(ebp, 0, edx, 0x4000u, Disp, esi, edi, eax, ebx);
+ TestLeaBaseIndexDisp(ebp, 0, esi, 0x3000u, Disp, edi, eax, ebx, ecx);
+ TestLeaBaseIndexDisp(ebp, 0, edi, 0x2000u, Disp, eax, ebx, ecx, edx);
+ TestLeaBaseIndexDisp(ebp, 0, eax, 0x1000u, Disp, ebx, ecx, edx, esi);
+
+ TestLeaBaseIndexDisp(eax, 0x1000000u, ebp, 0, Disp, ecx, edx, esi, edi);
+ TestLeaBaseIndexDisp(ebx, 0x2000000u, ebp, 0, Disp, edx, esi, edi, eax);
+ TestLeaBaseIndexDisp(ecx, 0x3000000u, ebp, 0, Disp, esi, edi, eax, ebx);
+ TestLeaBaseIndexDisp(edx, 0x4000000u, ebp, 0, Disp, edi, eax, ebx, ecx);
+ TestLeaBaseIndexDisp(esi, 0x5000000u, ebp, 0, Disp, eax, ebx, ecx, edx);
+ TestLeaBaseIndexDisp(edi, 0x6000000u, ebp, 0, Disp, ebx, ecx, edx, esi);
+
+ TestLeaBaseIndexDisp(esp, 0, ebp, 0, Disp, ebx, ecx, edx, esi);
+ }
+
+// Absolute addressing mode is tested in the Low Level tests. The encoding used
+// by the assembler has different meanings in x86-32 and x86-64.
+#undef TestLeaBaseIndexDisp
+#undef TestLeaScaled32bitDisp
+#undef TestLeaBaseDisp
+}
+
+TEST_F(AssemblerX8632LowLevelTest, LeaAbsolute) {
+#define TestLeaAbsolute(Dst, Value) \
+ do { \
+ static constexpr char TestString[] = "(" #Dst ", " #Value ")"; \
+ __ lea(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \
+ Address(Address::ABSOLUTE, Value)); \
+ static constexpr uint32_t ByteCount = 6; \
+ ASSERT_EQ(ByteCount, codeBytesSize()) << TestString; \
+ static constexpr uint8_t Opcode = 0x8D; \
+ static constexpr uint8_t ModRM = \
+ /*mod=*/0x00 | /*reg*/ (GPRRegister::Encoded_Reg_##Dst << 3) | \
+ /*rm*/ GPRRegister::Encoded_Reg_ebp; \
+ verifyBytes<ByteCount>(codeBytes(), Opcode, ModRM, (Value)&0xFF, \
+ (Value >> 8) & 0xFF, (Value >> 16) & 0xFF, \
+ (Value >> 24) & 0xFF); \
+ reset(); \
+ } while (0)
+
+ TestLeaAbsolute(eax, 0x11BEEF22);
+ TestLeaAbsolute(ebx, 0x33BEEF44);
+ TestLeaAbsolute(ecx, 0x55BEEF66);
+ TestLeaAbsolute(edx, 0x77BEEF88);
+ TestLeaAbsolute(esi, 0x99BEEFAA);
+ TestLeaAbsolute(edi, 0xBBBEEFBB);
+
+#undef TesLeaAbsolute
+}
+
+TEST_F(AssemblerX8632Test, CmovRegReg) {
+#define TestCmovRegReg(C, Src0, Value0, Src1, Value1, Dest, IsTrue) \
+ do { \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src0, Immediate(Value0)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src1, Immediate(Value1)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(Value0)); \
+ __ cmp(IceType_i32, GPRRegister::Encoded_Reg_##Src0, \
+ GPRRegister::Encoded_Reg_##Src1); \
+ __ cmov(IceType_i32, Cond::Br_##C, GPRRegister::Encoded_Reg_##Dest, \
+ GPRRegister::Encoded_Reg_##Src1); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ ASSERT_EQ((IsTrue) ? (Value1) : (Value0), test.Dest()) \
+ << "(" #C ", " #Src0 ", " #Value0 ", " #Src1 ", " #Value1 ", " #Dest \
+ ", " #IsTrue ")"; \
+ \
+ reset(); \
+ } while (0)
+
+ TestCmovRegReg(o, eax, 0x80000000u, ebx, 0x1u, ecx, 1u);
+ TestCmovRegReg(o, eax, 0x1u, ebx, 0x10000000u, ecx, 0u);
+
+ TestCmovRegReg(no, ebx, 0x1u, ecx, 0x10000000u, edx, 1u);
+ TestCmovRegReg(no, ebx, 0x80000000u, ecx, 0x1u, edx, 0u);
+
+ TestCmovRegReg(b, ecx, 0x1, edx, 0x80000000u, eax, 1u);
+ TestCmovRegReg(b, ecx, 0x80000000u, edx, 0x1u, eax, 0u);
+
+ TestCmovRegReg(ae, edx, 0x80000000u, edi, 0x1u, ebx, 1u);
+ TestCmovRegReg(ae, edx, 0x1u, edi, 0x80000000u, ebx, 0u);
+
+ TestCmovRegReg(e, edi, 0x1u, esi, 0x1u, ecx, 1u);
+ TestCmovRegReg(e, edi, 0x1u, esi, 0x11111u, ecx, 0u);
+
+ TestCmovRegReg(ne, esi, 0x80000000u, eax, 0x1u, edx, 1u);
+ TestCmovRegReg(ne, esi, 0x1u, eax, 0x1u, edx, 0u);
+
+ TestCmovRegReg(be, eax, 0x1u, ebx, 0x80000000u, eax, 1u);
+ TestCmovRegReg(be, eax, 0x80000000u, ebx, 0x1u, eax, 0u);
+
+ TestCmovRegReg(a, ebx, 0x80000000u, ecx, 0x1u, ebx, 1u);
+ TestCmovRegReg(a, ebx, 0x1u, ecx, 0x80000000u, ebx, 0u);
+
+ TestCmovRegReg(s, ecx, 0x1u, edx, 0x80000000u, ecx, 1u);
+ TestCmovRegReg(s, ecx, 0x80000000u, edx, 0x1u, ecx, 0u);
+
+ TestCmovRegReg(ns, edx, 0x80000000u, edi, 0x1u, ecx, 1u);
+ TestCmovRegReg(ns, edx, 0x1u, edi, 0x80000000u, ecx, 0u);
+
+ TestCmovRegReg(p, edi, 0x80000000u, esi, 0x1u, edx, 1u);
+ TestCmovRegReg(p, edi, 0x1u, esi, 0x80000000u, edx, 0u);
+
+ TestCmovRegReg(np, esi, 0x1u, edi, 0x80000000u, eax, 1u);
+ TestCmovRegReg(np, esi, 0x80000000u, edi, 0x1u, eax, 0u);
+
+ TestCmovRegReg(l, edi, 0x80000000u, eax, 0x1u, ebx, 1u);
+ TestCmovRegReg(l, edi, 0x1u, eax, 0x80000000u, ebx, 0u);
+
+ TestCmovRegReg(ge, eax, 0x1u, ebx, 0x80000000u, ecx, 1u);
+ TestCmovRegReg(ge, eax, 0x80000000u, ebx, 0x1u, ecx, 0u);
+
+ TestCmovRegReg(le, ebx, 0x80000000u, ecx, 0x1u, edx, 1u);
+ TestCmovRegReg(le, ebx, 0x1u, ecx, 0x80000000u, edx, 0u);
+
+#undef TestCmovRegReg
+}
+
+TEST_F(AssemblerX8632Test, CmovRegAddr) {
+#define TestCmovRegAddr(C, Src0, Value0, Value1, Dest, IsTrue) \
+ do { \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Value1; \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src0, Immediate(Value0)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(Value0)); \
+ __ cmp(IceType_i32, GPRRegister::Encoded_Reg_##Src0, dwordAddress(T0)); \
+ __ cmov(IceType_i32, Cond::Br_##C, GPRRegister::Encoded_Reg_##Dest, \
+ dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ ASSERT_EQ((IsTrue) ? (Value1) : (Value0), test.Dest()) \
+ << "(" #C ", " #Src0 ", " #Value0 ", " #Value1 ", " #Dest ", " #IsTrue \
+ ")"; \
+ \
+ reset(); \
+ } while (0)
+
+ TestCmovRegAddr(o, eax, 0x80000000u, 0x1u, ecx, 1u);
+ TestCmovRegAddr(o, eax, 0x1u, 0x10000000u, ecx, 0u);
+
+ TestCmovRegAddr(no, ebx, 0x1u, 0x10000000u, edx, 1u);
+ TestCmovRegAddr(no, ebx, 0x80000000u, 0x1u, edx, 0u);
+
+ TestCmovRegAddr(b, ecx, 0x1, 0x80000000u, eax, 1u);
+ TestCmovRegAddr(b, ecx, 0x80000000u, 0x1u, eax, 0u);
+
+ TestCmovRegAddr(ae, edx, 0x80000000u, 0x1u, ebx, 1u);
+ TestCmovRegAddr(ae, edx, 0x1u, 0x80000000u, ebx, 0u);
+
+ TestCmovRegAddr(e, edi, 0x1u, 0x1u, ecx, 1u);
+ TestCmovRegAddr(e, edi, 0x1u, 0x11111u, ecx, 0u);
+
+ TestCmovRegAddr(ne, esi, 0x80000000u, 0x1u, edx, 1u);
+ TestCmovRegAddr(ne, esi, 0x1u, 0x1u, edx, 0u);
+
+ TestCmovRegAddr(be, eax, 0x1u, 0x80000000u, eax, 1u);
+ TestCmovRegAddr(be, eax, 0x80000000u, 0x1u, eax, 0u);
+
+ TestCmovRegAddr(a, ebx, 0x80000000u, 0x1u, ebx, 1u);
+ TestCmovRegAddr(a, ebx, 0x1u, 0x80000000u, ebx, 0u);
+
+ TestCmovRegAddr(s, ecx, 0x1u, 0x80000000u, ecx, 1u);
+ TestCmovRegAddr(s, ecx, 0x80000000u, 0x1u, ecx, 0u);
+
+ TestCmovRegAddr(ns, edx, 0x80000000u, 0x1u, ecx, 1u);
+ TestCmovRegAddr(ns, edx, 0x1u, 0x80000000u, ecx, 0u);
+
+ TestCmovRegAddr(p, edi, 0x80000000u, 0x1u, edx, 1u);
+ TestCmovRegAddr(p, edi, 0x1u, 0x80000000u, edx, 0u);
+
+ TestCmovRegAddr(np, esi, 0x1u, 0x80000000u, eax, 1u);
+ TestCmovRegAddr(np, esi, 0x80000000u, 0x1u, eax, 0u);
+
+ TestCmovRegAddr(l, edi, 0x80000000u, 0x1u, ebx, 1u);
+ TestCmovRegAddr(l, edi, 0x1u, 0x80000000u, ebx, 0u);
+
+ TestCmovRegAddr(ge, eax, 0x1u, 0x80000000u, ecx, 1u);
+ TestCmovRegAddr(ge, eax, 0x80000000u, 0x1u, ecx, 0u);
+
+ TestCmovRegAddr(le, ebx, 0x80000000u, 0x1u, edx, 1u);
+ TestCmovRegAddr(le, ebx, 0x1u, 0x80000000u, edx, 0u);
+
+#undef TestCmovRegAddr
+}
+
+TEST_F(AssemblerX8632LowLevelTest, RepMovsb) {
+ __ rep_movsb();
+
+ static constexpr uint32_t ByteCount = 2;
+ static constexpr uint8_t Prefix = 0xF3;
+ static constexpr uint8_t Opcode = 0xA4;
+
+ ASSERT_EQ(ByteCount, codeBytesSize());
+ verifyBytes<ByteCount>(codeBytes(), Prefix, Opcode);
+}
+
+TEST_F(AssemblerX8632Test, MovssXmmAddr) {
+#define TestMovssXmmAddrFloatLength(FloatLength, Xmm, Value) \
+ do { \
+ static_assert((FloatLength) == 32 || (FloatLength) == 64, \
+ "Invalid fp length #FloatLength"); \
+ using Type = std::conditional<FloatLength == 32, float, double>::type; \
+ \
+ static constexpr char TestString[] = "(" #FloatLength ", " #Xmm ")"; \
+ static constexpr bool IsDouble = std::is_same<Type, double>::value; \
+ const uint32_t T0 = allocateQword(); \
+ const Type V0 = Value; \
+ \
+ __ movss(IceType_f##FloatLength, XmmRegister::Encoded_Reg_##Xmm, \
+ dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ if (IsDouble) { \
+ test.setQwordTo(T0, static_cast<double>(V0)); \
+ } else { \
+ test.setDwordTo(T0, static_cast<float>(V0)); \
+ } \
+ test.run(); \
+ ASSERT_DOUBLE_EQ(Value, test.Xmm<Type>()) << TestString << " value is " \
+ << Value; \
+ reset(); \
+ } while (0)
+
+#define TestMovssXmmAddr(FloatLength) \
+ do { \
+ using Type = std::conditional<FloatLength == 32, float, double>::type; \
+ for (const Type Value : {0.0, -0.0, 1.0, -1.0, 3.14, 99999.9999}) { \
+ TestMovssXmmAddrFloatLength(FloatLength, xmm0, Value); \
+ TestMovssXmmAddrFloatLength(FloatLength, xmm1, Value); \
+ TestMovssXmmAddrFloatLength(FloatLength, xmm2, Value); \
+ TestMovssXmmAddrFloatLength(FloatLength, xmm3, Value); \
+ TestMovssXmmAddrFloatLength(FloatLength, xmm4, Value); \
+ TestMovssXmmAddrFloatLength(FloatLength, xmm5, Value); \
+ TestMovssXmmAddrFloatLength(FloatLength, xmm6, Value); \
+ TestMovssXmmAddrFloatLength(FloatLength, xmm7, Value); \
+ } \
+ } while (0)
+
+ TestMovssXmmAddr(32);
+ TestMovssXmmAddr(64);
+
+#undef TestMovssXmmAddr
+#undef TestMovssXmmAddrType
+}
+
+TEST_F(AssemblerX8632Test, MovssAddrXmm) {
+#define TestMovssAddrXmmFloatLength(FloatLength, Xmm, Value) \
+ do { \
+ static_assert((FloatLength) == 32 || (FloatLength) == 64, \
+ "Invalid fp length #FloatLength"); \
+ using Type = std::conditional<FloatLength == 32, float, double>::type; \
+ \
+ static constexpr char TestString[] = "(" #FloatLength ", " #Xmm ")"; \
+ static constexpr bool IsDouble = std::is_same<Type, double>::value; \
+ const uint32_t T0 = allocateQword(); \
+ const Type V0 = Value; \
+ const uint32_t T1 = allocateQword(); \
+ static_assert(std::numeric_limits<Type>::has_quiet_NaN, \
+ "f" #FloatLength " does not have quiet nan."); \
+ const Type V1 = std::numeric_limits<Type>::quiet_NaN(); \
+ \
+ __ movss(IceType_f##FloatLength, XmmRegister::Encoded_Reg_##Xmm, \
+ dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ if (IsDouble) { \
+ test.setQwordTo(T0, static_cast<double>(V0)); \
+ test.setQwordTo(T1, static_cast<double>(V1)); \
+ } else { \
+ test.setDwordTo(T0, static_cast<float>(V0)); \
+ test.setDwordTo(T1, static_cast<float>(V1)); \
+ } \
+ test.run(); \
+ ASSERT_DOUBLE_EQ(Value, test.Xmm<Type>()) << TestString << " value is " \
+ << Value; \
+ reset(); \
+ } while (0)
+
+#define TestMovssAddrXmm(FloatLength) \
+ do { \
+ using Type = std::conditional<FloatLength == 32, float, double>::type; \
+ for (const Type Value : {0.0, -0.0, 1.0, -1.0, 3.14, 99999.9999}) { \
+ TestMovssAddrXmmFloatLength(FloatLength, xmm0, Value); \
+ TestMovssAddrXmmFloatLength(FloatLength, xmm1, Value); \
+ TestMovssAddrXmmFloatLength(FloatLength, xmm2, Value); \
+ TestMovssAddrXmmFloatLength(FloatLength, xmm3, Value); \
+ TestMovssAddrXmmFloatLength(FloatLength, xmm4, Value); \
+ TestMovssAddrXmmFloatLength(FloatLength, xmm5, Value); \
+ TestMovssAddrXmmFloatLength(FloatLength, xmm6, Value); \
+ TestMovssAddrXmmFloatLength(FloatLength, xmm7, Value); \
+ } \
+ } while (0)
+
+ TestMovssAddrXmm(32);
+ TestMovssAddrXmm(64);
+
+#undef TestMovssAddrXmm
+#undef TestMovssAddrXmmType
+}
+
+TEST_F(AssemblerX8632Test, MovssXmmXmm) {
+#define TestMovssXmmXmmFloatLength(FloatLength, Src, Dst, Value) \
+ do { \
+ static_assert((FloatLength) == 32 || (FloatLength) == 64, \
+ "Invalid fp length #FloatLength"); \
+ using Type = std::conditional<FloatLength == 32, float, double>::type; \
+ \
+ static constexpr char TestString[] = \
+ "(" #FloatLength ", " #Src ", " #Dst ")"; \
+ static constexpr bool IsDouble = std::is_same<Type, double>::value; \
+ const uint32_t T0 = allocateQword(); \
+ const Type V0 = Value; \
+ const uint32_t T1 = allocateQword(); \
+ static_assert(std::numeric_limits<Type>::has_quiet_NaN, \
+ "f" #FloatLength " does not have quiet nan."); \
+ const Type V1 = std::numeric_limits<Type>::quiet_NaN(); \
+ \
+ __ movss(IceType_f##FloatLength, XmmRegister::Encoded_Reg_##Src, \
+ dwordAddress(T0)); \
+ __ movss(IceType_f##FloatLength, XmmRegister::Encoded_Reg_##Dst, \
+ dwordAddress(T1)); \
+ __ movss(IceType_f##FloatLength, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ if (IsDouble) { \
+ test.setQwordTo(T0, static_cast<double>(V0)); \
+ test.setQwordTo(T1, static_cast<double>(V1)); \
+ } else { \
+ test.setDwordTo(T0, static_cast<float>(V0)); \
+ test.setDwordTo(T1, static_cast<float>(V1)); \
+ } \
+ test.run(); \
+ ASSERT_DOUBLE_EQ(Value, test.Dst<Type>()) << TestString << " value is " \
+ << Value; \
+ reset(); \
+ } while (0)
+
+#define TestMovssXmmXmm(FloatLength) \
+ do { \
+ using Type = std::conditional<FloatLength == 32, float, double>::type; \
+ for (const Type Value : {0.0, -0.0, 1.0, -1.0, 3.14, 99999.9999}) { \
+ TestMovssXmmXmmFloatLength(FloatLength, xmm0, xmm1, Value); \
+ TestMovssXmmXmmFloatLength(FloatLength, xmm1, xmm2, Value); \
+ TestMovssXmmXmmFloatLength(FloatLength, xmm2, xmm3, Value); \
+ TestMovssXmmXmmFloatLength(FloatLength, xmm3, xmm4, Value); \
+ TestMovssXmmXmmFloatLength(FloatLength, xmm4, xmm5, Value); \
+ TestMovssXmmXmmFloatLength(FloatLength, xmm5, xmm6, Value); \
+ TestMovssXmmXmmFloatLength(FloatLength, xmm6, xmm7, Value); \
+ TestMovssXmmXmmFloatLength(FloatLength, xmm7, xmm0, Value); \
+ } \
+ } while (0)
+
+ TestMovssXmmXmm(32);
+ TestMovssXmmXmm(64);
+
+#undef TestMovssXmmXmm
+#undef TestMovssXmmXmmType
+}
+
+TEST_F(AssemblerX8632Test, MovdToXmm) {
+#define TestMovdXmmReg(Src, Dst, Value) \
+ do { \
+ assert(((Value)&0xFFFFFFFF) == (Value)); \
+ static constexpr char TestString[] = "(" #Src ", " #Dst ")"; \
+ const uint32_t T0 = allocateQword(); \
+ const uint64_t V0 = 0xFFFFFFFF00000000ull; \
+ \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src, Immediate(Value)); \
+ __ movss(IceType_f64, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movd(XmmRegister::Encoded_Reg_##Dst, GPRRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ \
+ test.setQwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is " \
+ << Value; \
+ reset(); \
+ } while (0)
+
+#define TestMovdXmmAddr(Dst, Value) \
+ do { \
+ assert(((Value)&0xFFFFFFFF) == (Value)); \
+ static constexpr char TestString[] = "(" #Dst ", Addr)"; \
+ const uint32_t T0 = allocateQword(); \
+ const uint32_t V0 = Value; \
+ const uint32_t T1 = allocateQword(); \
+ const uint64_t V1 = 0xFFFFFFFF00000000ull; \
+ \
+ __ movss(IceType_f64, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \
+ __ movd(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ \
+ test.setDwordTo(T0, V0); \
+ test.setQwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is " \
+ << Value; \
+ reset(); \
+ } while (0)
+
+#define TestMovd(Dst) \
+ do { \
+ for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) { \
+ TestMovdXmmReg(eax, Dst, Value); \
+ TestMovdXmmReg(ebx, Dst, Value); \
+ TestMovdXmmReg(ecx, Dst, Value); \
+ TestMovdXmmReg(edx, Dst, Value); \
+ TestMovdXmmReg(esi, Dst, Value); \
+ TestMovdXmmReg(edi, Dst, Value); \
+ TestMovdXmmAddr(Dst, Value); \
+ } \
+ } while (0)
+
+ TestMovd(xmm0);
+ TestMovd(xmm1);
+ TestMovd(xmm2);
+ TestMovd(xmm3);
+ TestMovd(xmm4);
+ TestMovd(xmm5);
+ TestMovd(xmm6);
+ TestMovd(xmm7);
+
+#undef TestMovdXmmAddr
+#undef TestMovdXmmReg
+#undef TestMovd
+}
+
+TEST_F(AssemblerX8632Test, MovdFromXmm) {
+#define TestMovdRegXmm(Src, Dst, Value) \
+ do { \
+ assert(((Value)&0xFFFFFFFF) == (Value)); \
+ static constexpr char TestString[] = "(" #Src ", " #Dst ")"; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Value; \
+ \
+ __ movss(IceType_f64, XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ movd(GPRRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(Value, test.contentsOfDword(T0)) << TestString << " value is " \
+ << Value; \
+ reset(); \
+ } while (0)
+
+#define TestMovdAddrXmm(Src, Value) \
+ do { \
+ assert(((Value)&0xFFFFFFFF) == (Value)); \
+ static constexpr char TestString[] = "(" #Src ", Addr)"; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Value; \
+ const uint32_t T1 = allocateDword(); \
+ const uint32_t V1 = ~(Value); \
+ \
+ __ movss(IceType_f64, XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ movd(dwordAddress(T1), XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ \
+ test.setDwordTo(T0, V0); \
+ test.setDwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(Value, test.contentsOfDword(T1)) << TestString << " value is " \
+ << Value; \
+ reset(); \
+ } while (0)
+
+#define TestMovd(Src) \
+ do { \
+ for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) { \
+ TestMovdRegXmm(Src, eax, Value); \
+ TestMovdRegXmm(Src, ebx, Value); \
+ TestMovdRegXmm(Src, ecx, Value); \
+ TestMovdRegXmm(Src, edx, Value); \
+ TestMovdRegXmm(Src, esi, Value); \
+ TestMovdRegXmm(Src, edi, Value); \
+ TestMovdAddrXmm(Src, Value); \
+ } \
+ } while (0)
+
+ TestMovd(xmm0);
+ TestMovd(xmm1);
+ TestMovd(xmm2);
+ TestMovd(xmm3);
+ TestMovd(xmm4);
+ TestMovd(xmm5);
+ TestMovd(xmm6);
+ TestMovd(xmm7);
+
+#undef TestMovdAddrXmm
+#undef TestMovdRegXmm
+#undef TestMovd
+}
+
+TEST_F(AssemblerX8632Test, MovqXmmAddr) {
+#define TestMovd(Dst, Value) \
+ do { \
+ static constexpr char TestString[] = "(" #Dst ", Addr)"; \
+ const uint32_t T0 = allocateQword(); \
+ const uint64_t V0 = Value; \
+ const uint32_t T1 = allocateQword(); \
+ const uint64_t V1 = ~(Value); \
+ \
+ __ movss(IceType_f64, XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \
+ __ movq(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ \
+ test.setQwordTo(T0, V0); \
+ test.setQwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is " \
+ << Value; \
+ reset(); \
+ } while (0)
+
+ for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) {
+ TestMovd(xmm0, Value);
+ TestMovd(xmm1, Value);
+ TestMovd(xmm2, Value);
+ TestMovd(xmm3, Value);
+ TestMovd(xmm4, Value);
+ TestMovd(xmm5, Value);
+ TestMovd(xmm6, Value);
+ TestMovd(xmm7, Value);
+ }
+
+#undef TestMovd
+}
+
+TEST_F(AssemblerX8632Test, MovqAddrXmm) {
+#define TestMovd(Dst, Value) \
+ do { \
+ static constexpr char TestString[] = "(" #Dst ", Addr)"; \
+ const uint32_t T0 = allocateQword(); \
+ const uint64_t V0 = Value; \
+ const uint32_t T1 = allocateQword(); \
+ const uint64_t V1 = ~(Value); \
+ \
+ __ movq(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movq(dwordAddress(T1), XmmRegister::Encoded_Reg_##Dst); \
+ \
+ AssembledTest test = assemble(); \
+ \
+ test.setQwordTo(T0, V0); \
+ test.setQwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is " \
+ << Value; \
+ reset(); \
+ } while (0)
+
+ for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) {
+ TestMovd(xmm0, Value);
+ TestMovd(xmm1, Value);
+ TestMovd(xmm2, Value);
+ TestMovd(xmm3, Value);
+ TestMovd(xmm4, Value);
+ TestMovd(xmm5, Value);
+ TestMovd(xmm6, Value);
+ TestMovd(xmm7, Value);
+ }
+
+#undef TestMovd
+}
+
+TEST_F(AssemblerX8632Test, MovqXmmXmm) {
+#define TestMovd(Src, Dst, Value) \
+ do { \
+ static constexpr char TestString[] = "(" #Src ", " #Dst ")"; \
+ const uint32_t T0 = allocateQword(); \
+ const uint64_t V0 = Value; \
+ const uint32_t T1 = allocateQword(); \
+ const uint64_t V1 = ~(Value); \
+ \
+ __ movq(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ movq(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \
+ __ movq(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ \
+ test.setQwordTo(T0, V0); \
+ test.setQwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(Value, test.Dst<uint64_t>()) << TestString << " value is " \
+ << Value; \
+ reset(); \
+ } while (0)
+
+ for (uint32_t Value : {0u, 1u, 0x7FFFFFFFu, 0x80000000u, 0xFFFFFFFFu}) {
+ TestMovd(xmm0, xmm1, Value);
+ TestMovd(xmm1, xmm2, Value);
+ TestMovd(xmm2, xmm3, Value);
+ TestMovd(xmm3, xmm4, Value);
+ TestMovd(xmm4, xmm5, Value);
+ TestMovd(xmm5, xmm6, Value);
+ TestMovd(xmm6, xmm7, Value);
+ TestMovd(xmm7, xmm0, Value);
+ }
+
+#undef TestMovd
+}
+
+TEST_F(AssemblerX8632Test, 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, XmmRegister::Encoded_Reg_##Dst, \
+ dwordAddress(T0)); \
+ __ movss(IceType_f##FloatSize, XmmRegister::Encoded_Reg_##Src, \
+ dwordAddress(T1)); \
+ __ Inst(IceType_f##FloatSize, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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, XmmRegister::Encoded_Reg_##Dst, \
+ dwordAddress(T0)); \
+ __ Inst(IceType_f##FloatSize, XmmRegister::Encoded_Reg_##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)
+
+ TestArithSS(32, xmm0, xmm1, xmm2);
+ TestArithSS(32, xmm1, xmm2, xmm3);
+ TestArithSS(32, xmm2, xmm3, xmm4);
+ TestArithSS(32, xmm3, xmm4, xmm5);
+ TestArithSS(32, xmm4, xmm5, xmm6);
+ TestArithSS(32, xmm5, xmm6, xmm7);
+ TestArithSS(32, xmm6, xmm7, xmm0);
+ TestArithSS(32, xmm7, xmm0, xmm1);
+
+ TestArithSS(64, xmm0, xmm1, xmm2);
+ TestArithSS(64, xmm1, xmm2, xmm3);
+ TestArithSS(64, xmm2, xmm3, xmm4);
+ TestArithSS(64, xmm3, xmm4, xmm5);
+ TestArithSS(64, xmm4, xmm5, xmm6);
+ TestArithSS(64, xmm5, xmm6, xmm7);
+ TestArithSS(64, xmm6, xmm7, xmm0);
+ TestArithSS(64, xmm7, xmm0, xmm1);
+
+#undef TestArithSS
+#undef TestArithSSXmmAddr
+#undef TestArithSSXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, MovupsXmmAddr) {
+#define TestMovups(Dst) \
+ do { \
+ static constexpr char TestString[] = "(" #Dst ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(), \
+ std::numeric_limits<float>::infinity()); \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(V0, test.Dst<Dqword>()) << TestString; \
+ reset(); \
+ } while (0)
+
+ TestMovups(xmm0);
+ TestMovups(xmm1);
+ TestMovups(xmm2);
+ TestMovups(xmm3);
+ TestMovups(xmm4);
+ TestMovups(xmm5);
+ TestMovups(xmm6);
+ TestMovups(xmm7);
+
+#undef TestMovups
+}
+
+TEST_F(AssemblerX8632Test, MovupsAddrXmm) {
+#define TestMovups(Src) \
+ do { \
+ static constexpr char TestString[] = "(" #Src ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(), \
+ std::numeric_limits<float>::infinity()); \
+ const uint32_t T1 = allocateDqword(); \
+ const Dqword V1(0.0, 0.0, 0.0, 0.0); \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ movups(dwordAddress(T1), XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.setDqwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(V0, test.contentsOfDqword(T1)) << TestString; \
+ reset(); \
+ } while (0)
+
+ TestMovups(xmm0);
+ TestMovups(xmm1);
+ TestMovups(xmm2);
+ TestMovups(xmm3);
+ TestMovups(xmm4);
+ TestMovups(xmm5);
+ TestMovups(xmm6);
+ TestMovups(xmm7);
+
+#undef TestMovups
+}
+
+TEST_F(AssemblerX8632Test, MovupsXmmXmm) {
+#define TestMovups(Dst, Src) \
+ do { \
+ static constexpr char TestString[] = "(" #Dst ", " #Src ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(), \
+ std::numeric_limits<float>::infinity()); \
+ const uint32_t T1 = allocateDqword(); \
+ const Dqword V1(0.0, 0.0, 0.0, 0.0); \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.setDqwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(V0, test.Dst<Dqword>()) << TestString; \
+ reset(); \
+ } while (0)
+
+ TestMovups(xmm0, xmm1);
+ TestMovups(xmm1, xmm2);
+ TestMovups(xmm2, xmm3);
+ TestMovups(xmm3, xmm4);
+ TestMovups(xmm4, xmm5);
+ TestMovups(xmm5, xmm6);
+ TestMovups(xmm6, xmm7);
+ TestMovups(xmm7, xmm0);
+
+#undef TestMovups
+}
+
+TEST_F(AssemblerX8632Test, MovapsXmmXmm) {
+#define TestMovaps(Dst, Src) \
+ do { \
+ static constexpr char TestString[] = "(" #Dst ", " #Src ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ const Dqword V0(1.0f, -1.0, std::numeric_limits<float>::quiet_NaN(), \
+ std::numeric_limits<float>::infinity()); \
+ const uint32_t T1 = allocateDqword(); \
+ const Dqword V1(0.0, 0.0, 0.0, 0.0); \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \
+ __ movaps(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.setDqwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(V0, test.Dst<Dqword>()) << TestString; \
+ reset(); \
+ } while (0)
+
+ TestMovaps(xmm0, xmm1);
+ TestMovaps(xmm1, xmm2);
+ TestMovaps(xmm2, xmm3);
+ TestMovaps(xmm3, xmm4);
+ TestMovaps(xmm4, xmm5);
+ TestMovaps(xmm5, xmm6);
+ TestMovaps(xmm6, xmm7);
+ TestMovaps(xmm7, xmm0);
+
+#undef TestMovaps
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ Inst(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ Inst(IceType_i##Size, XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ Inst(IceType_i##Size, XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ pandn(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ pandn(IceType_i##Size, XmmRegister::Encoded_Reg_##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, xmm0);
+
+#undef TestPArith
+#undef TestPArithSize
+#undef TestPAndnXmmAddr
+#undef TestPAndnXmmXmm
+#undef TestPArithXmmImm
+#undef TestPArithXmmAddr
+#undef TestPArithXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, ArithPS) {
+#define TestArithPSXmmXmm(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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ Inst(IceType_f32, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ Inst(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ Inst(XmmRegister::Encoded_Reg_##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(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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ Inst(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##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(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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ Inst(IceType_f32, XmmRegister::Encoded_Reg_##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(Src, Dst) \
+ do { \
+ TestArithPSXmmXmm(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \
+ (0.55, 0.43, 0.23, 1.21), addps, +, float); \
+ TestArithPSXmmAddr(Src, (1.0, 100.0, -1000.0, 20.0), \
+ (0.55, 0.43, 0.23, 1.21), addps, +, float); \
+ TestArithPSXmmXmm(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \
+ (0.55, 0.43, 0.23, 1.21), subps, -, float); \
+ TestArithPSXmmAddr(Src, (1.0, 100.0, -1000.0, 20.0), \
+ (0.55, 0.43, 0.23, 1.21), subps, -, float); \
+ TestArithPSXmmXmm(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \
+ (0.55, 0.43, 0.23, 1.21), mulps, *, float); \
+ TestArithPSXmmAddr(Src, (1.0, 100.0, -1000.0, 20.0), \
+ (0.55, 0.43, 0.23, 1.21), mulps, *, float); \
+ TestArithPSXmmXmmUntyped(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \
+ (0.55, 0.43, 0.23, 1.21), andps, &, float); \
+ TestArithPSXmmXmmUntyped(Src, (1.0, -1000.0), Dst, (0.55, 1.21), andpd, &, \
+ double); \
+ TestArithPSXmmAddrUntyped(Src, (1.0, -1000.0), (0.55, 1.21), andpd, &, \
+ double); \
+ TestArithPSXmmXmmUntyped(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \
+ (0.55, 0.43, 0.23, 1.21), orps, |, float); \
+ TestArithPSXmmXmmUntyped(Src, (1.0, -1000.0), Dst, (0.55, 1.21), orpd, |, \
+ double); \
+ TestMinMaxPS(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \
+ (0.55, 0.43, 0.23, 1.21), minps, float); \
+ TestMinMaxPS(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \
+ (0.55, 0.43, 0.23, 1.21), maxps, float); \
+ TestMinMaxPS(Src, (1.0, -1000.0), Dst, (0.55, 1.21), minpd, double); \
+ TestMinMaxPS(Src, (1.0, -1000.0), Dst, (0.55, 1.21), maxpd, double); \
+ TestArithPSXmmXmmUntyped(Src, (1.0, 100.0, -1000.0, 20.0), Dst, \
+ (0.55, 0.43, 0.23, 1.21), xorps, ^, float); \
+ TestArithPSXmmAddrUntyped(Src, (1.0, 100.0, -1000.0, 20.0), \
+ (0.55, 0.43, 0.23, 1.21), xorps, ^, float); \
+ TestArithPSXmmXmmUntyped(Src, (1.0, -1000.0), Dst, (0.55, 1.21), xorpd, ^, \
+ double); \
+ TestArithPSXmmAddrUntyped(Src, (1.0, -1000.0), (0.55, 1.21), xorpd, ^, \
+ double); \
+ } while (0)
+
+#if 0
+
+#endif
+
+ TestArithPS(xmm0, xmm1);
+ TestArithPS(xmm1, xmm2);
+ TestArithPS(xmm2, xmm3);
+ TestArithPS(xmm3, xmm4);
+ TestArithPS(xmm4, xmm5);
+ TestArithPS(xmm5, xmm6);
+ TestArithPS(xmm6, xmm7);
+ TestArithPS(xmm7, xmm0);
+
+#undef TestArithPs
+#undef TestMinMaxPS
+#undef TestArithPSXmmXmmUntyped
+#undef TestArithPSXmmAddr
+#undef TestArithPSXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_xmm0, dwordAddress(Mask)); \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ Inst(IceType_##Type, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_xmm0, dwordAddress(Mask)); \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ Inst(IceType_##Type, XmmRegister::Encoded_Reg_##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, xmm1);
+
+#undef TestBlending
+#undef TestBlendingXmmAddr
+#undef TestBlendingXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, Cmpps) {
+#define TestCmppsXmmXmm(Dst, Src, C, Op) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Src ", " #Dst ", " #C ", " #Op ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ const Dqword V0(-1.0, 1.0, 3.14, 1024.5); \
+ const uint32_t T1 = allocateDqword(); \
+ const Dqword V1(-1.0, 1.0, 3.14, 1024.5); \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ cmpps(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src, \
+ Cond::Cmpps_##C); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.setDqwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(packedAs<float>(V0) Op V1, test.Dst<Dqword>()) << TestString; \
+ ; \
+ reset(); \
+ } while (0)
+
+#define TestCmppsXmmAddr(Dst, C, Op) \
+ do { \
+ static constexpr char TestString[] = "(" #Dst ", Addr, " #C ", " #Op ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ const Dqword V0(-1.0, 1.0, 3.14, 1024.5); \
+ const uint32_t T1 = allocateDqword(); \
+ const Dqword V1(-1.0, 1.0, 3.14, 1024.5); \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ cmpps(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1), \
+ Cond::Cmpps_##C); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.setDqwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(packedAs<float>(V0) Op V1, test.Dst<Dqword>()) << TestString; \
+ ; \
+ reset(); \
+ } while (0)
+
+#define TestCmppsOrdUnordXmmXmm(Dst, Src, C) \
+ 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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ cmpps(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src, \
+ Cond::Cmpps_##C); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.setDqwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(packedAs<float>(V0).C(V1), test.Dst<Dqword>()) << TestString; \
+ ; \
+ reset(); \
+ } while (0)
+
+#define TestCmppsOrdUnordXmmAddr(Dst, C) \
+ 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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ cmpps(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1), \
+ Cond::Cmpps_##C); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.setDqwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(packedAs<float>(V0).C(V1), test.Dst<Dqword>()) << TestString; \
+ ; \
+ reset(); \
+ } while (0)
+
+#define TestCmpps(Dst, Src) \
+ do { \
+ TestCmppsXmmXmm(Dst, Src, eq, == ); \
+ TestCmppsXmmAddr(Dst, eq, == ); \
+ TestCmppsXmmXmm(Dst, Src, eq, == ); \
+ TestCmppsXmmAddr(Dst, eq, == ); \
+ TestCmppsXmmXmm(Dst, Src, eq, == ); \
+ TestCmppsXmmAddr(Dst, eq, == ); \
+ TestCmppsOrdUnordXmmXmm(Dst, Src, unord); \
+ TestCmppsOrdUnordXmmAddr(Dst, unord); \
+ TestCmppsXmmXmm(Dst, Src, eq, == ); \
+ TestCmppsXmmAddr(Dst, eq, == ); \
+ TestCmppsXmmXmm(Dst, Src, eq, == ); \
+ TestCmppsXmmAddr(Dst, eq, == ); \
+ TestCmppsXmmXmm(Dst, Src, eq, == ); \
+ TestCmppsXmmAddr(Dst, eq, == ); \
+ TestCmppsOrdUnordXmmXmm(Dst, Src, unord); \
+ TestCmppsOrdUnordXmmAddr(Dst, unord); \
+ } while (0)
+
+ TestCmpps(xmm0, xmm1);
+ TestCmpps(xmm1, xmm2);
+ TestCmpps(xmm2, xmm3);
+ TestCmpps(xmm3, xmm4);
+ TestCmpps(xmm4, xmm5);
+ TestCmpps(xmm5, xmm6);
+ TestCmpps(xmm6, xmm7);
+ TestCmpps(xmm7, xmm0);
+
+#undef TestCmpps
+#undef TestCmppsOrdUnordXmmAddr
+#undef TestCmppsOrdUnordXmmXmm
+#undef TestCmppsXmmAddr
+#undef TestCmppsXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ Inst(XmmRegister::Encoded_Reg_##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);
+
+#undef TestImpl
+#undef TestImplSingle
+}
+
+TEST_F(AssemblerX8632Test, Movhlps_Movlhps) {
+#define TestImplSingle(Dst, Src, Inst, Expect) \
+ do { \
+ static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Inst ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ const Dqword V0(uint64_t(0xAAAAAAAABBBBBBBBull), \
+ uint64_t(0xCCCCCCCCDDDDDDDDull)); \
+ const uint32_t T1 = allocateDqword(); \
+ const Dqword V1(uint64_t(0xEEEEEEEEFFFFFFFFull), \
+ uint64_t(0x9999999988888888ull)); \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ Inst(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.setDqwordTo(T1, V1); \
+ test.run(); \
+ \
+ ASSERT_EQ(Dqword Expect, test.Dst<Dqword>()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImpl(Dst, Src) \
+ do { \
+ TestImplSingle(Dst, Src, movhlps, (uint64_t(0x9999999988888888ull), \
+ uint64_t(0xCCCCCCCCDDDDDDDDull))); \
+ TestImplSingle(Dst, Src, movlhps, (uint64_t(0xAAAAAAAABBBBBBBBull), \
+ uint64_t(0xEEEEEEEEFFFFFFFFull))); \
+ } while (0)
+
+ TestImpl(xmm0, xmm1);
+ TestImpl(xmm1, xmm2);
+ TestImpl(xmm2, xmm3);
+ TestImpl(xmm3, xmm4);
+ TestImpl(xmm4, xmm5);
+ TestImpl(xmm5, xmm6);
+ TestImpl(xmm6, xmm7);
+ TestImpl(xmm7, xmm0);
+
+#undef TestImpl
+#undef TestImplSingle
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ Inst(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##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, xmm0);
+
+#undef TestImpl
+#undef TestImplSingle
+}
+
+TEST_F(AssemblerX8632Test, 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));
+ const Dqword shufpsUntypedExpected(uint64_t(0x7777777711111111ull),
+ uint64_t(0xCCCCCCCCCCCCCCCCull));
+
+ const uint8_t shufpdImm = 0x02;
+ const Dqword shufpdUntypedExpected(uint64_t(0x1111111122222222ull),
+ uint64_t(0xCCCCCCCCDDDDDDDDull));
+
+#define TestImplSingleXmmXmm(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_f32, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ Inst(IceType_f32, XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ Inst(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##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); \
+ TestImplSingleXmmXmmUntyped(Dst, Src, shufps); \
+ TestImplSingleXmmXmmUntyped(Dst, Src, shufpd); \
+ } 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 TestImplSingleXmmXmmUntyped
+#undef TestImplSingleXmmAddr
+#undef TestImplSingleXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ cvt##Inst(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Dst ", " #GPR ", cvt" #Inst ", f" #Size ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##GPR, \
+ Immediate(Inst##Size##SrcValue)); \
+ __ cvt##Inst(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##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, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #GPR ", " #Src ", cvt" #Inst ", f" #Size ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##GPR, \
+ Immediate(Inst##Size##DstValue)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ cvt##Inst(IceType_f##Size, GPRRegister::Encoded_Reg_##GPR, \
+ XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, Inst##Size##SrcValue); \
+ test.run(); \
+ \
+ ASSERT_EQ(static_cast<uint32_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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ cvt##Inst(IceType_f##Size, XmmRegister::Encoded_Reg_##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) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Dst ", Addr, cvt" #Inst ", f" #Size ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ const uint32_t T1 = allocateDword(); \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ cvt##Inst(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \
+ 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, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #GPR ", Addr, cvt" #Inst ", f" #Size ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##GPR, \
+ Immediate(Inst##Size##DstValue)); \
+ __ cvt##Inst(IceType_f##Size, GPRRegister::Encoded_Reg_##GPR, \
+ dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, Inst##Size##SrcValue); \
+ test.run(); \
+ \
+ ASSERT_EQ(static_cast<uint32_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); \
+ TestImplSXmmAddr(Dst, si2ss, Size); \
+ TestImplSRegXmm(GPR, Src, tss2si, Size); \
+ TestImplSRegAddr(GPR, tss2si, 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, eax);
+ TestImpl(xmm1, xmm2, ebx);
+ TestImpl(xmm2, xmm3, ecx);
+ TestImpl(xmm3, xmm4, edx);
+ TestImpl(xmm4, xmm5, esi);
+ TestImpl(xmm5, xmm6, edi);
+ TestImpl(xmm6, xmm7, eax);
+ TestImpl(xmm7, xmm0, ebx);
+
+#undef TestImpl
+#undef TestImplSize
+#undef TestImplSRegAddr
+#undef TestImplSXmmAddr
+#undef TestImplPXmmAddr
+#undef TestImplSRegXmm
+#undef TestImplSXmmReg
+#undef TestImplPXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, 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 = AssemblerX8632::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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ImmIfFalse)); \
+ __ ucomiss(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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 = AssemblerX8632::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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ImmIfFalse)); \
+ __ ucomiss(IceType_f##Size, XmmRegister::Encoded_Reg_##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, xmm0);
+
+#undef TestImpl
+#undef TestImplSize
+#undef TestImplCond
+#undef TestImplXmmAddr
+#undef TestImplXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, Movmsk) {
+#define TestMovmskGPRXmm(GPR, Src, Value1, Expected, Inst) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #GPR ", " #Src ", " #Value1 ", " #Expected ", " #Inst ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ const Dqword V0 Value1; \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ Inst(GPRRegister::Encoded_Reg_##GPR, XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(Expected, test.GPR()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestMovmsk(GPR, Src) \
+ do { \
+ TestMovmskGPRXmm(GPR, Src, (-1.0, 1.0, -1.0, 1.0), 0x05ul, movmskps); \
+ TestMovmskGPRXmm(GPR, Src, (1.0, -1.0), 0x02ul, movmskpd); \
+ } while (0)
+
+ TestMovmsk(eax, xmm0);
+ TestMovmsk(ebx, xmm1);
+ TestMovmsk(ecx, xmm2);
+ TestMovmsk(edx, xmm3);
+ TestMovmsk(esi, xmm4);
+ TestMovmsk(edi, xmm5);
+ TestMovmsk(eax, xmm6);
+ TestMovmsk(ebx, xmm7);
+
+#undef TestMovmskGPRXmm
+#undef TestMovmsk
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \
+ __ sqrtss(IceType_f##Size, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \
+ __ sqrtss(IceType_f##Size, XmmRegister::Encoded_Reg_##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); \
+ }
+
+#define TestSqrtss(Dst, Src) \
+ do { \
+ TestSqrtssSizeS(Dst, Src, 32); \
+ TestSqrtssSizeS(Dst, Src, 64); \
+ } while (0)
+
+#undef TestSqrtss
+#undef TestSqrtssSize
+#undef TestSqrtssXmmAddr
+#undef TestSqrtssXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ insertps(IceType_v4f32, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ insertps(IceType_v4f32, XmmRegister::Encoded_Reg_##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);
+
+#undef TestInsertps
+#undef TestInsertpsXmmXmmAddr
+#undef TestInsertpsXmmXmmImm
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##GPR, Immediate(Value1)); \
+ __ pinsr(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ pinsr(IceType_i##Size, XmmRegister::Encoded_Reg_##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, eax);
+ TestPinsr(xmm1, ebx);
+ TestPinsr(xmm2, ecx);
+ TestPinsr(xmm3, edx);
+ TestPinsr(xmm4, esi);
+ TestPinsr(xmm5, edi);
+ TestPinsr(xmm6, eax);
+ TestPinsr(xmm7, ebx);
+
+#undef TestPinsr
+#undef TestPinsrSize
+#undef TestPinsrXmmAddrImm
+#undef TestPinsrXmmGPRImm
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ pextr(IceType_i##Size, GPRRegister::Encoded_Reg_##GPR, \
+ XmmRegister::Encoded_Reg_##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(eax, xmm0);
+ TestPextr(ebx, xmm1);
+ TestPextr(ecx, xmm2);
+ TestPextr(edx, xmm3);
+ TestPextr(esi, xmm4);
+ TestPextr(edi, xmm5);
+ TestPextr(eax, xmm6);
+ TestPextr(ebx, xmm7);
+
+#undef TestPextr
+#undef TestPextrSize
+#undef TestPextrXmmGPRImm
+}
+
+TEST_F(AssemblerX8632Test, Pmovsxdq) {
+#define TestPmovsxdqXmmXmm(Dst, Src, Value1) \
+ do { \
+ static constexpr char TestString[] = "(" #Dst ", " #Src ", " #Value1 ")"; \
+ const uint32_t T0 = allocateDqword(); \
+ const Dqword V0 Value1; \
+ const uint32_t T1 = allocateDqword(); \
+ const Dqword V1(uint64_t(0), uint64_t(0)); \
+ \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T1)); \
+ __ pmovsxdq(XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDqwordTo(T0, V0); \
+ test.setDqwordTo(T1, V1); \
+ test.run(); \
+ \
+ const Dqword Expected(uint64_t(V0.I32[0]), uint64_t(V0.I32[1])); \
+ ASSERT_EQ(Expected, test.Dst<Dqword>()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestPmovsxdq(Dst, Src) \
+ do { \
+ TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x700000007FFFFFFFull), \
+ uint64_t(0xAAAAAAAAEEEEEEEEull))); \
+ TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x800000007FFFFFFFull), \
+ uint64_t(0xAAAAAAAAEEEEEEEEull))); \
+ TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x70000000FFFFFFFFull), \
+ uint64_t(0xAAAAAAAAEEEEEEEEull))); \
+ TestPmovsxdqXmmXmm(Dst, Src, (uint64_t(0x80000000FFFFFFFFull), \
+ uint64_t(0xAAAAAAAAEEEEEEEEull))); \
+ } while (0)
+
+ TestPmovsxdq(xmm0, xmm1);
+ TestPmovsxdq(xmm1, xmm2);
+ TestPmovsxdq(xmm2, xmm3);
+ TestPmovsxdq(xmm3, xmm4);
+ TestPmovsxdq(xmm4, xmm5);
+ TestPmovsxdq(xmm5, xmm6);
+ TestPmovsxdq(xmm6, xmm7);
+ TestPmovsxdq(xmm7, xmm0);
+
+#undef TestPmovsxdq
+#undef TestPmovsxdqXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ Inst(IceType_i##Size, XmmRegister::Encoded_Reg_##Dst, \
+ XmmRegister::Encoded_Reg_##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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ Inst(IceType_i##Size, XmmRegister::Encoded_Reg_##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, xmm0);
+
+#undef TestPcmp
+#undef TestPcmpSize
+#undef TestPcmpValues
+#undef TestPcmpXmmAddr
+#undef TestPcmpXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, 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(XmmRegister::Encoded_Reg_##Dst, dwordAddress(T0)); \
+ __ movups(XmmRegister::Encoded_Reg_##Src, dwordAddress(T1)); \
+ __ roundsd(XmmRegister::Encoded_Reg_##Dst, XmmRegister::Encoded_Reg_##Src, \
+ AssemblerX8632::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, xmm0);
+
+#undef TestRoundsd
+#undef TestRoundsdXmmXmm
+}
+
+TEST_F(AssemblerX8632Test, Test) {
+ static constexpr uint32_t Mask8 = 0xFF;
+ static constexpr uint32_t Mask16 = 0xFFFF;
+ static constexpr uint32_t Mask32 = 0xFFFFFFFF;
+
+#define TestImplRegReg(Dst, Value0, Src, Value1, Size) \
+ do { \
+ static constexpr bool NearJump = true; \
+ static constexpr char TestString[] = \
+ "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Size ")"; \
+ static constexpr uint32_t ValueIfTrue = 0xBEEFFEEB; \
+ static constexpr uint32_t ValueIfFalse = 0x11111111; \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate(Value0)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate(Value1)); \
+ __ test(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate(ValueIfFalse)); \
+ Label Done; \
+ __ j(Cond::Br_e, &Done, NearJump); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate(ValueIfTrue)); \
+ __ bind(&Done); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(((Value0)&Mask##Size) & ((Value1)&Mask##Size) ? ValueIfTrue \
+ : ValueIfFalse, \
+ test.Dst()) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplRegImm(Dst, Value0, Imm, Size) \
+ do { \
+ static constexpr bool NearJump = true; \
+ static constexpr char TestString[] = \
+ "(" #Dst ", " #Value0 ", " #Imm ", " #Size ")"; \
+ static constexpr uint32_t ValueIfTrue = 0xBEEFFEEB; \
+ static constexpr uint32_t ValueIfFalse = 0x11111111; \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate(Value0)); \
+ __ test(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate((Imm)&Mask##Size)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate(ValueIfFalse)); \
+ Label Done; \
+ __ j(Cond::Br_e, &Done, NearJump); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate(ValueIfTrue)); \
+ __ bind(&Done); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(((Value0)&Mask##Size) & ((Imm)&Mask##Size) ? ValueIfTrue \
+ : ValueIfFalse, \
+ test.Dst()) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddrReg(Value0, Src, Value1, Size) \
+ do { \
+ static constexpr bool NearJump = true; \
+ static constexpr char TestString[] = \
+ "(Addr, " #Value0 ", " #Src ", " #Value1 ", " #Size ")"; \
+ static constexpr uint32_t ValueIfTrue = 0xBEEFFEEB; \
+ static constexpr uint32_t ValueIfFalse = 0x11111111; \
+ const uint32_t T0 = allocateDword(); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate(Value1)); \
+ __ test(IceType_i##Size, dwordAddress(T0), \
+ GPRRegister::Encoded_Reg_##Src); \
+ __ mov(IceType_i32, dwordAddress(T0), Immediate(ValueIfFalse)); \
+ Label Done; \
+ __ j(Cond::Br_e, &Done, NearJump); \
+ __ mov(IceType_i32, dwordAddress(T0), Immediate(ValueIfTrue)); \
+ __ bind(&Done); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, uint32_t(Value0)); \
+ test.run(); \
+ \
+ ASSERT_EQ(((Value0)&Mask##Size) & ((Value1)&Mask##Size) ? ValueIfTrue \
+ : ValueIfFalse, \
+ test.contentsOfDword(T0)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddrImm(Value0, Value1, Size) \
+ do { \
+ static constexpr bool NearJump = true; \
+ static constexpr char TestString[] = \
+ "(Addr, " #Value0 ", " #Value1 ", " #Size ")"; \
+ static constexpr uint32_t ValueIfTrue = 0xBEEFFEEB; \
+ static constexpr uint32_t ValueIfFalse = 0x11111111; \
+ const uint32_t T0 = allocateDword(); \
+ \
+ __ test(IceType_i##Size, dwordAddress(T0), \
+ Immediate((Value1)&Mask##Size)); \
+ __ mov(IceType_i32, dwordAddress(T0), Immediate(ValueIfFalse)); \
+ Label Done; \
+ __ j(Cond::Br_e, &Done, NearJump); \
+ __ mov(IceType_i32, dwordAddress(T0), Immediate(ValueIfTrue)); \
+ __ bind(&Done); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, uint32_t(Value0)); \
+ test.run(); \
+ \
+ ASSERT_EQ(((Value0)&Mask##Size) & ((Value1)&Mask##Size) ? ValueIfTrue \
+ : ValueIfFalse, \
+ test.contentsOfDword(T0)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplValues(Dst, Value0, Src, Value1, Size) \
+ do { \
+ TestImplRegReg(Dst, Value0, Src, Value1, Size); \
+ TestImplRegImm(Dst, Value0, Value1, Size); \
+ TestImplAddrReg(Value0, Src, Value1, Size); \
+ TestImplAddrImm(Value0, Value1, Size); \
+ } while (0)
+
+#define TestImplSize(Dst, Src, Size) \
+ do { \
+ TestImplValues(Dst, 0xF0F12101, Src, 0x00000000, Size); \
+ TestImplValues(Dst, 0xF0000000, Src, 0xF0000000, Size); \
+ TestImplValues(Dst, 0x0F00000F, Src, 0xF00000F0, Size); \
+ } while (0)
+
+#define TestImpl(Dst, Src) \
+ do { \
+ TestImplSize(Dst, Src, 8); \
+ TestImplSize(Dst, Src, 16); \
+ TestImplSize(Dst, Src, 32); \
+ } while (0)
+
+ TestImpl(eax, ebx);
+ TestImpl(ebx, ecx);
+ TestImpl(ecx, edx);
+ TestImpl(edx, esi);
+ TestImpl(esi, edi);
+ TestImpl(edi, eax);
+
+#undef TestImpl
+#undef TestImplSize
+#undef TestImplValues
+#undef TestImplAddrImm
+#undef TestImplAddrReg
+#undef TestImplRegImm
+#undef TestImplRegReg
+}
+
+// No mull/div because x86.
+// No shift because x86.
+TEST_F(AssemblerX8632Test, Arith_most) {
+ static constexpr uint32_t Mask8 = 0xFF;
+ static constexpr uint32_t Mask16 = 0xFFFF;
+ static constexpr uint32_t Mask32 = 0xFFFFFFFF;
+
+#define TestImplRegReg(Inst, Dst, Value0, Src, Value1, Type, Size, Op) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Value0 ", " #Src ", " #Value1 \
+ ", " #Type #Size "_t, " #Op ")"; \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate(Value0)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate(Value1)); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(Mask##Size &static_cast<uint32_t>( \
+ static_cast<Type##Size##_t>((Value0)&Mask##Size) \
+ Op static_cast<Type##Size##_t>((Value1)&Mask##Size)), \
+ Mask##Size &test.Dst()) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplRegAddr(Inst, Dst, Value0, Value1, Type, Size, Op) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Value0 ", Addr, " #Value1 ", " #Type #Size \
+ "_t, " #Op ")"; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Value1; \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate(Value0)); \
+ __ mov(IceType_i##Size, dwordAddress(T0), Immediate(Value1)); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(Mask##Size &static_cast<uint32_t>( \
+ static_cast<Type##Size##_t>((Value0)&Mask##Size) \
+ Op static_cast<Type##Size##_t>((Value1)&Mask##Size)), \
+ Mask##Size &test.Dst()) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplRegImm(Inst, Dst, Value0, Imm, Type, Size, Op) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Value0 ", Imm(" #Imm "), " #Type #Size \
+ "_t, " #Op ")"; \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate(Value0)); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate((Imm)&Mask##Size)); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(Mask##Size &static_cast<uint32_t>( \
+ static_cast<Type##Size##_t>((Value0)&Mask##Size) \
+ Op static_cast<Type##Size##_t>((Imm)&Mask##Size)), \
+ Mask##Size &test.Dst()) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddrReg(Inst, Value0, Src, Value1, Type, Size, Op) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", Addr, " #Value0 ", " #Src ", " #Value1 ", " #Type #Size \
+ "_t, " #Op ")"; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Value0; \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate(Value1)); \
+ __ Inst(IceType_i##Size, dwordAddress(T0), \
+ GPRRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(Mask##Size &static_cast<uint32_t>( \
+ static_cast<Type##Size##_t>((Value0)&Mask##Size) \
+ Op static_cast<Type##Size##_t>((Value1)&Mask##Size)), \
+ Mask##Size &test.contentsOfDword(T0)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddrImm(Inst, Value0, Imm, Type, Size, Op) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", Addr, " #Value0 ", Imm, " #Imm ", " #Type #Size \
+ "_t, " #Op ")"; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Value0; \
+ \
+ __ Inst(IceType_i##Size, dwordAddress(T0), Immediate((Imm)&Mask##Size)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(Mask##Size &static_cast<uint32_t>( \
+ static_cast<Type##Size##_t>((Value0)&Mask##Size) \
+ Op static_cast<Type##Size##_t>((Imm)&Mask##Size)), \
+ Mask##Size &test.contentsOfDword(T0)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplOp(Inst, Dst, Value0, Src, Value1, Type, Size, Op) \
+ do { \
+ TestImplRegReg(Inst, Dst, Value0, Src, Value1, Type, Size, Op); \
+ TestImplRegAddr(Inst, Dst, Value0, Value1, Type, Size, Op); \
+ TestImplRegImm(Inst, Dst, Value0, Value1, Type, Size, Op); \
+ TestImplAddrReg(Inst, Value0, Src, Value1, Type, Size, Op); \
+ TestImplAddrImm(Inst, Value0, Value1, Type, Size, Op); \
+ } while (0)
+
+#define TestImplValues(Dst, Value0, Src, Value1, Size) \
+ do { \
+ TestImplOp(And, Dst, Value0, Src, Value1, int, Size, &); \
+ TestImplOp(And, Dst, Value0, Src, Value1, uint, Size, &); \
+ TestImplOp(Or, Dst, Value0, Src, Value1, int, Size, | ); \
+ TestImplOp(Or, Dst, Value0, Src, Value1, uint, Size, | ); \
+ TestImplOp(Xor, Dst, Value0, Src, Value1, int, Size, ^); \
+ TestImplOp(Xor, Dst, Value0, Src, Value1, uint, Size, ^); \
+ TestImplOp(add, Dst, Value0, Src, Value1, int, Size, +); \
+ TestImplOp(add, Dst, Value0, Src, Value1, uint, Size, +); \
+ TestImplOp(sub, Dst, Value0, Src, Value1, int, Size, -); \
+ TestImplOp(sub, Dst, Value0, Src, Value1, uint, Size, -); \
+ } while (0)
+
+#define TestImplSize(Dst, Src, Size) \
+ do { \
+ TestImplValues(Dst, 0xF0F12101, Src, 0x00000000, Size); \
+ TestImplValues(Dst, 0xF0000000, Src, 0xF0000000, Size); \
+ TestImplValues(Dst, 0x0F00000F, Src, 0xF0000070, Size); \
+ TestImplValues(Dst, 0x0F00F00F, Src, 0xF000F070, Size); \
+ } while (0)
+
+#define TestImpl(Dst, Src) \
+ do { \
+ if (GPRRegister::Encoded_Reg_##Src <= 3 && \
+ GPRRegister::Encoded_Reg_##Dst <= 3) { \
+ TestImplSize(Dst, Src, 8); \
+ } \
+ TestImplSize(Dst, Src, 16); \
+ TestImplSize(Dst, Src, 32); \
+ } while (0)
+
+ TestImpl(eax, ebx);
+ TestImpl(ebx, ecx);
+ TestImpl(ecx, edx);
+ TestImpl(edx, esi);
+ TestImpl(esi, edi);
+ TestImpl(edi, eax);
+
+#undef TestImpl
+#undef TestImplSize
+#undef TestImplValues
+#undef TestImplOp
+#undef TestImplAddrImm
+#undef TestImplAddrReg
+#undef TestImplRegImm
+#undef TestImplRegAddr
+#undef TestImplRegReg
+}
+
+TEST_F(AssemblerX8632Test, Arith_BorrowNCarry) {
+ const uint32_t Mask8 = 0x000000FF;
+ const uint32_t Mask16 = 0x0000FFFF;
+ const uint32_t Mask32 = 0xFFFFFFFF;
+
+ const uint64_t ResultMask8 = 0x000000000000FFFFull;
+ const uint64_t ResultMask16 = 0x00000000FFFFFFFFull;
+ const uint64_t ResultMask32 = 0xFFFFFFFFFFFFFFFFull;
+
+#define TestImplRegReg(Inst0, Inst1, Dst0, Dst1, Value0, Src0, Src1, Value1, \
+ Op, Size) \
+ do { \
+ static_assert(Size == 8 || Size == 16 || Size == 32, \
+ "Invalid size " #Size); \
+ static constexpr char TestString[] = \
+ "(" #Inst0 ", " #Inst1 ", " #Dst0 ", " #Dst1 ", " #Value0 ", " #Src0 \
+ ", " #Src1 ", " #Value1 ", " #Op ", " #Size ")"; \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \
+ Immediate(uint64_t(Value0) & Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \
+ Immediate((uint64_t(Value0) >> Size) & Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src0, \
+ Immediate(uint64_t(Value1) & Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src1, \
+ Immediate((uint64_t(Value1) >> Size) & Mask##Size)); \
+ __ Inst0(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \
+ GPRRegister::Encoded_Reg_##Src0); \
+ __ Inst1(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \
+ GPRRegister::Encoded_Reg_##Src1); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ static constexpr uint64_t Result = \
+ (uint64_t(Value0) & ResultMask##Size)Op(uint64_t(Value1) & \
+ ResultMask##Size); \
+ static constexpr uint32_t Expected0 = Result & Mask##Size; \
+ static constexpr uint32_t Expected1 = (Result >> Size) & Mask##Size; \
+ ASSERT_EQ(Expected0, test.Dst0()) << TestString << ": 0"; \
+ ASSERT_EQ(Expected1, test.Dst1()) << TestString << ": 1"; \
+ reset(); \
+ } while (0)
+
+#define TestImplRegAddr(Inst0, Inst1, Dst0, Dst1, Value0, Value1, Op, Size) \
+ do { \
+ static_assert(Size == 8 || Size == 16 || Size == 32, \
+ "Invalid size " #Size); \
+ static constexpr char TestString[] = \
+ "(" #Inst0 ", " #Inst1 ", " #Dst0 ", " #Dst1 ", " #Value0 \
+ ", Addr, " #Value1 ", " #Op ", " #Size ")"; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = uint64_t(Value1) & Mask##Size; \
+ const uint32_t T1 = allocateDword(); \
+ const uint32_t V1 = (uint64_t(Value1) >> Size) & Mask##Size; \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \
+ Immediate(uint64_t(Value0) & Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \
+ Immediate((uint64_t(Value0) >> Size) & Mask##Size)); \
+ __ Inst0(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \
+ dwordAddress(T0)); \
+ __ Inst1(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \
+ dwordAddress(T1)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.setDwordTo(T1, V1); \
+ test.run(); \
+ \
+ static constexpr uint64_t Result = \
+ (uint64_t(Value0) & ResultMask##Size)Op(uint64_t(Value1) & \
+ ResultMask##Size); \
+ static constexpr uint32_t Expected0 = Result & Mask##Size; \
+ static constexpr uint32_t Expected1 = (Result >> Size) & Mask##Size; \
+ ASSERT_EQ(Expected0, test.Dst0()) << TestString << ": 0"; \
+ ASSERT_EQ(Expected1, test.Dst1()) << TestString << ": 1"; \
+ reset(); \
+ } while (0)
+
+#define TestImplRegImm(Inst0, Inst1, Dst0, Dst1, Value0, Imm, Op, Size) \
+ do { \
+ static_assert(Size == 8 || Size == 16 || Size == 32, \
+ "Invalid size " #Size); \
+ static constexpr char TestString[] = \
+ "(" #Inst0 ", " #Inst1 ", " #Dst0 ", " #Dst1 ", " #Value0 \
+ ", Imm(" #Imm "), " #Op ", " #Size ")"; \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \
+ Immediate(uint64_t(Value0) & Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \
+ Immediate((uint64_t(Value0) >> Size) & Mask##Size)); \
+ __ Inst0(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst0, \
+ Immediate(uint64_t(Imm) & Mask##Size)); \
+ __ Inst1(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \
+ Immediate((uint64_t(Imm) >> Size) & Mask##Size)); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ static constexpr uint64_t Result = \
+ (uint64_t(Value0) & ResultMask##Size)Op(uint64_t(Imm) & \
+ ResultMask##Size); \
+ static constexpr uint32_t Expected0 = Result & Mask##Size; \
+ static constexpr uint32_t Expected1 = (Result >> Size) & Mask##Size; \
+ ASSERT_EQ(Expected0, test.Dst0()) << TestString << ": 0"; \
+ ASSERT_EQ(Expected1, test.Dst1()) << TestString << ": 1"; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddrReg(Inst0, Inst1, Value0, Src0, Src1, Value1, Op, Size) \
+ do { \
+ static_assert(Size == 8 || Size == 16 || Size == 32, \
+ "Invalid size " #Size); \
+ static constexpr char TestString[] = \
+ "(" #Inst0 ", " #Inst1 ", Addr, " #Value0 ", " #Src0 ", " #Src1 \
+ ", " #Value1 ", " #Op ", " #Size ")"; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = uint64_t(Value0) & Mask##Size; \
+ const uint32_t T1 = allocateDword(); \
+ const uint32_t V1 = (uint64_t(Value0) >> Size) & Mask##Size; \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src0, \
+ Immediate(uint64_t(Value1) & Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src1, \
+ Immediate((uint64_t(Value1) >> Size) & Mask##Size)); \
+ __ Inst0(IceType_i##Size, dwordAddress(T0), \
+ GPRRegister::Encoded_Reg_##Src0); \
+ __ Inst1(IceType_i##Size, dwordAddress(T1), \
+ GPRRegister::Encoded_Reg_##Src1); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.setDwordTo(T1, V1); \
+ test.run(); \
+ \
+ static constexpr uint64_t Result = \
+ (uint64_t(Value0) & ResultMask##Size)Op(uint64_t(Value1) & \
+ ResultMask##Size); \
+ static constexpr uint32_t Expected0 = Result & Mask##Size; \
+ static constexpr uint32_t Expected1 = (Result >> Size) & Mask##Size; \
+ ASSERT_EQ(Expected0, test.contentsOfDword(T0)) << TestString << ": 0"; \
+ ASSERT_EQ(Expected1, test.contentsOfDword(T1)) << TestString << ": 1"; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddrImm(Inst0, Inst1, Value0, Imm, Op, Size) \
+ do { \
+ static_assert(Size == 8 || Size == 16 || Size == 32, \
+ "Invalid size " #Size); \
+ static constexpr char TestString[] = \
+ "(" #Inst0 ", " #Inst1 ", Addr, " #Value0 ", Imm(" #Imm "), " #Op \
+ ", " #Size ")"; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = uint64_t(Value0) & Mask##Size; \
+ const uint32_t T1 = allocateDword(); \
+ const uint32_t V1 = (uint64_t(Value0) >> Size) & Mask##Size; \
+ __ Inst0(IceType_i##Size, dwordAddress(T0), \
+ Immediate(uint64_t(Imm) & Mask##Size)); \
+ __ Inst1(IceType_i##Size, dwordAddress(T1), \
+ Immediate((uint64_t(Imm) >> Size) & Mask##Size)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.setDwordTo(T1, V1); \
+ test.run(); \
+ \
+ static constexpr uint64_t Result = \
+ (uint64_t(Value0) & ResultMask##Size)Op(uint64_t(Imm) & \
+ ResultMask##Size); \
+ static constexpr uint32_t Expected0 = Result & Mask##Size; \
+ static constexpr uint32_t Expected1 = (Result >> Size) & Mask##Size; \
+ ASSERT_EQ(Expected0, test.contentsOfDword(T0)) << TestString << ": 0"; \
+ ASSERT_EQ(Expected1, test.contentsOfDword(T1)) << TestString << ": 1"; \
+ reset(); \
+ } while (0)
+
+#define TestImplOp(Inst0, Inst1, Dst0, Dst1, Value0, Src0, Src1, Value1, Op, \
+ Size) \
+ do { \
+ TestImplRegReg(Inst0, Inst1, Dst0, Dst1, Value0, Src0, Src1, Value1, Op, \
+ Size); \
+ TestImplRegAddr(Inst0, Inst1, Dst0, Dst1, Value0, Value1, Op, Size); \
+ TestImplRegImm(Inst0, Inst1, Dst0, Dst1, Value0, Value1, Op, Size); \
+ TestImplAddrReg(Inst0, Inst1, Value0, Src0, Src1, Value1, Op, Size); \
+ TestImplAddrImm(Inst0, Inst1, Value0, Value1, Op, Size); \
+ } while (0)
+
+#define TestImplValues(Dst0, Dst1, Value0, Src0, Src1, Value1, Size) \
+ do { \
+ TestImplOp(add, adc, Dst0, Dst1, Value0, Src0, Src1, Value1, +, Size); \
+ TestImplOp(sub, sbb, Dst0, Dst1, Value0, Src0, Src1, Value1, -, Size); \
+ } while (0)
+
+#define TestImplSize(Dst0, Dst1, Src0, Src1, Size) \
+ do { \
+ TestImplValues(Dst0, Dst1, 0xFFFFFFFFFFFFFF00ull, Src0, Src1, \
+ 0xFFFFFFFF0000017Full, Size); \
+ } while (0)
+
+#define TestImpl(Dst0, Dst1, Src0, Src1) \
+ do { \
+ if (GPRRegister::Encoded_Reg_##Dst0 <= 3 && \
+ GPRRegister::Encoded_Reg_##Dst1 <= 3 && \
+ GPRRegister::Encoded_Reg_##Src0 <= 3 && \
+ GPRRegister::Encoded_Reg_##Src1 <= 3) { \
+ TestImplSize(Dst0, Dst1, Src0, Src1, 8); \
+ } \
+ TestImplSize(Dst0, Dst1, Src0, Src1, 16); \
+ TestImplSize(Dst0, Dst1, Src0, Src1, 32); \
+ } while (0)
+
+ TestImpl(eax, ebx, ecx, edx);
+ TestImpl(ebx, ecx, edx, esi);
+ TestImpl(ecx, edx, esi, edi);
+ TestImpl(edx, esi, edi, eax);
+ TestImpl(esi, edi, eax, ebx);
+ TestImpl(edi, eax, ebx, ecx);
+
+#undef TestImpl
+#undef TestImplSize
+#undef TestImplValues
+#undef TestImplOp
+#undef TestImplAddrImm
+#undef TestImplAddrReg
+#undef TestImplRegImm
+#undef TestImplRegAddr
+#undef TestImplRegReg
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Cbw_Cwd_Cdq) {
+#define TestImpl(Inst, BytesSize, ...) \
+ do { \
+ __ Inst(); \
+ ASSERT_EQ(BytesSize, codeBytesSize()) << #Inst; \
+ verifyBytes<BytesSize>(codeBytes(), __VA_ARGS__); \
jvoung (off chromium) 2015/07/23 17:59:36 Similar question about ASSERT_TRUE, except now it'
John 2015/07/27 20:35:58 Having the ASSERT_TRUE outside verifyFunction help
+ reset(); \
+ } while (0)
+
+ TestImpl(cbw, 2u, 0x66, 0x98);
+ TestImpl(cwd, 2u, 0x66, 0x99);
+ TestImpl(cdq, 1u, 0x99);
+
+#undef TestImpl
+}
+
+TEST_F(AssemblerX8632Test, SingleOperandMul) {
+ static constexpr uint32_t Mask8 = 0x000000FF;
+ static constexpr uint32_t Mask16 = 0x0000FFFF;
+ static constexpr uint32_t Mask32 = 0xFFFFFFFF;
+
+#define TestImplReg(Inst, Value0, Src, Value1, Type, Size) \
+ do { \
+ static_assert(GPRRegister::Encoded_Reg_eax != \
+ GPRRegister::Encoded_Reg_##Src, \
+ "eax can not be src1."); \
+ \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Value0 ", " #Src ", " #Value1 ", " #Type ", " #Size \
+ ")"; \
+ static constexpr Type##64_t OperandEax = \
+ static_cast<Type##Size##_t>((Value0)&Mask##Size); \
+ static constexpr Type##64_t OperandOther = \
+ static_cast<Type##Size##_t>((Value1)&Mask##Size); \
+ static constexpr uint32_t ExpectedEax = \
+ Mask##Size & (OperandEax * OperandOther); \
+ static constexpr uint32_t ExpectedEdx = \
+ Mask##Size & ((OperandEax * OperandOther) >> Size); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \
+ Immediate((Value0)&Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate((Value1)&Mask##Size)); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Src); \
+ \
+ if (Size == 8) { \
+ /* mov %ah, %dl */ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \
+ GPRRegister::Encoded_Reg_esp); \
+ __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \
+ if (GPRRegister::Encoded_Reg_##Src == GPRRegister::Encoded_Reg_esi) { \
+ /* src == dh; clear dx's upper 8 bits. */ \
+ __ And(IceType_i16, GPRRegister::Encoded_Reg_edx, Immediate(0x00FF)); \
+ } \
+ } \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(ExpectedEax, test.eax()) << TestString; \
+ ASSERT_EQ(ExpectedEdx, test.edx()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddr(Inst, Value0, Value1, Type, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Value0 ", Addr, " #Value1 ", " #Type ", " #Size ")"; \
+ static const uint32_t T0 = allocateDword(); \
+ static constexpr uint32_t V0 = Value1; \
+ static constexpr Type##64_t OperandEax = \
+ static_cast<Type##Size##_t>((Value0)&Mask##Size); \
+ static constexpr Type##64_t OperandOther = \
+ static_cast<Type##Size##_t>((Value1)&Mask##Size); \
+ static constexpr uint32_t ExpectedEax = \
+ Mask##Size & (OperandEax * OperandOther); \
+ static constexpr uint32_t ExpectedEdx = \
+ Mask##Size & ((OperandEax * OperandOther) >> Size); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \
+ Immediate((Value0)&Mask##Size)); \
+ __ Inst(IceType_i##Size, dwordAddress(T0)); \
+ \
+ if (Size == 8) { \
+ /* mov %ah, %dl */ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \
+ GPRRegister::Encoded_Reg_esp); \
+ __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \
+ } \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(ExpectedEax, test.eax()) << TestString; \
+ ASSERT_EQ(ExpectedEdx, test.edx()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplOp(Inst, Value0, Src, Value1, Type, Size) \
+ do { \
+ TestImplReg(Inst, Value0, Src, Value1, Type, Size); \
+ TestImplAddr(Inst, Value0, Value1, Type, Size); \
+ } while (0)
+
+#define TestImplValue(Value0, Src, Value1, Size) \
+ do { \
+ TestImplOp(mul, Value0, Src, Value1, uint, Size); \
+ TestImplOp(imul, Value0, Src, Value1, int, Size); \
+ } while (0)
+
+#define TestImplSize(Src, Size) \
+ do { \
+ TestImplValue(10, Src, 1, Size); \
+ TestImplValue(10, Src, -1, Size); \
+ TestImplValue(-10, Src, 37, Size); \
+ TestImplValue(-10, Src, -15, Size); \
+ } while (0)
+
+#define TestImpl(Src) \
+ do { \
+ TestImplSize(Src, 8); \
+ TestImplSize(Src, 16); \
+ TestImplSize(Src, 32); \
+ } while (0)
+
+ TestImpl(ebx);
+ TestImpl(ecx);
+ TestImpl(edx);
+ TestImpl(esi);
+ TestImpl(edi);
+
+#undef TestImpl
+#undef TestImplSize
+#undef TestImplValue
+#undef TestImplOp
+#undef TestImplAddr
+#undef TestImplReg
+}
+
+TEST_F(AssemblerX8632Test, TwoOperandImul) {
+ static constexpr uint32_t Mask16 = 0x0000FFFF;
+ static constexpr uint32_t Mask32 = 0xFFFFFFFF;
+
+#define TestImplRegReg(Dst, Value0, Src, Value1, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Size ")"; \
+ static constexpr int64_t Operand0 = \
+ static_cast<int##Size##_t>((Value0)&Mask##Size); \
+ static constexpr int64_t Operand1 = \
+ static_cast<int##Size##_t>((Value1)&Mask##Size); \
+ static constexpr uint32_t Expected = Mask##Size & (Operand0 * Operand1); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate((Value0)&Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate((Value1)&Mask##Size)); \
+ __ imul(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src); \
+ \
+ if (Size == 8) { \
+ /* mov %ah, %dl */ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \
+ GPRRegister::Encoded_Reg_esp); \
+ __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \
+ if (GPRRegister::Encoded_Reg_##Src == GPRRegister::Encoded_Reg_esi) { \
+ /* src == dh; clear dx's upper 8 bits. */ \
+ __ And(IceType_i16, GPRRegister::Encoded_Reg_edx, Immediate(0x00FF)); \
+ } \
+ } \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(Expected, test.Dst()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplRegImm(Dst, Value0, Imm, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Dst ", " #Value0 ", Imm(" #Imm "), " #Size ")"; \
+ static constexpr int64_t Operand0 = \
+ static_cast<int##Size##_t>((Value0)&Mask##Size); \
+ static constexpr int64_t Operand1 = \
+ static_cast<int##Size##_t>((Imm)&Mask##Size); \
+ static constexpr uint32_t Expected = Mask##Size & (Operand0 * Operand1); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate((Value0)&Mask##Size)); \
+ __ imul(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, Immediate(Imm)); \
+ \
+ if (Size == 8) { \
+ /* mov %ah, %dl */ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \
+ GPRRegister::Encoded_Reg_esp); \
+ __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \
+ } \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(Expected, test.Dst()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplRegAddr(Dst, Value0, Value1, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Dst ", " #Value0 ", Addr," #Value1 ", " #Size ")"; \
+ static constexpr int64_t Operand0 = \
+ static_cast<int##Size##_t>((Value0)&Mask##Size); \
+ static constexpr int64_t Operand1 = \
+ static_cast<int##Size##_t>((Value1)&Mask##Size); \
+ static constexpr uint32_t Expected = Mask##Size & (Operand0 * Operand1); \
+ const uint32_t T0 = allocateDword(); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate((Value0)&Mask##Size)); \
+ __ imul(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ dwordAddress(T0)); \
+ \
+ if (Size == 8) { \
+ /* mov %ah, %dl */ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \
+ GPRRegister::Encoded_Reg_esp); \
+ __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \
+ } \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, static_cast<uint32_t>(Operand1)); \
+ test.run(); \
+ \
+ ASSERT_EQ(Expected, test.Dst()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplValue(Dst, Value0, Src, Value1, Size) \
+ do { \
+ TestImplRegReg(Dst, Value0, Src, Value1, Size); \
+ TestImplRegImm(Dst, Value0, Value1, Size); \
+ TestImplRegAddr(Dst, Value0, Value1, Size); \
+ } while (0)
+
+#define TestImplSize(Dst, Src, Size) \
+ do { \
+ TestImplValue(Dst, 1, Src, 1, Size); \
+ TestImplValue(Dst, -10, Src, 0x4050AA20, Size); \
+ TestImplValue(Dst, -2, Src, -55, Size); \
+ } while (0)
+
+#define TestImpl(Dst, Src) \
+ do { \
+ TestImplSize(Dst, Src, 16); \
+ TestImplSize(Dst, Src, 32); \
+ } while (0)
+
+ TestImpl(eax, ebx);
+ TestImpl(ebx, ecx);
+ TestImpl(ecx, edx);
+ TestImpl(edx, esi);
+ TestImpl(esi, edi);
+ TestImpl(edi, eax);
+
+#undef TestImpl
+#undef TestImplSize
+#undef TestImplValue
+#undef TestImplRegAddr
+#undef TestImplRegImm
+#undef TestImplRegReg
+}
+
+TEST_F(AssemblerX8632Test, Div) {
+ static constexpr uint32_t Mask8 = 0x000000FF;
+ static constexpr uint32_t Mask16 = 0x0000FFFF;
+ static constexpr uint32_t Mask32 = 0xFFFFFFFF;
+
+ static constexpr uint64_t Operand0Mask8 = 0x00000000000000FFull;
+ static constexpr uint64_t Operand0Mask16 = 0x00000000FFFFFFFFull;
+ static constexpr uint64_t Operand0Mask32 = 0xFFFFFFFFFFFFFFFFull;
+
+ using Operand0Type_int8 = int16_t;
+ using Operand0Type_uint8 = uint16_t;
+ using Operand0Type_int16 = int32_t;
+ using Operand0Type_uint16 = uint32_t;
+ using Operand0Type_int32 = int64_t;
+ using Operand0Type_uint32 = uint64_t;
+
+#define TestImplReg(Inst, Value0, Src, Value1, Type, Size) \
+ do { \
+ static_assert(GPRRegister::Encoded_Reg_eax != \
+ GPRRegister::Encoded_Reg_##Src, \
+ "eax can not be src1."); \
+ static_assert(GPRRegister::Encoded_Reg_edx != \
+ GPRRegister::Encoded_Reg_##Src, \
+ "edx can not be src1."); \
+ \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Value0 ", " #Src ", " #Value1 ", " #Type ", " #Size \
+ ")"; \
+ static constexpr Operand0Type_##Type##Size Operand0 = \
+ static_cast<Type##64_t>(Value0) & Operand0Mask##Size; \
+ static constexpr Type##Size##_t Operand0Lo = Operand0 & Mask##Size; \
+ static constexpr Type##Size##_t Operand0Hi = \
+ (Operand0 >> Size) & Mask##Size; \
+ static constexpr Type##Size##_t Operand1 = \
+ static_cast<Type##Size##_t>(Value1) & Mask##Size; \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \
+ Immediate(Operand0Lo)); \
+ if (Size == 8) { \
+ /* mov Operand0Hi, %al */ \
+ __ mov(IceType_i8, GPRRegister::Encoded_Reg_esp, Immediate(Operand0Hi)); \
+ } else { \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \
+ Immediate(Operand0Hi)); \
+ } \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate(Operand1)); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Src); \
+ if (Size == 8) { \
+ /* mov %ah, %dl */ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \
+ GPRRegister::Encoded_Reg_esp); \
+ __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \
+ if (GPRRegister::Encoded_Reg_##Src == GPRRegister::Encoded_Reg_esi) { \
+ __ And(IceType_i16, GPRRegister::Encoded_Reg_edx, Immediate(0x00FF)); \
+ } \
+ } \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ static constexpr uint32_t Quocient = (Operand0 / Operand1) & Mask##Size; \
+ static constexpr uint32_t Reminder = (Operand0 % Operand1) & Mask##Size; \
+ EXPECT_EQ(Quocient, test.eax()) << TestString; \
+ EXPECT_EQ(Reminder, test.edx()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddr(Inst, Value0, Value1, Type, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Value0 ", Addr, " #Value1 ", " #Type ", " #Size ")"; \
+ static constexpr Operand0Type_##Type##Size Operand0 = \
+ static_cast<Type##64_t>(Value0) & Operand0Mask##Size; \
+ static constexpr Type##Size##_t Operand0Lo = Operand0 & Mask##Size; \
+ static constexpr Type##Size##_t Operand0Hi = \
+ (Operand0 >> Size) & Mask##Size; \
+ const uint32_t T0 = allocateDword(); \
+ static constexpr Type##Size##_t V0 = \
+ static_cast<Type##Size##_t>(Value1) & Mask##Size; \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \
+ Immediate(Operand0Lo)); \
+ if (Size == 8) { \
+ /* mov Operand0Hi, %al */ \
+ __ mov(IceType_i8, GPRRegister::Encoded_Reg_esp, Immediate(Operand0Hi)); \
+ } else { \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \
+ Immediate(Operand0Hi)); \
+ } \
+ __ Inst(IceType_i##Size, dwordAddress(T0)); \
+ if (Size == 8) { \
+ /* mov %ah, %dl */ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_edx, \
+ GPRRegister::Encoded_Reg_esp); \
+ __ And(IceType_i16, GPRRegister::Encoded_Reg_eax, Immediate(0x00FF)); \
+ } \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, static_cast<uint32_t>(V0)); \
+ test.run(); \
+ \
+ static constexpr uint32_t Quocient = (Operand0 / V0) & Mask##Size; \
+ static constexpr uint32_t Reminder = (Operand0 % V0) & Mask##Size; \
+ EXPECT_EQ(Quocient, test.eax()) << TestString; \
+ EXPECT_EQ(Reminder, test.edx()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplOp(Inst, Value0, Src, Value1, Type, Size) \
+ do { \
+ TestImplReg(Inst, Value0, Src, Value1, Type, Size); \
+ TestImplAddr(Inst, Value0, Value1, Type, Size); \
+ } while (0)
+
+#define TestImplValue(Value0, Src, Value1, Size) \
+ do { \
+ TestImplOp(div, Value0, Src, Value1, uint, Size); \
+ TestImplOp(idiv, Value0, Src, Value1, int, Size); \
+ } while (0)
+
+#define TestImplSize(Src, Size) \
+ do { \
+ TestImplValue(10, Src, 1, Size); \
+ TestImplValue(10, Src, -1, Size); \
+ } while (0)
+
+#define TestImpl(Src) \
+ do { \
+ TestImplSize(Src, 8); \
+ TestImplSize(Src, 16); \
+ TestImplSize(Src, 32); \
+ } while (0)
+
+ TestImpl(ebx);
+ TestImpl(ecx);
+ TestImpl(esi);
+ TestImpl(edi);
+
+#undef TestImpl
+#undef TestImplSize
+#undef TestImplValue
+#undef TestImplOp
+#undef TestImplAddr
+#undef TestImplReg
+}
+
+// This is not executable in x86-64 because the one byte inc/dec instructions
+// became the REX prefixes. Therefore, these are tested with the low-level test
+// infrastructure.
+TEST_F(AssemblerX8632LowLevelTest, Incl_Decl_Reg) {
+#define TestImpl(Inst, Dst, BaseOpcode) \
+ do { \
+ __ Inst(GPRRegister::Encoded_Reg_##Dst); \
+ static constexpr uint8_t ByteCount = 1; \
+ ASSERT_EQ(ByteCount, codeBytesSize()); \
+ verifyBytes<ByteCount>(codeBytes(), \
+ BaseOpcode | GPRRegister::Encoded_Reg_##Dst); \
+ reset(); \
+ } while (0)
+
+#define TestInc(Dst) \
+ do { \
+ constexpr uint8_t InclOpcode = 0x40; \
+ TestImpl(incl, Dst, InclOpcode); \
+ } while (0)
+
+#define TestDec(Dst) \
+ do { \
+ constexpr uint8_t DeclOpcode = 0x48; \
+ TestImpl(decl, Dst, DeclOpcode); \
+ } while (0)
+
+ TestInc(eax);
+ TestInc(ecx);
+ TestInc(edx);
+ TestInc(ebx);
+ TestInc(esp);
+ TestInc(ebp);
+ TestInc(esi);
+ TestInc(esi);
+
+ TestDec(eax);
+ TestDec(ecx);
+ TestDec(edx);
+ TestDec(ebx);
+ TestDec(esp);
+ TestDec(ebp);
+ TestDec(esi);
+ TestDec(esi);
+
+#undef TestInc
+#undef TestDec
+#undef TestImpl
+}
+
+TEST_F(AssemblerX8632Test, Incl_Decl_Addr) {
+#define TestImpl(Inst, Value0) \
+ do { \
+ const bool IsInc = std::string(#Inst).find("incl") != std::string::npos; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Value0; \
+ \
+ __ Inst(dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(static_cast<uint32_t>(Value0 + (IsInc ? 1 : -1)), \
+ test.contentsOfDword(T0)); \
+ reset(); \
+ } while (0)
+
+#define TestInc(Value0) \
+ do { \
+ TestImpl(incl, Value0); \
+ } while (0)
+
+#define TestDec(Value0) \
+ do { \
+ TestImpl(decl, Value0); \
+ } while (0)
+
+ TestInc(230);
+
+ TestDec(30);
+
+#undef TestInc
+#undef TestDec
+#undef TestImpl
+}
+
+TEST_F(AssemblerX8632Test, Shifts) {
+ static constexpr uint32_t Mask8 = 0x000000FF;
+ static constexpr uint32_t Mask16 = 0x0000FFFF;
+ static constexpr uint32_t Mask32 = 0xFFFFFFFF;
+
+#define TestImplRegImm(Inst, Dst, Value0, Imm, Op, Type, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Value0 ", Imm(" #Imm "), " #Op ", " #Type \
+ ", " #Size ")"; \
+ const bool IsRol = std::string(#Inst).find("rol") != std::string::npos; \
+ const uint##Size##_t Expected = \
+ Mask##Size & (static_cast<Type##Size##_t>(Value0) Op(Imm) | \
+ (!IsRol ? 0 : (Value0) >> (Size - Imm))); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate((Value0)&Mask##Size)); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate((Imm)&Mask##Size)); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(static_cast<uint32_t>(Expected), test.Dst()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplRegRegImm(Inst, Dst, Value0, Src, Value1, Count, Op0, Op1, \
+ Type, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Value0 ", " #Src ", " #Value1 \
+ ", Imm(" #Count "), " #Op0 ", " #Op1 ", " #Type ", " #Size ")"; \
+ const uint##Size##_t Expected = \
+ Mask##Size & (static_cast<Type##Size##_t>(Value0) Op0(Count) | \
+ (static_cast<Type##64_t>(Value1) Op1(Size - Count))); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate((Value0)&Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate((Value1)&Mask##Size)); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src, Immediate(Count)); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(static_cast<uint32_t>(Expected), test.Dst()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplRegCl(Inst, Dst, Value0, Count, Op, Type, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Value0 ", " #Count ", " #Op ", " #Type \
+ ", " #Size ")"; \
+ const bool IsRol = std::string(#Inst).find("rol") != std::string::npos; \
+ const uint##Size##_t Expected = \
+ Mask##Size & (static_cast<Type##Size##_t>(Value0) Op(Count) | \
+ (!IsRol ? 0 : Value0 >> (Size - Count))); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate((Value0)&Mask##Size)); \
+ __ mov(IceType_i8, GPRRegister::Encoded_Reg_ecx, \
+ Immediate((Count)&Mask##Size)); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_ecx); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(static_cast<uint32_t>(Expected), test.Dst()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplRegRegCl(Inst, Dst, Value0, Src, Value1, Count, Op0, Op1, \
+ Type, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Value0 ", " #Src ", " #Value1 ", " #Count \
+ ", " #Op0 ", " #Op1 ", " #Type ", " #Size ")"; \
+ const uint##Size##_t Expected = \
+ Mask##Size & (static_cast<Type##Size##_t>(Value0) Op0(Count) | \
+ (static_cast<Type##64_t>(Value1) Op1(Size - Count))); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate((Value0)&Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate((Value1)&Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_ecx, \
+ Immediate((Count)&0x7F)); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(static_cast<uint32_t>(Expected), test.Dst()) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddrCl(Inst, Value0, Count, Op, Type, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", Addr, " #Value0 ", " #Count ", " #Op ", " #Type \
+ ", " #Size ")"; \
+ const bool IsRol = std::string(#Inst).find("rol") != std::string::npos; \
+ const uint##Size##_t Expected = \
+ Mask##Size & (static_cast<Type##Size##_t>(Value0) Op(Count) | \
+ (!IsRol ? 0 : Value0 >> (Size - Count))); \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = Value0; \
+ \
+ __ mov(IceType_i8, GPRRegister::Encoded_Reg_ecx, \
+ Immediate((Count)&Mask##Size)); \
+ __ Inst(IceType_i##Size, dwordAddress(T0), GPRRegister::Encoded_Reg_ecx); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(static_cast<uint32_t>(Expected), \
+ Mask##Size &test.contentsOfDword(T0)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddrRegCl(Inst, Value0, Src, Value1, Count, Op0, Op1, Type, \
+ Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", Addr, " #Value0 ", " #Src ", " #Value1 ", " #Count \
+ ", " #Op0 ", " #Op1 ", " #Type ", " #Size ")"; \
+ const uint##Size##_t Expected = \
+ Mask##Size & (static_cast<Type##Size##_t>(Value0) Op0(Count) | \
+ (static_cast<Type##64_t>(Value1) Op1(Size - Count))); \
+ const uint32_t T0 = allocateDword(); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate((Value1)&Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_ecx, \
+ Immediate((Count)&0x7F)); \
+ __ Inst(IceType_i##Size, dwordAddress(T0), \
+ GPRRegister::Encoded_Reg_##Src); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, static_cast<uint32_t>(Value0)); \
+ test.run(); \
+ \
+ ASSERT_EQ(static_cast<uint32_t>(Expected), test.contentsOfDword(T0)) \
+ << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplOp(Inst, Dst, Value0, Count, Op, Type, Size) \
+ do { \
+ static_assert(GPRRegister::Encoded_Reg_##Dst != \
+ GPRRegister::Encoded_Reg_ecx, \
+ "ecx should not be specified as Dst"); \
+ TestImplRegImm(Inst, Dst, Value0, Count, Op, Type, Size); \
+ TestImplRegImm(Inst, ecx, Value0, Count, Op, Type, Size); \
+ TestImplRegCl(Inst, Dst, Value0, Count, Op, Type, Size); \
+ TestImplAddrCl(Inst, Value0, Count, Op, Type, Size); \
+ } while (0)
+
+#define TestImplThreeOperandOp(Inst, Dst, Value0, Src, Value1, Count, Op0, \
+ Op1, Type, Size) \
+ do { \
+ static_assert(GPRRegister::Encoded_Reg_##Dst != \
+ GPRRegister::Encoded_Reg_ecx, \
+ "ecx should not be specified as Dst"); \
+ static_assert(GPRRegister::Encoded_Reg_##Src != \
+ GPRRegister::Encoded_Reg_ecx, \
+ "ecx should not be specified as Src"); \
+ TestImplRegRegImm(Inst, Dst, Value0, Src, Value1, Count, Op0, Op1, Type, \
+ Size); \
+ TestImplRegRegCl(Inst, Dst, Value0, Src, Value1, Count, Op0, Op1, Type, \
+ Size); \
+ TestImplAddrRegCl(Inst, Value0, Src, Value1, Count, Op0, Op1, Type, Size); \
+ } while (0)
+
+#define TestImplValue(Dst, Value0, Count, Size) \
+ do { \
+ TestImplOp(rol, Dst, Value0, Count, <<, uint, Size); \
+ TestImplOp(shl, Dst, Value0, Count, <<, uint, Size); \
+ TestImplOp(shr, Dst, Value0, Count, >>, uint, Size); \
+ TestImplOp(sar, Dst, Value0, Count, >>, int, Size); \
+ } while (0)
+
+#define TestImplThreeOperandValue(Dst, Value0, Src, Value1, Count, Size) \
+ do { \
+ TestImplThreeOperandOp(shld, Dst, Value0, Src, Value1, Count, <<, >>, \
+ uint, Size); \
+ TestImplThreeOperandOp(shrd, Dst, Value0, Src, Value1, Count, >>, <<, \
+ uint, Size); \
+ } while (0)
+
+#define TestImplSize(Dst, Size) \
+ do { \
+ TestImplValue(Dst, 0x8F, 3, Size); \
+ TestImplValue(Dst, 0x8FFF, 7, Size); \
+ TestImplValue(Dst, 0x8FFFF, 7, Size); \
+ } while (0)
+
+#define TestImplThreeOperandSize(Dst, Src, Size) \
+ do { \
+ TestImplThreeOperandValue(Dst, 0xFFF3, Src, 0xA000, 8, Size); \
+ } while (0)
+
+#define TestImpl(Dst, Src) \
+ do { \
+ if (GPRRegister::Encoded_Reg_##Dst < 4) { \
+ TestImplSize(Dst, 8); \
+ } \
+ TestImplSize(Dst, 16); \
+ TestImplThreeOperandSize(Dst, Src, 16); \
+ TestImplSize(Dst, 32); \
+ TestImplThreeOperandSize(Dst, Src, 32); \
+ } while (0)
+
+ TestImpl(eax, ebx);
+ TestImpl(ebx, edx);
+ TestImpl(edx, esi);
+ TestImpl(esi, edi);
+ TestImpl(edi, eax);
+
+#undef TestImpl
+#undef TestImplThreeOperandSize
+#undef TestImplSize
+#undef TestImplValue
+#undef TestImplThreeOperandValue
+#undef TestImplOp
+#undef TestImplThreeOperandOp
+#undef TestImplAddrCl
+#undef TestImplRegRegCl
+#undef TestImplRegCl
+#undef TestImplRegRegImm
+#undef TestImplRegImm
+}
+
+TEST_F(AssemblerX8632Test, Neg) {
+ static constexpr uint32_t Mask8 = 0x000000ff;
+ static constexpr uint32_t Mask16 = 0x0000ffff;
+ static constexpr uint32_t Mask32 = 0xffffffff;
+
+#define TestImplReg(Dst, Size) \
+ do { \
+ static constexpr int32_t Value = 0xFF00A543; \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ Immediate(static_cast<int##Size##_t>(Value) & Mask##Size)); \
+ __ neg(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \
+ GPRRegister::Encoded_Reg_##Dst); \
+ __ And(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(Mask##Size)); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(1 + (~static_cast<int##Size##_t>(Value) & Mask##Size), \
+ test.eax()) \
+ << "(" #Dst ", " #Size ")"; \
+ reset(); \
+ } while (0)
+
+#define TestImplAddr(Size) \
+ do { \
+ static constexpr int32_t Value = 0xFF00A543; \
+ const uint32_t T0 = allocateDword(); \
+ __ neg(IceType_i##Size, dwordAddress(T0)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, Value &Mask##Size); \
+ test.run(); \
+ \
+ ASSERT_EQ(1 + (~static_cast<int##Size##_t>(Value) & Mask##Size), \
+ test.contentsOfDword(T0)) \
+ << "(Addr, " #Size ")"; \
+ reset(); \
+ } while (0)
+
+#define TestImpl(Size) \
+ do { \
+ TestImplAddr(Size); \
+ TestImplReg(eax, Size); \
+ TestImplReg(ebx, Size); \
+ TestImplReg(ecx, Size); \
+ TestImplReg(edx, Size); \
+ TestImplReg(esi, Size); \
+ TestImplReg(edi, Size); \
+ } while (0)
+
+ TestImpl(8);
+ TestImpl(16);
+ TestImpl(32);
+
+#undef TestImpl
+#undef TestImplAddr
+#undef TestImplReg
+}
+
+TEST_F(AssemblerX8632Test, Not) {
+#define TestImpl(Dst) \
+ do { \
+ static constexpr uint32_t Value = 0xFF00A543; \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, Immediate(Value)); \
+ __ notl(GPRRegister::Encoded_Reg_##Dst); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(~Value, test.Dst()) << "(" #Dst ")"; \
+ reset(); \
+ } while (0)
+
+ TestImpl(eax);
+ TestImpl(ebx);
+ TestImpl(ecx);
+ TestImpl(edx);
+ TestImpl(esi);
+ TestImpl(edi);
+
+#undef TestImpl
+}
+
+TEST_F(AssemblerX8632Test, Bswap) {
+#define TestImpl(Dst) \
+ do { \
+ static constexpr uint32_t Value = 0xFF00A543; \
+ static constexpr uint32_t Expected = 0x43A500FF; \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, Immediate(Value)); \
+ __ bswap(IceType_i32, GPRRegister::Encoded_Reg_##Dst); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(Expected, test.Dst()) << "(" #Dst ")"; \
+ reset(); \
+ } while (0)
+
+ TestImpl(eax);
+ TestImpl(ebx);
+ TestImpl(ecx);
+ TestImpl(edx);
+ TestImpl(esi);
+ TestImpl(edi);
+
+#undef TestImpl
+}
+
+TEST_F(AssemblerX8632Test, Bt) {
+#define TestImpl(Dst, Value0, Src, Value1) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Dst ", " #Value0 ", " #Src ", " #Value1 ")"; \
+ static constexpr uint32_t Expected = ((Value0) & (1u << (Value1))) != 0; \
+ \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dst, Immediate(Value0)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src, Immediate(Value1)); \
+ __ bt(GPRRegister::Encoded_Reg_##Dst, GPRRegister::Encoded_Reg_##Src); \
+ __ setcc(Cond::Br_b, ByteRegister::Encoded_Reg_al); \
+ __ And(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0xFFu)); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ ASSERT_EQ(Expected, test.eax()) << TestString; \
+ reset(); \
+ } while (0)
+
+ TestImpl(eax, 0x08000000, ebx, 27u);
+ TestImpl(ebx, 0x08000000, ecx, 23u);
+ TestImpl(ecx, 0x00000000, edx, 1u);
+ TestImpl(edx, 0x08000300, esi, 9u);
+ TestImpl(esi, 0x08000300, edi, 10u);
+ TestImpl(edi, 0x7FFFEFFF, eax, 13u);
+
+#undef TestImpl
+}
+
+template <uint32_t Value, uint32_t Bits> class BitScanHelper {
+ BitScanHelper() = delete;
+
+public:
+ static_assert(Bits == 16 || Bits == 32, "Bits must be 16 or 32");
+ using ValueType =
+ typename std::conditional<Bits == 16, uint16_t, uint32_t>::type;
+
+private:
+ static constexpr ValueType BitIndex(bool Forward, ValueType Index) {
+ return (Value == 0)
+ ? BitScanHelper<Value, Bits>::NoBitSet
+ : (Value & (1u << Index)
+ ? Index
+ : BitIndex(Forward, (Forward ? Index + 1 : Index - 1)));
+ }
+
+public:
+ static constexpr ValueType NoBitSet = static_cast<ValueType>(-1);
+ static constexpr ValueType bsf = BitIndex(/*Forward*/ true, /*Index=*/0);
+ static constexpr ValueType bsr =
+ BitIndex(/*Forward*/ false, /*Index=*/Bits - 1);
+};
+
+TEST_F(AssemblerX8632Test, BitScanOperations) {
+#define TestImplRegReg(Inst, Dst, Src, Value1, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", " #Src ", " #Value1 ", " #Size ")"; \
+ static constexpr uint32_t Expected = BitScanHelper<Value1, Size>::Inst; \
+ const uint32_t ZeroFlag = allocateDword(); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate(Value1)); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ GPRRegister::Encoded_Reg_##Src); \
+ __ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(ZeroFlag, 0u); \
+ test.run(); \
+ \
+ ASSERT_EQ((Expected == BitScanHelper<Value1, Size>::NoBitSet), \
+ test.contentsOfDword(ZeroFlag)) \
+ << TestString; \
+ if ((Expected != BitScanHelper<Value1, Size>::NoBitSet)) { \
+ ASSERT_EQ(Expected, test.Dst()) << TestString; \
+ } \
+ reset(); \
+ } while (0)
+
+#define TestImplRegAddr(Inst, Dst, Value1, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Inst ", " #Dst ", Addr, " #Value1 ", " #Size ")"; \
+ static constexpr uint32_t Expected = BitScanHelper<Value1, Size>::Inst; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t ZeroFlag = allocateDword(); \
+ __ Inst(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst, \
+ dwordAddress(T0)); \
+ __ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, Value1); \
+ test.setDwordTo(ZeroFlag, 0u); \
+ test.run(); \
+ \
+ ASSERT_EQ((Expected == BitScanHelper<Value1, Size>::NoBitSet), \
+ test.contentsOfDword(ZeroFlag)) \
+ << TestString; \
+ if (Expected != BitScanHelper<Value1, Size>::NoBitSet) { \
+ ASSERT_EQ(Expected, test.Dst()) << TestString; \
+ } \
+ reset(); \
+ } while (0)
+
+#define TestImplSize(Dst, Src, Value1, Size) \
+ do { \
+ TestImplRegReg(bsf, Dst, Src, Value1, Size); \
+ TestImplRegAddr(bsf, Dst, Value1, Size); \
+ TestImplRegReg(bsr, Dst, Src, Value1, Size); \
+ TestImplRegAddr(bsf, Dst, Value1, Size); \
+ } while (0)
+
+#define TestImplValue(Dst, Src, Value1) \
+ do { \
+ TestImplSize(Dst, Src, Value1, 16); \
+ TestImplSize(Dst, Src, Value1, 32); \
+ } while (0)
+
+#define TestImpl(Dst, Src) \
+ do { \
+ TestImplValue(Dst, Src, 0x80000001); \
+ TestImplValue(Dst, Src, 0x00000000); \
+ TestImplValue(Dst, Src, 0x80001000); \
+ TestImplValue(Dst, Src, 0x00FFFF00); \
+ } while (0)
+
+ TestImpl(eax, ebx);
+ TestImpl(ebx, ecx);
+ TestImpl(ecx, edx);
+ TestImpl(edx, esi);
+ TestImpl(esi, edi);
+ TestImpl(edi, eax);
+
+#undef TestImpl
+#undef TestImplValue
+#undef TestImplSize
+#undef TestImplRegAddr
+#undef TestImplRegReg
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Nop) {
+#define TestImpl(Size, ...) \
+ do { \
+ static constexpr char TestString[] = "(" #Size ", " #__VA_ARGS__ ")"; \
+ __ nop(Size); \
+ ASSERT_EQ(Size##u, codeBytesSize()) << TestString; \
+ ASSERT_TRUE(verifyBytes<Size>(codeBytes(), __VA_ARGS__)) << TestString; \
+ reset(); \
+ } while (0);
+
+ TestImpl(1, 0x90);
+ TestImpl(2, 0x66, 0x90);
+ TestImpl(3, 0x0F, 0x1F, 0x00);
+ TestImpl(4, 0x0F, 0x1F, 0x40, 0x00);
+ TestImpl(5, 0x0F, 0x1F, 0x44, 0x00, 0x00);
+ TestImpl(6, 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00);
+ TestImpl(7, 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00);
+ TestImpl(8, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+#undef TestImpl
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Int3) {
+ __ int3();
+ static constexpr uint32_t ByteCount = 1;
+ ASSERT_EQ(ByteCount, codeBytesSize());
+ verifyBytes<ByteCount>(codeBytes(), 0xCC);
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Hlt) {
+ __ hlt();
+ static constexpr uint32_t ByteCount = 1;
+ ASSERT_EQ(ByteCount, codeBytesSize());
+ verifyBytes<ByteCount>(codeBytes(), 0xF4);
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Ud2) {
+ __ ud2();
+ static constexpr uint32_t ByteCount = 2;
+ ASSERT_EQ(ByteCount, codeBytesSize());
+ verifyBytes<ByteCount>(codeBytes(), 0x0F, 0x0B);
+}
+
+TEST_F(AssemblerX8632Test, Jmp) {
+// TestImplReg uses jmp(Label), so jmp(Label) needs to be tested before it.
+#define TestImplAddr(Near) \
+ do { \
+ Label ForwardJmp; \
+ Label BackwardJmp; \
+ Label Done; \
+ \
+ __ jmp(&ForwardJmp, AssemblerX8632::k##Near##Jump); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ bind(&BackwardJmp); \
+ __ jmp(&Done, AssemblerX8632::k##Near##Jump); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ bind(&ForwardJmp); \
+ __ jmp(&BackwardJmp, AssemblerX8632::k##NearJump); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ bind(&Done); \
+ } while (0)
+
+#define TestImplReg(Dst) \
+ do { \
+ __ call(Immediate(16)); \
+ Label Done; \
+ __ jmp(&Done, AssemblerX8632::kNearJump); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ popl(GPRRegister::Encoded_Reg_##Dst); \
+ __ jmp(GPRRegister::Encoded_Reg_##Dst); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ hlt(); \
+ __ bind(&Done); \
+ \
+ AssembledTest test = assemble(); \
+ test.run(); \
+ \
+ reset(); \
+ } while (0)
+
+ TestImplAddr(Near);
+ TestImplAddr(Far);
+
+ TestImplReg(eax);
+ TestImplReg(ebx);
+ TestImplReg(ecx);
+ TestImplReg(edx);
+ TestImplReg(esi);
+ TestImplReg(edi);
+
+#undef TestImplReg
+#undef TestImplAddr
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Mfence) {
+ __ mfence();
+
+ static constexpr uint8_t ByteCount = 3;
+ ASSERT_EQ(ByteCount, codeBytesSize());
+ verifyBytes<ByteCount>(codeBytes(), 0x0F, 0xAE, 0xF0);
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Lock) {
+ __ lock();
+
+ static constexpr uint8_t ByteCount = 1;
+ ASSERT_EQ(ByteCount, codeBytesSize());
+ verifyBytes<ByteCount>(codeBytes(), 0xF0);
+}
+
+TEST_F(AssemblerX8632Test, Xchg) {
+ static constexpr uint32_t Mask8 = 0x000000FF;
+ static constexpr uint32_t Mask16 = 0x0000FFFF;
+ static constexpr uint32_t Mask32 = 0xFFFFFFFF;
+
+#define TestImplAddrReg(Value0, Dst1, Value1, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Value0 ", " #Dst1 ", " #Value1 ", " #Size ")"; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = (Value0)&Mask##Size; \
+ const uint32_t V1 = (Value1)&Mask##Size; \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \
+ Immediate(Value1)); \
+ __ xchg(IceType_i##Size, dwordAddress(T0), \
+ GPRRegister::Encoded_Reg_##Dst1); \
+ __ And(IceType_i32, GPRRegister::Encoded_Reg_##Dst1, \
+ Immediate(Mask##Size)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(V0, test.Dst1()) << TestString; \
+ ASSERT_EQ(V1, test.contentsOfDword(T0)) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplSize(Dst1, Size) \
+ do { \
+ TestImplAddrReg(0xa2b34567, Dst1, 0x0507ddee, Size); \
+ } while (0)
+
+#define TestImpl(Dst1) \
+ do { \
+ if (GPRRegister::Encoded_Reg_##Dst1 < 4) { \
+ TestImplSize(Dst1, 8); \
+ } \
+ TestImplSize(Dst1, 16); \
+ TestImplSize(Dst1, 32); \
+ } while (0)
+
+ TestImpl(eax);
+ TestImpl(ebx);
+ TestImpl(ecx);
+ TestImpl(edx);
+ TestImpl(esi);
+ TestImpl(edi);
+
+#undef TestImpl
+#undef TestImplSize
+#undef TestImplAddrReg
+}
+
+TEST_F(AssemblerX8632Test, Xadd) {
+ static constexpr bool NotLocked = false;
+ static constexpr bool Locked = true;
+
+ static constexpr uint32_t Mask8 = 0x000000FF;
+ static constexpr uint32_t Mask16 = 0x0000FFFF;
+ static constexpr uint32_t Mask32 = 0xFFFFFFFF;
+
+#define TestImplAddrReg(Value0, Dst1, Value1, LockedOrNot, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Value0 ", " #Dst1 ", " #Value1 ", " #Size ")"; \
+ const uint32_t T0 = allocateDword(); \
+ const uint32_t V0 = (Value0)&Mask##Size; \
+ const uint32_t V1 = (Value1)&Mask##Size; \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Dst1, \
+ Immediate(Value1)); \
+ __ xadd(IceType_i##Size, dwordAddress(T0), \
+ GPRRegister::Encoded_Reg_##Dst1, LockedOrNot); \
+ __ And(IceType_i32, GPRRegister::Encoded_Reg_##Dst1, \
+ Immediate(Mask##Size)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.run(); \
+ \
+ ASSERT_EQ(V0, test.Dst1()) << TestString; \
+ ASSERT_EQ(Mask##Size &(V1 + V0), test.contentsOfDword(T0)) << TestString; \
+ reset(); \
+ } while (0)
+
+#define TestImplSize(Dst1, Size) \
+ do { \
+ TestImplAddrReg(0xa2b34567, Dst1, 0x0507ddee, NotLocked, Size); \
+ TestImplAddrReg(0xa2b34567, Dst1, 0x0507ddee, Locked, Size); \
+ } while (0)
+
+#define TestImpl(Dst1) \
+ do { \
+ if (GPRRegister::Encoded_Reg_##Dst1 < 4) { \
+ TestImplSize(Dst1, 8); \
+ } \
+ TestImplSize(Dst1, 16); \
+ TestImplSize(Dst1, 32); \
+ } while (0)
+
+ TestImpl(eax);
+ TestImpl(ebx);
+ TestImpl(ecx);
+ TestImpl(edx);
+ TestImpl(esi);
+ TestImpl(edi);
+
+#undef TestImpl
+#undef TestImplSize
+#undef TestImplAddrReg
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Xadd) {
+ static constexpr bool NotLocked = false;
+ static constexpr bool Locked = true;
+
+ // Ensures that xadd emits a lock prefix accordingly.
+ {
+ __ xadd(IceType_i8, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, NotLocked);
+ static constexpr uint8_t ByteCountNotLocked8 = 7;
+ ASSERT_EQ(ByteCountNotLocked8, codeBytesSize());
+ verifyBytes<ByteCountNotLocked8>(codeBytes(), 0x0F, 0xC0, 0x35, 0x00, 0xFF,
+ 0x01, 0x00);
+ reset();
+
+ __ xadd(IceType_i8, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, Locked);
+ static constexpr uint8_t ByteCountLocked8 = 1 + ByteCountNotLocked8;
+ ASSERT_EQ(ByteCountLocked8, codeBytesSize());
+ verifyBytes<ByteCountLocked8>(codeBytes(), 0xF0, 0x0F, 0xC0, 0x35, 0x00,
+ 0xFF, 0x01, 0x00);
+ reset();
+ }
+
+ {
+ __ xadd(IceType_i16, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, NotLocked);
+ static constexpr uint8_t ByteCountNotLocked16 = 8;
+ ASSERT_EQ(ByteCountNotLocked16, codeBytesSize());
+ verifyBytes<ByteCountNotLocked16>(codeBytes(), 0x66, 0x0F, 0xC1, 0x35, 0x00,
+ 0xFF, 0x01, 0x00);
+ reset();
+
+ __ xadd(IceType_i16, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, Locked);
+ static constexpr uint8_t ByteCountLocked16 = 1 + ByteCountNotLocked16;
+ ASSERT_EQ(ByteCountLocked16, codeBytesSize());
+ verifyBytes<ByteCountLocked16>(codeBytes(), 0x66, 0xF0, 0x0F, 0xC1, 0x35,
+ 0x00, 0xFF, 0x01, 0x00);
+ reset();
+ }
+
+ {
+ __ xadd(IceType_i32, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, NotLocked);
+ static constexpr uint8_t ByteCountNotLocked32 = 7;
+ ASSERT_EQ(ByteCountNotLocked32, codeBytesSize());
+ verifyBytes<ByteCountNotLocked32>(codeBytes(), 0x0F, 0xC1, 0x35, 0x00, 0xFF,
+ 0x01, 0x00);
+ reset();
+
+ __ xadd(IceType_i32, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, Locked);
+ static constexpr uint8_t ByteCountLocked32 = 1 + ByteCountNotLocked32;
+ ASSERT_EQ(ByteCountLocked32, codeBytesSize());
+ verifyBytes<ByteCountLocked32>(codeBytes(), 0xF0, 0x0F, 0xC1, 0x35, 0x00,
+ 0xFF, 0x01, 0x00);
+ reset();
+ }
+}
+
+TEST_F(AssemblerX8632LowLevelTest, EmitSegmentOverride) {
+#define TestImpl(Prefix) \
+ do { \
+ static constexpr uint8_t ByteCount = 1; \
+ __ emitSegmentOverride(Prefix); \
+ ASSERT_EQ(ByteCount, codeBytesSize()) << Prefix; \
+ verifyBytes<ByteCount>(codeBytes(), Prefix); \
+ reset(); \
+ } while (0)
+
+ TestImpl(0x26);
+ TestImpl(0x2E);
+ TestImpl(0x36);
+ TestImpl(0x3E);
+ TestImpl(0x64);
+ TestImpl(0x65);
+ TestImpl(0x66);
+ TestImpl(0x67);
+
+#undef TestImpl
+}
+
+TEST_F(AssemblerX8632Test, Cmpxchg8b) {
+ static constexpr bool NotLocked = false;
+ static constexpr bool Locked = true;
+
+#define TestImpl(Value0, Value1, ValueMem, LockedOrNot) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Value0 ", " #Value1 ", " #ValueMem ", " #LockedOrNot ")"; \
+ const uint32_t T0 = allocateQword(); \
+ static constexpr uint64_t V0 = ValueMem; \
+ const uint32_t ZeroFlag = allocateDword(); \
+ \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, \
+ Immediate(uint64_t(Value0) & 0xFFFFFFFF)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, \
+ Immediate(uint64_t(Value0) >> 32)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, \
+ Immediate(uint64_t(Value1) & 0xFFFFFFFF)); \
+ __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, \
+ Immediate(uint64_t(Value1) >> 32)); \
+ __ cmpxchg8b(dwordAddress(T0), LockedOrNot); \
+ __ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setQwordTo(T0, V0); \
+ test.setDwordTo(ZeroFlag, uint32_t(0xFF)); \
+ test.run(); \
+ \
+ if (V0 == (Value0)) { \
+ ASSERT_EQ(uint64_t(Value1), test.contentsOfQword(T0)) << TestString; \
+ ASSERT_EQ(1u, test.contentsOfDword(ZeroFlag)) << TestString; \
+ } else { \
+ ASSERT_EQ(uint64_t(ValueMem) & 0xFFFFFFFF, test.eax()) << TestString; \
+ ASSERT_EQ((uint64_t(ValueMem) >> 32) & 0xFFFFFFFF, test.edx()) \
+ << TestString; \
+ ASSERT_EQ(0u, test.contentsOfDword(ZeroFlag)) << TestString; \
+ } \
+ reset(); \
+ } while (0)
+
+ TestImpl(0x98987676543210ull, 0x1, 0x98987676543210ull, NotLocked);
+ TestImpl(0x98987676543210ull, 0x1, 0x98987676543210ull, Locked);
+ TestImpl(0x98987676543210ull, 0x1, 0x98987676543211ull, NotLocked);
+ TestImpl(0x98987676543210ull, 0x1, 0x98987676543211ull, Locked);
+
+#undef TestImpl
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Cmpxchg8b) {
+ static constexpr bool NotLocked = false;
+ static constexpr bool Locked = true;
+
+ // Ensures that cmpxchg8b emits a lock prefix accordingly.
+ __ cmpxchg8b(Address::Absolute(0x1FF00), NotLocked);
+ static constexpr uint8_t ByteCountNotLocked = 7;
+ ASSERT_EQ(ByteCountNotLocked, codeBytesSize());
+ verifyBytes<ByteCountNotLocked>(codeBytes(), 0x0F, 0xC7, 0x0D, 0x00, 0xFF,
+ 0x01, 0x00);
+ reset();
+
+ __ cmpxchg8b(Address::Absolute(0x1FF00), Locked);
+ static constexpr uint8_t ByteCountLocked = 1 + ByteCountNotLocked;
+ ASSERT_EQ(ByteCountLocked, codeBytesSize());
+ verifyBytes<ByteCountLocked>(codeBytes(), 0xF0, 0x0F, 0xC7, 0x0D, 0x00, 0xFF,
+ 0x01, 0x00);
+ reset();
+}
+
+TEST_F(AssemblerX8632Test, Cmpxchg) {
+ static constexpr bool NotLocked = false;
+ static constexpr bool Locked = true;
+
+ static constexpr uint32_t Mask8 = 0x000000FF;
+ static constexpr uint32_t Mask16 = 0x0000FFFF;
+ static constexpr uint32_t Mask32 = 0xFFFFFFFF;
+
+#define TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, Size) \
+ do { \
+ static constexpr char TestString[] = \
+ "(" #Value0 ", " #Src ", " #Value1 ", " #ValueMem ", " #LockedOrNot \
+ ", " #Size ")"; \
+ const uint32_t T0 = allocateDword(); \
+ static constexpr uint32_t V0 = (ValueMem)&Mask##Size; \
+ const uint32_t ZeroFlag = allocateDword(); \
+ \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_eax, \
+ Immediate((Value0)&Mask##Size)); \
+ __ mov(IceType_i##Size, GPRRegister::Encoded_Reg_##Src, \
+ Immediate((Value1)&Mask##Size)); \
+ __ cmpxchg(IceType_i##Size, dwordAddress(T0), \
+ GPRRegister::Encoded_Reg_##Src, LockedOrNot); \
+ __ setcc(Cond::Br_e, dwordAddress(ZeroFlag)); \
+ \
+ AssembledTest test = assemble(); \
+ test.setDwordTo(T0, V0); \
+ test.setDwordTo(ZeroFlag, uint32_t(0xFF)); \
+ test.run(); \
+ \
+ if (V0 == (Mask##Size & (Value0))) { \
+ ASSERT_EQ(uint32_t((Value1)&Mask##Size), test.contentsOfDword(T0)) \
+ << TestString; \
+ ASSERT_EQ(1u, test.contentsOfDword(ZeroFlag)) << TestString; \
+ } else { \
+ ASSERT_EQ(uint32_t((ValueMem)&Mask##Size), test.eax()) << TestString; \
+ ASSERT_EQ(0u, test.contentsOfDword(ZeroFlag)) << TestString; \
+ } \
+ reset(); \
+ } while (0)
+
+#define TestImplValue(Value0, Src, Value1, ValueMem, LockedOrNot) \
+ do { \
+ if (GPRRegister::Encoded_Reg_##Src < 4) { \
+ TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, 8); \
+ } \
+ TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, 16); \
+ TestImplAddrReg(Value0, Src, Value1, ValueMem, LockedOrNot, 32); \
+ } while (0)
+
+#define TestImpl(Src, LockedOrNot) \
+ do { \
+ TestImplValue(0xFFFFFFFF, Src, 0x1, 0xFFFFFFFF, LockedOrNot); \
+ TestImplValue(0x0FFF0F0F, Src, 0x1, 0xFFFFFFFF, LockedOrNot); \
+ } while (0)
+
+ TestImpl(ebx, Locked);
+ TestImpl(edx, NotLocked);
+ TestImpl(ecx, Locked);
+ TestImpl(ecx, NotLocked);
+ TestImpl(edx, Locked);
+ TestImpl(edx, NotLocked);
+ TestImpl(esi, Locked);
+ TestImpl(esi, NotLocked);
+ TestImpl(edi, Locked);
+ TestImpl(edi, NotLocked);
+
+#undef TestImpl
+#undef TestImplValue
+#undef TestImplAddrReg
+}
+
+TEST_F(AssemblerX8632LowLevelTest, Cmpxchg) {
+ static constexpr bool NotLocked = false;
+ static constexpr bool Locked = true;
+
+ // Ensures that cmpxchg emits a lock prefix accordingly.
+ {
+ __ cmpxchg(IceType_i8, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, NotLocked);
+ static constexpr uint8_t ByteCountNotLocked8 = 7;
+ ASSERT_EQ(ByteCountNotLocked8, codeBytesSize());
+ verifyBytes<ByteCountNotLocked8>(codeBytes(), 0x0F, 0xB0, 0x35, 0x00, 0xFF,
+ 0x01, 0x00);
+ reset();
+
+ __ cmpxchg(IceType_i8, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, Locked);
+ static constexpr uint8_t ByteCountLocked8 = 1 + ByteCountNotLocked8;
+ ASSERT_EQ(ByteCountLocked8, codeBytesSize());
+ verifyBytes<ByteCountLocked8>(codeBytes(), 0xF0, 0x0F, 0xB0, 0x35, 0x00,
+ 0xFF, 0x01, 0x00);
+ reset();
+ }
+
+ {
+ __ cmpxchg(IceType_i16, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, NotLocked);
+ static constexpr uint8_t ByteCountNotLocked16 = 8;
+ ASSERT_EQ(ByteCountNotLocked16, codeBytesSize());
+ verifyBytes<ByteCountNotLocked16>(codeBytes(), 0x66, 0x0F, 0xB1, 0x35, 0x00,
+ 0xFF, 0x01, 0x00);
+ reset();
+
+ __ cmpxchg(IceType_i16, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, Locked);
+ static constexpr uint8_t ByteCountLocked16 = 1 + ByteCountNotLocked16;
+ ASSERT_EQ(ByteCountLocked16, codeBytesSize());
+ verifyBytes<ByteCountLocked16>(codeBytes(), 0x66, 0xF0, 0x0F, 0xB1, 0x35,
+ 0x00, 0xFF, 0x01, 0x00);
+ reset();
+ }
+
+ {
+ __ cmpxchg(IceType_i32, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, NotLocked);
+ static constexpr uint8_t ByteCountNotLocked32 = 7;
+ ASSERT_EQ(ByteCountNotLocked32, codeBytesSize());
+ verifyBytes<ByteCountNotLocked32>(codeBytes(), 0x0F, 0xB1, 0x35, 0x00, 0xFF,
+ 0x01, 0x00);
+ reset();
+
+ __ cmpxchg(IceType_i32, Address::Absolute(0x1FF00),
+ GPRRegister::Encoded_Reg_esi, Locked);
+ static constexpr uint8_t ByteCountLocked32 = 1 + ByteCountNotLocked32;
+ ASSERT_EQ(ByteCountLocked32, codeBytesSize());
+ verifyBytes<ByteCountLocked32>(codeBytes(), 0xF0, 0x0F, 0xB1, 0x35, 0x00,
+ 0xFF, 0x01, 0x00);
+ reset();
+ }
+}
+
+TEST_F(AssemblerX8632Test, Set1ps) {
+#define TestImpl(Xmm, Src, Imm) \
+ do { \
+ __ set1ps(XmmRegister::Encoded_Reg_##Xmm, GPRRegister::Encoded_Reg_##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, ebx, 1);
+ TestImpl(xmm1, ecx, 2);
+ TestImpl(xmm2, edx, 3);
+ TestImpl(xmm3, esi, 4);
+ TestImpl(xmm4, edi, 5);
+ TestImpl(xmm5, eax, 6);
+ TestImpl(xmm6, ebx, 7);
+ TestImpl(xmm7, ecx, 8);
+
+#undef TestImpl
+}
+
jvoung (off chromium) 2015/07/23 17:59:36 Uh... haven't read all this. I'm assuming this is
John 2015/07/27 20:35:58 Well, these are just dumb tests -- I went over the
#undef __
} // end of anonymous namespace

Powered by Google App Engine
This is Rietveld 408576698