mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-15 13:57:57 -07:00
Merge pull request #940 from delroth/x64emittertest
Add unit tests for the x64Emitter
This commit is contained in:
commit
f47c30a804
@ -666,7 +666,7 @@ void XEmitter::POP(int /*bits*/, const OpArg ®)
|
||||
if (reg.IsSimpleReg())
|
||||
POP(reg.GetSimpleReg());
|
||||
else
|
||||
INT3();
|
||||
_assert_msg_(DYNA_REC, 0, "POP - Unsupported encoding");
|
||||
}
|
||||
|
||||
void XEmitter::BSWAP(int bits, X64Reg reg)
|
||||
@ -720,6 +720,8 @@ void XEmitter::SETcc(CCFlags flag, OpArg dest)
|
||||
void XEmitter::CMOVcc(int bits, X64Reg dest, OpArg src, CCFlags flag)
|
||||
{
|
||||
if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "CMOVcc - Imm argument");
|
||||
if (bits == 8) _assert_msg_(DYNA_REC, 0, "CMOVcc - 8 bits unsupported");
|
||||
if (bits == 16) Write8(0x66);
|
||||
src.operandReg = dest;
|
||||
src.WriteRex(this, bits, bits);
|
||||
Write8(0x0F);
|
||||
@ -732,7 +734,7 @@ void XEmitter::WriteMulDivType(int bits, OpArg src, int ext)
|
||||
if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "WriteMulDivType - Imm argument");
|
||||
src.operandReg = ext;
|
||||
if (bits == 16) Write8(0x66);
|
||||
src.WriteRex(this, bits, bits);
|
||||
src.WriteRex(this, bits, bits, 0);
|
||||
if (bits == 8)
|
||||
{
|
||||
Write8(0xF6);
|
||||
@ -831,7 +833,7 @@ void XEmitter::MOVZX(int dbits, int sbits, X64Reg dest, OpArg src)
|
||||
}
|
||||
else
|
||||
{
|
||||
Crash();
|
||||
_assert_msg_(DYNA_REC, 0, "MOVZX - Invalid size");
|
||||
}
|
||||
src.WriteRest(this);
|
||||
}
|
||||
@ -1172,10 +1174,8 @@ void XEmitter::OR (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(t
|
||||
void XEmitter::XOR (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(this, bits, nrmXOR, a1, a2);}
|
||||
void XEmitter::MOV (int bits, const OpArg &a1, const OpArg &a2)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
_assert_msg_(DYNA_REC, !a1.IsSimpleReg() || !a2.IsSimpleReg() || a1.GetSimpleReg() != a2.GetSimpleReg(), "Redundant MOV @ %p - bug in JIT?",
|
||||
code);
|
||||
#endif
|
||||
if (a1.IsSimpleReg() && a2.IsSimpleReg() && a1.GetSimpleReg() == a2.GetSimpleReg())
|
||||
ERROR_LOG(DYNA_REC, "Redundant MOV @ %p - bug in JIT?", code);
|
||||
WriteNormalOp(this, bits, nrmMOV, a1, a2);
|
||||
}
|
||||
void XEmitter::TEST(int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(this, bits, nrmTEST, a1, a2);}
|
||||
@ -1658,28 +1658,30 @@ void XEmitter::FWAIT()
|
||||
}
|
||||
|
||||
// TODO: make this more generic
|
||||
void XEmitter::WriteFloatLoadStore(int bits, FloatOp op, OpArg arg)
|
||||
void XEmitter::WriteFloatLoadStore(int bits, FloatOp op, FloatOp op_80b, OpArg arg)
|
||||
{
|
||||
int mf = 0;
|
||||
|
||||
_assert_msg_(DYNA_REC, !(bits == 80 && op_80b == floatINVALID), "WriteFloatLoadStore: 80 bits not supported for this instruction");
|
||||
switch (bits)
|
||||
{
|
||||
case 32: mf = 0; break;
|
||||
case 64: mf = 2; break;
|
||||
default: _assert_msg_(DYNA_REC, 0, "WriteFloatLoadStore: bits is not 32 or 64");
|
||||
case 64: mf = 4; break;
|
||||
case 80: mf = 2; break;
|
||||
default: _assert_msg_(DYNA_REC, 0, "WriteFloatLoadStore: invalid bits (should be 32/64/80)");
|
||||
}
|
||||
|
||||
Write8(0xd9 | (mf << 1));
|
||||
Write8(0xd9 | mf);
|
||||
// x87 instructions use the reg field of the ModR/M byte as opcode:
|
||||
if (bits == 80)
|
||||
op = op_80b;
|
||||
arg.WriteRest(this, 0, (X64Reg) op);
|
||||
}
|
||||
|
||||
void XEmitter::FLD(int bits, OpArg src) {WriteFloatLoadStore(bits, floatLD, src);}
|
||||
void XEmitter::FST(int bits, OpArg dest) {WriteFloatLoadStore(bits, floatST, dest);}
|
||||
void XEmitter::FSTP(int bits, OpArg dest) {WriteFloatLoadStore(bits, floatSTP, dest);}
|
||||
void XEmitter::FLD(int bits, OpArg src) {WriteFloatLoadStore(bits, floatLD, floatLD80, src);}
|
||||
void XEmitter::FST(int bits, OpArg dest) {WriteFloatLoadStore(bits, floatST, floatINVALID, dest);}
|
||||
void XEmitter::FSTP(int bits, OpArg dest) {WriteFloatLoadStore(bits, floatSTP, floatSTP80, dest);}
|
||||
void XEmitter::FNSTSW_AX() { Write8(0xDF); Write8(0xE0); }
|
||||
|
||||
void XEmitter::RTDSC() { Write8(0x0F); Write8(0x31); }
|
||||
void XEmitter::RDTSC() { Write8(0x0F); Write8(0x31); }
|
||||
|
||||
// helper routines for setting pointers
|
||||
void XEmitter::CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2)
|
||||
|
@ -105,6 +105,10 @@ enum FloatOp {
|
||||
floatLD = 0,
|
||||
floatST = 2,
|
||||
floatSTP = 3,
|
||||
floatLD80 = 5,
|
||||
floatSTP80 = 7,
|
||||
|
||||
floatINVALID = -1,
|
||||
};
|
||||
|
||||
class XEmitter;
|
||||
@ -271,7 +275,7 @@ private:
|
||||
void WriteSSE41Op(int size, u16 sseOp, bool packed, X64Reg regOp, OpArg arg, int extrabytes = 0);
|
||||
void WriteAVXOp(int size, u16 sseOp, bool packed, X64Reg regOp, OpArg arg, int extrabytes = 0);
|
||||
void WriteAVXOp(int size, u16 sseOp, bool packed, X64Reg regOp1, X64Reg regOp2, OpArg arg, int extrabytes = 0);
|
||||
void WriteFloatLoadStore(int bits, FloatOp op, OpArg arg);
|
||||
void WriteFloatLoadStore(int bits, FloatOp op, FloatOp op_80b, OpArg arg);
|
||||
void WriteNormalOp(XEmitter *emit, int bits, NormalOp op, const OpArg &a1, const OpArg &a2);
|
||||
|
||||
protected:
|
||||
@ -336,7 +340,6 @@ public:
|
||||
FixupBranch J(bool force5bytes = false);
|
||||
|
||||
void JMP(const u8 * addr, bool force5Bytes = false);
|
||||
void JMP(OpArg arg);
|
||||
void JMPptr(const OpArg &arg);
|
||||
void JMPself(); //infinite loop!
|
||||
#ifdef CALL
|
||||
@ -536,11 +539,8 @@ public:
|
||||
// SSE/SSE2: Useful alternative to shuffle in some cases.
|
||||
void MOVDDUP(X64Reg regOp, OpArg arg);
|
||||
|
||||
// THESE TWO ARE NEW AND UNTESTED
|
||||
void UNPCKLPS(X64Reg dest, OpArg src);
|
||||
void UNPCKHPS(X64Reg dest, OpArg src);
|
||||
|
||||
// These are OK.
|
||||
void UNPCKLPD(X64Reg dest, OpArg src);
|
||||
void UNPCKHPD(X64Reg dest, OpArg src);
|
||||
|
||||
@ -588,21 +588,23 @@ public:
|
||||
void CVTPS2PD(X64Reg dest, OpArg src);
|
||||
void CVTPD2PS(X64Reg dest, OpArg src);
|
||||
void CVTSS2SD(X64Reg dest, OpArg src);
|
||||
void CVTSS2SI(X64Reg dest, OpArg src);
|
||||
void CVTSI2SS(X64Reg dest, OpArg src);
|
||||
void CVTSD2SS(X64Reg dest, OpArg src);
|
||||
void CVTSD2SI(X64Reg dest, OpArg src);
|
||||
void CVTSI2SD(X64Reg dest, OpArg src);
|
||||
void CVTDQ2PD(X64Reg regOp, OpArg arg);
|
||||
void CVTPD2DQ(X64Reg regOp, OpArg arg);
|
||||
void CVTDQ2PS(X64Reg regOp, OpArg arg);
|
||||
void CVTPS2DQ(X64Reg regOp, OpArg arg);
|
||||
|
||||
void CVTTSS2SI(X64Reg regOp, OpArg arg);
|
||||
void CVTTSD2SI(X64Reg regOp, OpArg arg);
|
||||
void CVTTPS2DQ(X64Reg regOp, OpArg arg);
|
||||
void CVTTPD2DQ(X64Reg regOp, OpArg arg);
|
||||
|
||||
// Destinations are X64 regs (rax, rbx, ...) for these instructions.
|
||||
void CVTSS2SI(X64Reg xregdest, OpArg src);
|
||||
void CVTSD2SI(X64Reg xregdest, OpArg src);
|
||||
void CVTTSS2SI(X64Reg xregdest, OpArg arg);
|
||||
void CVTTSD2SI(X64Reg xregdest, OpArg arg);
|
||||
|
||||
// SSE2: Packed integer instructions
|
||||
void PACKSSDW(X64Reg dest, OpArg arg);
|
||||
void PACKSSWB(X64Reg dest, OpArg arg);
|
||||
@ -706,7 +708,7 @@ public:
|
||||
void VPAND(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
||||
void VPANDN(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
||||
|
||||
void RTDSC();
|
||||
void RDTSC();
|
||||
|
||||
// Utility functions
|
||||
// The difference between this and CALL is that this aligns the stack
|
||||
|
@ -5,3 +5,4 @@ add_dolphin_test(FifoQueueTest FifoQueueTest.cpp)
|
||||
add_dolphin_test(FixedSizeQueueTest FixedSizeQueueTest.cpp)
|
||||
add_dolphin_test(FlagTest FlagTest.cpp)
|
||||
add_dolphin_test(MathUtilTest MathUtilTest.cpp)
|
||||
add_dolphin_test(x64EmitterTest x64EmitterTest.cpp)
|
||||
|
833
Source/UnitTests/Common/x64EmitterTest.cpp
Normal file
833
Source/UnitTests/Common/x64EmitterTest.cpp
Normal file
@ -0,0 +1,833 @@
|
||||
// Copyright 2014 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <disasm.h> // From Bochs, fallback included in Externals.
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// gtest defines the TEST macro to generate test case functions. It conflicts
|
||||
// with the TEST method in the x64Emitter.
|
||||
//
|
||||
// Since we use TEST_F in this file to attach the test cases to a fixture, we
|
||||
// can get away with simply undef'ing TEST. Phew.
|
||||
#undef TEST
|
||||
|
||||
#include "Common/x64Emitter.h"
|
||||
|
||||
namespace Gen
|
||||
{
|
||||
|
||||
struct NamedReg
|
||||
{
|
||||
X64Reg reg;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
const std::vector<NamedReg> reg8names {
|
||||
{ RAX, "al" }, { RBX, "bl" }, { RCX, "cl" }, { RDX, "dl" },
|
||||
{ RSI, "sil" }, { RDI, "dil" }, { RBP, "bpl" }, { RSP, "spl" },
|
||||
{ R8, "r8b" }, { R9, "r9b" }, { R10, "r10b" }, { R11, "r11b" },
|
||||
{ R12, "r12b" }, { R13, "r13b" }, { R14, "r14b" }, { R15, "r15b" },
|
||||
};
|
||||
|
||||
const std::vector<NamedReg> reg8hnames {
|
||||
{ AH, "ah" }, { BH, "bh" }, { CH, "ch" }, { DH, "dh" },
|
||||
};
|
||||
|
||||
const std::vector<NamedReg> reg16names {
|
||||
{ RAX, "ax" }, { RBX, "bx" }, { RCX, "cx" }, { RDX, "dx" },
|
||||
{ RSI, "si" }, { RDI, "di" }, { RBP, "bp" }, { RSP, "sp" },
|
||||
{ R8, "r8w" }, { R9, "r9w" }, { R10, "r10w" }, { R11, "r11w" },
|
||||
{ R12, "r12w" }, { R13, "r13w" }, { R14, "r14w" }, { R15, "r15w" },
|
||||
};
|
||||
|
||||
const std::vector<NamedReg> reg32names {
|
||||
{ RAX, "eax" }, { RBX, "ebx" }, { RCX, "ecx" }, { RDX, "edx" },
|
||||
{ RSI, "esi" }, { RDI, "edi" }, { RBP, "ebp" }, { RSP, "esp" },
|
||||
{ R8, "r8d" }, { R9, "r9d" }, { R10, "r10d" }, { R11, "r11d" },
|
||||
{ R12, "r12d" }, { R13, "r13d" }, { R14, "r14d" }, { R15, "r15d" },
|
||||
};
|
||||
|
||||
const std::vector<NamedReg> reg64names {
|
||||
{ RAX, "rax" }, { RBX, "rbx" }, { RCX, "rcx" }, { RDX, "rdx" },
|
||||
{ RSI, "rsi" }, { RDI, "rdi" }, { RBP, "rbp" }, { RSP, "rsp" },
|
||||
{ R8, "r8" }, { R9, "r9" }, { R10, "r10" }, { R11, "r11" },
|
||||
{ R12, "r12" }, { R13, "r13" }, { R14, "r14" }, { R15, "r15" },
|
||||
};
|
||||
|
||||
const std::vector<NamedReg> xmmnames {
|
||||
{ XMM0, "xmm0" }, { XMM1, "xmm1" }, { XMM2, "xmm2" }, { XMM3, "xmm3" },
|
||||
{ XMM4, "xmm4" }, { XMM5, "xmm5" }, { XMM6, "xmm6" }, { XMM7, "xmm7" },
|
||||
{ XMM8, "xmm8" }, { XMM9, "xmm9" }, { XMM10, "xmm10" }, { XMM11, "xmm11" },
|
||||
{ XMM12, "xmm12" }, { XMM13, "xmm13" }, { XMM14, "xmm14" }, { XMM15, "xmm15" },
|
||||
};
|
||||
|
||||
const std::vector<NamedReg> ymmnames {
|
||||
{ YMM0, "ymm0" }, { YMM1, "ymm1" }, { YMM2, "ymm2" }, { YMM3, "ymm3" },
|
||||
{ YMM4, "ymm4" }, { YMM5, "ymm5" }, { YMM6, "ymm6" }, { YMM7, "ymm7" },
|
||||
{ YMM8, "ymm8" }, { YMM9, "ymm9" }, { YMM10, "ymm10" }, { YMM11, "ymm11" },
|
||||
{ YMM12, "ymm12" }, { YMM13, "ymm13" }, { YMM14, "ymm14" }, { YMM15, "ymm15" },
|
||||
};
|
||||
|
||||
struct { CCFlags cc; std::string name; } ccnames[] = {
|
||||
{ CC_O, "o" }, { CC_NO, "no" },
|
||||
{ CC_B, "b" }, { CC_NB, "nb" },
|
||||
{ CC_Z, "z" }, { CC_NZ, "nz" },
|
||||
{ CC_BE, "be" }, { CC_NBE, "nbe" },
|
||||
{ CC_S, "s" }, { CC_NS, "ns" },
|
||||
{ CC_P, "p" }, { CC_NP, "np" },
|
||||
{ CC_L, "l" }, { CC_NL, "nl" },
|
||||
{ CC_LE, "le" }, { CC_NLE, "nle" },
|
||||
};
|
||||
|
||||
class x64EmitterTest : public testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
emitter.reset(new X64CodeBlock());
|
||||
emitter->AllocCodeSpace(4096);
|
||||
code_buffer = emitter->GetWritableCodePtr();
|
||||
|
||||
disasm.reset(new disassembler);
|
||||
disasm->set_syntax_intel();
|
||||
}
|
||||
|
||||
void ExpectDisassembly(const std::string& expected)
|
||||
{
|
||||
std::string disasmed;
|
||||
const u8* generated_code_iterator = code_buffer;
|
||||
while (generated_code_iterator < emitter->GetCodePtr())
|
||||
{
|
||||
char instr_buffer[1024] = "";
|
||||
generated_code_iterator += disasm->disasm64(
|
||||
(u64)generated_code_iterator,
|
||||
(u64)generated_code_iterator,
|
||||
generated_code_iterator, instr_buffer);
|
||||
disasmed += instr_buffer;
|
||||
disasmed += "\n";
|
||||
}
|
||||
|
||||
auto NormalizeAssembly = [](const std::string& str) -> std::string {
|
||||
// Normalize assembly code to make it suitable for equality checks.
|
||||
// In particular:
|
||||
// * Replace all whitespace characters by a single space.
|
||||
// * Remove leading and trailing whitespaces.
|
||||
// * Lowercase everything.
|
||||
// * Remove all (0x...) addresses.
|
||||
std::string out;
|
||||
bool previous_was_space = false;
|
||||
bool inside_parens = false;
|
||||
for (auto c : str)
|
||||
{
|
||||
c = tolower(c);
|
||||
if (c == '(')
|
||||
{
|
||||
inside_parens = true;
|
||||
continue;
|
||||
}
|
||||
else if (inside_parens)
|
||||
{
|
||||
if (c == ')')
|
||||
inside_parens = false;
|
||||
continue;
|
||||
}
|
||||
else if (isspace(c))
|
||||
{
|
||||
previous_was_space = true;
|
||||
continue;
|
||||
}
|
||||
else if (previous_was_space)
|
||||
{
|
||||
previous_was_space = false;
|
||||
if (!out.empty())
|
||||
out += ' ';
|
||||
}
|
||||
out += c;
|
||||
}
|
||||
return out;
|
||||
};
|
||||
std::string expected_norm = NormalizeAssembly(expected);
|
||||
std::string disasmed_norm = NormalizeAssembly(disasmed);
|
||||
|
||||
EXPECT_EQ(expected_norm, disasmed_norm);
|
||||
|
||||
// Reset code buffer afterwards.
|
||||
emitter->SetCodePtr(code_buffer);
|
||||
}
|
||||
|
||||
std::unique_ptr<X64CodeBlock> emitter;
|
||||
std::unique_ptr<disassembler> disasm;
|
||||
u8* code_buffer;
|
||||
};
|
||||
|
||||
#define TEST_INSTR_NO_OPERANDS(Name, ExpectedDisasm) \
|
||||
TEST_F(x64EmitterTest, Name) \
|
||||
{ \
|
||||
emitter->Name(); \
|
||||
ExpectDisassembly(ExpectedDisasm); \
|
||||
}
|
||||
|
||||
TEST_INSTR_NO_OPERANDS(INT3, "int3")
|
||||
TEST_INSTR_NO_OPERANDS(NOP, "nop")
|
||||
TEST_INSTR_NO_OPERANDS(PAUSE, "pause")
|
||||
TEST_INSTR_NO_OPERANDS(STC, "stc")
|
||||
TEST_INSTR_NO_OPERANDS(CLC, "clc")
|
||||
TEST_INSTR_NO_OPERANDS(CMC, "cmc")
|
||||
TEST_INSTR_NO_OPERANDS(LAHF, "lahf")
|
||||
TEST_INSTR_NO_OPERANDS(SAHF, "sahf")
|
||||
TEST_INSTR_NO_OPERANDS(PUSHF, "pushf")
|
||||
TEST_INSTR_NO_OPERANDS(POPF, "popf")
|
||||
TEST_INSTR_NO_OPERANDS(RET, "ret")
|
||||
TEST_INSTR_NO_OPERANDS(RET_FAST, "rep ret")
|
||||
TEST_INSTR_NO_OPERANDS(UD2, "ud2a")
|
||||
TEST_INSTR_NO_OPERANDS(JMPself, "jmp .-2")
|
||||
TEST_INSTR_NO_OPERANDS(LFENCE, "lfence")
|
||||
TEST_INSTR_NO_OPERANDS(MFENCE, "mfence")
|
||||
TEST_INSTR_NO_OPERANDS(SFENCE, "sfence")
|
||||
TEST_INSTR_NO_OPERANDS(CWD, "cwd")
|
||||
TEST_INSTR_NO_OPERANDS(CDQ, "cdq")
|
||||
TEST_INSTR_NO_OPERANDS(CQO, "cqo")
|
||||
TEST_INSTR_NO_OPERANDS(CBW, "cbw")
|
||||
TEST_INSTR_NO_OPERANDS(CWDE, "cwde")
|
||||
TEST_INSTR_NO_OPERANDS(CDQE, "cdqe")
|
||||
TEST_INSTR_NO_OPERANDS(XCHG_AHAL, "xchg al, ah")
|
||||
TEST_INSTR_NO_OPERANDS(FWAIT, "fwait")
|
||||
TEST_INSTR_NO_OPERANDS(FNSTSW_AX, "fnstsw ax")
|
||||
TEST_INSTR_NO_OPERANDS(RDTSC, "rdtsc")
|
||||
|
||||
TEST_F(x64EmitterTest, NOP_MultiByte)
|
||||
{
|
||||
// 2 bytes is "rep nop", still a simple nop.
|
||||
emitter->NOP(2);
|
||||
ExpectDisassembly("nop");
|
||||
|
||||
for (int i = 3; i <= 11; ++i)
|
||||
{
|
||||
emitter->NOP(i);
|
||||
ExpectDisassembly("multibyte nop");
|
||||
}
|
||||
|
||||
// Larger NOPs are split into several NOPs.
|
||||
emitter->NOP(20);
|
||||
ExpectDisassembly("multibyte nop "
|
||||
"multibyte nop");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, PUSH_Register)
|
||||
{
|
||||
for (const auto& r : reg64names)
|
||||
{
|
||||
emitter->PUSH(r.reg);
|
||||
ExpectDisassembly("push " + r.name);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, PUSH_Immediate)
|
||||
{
|
||||
emitter->PUSH(64, Imm8(0xf0));
|
||||
ExpectDisassembly("push 0xfffffffffffffff0");
|
||||
|
||||
// X64 is weird like that... this pushes 2 bytes, not 8 bytes with sext.
|
||||
emitter->PUSH(64, Imm16(0xe0f0));
|
||||
ExpectDisassembly("push 0xe0f0");
|
||||
|
||||
emitter->PUSH(64, Imm32(0xc0d0e0f0));
|
||||
ExpectDisassembly("push 0xffffffffc0d0e0f0");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, PUSH_MComplex)
|
||||
{
|
||||
emitter->PUSH(64, MComplex(RAX, RBX, SCALE_2, 4));
|
||||
ExpectDisassembly("push qword ptr ds:[rax+rbx*2+4]");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, POP_Register)
|
||||
{
|
||||
for (const auto& r : reg64names)
|
||||
{
|
||||
emitter->POP(r.reg);
|
||||
ExpectDisassembly("pop " + r.name);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, JMP)
|
||||
{
|
||||
emitter->NOP(6);
|
||||
emitter->JMP(code_buffer);
|
||||
ExpectDisassembly("multibyte nop "
|
||||
"jmp .-8");
|
||||
|
||||
emitter->NOP(6);
|
||||
emitter->JMP(code_buffer, true);
|
||||
ExpectDisassembly("multibyte nop "
|
||||
"jmp .-11");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, JMPptr_Register)
|
||||
{
|
||||
for (const auto& r : reg64names)
|
||||
{
|
||||
emitter->JMPptr(R(r.reg));
|
||||
ExpectDisassembly("jmp " + r.name);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: J/SetJumpTarget
|
||||
|
||||
// TODO: CALL
|
||||
|
||||
// TODO: J_CC
|
||||
|
||||
TEST_F(x64EmitterTest, SETcc)
|
||||
{
|
||||
for (const auto& cc : ccnames)
|
||||
{
|
||||
for (const auto& r : reg8names)
|
||||
{
|
||||
emitter->SETcc(cc.cc, R(r.reg));
|
||||
ExpectDisassembly("set" + cc.name + " " + r.name);
|
||||
}
|
||||
for (const auto& r : reg8hnames)
|
||||
{
|
||||
emitter->SETcc(cc.cc, R(r.reg));
|
||||
ExpectDisassembly("set" + cc.name + " " + r.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, CMOVcc_Register)
|
||||
{
|
||||
for (const auto& cc : ccnames)
|
||||
{
|
||||
emitter->CMOVcc(64, RAX, R(R12), cc.cc);
|
||||
emitter->CMOVcc(32, RAX, R(R12), cc.cc);
|
||||
emitter->CMOVcc(16, RAX, R(R12), cc.cc);
|
||||
|
||||
ExpectDisassembly("cmov" + cc.name + " rax, r12 "
|
||||
"cmov" + cc.name + " eax, r12d "
|
||||
"cmov" + cc.name + " ax, r12w");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, BSF)
|
||||
{
|
||||
emitter->BSF(64, R12, R(RAX));
|
||||
emitter->BSF(32, R12, R(RAX));
|
||||
emitter->BSF(16, R12, R(RAX));
|
||||
|
||||
emitter->BSF(64, R12, MatR(RAX));
|
||||
emitter->BSF(32, R12, MatR(RAX));
|
||||
emitter->BSF(16, R12, MatR(RAX));
|
||||
|
||||
ExpectDisassembly("bsf r12, rax "
|
||||
"bsf r12d, eax "
|
||||
"bsf r12w, ax "
|
||||
"bsf r12, qword ptr ds:[rax] "
|
||||
"bsf r12d, dword ptr ds:[rax] "
|
||||
"bsf r12w, word ptr ds:[rax]");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, BSR)
|
||||
{
|
||||
emitter->BSR(64, R12, R(RAX));
|
||||
emitter->BSR(32, R12, R(RAX));
|
||||
emitter->BSR(16, R12, R(RAX));
|
||||
|
||||
emitter->BSR(64, R12, MatR(RAX));
|
||||
emitter->BSR(32, R12, MatR(RAX));
|
||||
emitter->BSR(16, R12, MatR(RAX));
|
||||
|
||||
ExpectDisassembly("bsr r12, rax "
|
||||
"bsr r12d, eax "
|
||||
"bsr r12w, ax "
|
||||
"bsr r12, qword ptr ds:[rax] "
|
||||
"bsr r12d, dword ptr ds:[rax] "
|
||||
"bsr r12w, word ptr ds:[rax]");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, PREFETCH)
|
||||
{
|
||||
emitter->PREFETCH(XEmitter::PF_NTA, MatR(R12));
|
||||
emitter->PREFETCH(XEmitter::PF_T0, MatR(R12));
|
||||
emitter->PREFETCH(XEmitter::PF_T1, MatR(R12));
|
||||
emitter->PREFETCH(XEmitter::PF_T2, MatR(R12));
|
||||
|
||||
ExpectDisassembly("prefetchnta byte ptr ds:[r12] "
|
||||
"prefetcht0 byte ptr ds:[r12] "
|
||||
"prefetcht1 byte ptr ds:[r12] "
|
||||
"prefetcht2 byte ptr ds:[r12]");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, MOVNTI)
|
||||
{
|
||||
emitter->MOVNTI(32, MatR(RAX), R12);
|
||||
emitter->MOVNTI(32, M(code_buffer), R12);
|
||||
emitter->MOVNTI(64, MatR(RAX), R12);
|
||||
emitter->MOVNTI(64, M(code_buffer), R12);
|
||||
|
||||
ExpectDisassembly("movnti dword ptr ds:[rax], r12d "
|
||||
"movnti dword ptr ds:[rip-12], r12d "
|
||||
"movnti qword ptr ds:[rax], r12 "
|
||||
"movnti qword ptr ds:[rip-24], r12");
|
||||
}
|
||||
|
||||
// Grouped together since these 3 instructions do exactly the same thing.
|
||||
TEST_F(x64EmitterTest, MOVNT_DQ_PS_PD)
|
||||
{
|
||||
for (const auto& r : xmmnames)
|
||||
{
|
||||
emitter->MOVNTDQ(MatR(RAX), r.reg);
|
||||
emitter->MOVNTPS(MatR(RAX), r.reg);
|
||||
emitter->MOVNTPD(MatR(RAX), r.reg);
|
||||
ExpectDisassembly("movntdq dqword ptr ds:[rax], " + r.name + " "
|
||||
"movntps dqword ptr ds:[rax], " + r.name + " "
|
||||
"movntpd dqword ptr ds:[rax], " + r.name);
|
||||
}
|
||||
}
|
||||
|
||||
#define MUL_DIV_TEST(Name) \
|
||||
TEST_F(x64EmitterTest, Name) \
|
||||
{ \
|
||||
struct { \
|
||||
int bits; \
|
||||
std::vector<NamedReg> regs; \
|
||||
std::string out_name; \
|
||||
} regsets[] = { \
|
||||
{ 8, reg8names, "al" }, \
|
||||
{ 8, reg8hnames, "al" }, \
|
||||
{ 16, reg16names, "ax" }, \
|
||||
{ 32, reg32names, "eax" }, \
|
||||
{ 64, reg64names, "rax" }, \
|
||||
}; \
|
||||
for (const auto& regset : regsets) \
|
||||
for (const auto& r : regset.regs) \
|
||||
{ \
|
||||
emitter->Name(regset.bits, R(r.reg)); \
|
||||
ExpectDisassembly(#Name " " + regset.out_name + ", " + r.name); \
|
||||
} \
|
||||
}
|
||||
|
||||
MUL_DIV_TEST(MUL)
|
||||
MUL_DIV_TEST(IMUL)
|
||||
MUL_DIV_TEST(DIV)
|
||||
MUL_DIV_TEST(IDIV)
|
||||
|
||||
// TODO: More complex IMUL variants.
|
||||
|
||||
#define SHIFT_TEST(Name) \
|
||||
TEST_F(x64EmitterTest, Name) \
|
||||
{ \
|
||||
struct { \
|
||||
int bits; \
|
||||
std::vector<NamedReg> regs; \
|
||||
} regsets[] = { \
|
||||
{ 8, reg8names }, \
|
||||
{ 8, reg8hnames }, \
|
||||
{ 16, reg16names }, \
|
||||
{ 32, reg32names }, \
|
||||
{ 64, reg64names }, \
|
||||
}; \
|
||||
for (const auto& regset : regsets) \
|
||||
for (const auto& r : regset.regs) \
|
||||
{ \
|
||||
emitter->Name(regset.bits, R(r.reg), Imm8(1)); \
|
||||
emitter->Name(regset.bits, R(r.reg), Imm8(4)); \
|
||||
emitter->Name(regset.bits, R(r.reg), R(CL)); \
|
||||
ExpectDisassembly(#Name " " + r.name + ", 1 " \
|
||||
#Name " " + r.name + ", 0x04 " \
|
||||
#Name " " + r.name + ", cl"); \
|
||||
} \
|
||||
}
|
||||
|
||||
SHIFT_TEST(ROL)
|
||||
SHIFT_TEST(ROR)
|
||||
SHIFT_TEST(RCL)
|
||||
SHIFT_TEST(RCR)
|
||||
SHIFT_TEST(SHL)
|
||||
SHIFT_TEST(SHR)
|
||||
SHIFT_TEST(SAR)
|
||||
|
||||
#define BT_TEST(Name) \
|
||||
TEST_F(x64EmitterTest, Name) \
|
||||
{ \
|
||||
struct { \
|
||||
int bits; \
|
||||
std::vector<NamedReg> regs; \
|
||||
std::string out_name; \
|
||||
std::string size; \
|
||||
} regsets[] = { \
|
||||
{ 16, reg16names, "ax", "word" }, \
|
||||
{ 32, reg32names, "eax", "dword" }, \
|
||||
{ 64, reg64names, "rax", "qword" }, \
|
||||
}; \
|
||||
for (const auto& regset : regsets) \
|
||||
for (const auto& r : regset.regs) \
|
||||
{ \
|
||||
emitter->Name(regset.bits, R(r.reg), R(RAX)); \
|
||||
emitter->Name(regset.bits, R(RAX), R(r.reg)); \
|
||||
emitter->Name(regset.bits, R(r.reg), Imm8(0x42)); \
|
||||
emitter->Name(regset.bits, MatR(R12), R(r.reg)); \
|
||||
ExpectDisassembly(#Name " " + r.name + ", " + regset.out_name + " " \
|
||||
#Name " " + regset.out_name + ", " + r.name + " " \
|
||||
#Name " " + r.name + ", 0x42 " \
|
||||
#Name " " + regset.size + " ptr ds:[r12], " + r.name); \
|
||||
} \
|
||||
}
|
||||
|
||||
BT_TEST(BT)
|
||||
BT_TEST(BTS)
|
||||
BT_TEST(BTR)
|
||||
BT_TEST(BTC)
|
||||
|
||||
// TODO: LEA tests
|
||||
|
||||
#define ONE_OP_ARITH_TEST(Name) \
|
||||
TEST_F(x64EmitterTest, Name) \
|
||||
{ \
|
||||
struct { \
|
||||
int bits; \
|
||||
std::vector<NamedReg> regs; \
|
||||
std::string size; \
|
||||
} regsets[] = { \
|
||||
{ 8, reg8names, "byte" }, \
|
||||
{ 8, reg8hnames, "byte" }, \
|
||||
{ 16, reg16names, "word" }, \
|
||||
{ 32, reg32names, "dword" }, \
|
||||
{ 64, reg64names, "qword" }, \
|
||||
}; \
|
||||
for (const auto& regset : regsets) \
|
||||
for (const auto& r : regset.regs) \
|
||||
{ \
|
||||
emitter->Name(regset.bits, R(r.reg)); \
|
||||
emitter->Name(regset.bits, MatR(RAX)); \
|
||||
emitter->Name(regset.bits, MatR(R12)); \
|
||||
ExpectDisassembly(#Name " " + r.name + " " \
|
||||
#Name " " + regset.size + " ptr ds:[rax] " \
|
||||
#Name " " + regset.size + " ptr ds:[r12]"); \
|
||||
} \
|
||||
}
|
||||
|
||||
ONE_OP_ARITH_TEST(NOT)
|
||||
ONE_OP_ARITH_TEST(NEG)
|
||||
|
||||
#define TWO_OP_ARITH_TEST(Name) \
|
||||
TEST_F(x64EmitterTest, Name) \
|
||||
{ \
|
||||
struct { \
|
||||
int bits; \
|
||||
std::vector<NamedReg> regs; \
|
||||
std::string size; \
|
||||
std::string rax_name; \
|
||||
} regsets[] = { \
|
||||
{ 8, reg8names, "byte", "al" }, \
|
||||
{ 8, reg8hnames, "byte", "al" }, \
|
||||
{ 16, reg16names, "word", "ax" }, \
|
||||
{ 32, reg32names, "dword", "eax" }, \
|
||||
{ 64, reg64names, "qword", "rax" }, \
|
||||
}; \
|
||||
for (const auto& regset : regsets) \
|
||||
for (const auto& r : regset.regs) \
|
||||
{ \
|
||||
emitter->Name(regset.bits, R(r.reg), R(RAX)); \
|
||||
emitter->Name(regset.bits, R(RAX), R(r.reg)); \
|
||||
emitter->Name(regset.bits, R(r.reg), MatR(RAX)); \
|
||||
emitter->Name(regset.bits, MatR(RAX), R(r.reg)); \
|
||||
ExpectDisassembly(#Name " " + r.name + ", " + regset.rax_name + " " \
|
||||
#Name " " + regset.rax_name + ", " + r.name + " " \
|
||||
#Name " " + r.name + ", " + regset.size + " ptr ds:[rax] " \
|
||||
#Name " " + regset.size + " ptr ds:[rax], " + r.name); \
|
||||
} \
|
||||
}
|
||||
|
||||
TWO_OP_ARITH_TEST(ADD)
|
||||
TWO_OP_ARITH_TEST(ADC)
|
||||
TWO_OP_ARITH_TEST(SUB)
|
||||
TWO_OP_ARITH_TEST(SBB)
|
||||
TWO_OP_ARITH_TEST(AND)
|
||||
TWO_OP_ARITH_TEST(CMP)
|
||||
TWO_OP_ARITH_TEST(OR)
|
||||
TWO_OP_ARITH_TEST(XOR)
|
||||
TWO_OP_ARITH_TEST(MOV)
|
||||
|
||||
// TODO: Disassembler inverts operands here.
|
||||
// TWO_OP_ARITH_TEST(XCHG)
|
||||
// TWO_OP_ARITH_TEST(TEST)
|
||||
|
||||
TEST_F(x64EmitterTest, BSWAP)
|
||||
{
|
||||
struct {
|
||||
int bits;
|
||||
std::vector<NamedReg> regs;
|
||||
} regsets[] = {
|
||||
{ 32, reg32names },
|
||||
{ 64, reg64names },
|
||||
};
|
||||
for (const auto& regset : regsets)
|
||||
for (const auto& r : regset.regs)
|
||||
{
|
||||
emitter->BSWAP(regset.bits, r.reg);
|
||||
ExpectDisassembly("bswap " + r.name);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, MOVSX)
|
||||
{
|
||||
emitter->MOVSX(16, 8, RAX, R(AH));
|
||||
emitter->MOVSX(32, 8, RAX, R(R12));
|
||||
emitter->MOVSX(32, 16, R12, R(RBX));
|
||||
emitter->MOVSX(64, 8, R12, R(RBX));
|
||||
emitter->MOVSX(64, 16, RAX, R(R12));
|
||||
emitter->MOVSX(64, 32, R12, R(RSP));
|
||||
ExpectDisassembly("movsx ax, ah "
|
||||
"movsx eax, r12b "
|
||||
"movsx r12d, bx "
|
||||
"movsx r12, bl "
|
||||
"movsx rax, r12w "
|
||||
"movsxd r12, esp");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, MOVZX)
|
||||
{
|
||||
emitter->MOVZX(16, 8, RAX, R(AH));
|
||||
emitter->MOVZX(32, 8, R12, R(RBP));
|
||||
emitter->MOVZX(64, 8, R12, R(RDI));
|
||||
emitter->MOVZX(32, 16, RAX, R(R12));
|
||||
emitter->MOVZX(64, 16, RCX, R(RSI));
|
||||
ExpectDisassembly("movzx ax, ah "
|
||||
"movzx r12d, bpl "
|
||||
"movzx r12d, dil " // Generates 32 bit movzx
|
||||
"movzx eax, r12w "
|
||||
"movzx ecx, si");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, MOVBE)
|
||||
{
|
||||
emitter->MOVBE(16, R(RAX), MatR(R12));
|
||||
emitter->MOVBE(16, MatR(RAX), R(R12));
|
||||
emitter->MOVBE(32, R(RAX), MatR(R12));
|
||||
emitter->MOVBE(32, MatR(RAX), R(R12));
|
||||
emitter->MOVBE(64, R(RAX), MatR(R12));
|
||||
emitter->MOVBE(64, MatR(RAX), R(R12));
|
||||
ExpectDisassembly("movbe ax, word ptr ds:[r12] "
|
||||
"movbe word ptr ds:[rax], r12w "
|
||||
"movbe eax, dword ptr ds:[r12] "
|
||||
"movbe dword ptr ds:[rax], r12d "
|
||||
"movbe rax, qword ptr ds:[r12] "
|
||||
"movbe qword ptr ds:[rax], r12");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, STMXCSR)
|
||||
{
|
||||
emitter->STMXCSR(MatR(R12));
|
||||
ExpectDisassembly("stmxcsr dword ptr ds:[r12]");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, LDMXCSR)
|
||||
{
|
||||
emitter->LDMXCSR(MatR(R12));
|
||||
ExpectDisassembly("ldmxcsr dword ptr ds:[r12]");
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, FLD_FST_FSTP)
|
||||
{
|
||||
emitter->FLD(32, MatR(RBP));
|
||||
emitter->FLD(64, MatR(RBP));
|
||||
emitter->FLD(80, MatR(RBP));
|
||||
|
||||
emitter->FST(32, MatR(RBP));
|
||||
emitter->FST(64, MatR(RBP));
|
||||
// No 80 bit version of FST
|
||||
|
||||
emitter->FSTP(32, MatR(RBP));
|
||||
emitter->FSTP(64, MatR(RBP));
|
||||
emitter->FSTP(80, MatR(RBP));
|
||||
|
||||
ExpectDisassembly("fld dword ptr ss:[rbp] "
|
||||
"fld qword ptr ss:[rbp] "
|
||||
"fld tbyte ptr ss:[rbp] "
|
||||
"fst dword ptr ss:[rbp] "
|
||||
"fst qword ptr ss:[rbp] "
|
||||
"fstp dword ptr ss:[rbp] "
|
||||
"fstp qword ptr ss:[rbp] "
|
||||
"fstp tbyte ptr ss:[rbp]");
|
||||
}
|
||||
|
||||
#define TWO_OP_SSE_TEST(Name, MemBits) \
|
||||
TEST_F(x64EmitterTest, Name) \
|
||||
{ \
|
||||
for (const auto& r1 : xmmnames) \
|
||||
{ \
|
||||
for (const auto& r2 : xmmnames) \
|
||||
{ \
|
||||
emitter->Name(r1.reg, R(r2.reg)); \
|
||||
ExpectDisassembly(#Name " " + r1.name + ", " + r2.name); \
|
||||
} \
|
||||
emitter->Name(r1.reg, MatR(R12)); \
|
||||
ExpectDisassembly(#Name " " + r1.name + ", " MemBits " ptr ds:[r12]"); \
|
||||
} \
|
||||
}
|
||||
|
||||
TWO_OP_SSE_TEST(ADDSS, "dword")
|
||||
TWO_OP_SSE_TEST(SUBSS, "dword")
|
||||
TWO_OP_SSE_TEST(MULSS, "dword")
|
||||
TWO_OP_SSE_TEST(DIVSS, "dword")
|
||||
TWO_OP_SSE_TEST(MINSS, "dword")
|
||||
TWO_OP_SSE_TEST(MAXSS, "dword")
|
||||
TWO_OP_SSE_TEST(SQRTSS, "dword")
|
||||
TWO_OP_SSE_TEST(RSQRTSS, "dword")
|
||||
|
||||
TWO_OP_SSE_TEST(ADDSD, "qword")
|
||||
TWO_OP_SSE_TEST(SUBSD, "qword")
|
||||
TWO_OP_SSE_TEST(MULSD, "qword")
|
||||
TWO_OP_SSE_TEST(DIVSD, "qword")
|
||||
TWO_OP_SSE_TEST(MINSD, "qword")
|
||||
TWO_OP_SSE_TEST(MAXSD, "qword")
|
||||
TWO_OP_SSE_TEST(SQRTSD, "qword")
|
||||
|
||||
TWO_OP_SSE_TEST(ADDPS, "dqword")
|
||||
TWO_OP_SSE_TEST(SUBPS, "dqword")
|
||||
TWO_OP_SSE_TEST(MULPS, "dqword")
|
||||
TWO_OP_SSE_TEST(DIVPS, "dqword")
|
||||
TWO_OP_SSE_TEST(MINPS, "dqword")
|
||||
TWO_OP_SSE_TEST(MAXPS, "dqword")
|
||||
TWO_OP_SSE_TEST(SQRTPS, "dqword")
|
||||
TWO_OP_SSE_TEST(RSQRTPS, "dqword")
|
||||
TWO_OP_SSE_TEST(ANDPS, "dqword")
|
||||
TWO_OP_SSE_TEST(ANDNPS, "dqword")
|
||||
TWO_OP_SSE_TEST(ORPS, "dqword")
|
||||
TWO_OP_SSE_TEST(XORPS, "dqword")
|
||||
|
||||
TWO_OP_SSE_TEST(ADDPD, "dqword")
|
||||
TWO_OP_SSE_TEST(SUBPD, "dqword")
|
||||
TWO_OP_SSE_TEST(MULPD, "dqword")
|
||||
TWO_OP_SSE_TEST(DIVPD, "dqword")
|
||||
TWO_OP_SSE_TEST(MINPD, "dqword")
|
||||
TWO_OP_SSE_TEST(MAXPD, "dqword")
|
||||
TWO_OP_SSE_TEST(SQRTPD, "dqword")
|
||||
TWO_OP_SSE_TEST(ANDPD, "dqword")
|
||||
TWO_OP_SSE_TEST(ANDNPD, "dqword")
|
||||
TWO_OP_SSE_TEST(ORPD, "dqword")
|
||||
TWO_OP_SSE_TEST(XORPD, "dqword")
|
||||
|
||||
TWO_OP_SSE_TEST(MOVDDUP, "qword")
|
||||
|
||||
TWO_OP_SSE_TEST(UNPCKLPS, "dqword")
|
||||
TWO_OP_SSE_TEST(UNPCKHPS, "dqword")
|
||||
TWO_OP_SSE_TEST(UNPCKLPD, "dqword")
|
||||
TWO_OP_SSE_TEST(UNPCKHPD, "dqword")
|
||||
|
||||
TWO_OP_SSE_TEST(COMISS, "dword")
|
||||
TWO_OP_SSE_TEST(UCOMISS, "dword")
|
||||
TWO_OP_SSE_TEST(COMISD, "qword")
|
||||
TWO_OP_SSE_TEST(UCOMISD, "qword")
|
||||
|
||||
// TODO: CMPSS/SD
|
||||
// TODO: SHUFPS/PD
|
||||
// TODO: SSE MOVs
|
||||
// TODO: MOVMSK
|
||||
|
||||
TEST_F(x64EmitterTest, MASKMOVDQU)
|
||||
{
|
||||
for (const auto& r1 : xmmnames)
|
||||
{
|
||||
for (const auto& r2 : xmmnames)
|
||||
{
|
||||
emitter->MASKMOVDQU(r1.reg, r2.reg);
|
||||
ExpectDisassembly("maskmovdqu " + r1.name + ", " + r2.name + ", dqword ptr ds:[rdi]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(x64EmitterTest, LDDQU)
|
||||
{
|
||||
for (const auto& r : xmmnames)
|
||||
{
|
||||
emitter->LDDQU(r.reg, MatR(R12));
|
||||
ExpectDisassembly("lddqu " + r.name + ", dqword ptr ds:[r12]");
|
||||
}
|
||||
}
|
||||
|
||||
TWO_OP_SSE_TEST(CVTPS2PD, "dqword")
|
||||
TWO_OP_SSE_TEST(CVTPD2PS, "dqword")
|
||||
TWO_OP_SSE_TEST(CVTSS2SD, "dword")
|
||||
TWO_OP_SSE_TEST(CVTSD2SS, "qword")
|
||||
TWO_OP_SSE_TEST(CVTDQ2PD, "qword")
|
||||
TWO_OP_SSE_TEST(CVTPD2DQ, "dqword")
|
||||
TWO_OP_SSE_TEST(CVTDQ2PS, "dqword")
|
||||
TWO_OP_SSE_TEST(CVTPS2DQ, "dqword")
|
||||
TWO_OP_SSE_TEST(CVTTPS2DQ, "dqword")
|
||||
TWO_OP_SSE_TEST(CVTTPD2DQ, "dqword")
|
||||
|
||||
// TODO: CVT2SI
|
||||
|
||||
TWO_OP_SSE_TEST(PACKSSDW, "dqword")
|
||||
TWO_OP_SSE_TEST(PACKSSWB, "dqword")
|
||||
TWO_OP_SSE_TEST(PACKUSDW, "dqword")
|
||||
TWO_OP_SSE_TEST(PACKUSWB, "dqword")
|
||||
|
||||
TWO_OP_SSE_TEST(PUNPCKLBW, "dqword")
|
||||
TWO_OP_SSE_TEST(PUNPCKLWD, "dqword")
|
||||
TWO_OP_SSE_TEST(PUNPCKLDQ, "dqword")
|
||||
|
||||
TWO_OP_SSE_TEST(PTEST, "dqword")
|
||||
TWO_OP_SSE_TEST(PAND, "dqword")
|
||||
TWO_OP_SSE_TEST(PANDN, "dqword")
|
||||
TWO_OP_SSE_TEST(POR, "dqword")
|
||||
TWO_OP_SSE_TEST(PXOR, "dqword")
|
||||
TWO_OP_SSE_TEST(PADDB, "dqword")
|
||||
TWO_OP_SSE_TEST(PADDW, "dqword")
|
||||
TWO_OP_SSE_TEST(PADDD, "dqword")
|
||||
TWO_OP_SSE_TEST(PADDQ, "dqword")
|
||||
TWO_OP_SSE_TEST(PADDSB, "dqword")
|
||||
TWO_OP_SSE_TEST(PADDSW, "dqword")
|
||||
TWO_OP_SSE_TEST(PADDUSB, "dqword")
|
||||
TWO_OP_SSE_TEST(PADDUSW, "dqword")
|
||||
TWO_OP_SSE_TEST(PSUBB, "dqword")
|
||||
TWO_OP_SSE_TEST(PSUBW, "dqword")
|
||||
TWO_OP_SSE_TEST(PSUBD, "dqword")
|
||||
TWO_OP_SSE_TEST(PSUBQ, "dqword")
|
||||
TWO_OP_SSE_TEST(PSUBUSB, "dqword")
|
||||
TWO_OP_SSE_TEST(PSUBUSW, "dqword")
|
||||
TWO_OP_SSE_TEST(PAVGB, "dqword")
|
||||
TWO_OP_SSE_TEST(PAVGW, "dqword")
|
||||
TWO_OP_SSE_TEST(PCMPEQB, "dqword")
|
||||
TWO_OP_SSE_TEST(PCMPEQW, "dqword")
|
||||
TWO_OP_SSE_TEST(PCMPEQD, "dqword")
|
||||
TWO_OP_SSE_TEST(PCMPGTB, "dqword")
|
||||
TWO_OP_SSE_TEST(PCMPGTW, "dqword")
|
||||
TWO_OP_SSE_TEST(PCMPGTD, "dqword")
|
||||
TWO_OP_SSE_TEST(PMADDWD, "dqword")
|
||||
TWO_OP_SSE_TEST(PSADBW, "dqword")
|
||||
TWO_OP_SSE_TEST(PMAXSW, "dqword")
|
||||
TWO_OP_SSE_TEST(PMAXUB, "dqword")
|
||||
TWO_OP_SSE_TEST(PMINSW, "dqword")
|
||||
TWO_OP_SSE_TEST(PMINUB, "dqword")
|
||||
TWO_OP_SSE_TEST(PSHUFB, "dqword")
|
||||
|
||||
// TODO: PEXT/INS/SHUF/MOVMSK
|
||||
|
||||
TWO_OP_SSE_TEST(PMOVSXBW, "qword")
|
||||
TWO_OP_SSE_TEST(PMOVSXBD, "dword")
|
||||
TWO_OP_SSE_TEST(PMOVSXBQ, "word")
|
||||
TWO_OP_SSE_TEST(PMOVSXWD, "qword")
|
||||
TWO_OP_SSE_TEST(PMOVSXWQ, "dword")
|
||||
TWO_OP_SSE_TEST(PMOVSXDQ, "qword")
|
||||
|
||||
TWO_OP_SSE_TEST(PMOVZXBW, "qword")
|
||||
TWO_OP_SSE_TEST(PMOVZXBD, "dword")
|
||||
TWO_OP_SSE_TEST(PMOVZXBQ, "word")
|
||||
TWO_OP_SSE_TEST(PMOVZXWD, "qword")
|
||||
TWO_OP_SSE_TEST(PMOVZXWQ, "dword")
|
||||
TWO_OP_SSE_TEST(PMOVZXDQ, "qword")
|
||||
|
||||
// TODO: BLEND
|
||||
|
||||
// TODO: AVX
|
||||
|
||||
} // namespace Gen
|
Loading…
Reference in New Issue
Block a user