mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-23 03:09:10 +02:00
The JIT backends' calls out of generated code (the MIPS and IR interpreter fallbacks, and the bad address report) now take the state the backend was built for instead of reading currentMIPS. Likewise the interpreter's tick check, the IR dispatcher's Advance, and a couple of debugger helpers. MIPSState::InitRegisters() no longer sets currentMIPS; the boot paths set it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1486 lines
59 KiB
C++
1486 lines
59 KiB
C++
// Copyright (C) 2003 Dolphin Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official SVN repository and contact information can be found at
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
#pragma once
|
|
|
|
#include "ppsspp_config.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/Log.h"
|
|
#include "Common/CodeBlock.h"
|
|
|
|
#if PPSSPP_ARCH(64BIT)
|
|
#define PTRBITS 64
|
|
#else
|
|
#define PTRBITS 32
|
|
#endif
|
|
|
|
namespace Gen
|
|
{
|
|
|
|
enum X64Reg : u32
|
|
{
|
|
EAX = 0, EBX = 3, ECX = 1, EDX = 2,
|
|
ESI = 6, EDI = 7, EBP = 5, ESP = 4,
|
|
|
|
RAX = 0, RBX = 3, RCX = 1, RDX = 2,
|
|
RSI = 6, RDI = 7, RBP = 5, RSP = 4,
|
|
R8 = 8, R9 = 9, R10 = 10,R11 = 11,
|
|
R12 = 12,R13 = 13,R14 = 14,R15 = 15,
|
|
|
|
AL = 0, BL = 3, CL = 1, DL = 2,
|
|
SIL = 6, DIL = 7, BPL = 5, SPL = 4,
|
|
AH = 0x104, BH = 0x107, CH = 0x105, DH = 0x106,
|
|
|
|
AX = 0, BX = 3, CX = 1, DX = 2,
|
|
SI = 6, DI = 7, BP = 5, SP = 4,
|
|
|
|
XMM0=0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7,
|
|
XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15,
|
|
|
|
YMM0=0, YMM1, YMM2, YMM3, YMM4, YMM5, YMM6, YMM7,
|
|
YMM8, YMM9, YMM10, YMM11, YMM12, YMM13, YMM14, YMM15,
|
|
|
|
INVALID_REG = 0xFFFFFFFF
|
|
};
|
|
|
|
enum CCFlags
|
|
{
|
|
CC_O = 0,
|
|
CC_NO = 1,
|
|
CC_B = 2, CC_C = 2, CC_NAE = 2,
|
|
CC_NB = 3, CC_NC = 3, CC_AE = 3,
|
|
CC_Z = 4, CC_E = 4,
|
|
CC_NZ = 5, CC_NE = 5,
|
|
CC_BE = 6, CC_NA = 6,
|
|
CC_NBE = 7, CC_A = 7,
|
|
CC_S = 8,
|
|
CC_NS = 9,
|
|
CC_P = 0xA, CC_PE = 0xA,
|
|
CC_NP = 0xB, CC_PO = 0xB,
|
|
CC_L = 0xC, CC_NGE = 0xC,
|
|
CC_NL = 0xD, CC_GE = 0xD,
|
|
CC_LE = 0xE, CC_NG = 0xE,
|
|
CC_NLE = 0xF, CC_G = 0xF
|
|
};
|
|
|
|
enum
|
|
{
|
|
NUMGPRs = 16,
|
|
NUMXMMs = 16,
|
|
};
|
|
|
|
enum
|
|
{
|
|
SCALE_NONE = 0,
|
|
SCALE_1 = 1,
|
|
SCALE_2 = 2,
|
|
SCALE_4 = 4,
|
|
SCALE_8 = 8,
|
|
SCALE_ATREG = 16,
|
|
//SCALE_NOBASE_1 is not supported and can be replaced with SCALE_ATREG
|
|
SCALE_NOBASE_2 = 34,
|
|
SCALE_NOBASE_4 = 36,
|
|
SCALE_NOBASE_8 = 40,
|
|
SCALE_RIP = 0xFF,
|
|
SCALE_IMM8 = 0xF0,
|
|
SCALE_IMM16 = 0xF1,
|
|
SCALE_IMM32 = 0xF2,
|
|
SCALE_IMM64 = 0xF3,
|
|
};
|
|
|
|
enum NormalOp {
|
|
nrmADD,
|
|
nrmADC,
|
|
nrmSUB,
|
|
nrmSBB,
|
|
nrmAND,
|
|
nrmOR ,
|
|
nrmXOR,
|
|
nrmMOV,
|
|
nrmTEST,
|
|
nrmCMP,
|
|
nrmXCHG,
|
|
};
|
|
|
|
enum {
|
|
CMP_EQ = 0,
|
|
CMP_LT = 1,
|
|
CMP_LE = 2,
|
|
CMP_UNORD = 3,
|
|
CMP_NEQ = 4,
|
|
CMP_NLT = 5,
|
|
CMP_NLE = 6,
|
|
CMP_ORD = 7,
|
|
};
|
|
|
|
enum FloatOp {
|
|
floatLD = 0,
|
|
floatST = 2,
|
|
floatSTP = 3,
|
|
floatLD80 = 5,
|
|
floatSTP80 = 7,
|
|
|
|
floatINVALID = -1,
|
|
};
|
|
|
|
enum FloatRound {
|
|
FROUND_NEAREST = 0,
|
|
FROUND_FLOOR = 1,
|
|
FROUND_CEIL = 2,
|
|
FROUND_ZERO = 3,
|
|
FROUND_MXCSR = 4,
|
|
|
|
FROUND_RAISE_PRECISION = 0,
|
|
FROUND_IGNORE_PRECISION = 8,
|
|
};
|
|
|
|
class XEmitter;
|
|
|
|
// RIP addressing does not benefit from micro op fusion on Core arch
|
|
struct OpArg
|
|
{
|
|
OpArg() {} // dummy op arg, used for storage
|
|
OpArg(u64 _offset, int _scale, X64Reg rmReg = RAX, X64Reg scaledReg = RAX)
|
|
{
|
|
operandReg = 0;
|
|
scale = (u8)_scale;
|
|
offsetOrBaseReg = (u16)rmReg;
|
|
indexReg = (u16)scaledReg;
|
|
//if scale == 0 never mind offsetting
|
|
offset = _offset;
|
|
}
|
|
bool operator==(const OpArg &b) const
|
|
{
|
|
return operandReg == b.operandReg && scale == b.scale && offsetOrBaseReg == b.offsetOrBaseReg &&
|
|
indexReg == b.indexReg && offset == b.offset;
|
|
}
|
|
void WriteRex(XEmitter *emit, int opBits, int bits, int customOp = -1) const;
|
|
void WriteVex(XEmitter* emit, X64Reg regOp1, X64Reg regOp2, int L, int pp, int mmmmm, int W = 0) const;
|
|
void WriteRest(XEmitter *emit, int extraBytes=0, X64Reg operandReg=INVALID_REG, bool warn_64bit_offset = true) const;
|
|
void WriteSingleByteOp(XEmitter *emit, u8 op, X64Reg operandReg, int bits);
|
|
// This one is public - must be written to
|
|
u64 offset; // use RIP-relative as much as possible - 64-bit immediates are not available.
|
|
u16 operandReg;
|
|
|
|
void WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg &operand, int bits) const;
|
|
bool IsImm() const {return scale == SCALE_IMM8 || scale == SCALE_IMM16 || scale == SCALE_IMM32 || scale == SCALE_IMM64;}
|
|
bool IsSimpleReg() const {return scale == SCALE_NONE;}
|
|
bool IsSimpleReg(X64Reg reg) const
|
|
{
|
|
if (!IsSimpleReg())
|
|
return false;
|
|
return GetSimpleReg() == reg;
|
|
}
|
|
bool IsIndexedReg(X64Reg reg) const {
|
|
if (scale >= 1 && scale <= 4)
|
|
return indexReg == reg;
|
|
if (scale >= 32 && scale <= 40)
|
|
return indexReg == reg;
|
|
return false;
|
|
}
|
|
|
|
bool CanDoOpWith(const OpArg &other) const
|
|
{
|
|
if (IsSimpleReg()) return true;
|
|
if (!IsSimpleReg() && !other.IsSimpleReg() && !other.IsImm()) return false;
|
|
return true;
|
|
}
|
|
|
|
int GetImmBits() const
|
|
{
|
|
switch (scale)
|
|
{
|
|
case SCALE_IMM8: return 8;
|
|
case SCALE_IMM16: return 16;
|
|
case SCALE_IMM32: return 32;
|
|
case SCALE_IMM64: return 64;
|
|
default: return -1;
|
|
}
|
|
}
|
|
|
|
void SetImmBits(int bits) {
|
|
switch (bits)
|
|
{
|
|
case 8: scale = SCALE_IMM8; break;
|
|
case 16: scale = SCALE_IMM16; break;
|
|
case 32: scale = SCALE_IMM32; break;
|
|
case 64: scale = SCALE_IMM64; break;
|
|
}
|
|
}
|
|
|
|
X64Reg GetSimpleReg() const
|
|
{
|
|
if (scale == SCALE_NONE)
|
|
return (X64Reg)offsetOrBaseReg;
|
|
else
|
|
return INVALID_REG;
|
|
}
|
|
|
|
u32 GetImmValue() const {
|
|
return (u32)offset;
|
|
}
|
|
|
|
// For loops.
|
|
void IncreaseOffset(int sz) {
|
|
offset += sz;
|
|
}
|
|
|
|
private:
|
|
u8 scale;
|
|
u16 offsetOrBaseReg;
|
|
u16 indexReg;
|
|
};
|
|
|
|
inline OpArg M(const void *ptr) {return OpArg((u64)ptr, (int)SCALE_RIP);}
|
|
template <typename T>
|
|
inline OpArg M(const T *ptr) {return OpArg((u64)(const void *)ptr, (int)SCALE_RIP);}
|
|
inline OpArg R(X64Reg value) {return OpArg(0, SCALE_NONE, value);}
|
|
inline OpArg MatR(X64Reg value) {return OpArg(0, SCALE_ATREG, value);}
|
|
|
|
inline OpArg MDisp(X64Reg value, int offset)
|
|
{
|
|
return OpArg((u32)offset, SCALE_ATREG, value);
|
|
}
|
|
|
|
inline OpArg MComplex(X64Reg base, X64Reg scaled, int scale, int offset)
|
|
{
|
|
return OpArg(offset, scale, base, scaled);
|
|
}
|
|
|
|
inline OpArg MScaled(X64Reg scaled, int scale, int offset)
|
|
{
|
|
if (scale == SCALE_1)
|
|
return OpArg(offset, SCALE_ATREG, scaled);
|
|
else
|
|
return OpArg(offset, scale | 0x20, RAX, scaled);
|
|
}
|
|
|
|
inline OpArg MRegSum(X64Reg base, X64Reg offset)
|
|
{
|
|
return MComplex(base, offset, 1, 0);
|
|
}
|
|
|
|
template <typename T>
|
|
inline bool Accessible(const T *t1, const T *t2) {
|
|
ptrdiff_t diff = (const uint8_t *)t1 - (const uint8_t *)t2;
|
|
if (diff < 0) {
|
|
diff = -diff;
|
|
}
|
|
return diff < (ptrdiff_t)0x7FFFFFE0;
|
|
}
|
|
|
|
template <typename T>
|
|
inline OpArg MAccessibleDisp(X64Reg r, const T *tbase, const T *t) {
|
|
_assert_(Accessible(tbase, t));
|
|
ptrdiff_t diff = (const uint8_t *)t - (const uint8_t *)tbase;
|
|
return MDisp(r, (int)diff);
|
|
}
|
|
|
|
inline OpArg Imm8 (u8 imm) {return OpArg(imm, SCALE_IMM8);}
|
|
inline OpArg Imm16(u16 imm) {return OpArg(imm, SCALE_IMM16);} //rarely used
|
|
inline OpArg Imm32(u32 imm) {return OpArg(imm, SCALE_IMM32);}
|
|
inline OpArg Imm64(u64 imm) {return OpArg(imm, SCALE_IMM64);}
|
|
|
|
template<int N> OpArg ImmPtrTpl(const void *imm);
|
|
template<> inline OpArg ImmPtrTpl<8>(const void *imm) {return Imm64((u64)imm);}
|
|
template<> inline OpArg ImmPtrTpl<4>(const void *imm) {return Imm32((u32)(uintptr_t)imm);}
|
|
inline OpArg ImmPtr(const void* imm) {return ImmPtrTpl<sizeof(void*)>(imm);}
|
|
|
|
inline OpArg UImmAuto(u32 imm) {
|
|
return OpArg(imm, imm >= 128 ? SCALE_IMM32 : SCALE_IMM8);
|
|
}
|
|
inline OpArg SImmAuto(s32 imm) {
|
|
return OpArg(imm, (imm >= 128 || imm < -128) ? SCALE_IMM32 : SCALE_IMM8);
|
|
}
|
|
|
|
template<int N> u32 PtrOffsetTpl(const void* ptr, const void* base);
|
|
template<> inline u32 PtrOffsetTpl<8>(const void *ptr, const void* base) {
|
|
s64 distance = (s64)ptr-(s64)base;
|
|
if (distance >= 0x80000000LL ||
|
|
distance < -0x80000000LL)
|
|
{
|
|
_assert_msg_(false, "pointer offset out of range");
|
|
return 0;
|
|
}
|
|
|
|
return (u32)distance;
|
|
}
|
|
template<> inline u32 PtrOffsetTpl<4>(const void *ptr, const void* base) {
|
|
return (u32)(uintptr_t)ptr-(u32)(uintptr_t)base;
|
|
}
|
|
inline u32 PtrOffset(const void* ptr, const void* base) {
|
|
return PtrOffsetTpl<sizeof(void*)>(ptr, base);
|
|
}
|
|
|
|
//usage: int a[]; ARRAY_OFFSET(a,10)
|
|
#define ARRAY_OFFSET(array,index) ((u32)((u64)&(array)[index]-(u64)&(array)[0]))
|
|
//usage: struct {int e;} s; STRUCT_OFFSET(s,e)
|
|
#define STRUCT_OFFSET(str,elem) ((u32)((u64)&(str).elem-(u64)&(str)))
|
|
|
|
struct FixupBranch
|
|
{
|
|
u8 *ptr;
|
|
int type; //0 = 8bit 1 = 32bit
|
|
};
|
|
|
|
enum SSECompare
|
|
{
|
|
EQ = 0,
|
|
LT,
|
|
LE,
|
|
UNORD,
|
|
NEQ,
|
|
NLT,
|
|
NLE,
|
|
ORD,
|
|
};
|
|
|
|
typedef const u8* JumpTarget;
|
|
|
|
class XEmitter
|
|
{
|
|
friend struct OpArg; // for Write8 etc
|
|
private:
|
|
u8 *code;
|
|
bool flags_locked;
|
|
|
|
void CheckFlags();
|
|
|
|
void Rex(int w, int r, int x, int b);
|
|
void WriteSimple1Byte(int bits, u8 byte, X64Reg reg);
|
|
void WriteSimple2Byte(int bits, u8 byte1, u8 byte2, X64Reg reg);
|
|
void WriteMulDivType(int bits, OpArg src, int ext);
|
|
void WriteBitSearchType(int bits, X64Reg dest, OpArg src, u8 byte2, bool rep = false);
|
|
void WriteShift(int bits, OpArg dest, OpArg &shift, int ext);
|
|
void WriteBitTest(int bits, OpArg &dest, OpArg &index, int ext);
|
|
void WriteMXCSR(OpArg arg, int ext);
|
|
void WriteSSEOp(u8 opPrefix, u16 op, X64Reg regOp, OpArg arg, int extrabytes = 0);
|
|
void WriteSSSE3Op(u8 opPrefix, u16 op, X64Reg regOp, OpArg arg, int extrabytes = 0);
|
|
void WriteSSE41Op(u8 opPrefix, u16 op, X64Reg regOp, OpArg arg, int extrabytes = 0);
|
|
void WriteAVXOp(int bits, u8 opPrefix, u16 op, X64Reg regOp, OpArg arg, int extrabytes = 0, int W = 0);
|
|
void WriteAVXOp(int bits, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, OpArg arg, int extrabytes = 0, int W = 0);
|
|
// AVX for 128, AVX2 for 256.
|
|
void WriteAVX12Op(int bits, u8 opPrefix, u16 op, X64Reg regOp, OpArg arg, int extrabytes = 0, int W = 0);
|
|
void WriteAVX12Op(int bits, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, OpArg arg, int extrabytes = 0, int W = 0);
|
|
void WriteAVX2Op(int bits, u8 opPrefix, u16 op, X64Reg regOp, OpArg arg, int extrabytes = 0, int W = 0);
|
|
void WriteAVX2Op(int bits, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, OpArg arg, int extrabytes = 0, int W = 0);
|
|
void WriteVEXOp(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, OpArg arg, int extrabytes = 0);
|
|
void WriteBMI1Op(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, OpArg arg, int extrabytes = 0);
|
|
void WriteBMI2Op(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, OpArg arg, int extrabytes = 0);
|
|
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:
|
|
inline void Write8(u8 value) {*code++ = value;}
|
|
inline void Write16(u16 value) {std::memcpy(code, &value, sizeof(u16)); code += 2;}
|
|
inline void Write32(u32 value) {std::memcpy(code, &value, sizeof(u32)); code += 4;}
|
|
inline void Write64(u64 value) {std::memcpy(code, &value, sizeof(u64)); code += 8;}
|
|
|
|
public:
|
|
XEmitter() { code = nullptr; flags_locked = false; }
|
|
XEmitter(u8 *code_ptr) { code = code_ptr; flags_locked = false; }
|
|
virtual ~XEmitter() {}
|
|
|
|
void WriteModRM(int mod, int reg, int rm);
|
|
void WriteSIB(int scale, int index, int base);
|
|
|
|
void SetCodePointer(u8 *ptr, u8 *writePtr);
|
|
const u8 *GetCodePointer() const;
|
|
|
|
void ReserveCodeSpace(int bytes);
|
|
const u8 *AlignCode4();
|
|
const u8 *AlignCode16();
|
|
const u8 *AlignCodePage();
|
|
|
|
// Nops until the code pointer is 16-byte aligned. Good for loops.
|
|
const u8 *NopAlignCode16();
|
|
|
|
u8 *GetWritableCodePtr();
|
|
|
|
void LockFlags() { flags_locked = true; }
|
|
void UnlockFlags() { flags_locked = false; }
|
|
|
|
// Looking for one of these? It's BANNED!! Some instructions are slow on modern CPU
|
|
// INC, DEC, LOOP, LOOPNE, LOOPE, ENTER, LEAVE, XCHG, XLAT, REP MOVSB/MOVSD, REP SCASD + other string instr.,
|
|
// INC and DEC are slow on Intel Core, but not on AMD. They create a
|
|
// false flag dependency because they only update a subset of the flags.
|
|
// XCHG is SLOW and should be avoided.
|
|
// Actually, REP MOVSB has gotten fast again on recent CPU generations, though it's not all that useful anyway.
|
|
|
|
// Debug breakpoint
|
|
void INT3();
|
|
|
|
// Do nothing
|
|
void NOP(size_t count = 1);
|
|
|
|
// Save energy in wait-loops on P4 only. Probably not too useful.
|
|
void PAUSE();
|
|
|
|
// Flag control
|
|
void STC();
|
|
void CLC();
|
|
void CMC();
|
|
|
|
// These two can not be executed in 64-bit mode on early Intel 64-bit CPU:s, only on Core2 and AMD!
|
|
void LAHF(); // 3 cycle vector path
|
|
void SAHF(); // direct path fast
|
|
|
|
// Stack control
|
|
void PUSH(X64Reg reg);
|
|
void POP(X64Reg reg);
|
|
void PUSH(int bits, const OpArg ®);
|
|
void POP(int bits, const OpArg ®);
|
|
void PUSHF();
|
|
void POPF();
|
|
|
|
// Flow control
|
|
void RET();
|
|
void RET_FAST();
|
|
void UD2();
|
|
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
|
|
#undef CALL
|
|
#endif
|
|
bool CanCALLDirect(const void *fnptr);
|
|
void CALL(const void *fnptr);
|
|
void CALLptr(OpArg arg);
|
|
|
|
FixupBranch J_CC(CCFlags conditionCode, bool force5bytes = false);
|
|
//void J_CC(CCFlags conditionCode, JumpTarget target);
|
|
void J_CC(CCFlags conditionCode, const u8 * addr, bool force5Bytes = false);
|
|
|
|
void SetJumpTarget(const FixupBranch &branch);
|
|
|
|
void SETcc(CCFlags flag, OpArg dest);
|
|
// Note: CMOV brings small if any benefit on current cpus.
|
|
void CMOVcc(int bits, X64Reg dest, OpArg src, CCFlags flag);
|
|
|
|
// Fences
|
|
void LFENCE();
|
|
void MFENCE();
|
|
void SFENCE();
|
|
|
|
// Bit scan
|
|
void BSF(int bits, X64Reg dest, OpArg src); //bottom bit to top bit
|
|
void BSR(int bits, X64Reg dest, OpArg src); //top bit to bottom bit
|
|
|
|
// Cache control
|
|
enum PrefetchLevel
|
|
{
|
|
PF_NTA, //Non-temporal (data used once and only once)
|
|
PF_T0, //All cache levels
|
|
PF_T1, //Levels 2+ (aliased to T0 on AMD)
|
|
PF_T2, //Levels 3+ (aliased to T0 on AMD)
|
|
};
|
|
void PREFETCH(PrefetchLevel level, OpArg arg);
|
|
void MOVNTI(int bits, OpArg dest, X64Reg src);
|
|
void MOVNTDQ(OpArg arg, X64Reg regOp);
|
|
void MOVNTPS(OpArg arg, X64Reg regOp);
|
|
void MOVNTPD(OpArg arg, X64Reg regOp);
|
|
|
|
// Multiplication / division
|
|
void MUL(int bits, OpArg src); //UNSIGNED
|
|
void IMUL(int bits, OpArg src); //SIGNED
|
|
void IMUL(int bits, X64Reg regOp, OpArg src);
|
|
void IMUL(int bits, X64Reg regOp, OpArg src, OpArg imm);
|
|
void DIV(int bits, OpArg src);
|
|
void IDIV(int bits, OpArg src);
|
|
|
|
// Shift
|
|
void ROL(int bits, OpArg dest, OpArg shift);
|
|
void ROR(int bits, OpArg dest, OpArg shift);
|
|
void RCL(int bits, OpArg dest, OpArg shift);
|
|
void RCR(int bits, OpArg dest, OpArg shift);
|
|
void SHL(int bits, OpArg dest, OpArg shift);
|
|
void SHR(int bits, OpArg dest, OpArg shift);
|
|
void SAR(int bits, OpArg dest, OpArg shift);
|
|
|
|
// Bit Test
|
|
void BT(int bits, OpArg dest, OpArg index);
|
|
void BTS(int bits, OpArg dest, OpArg index);
|
|
void BTR(int bits, OpArg dest, OpArg index);
|
|
void BTC(int bits, OpArg dest, OpArg index);
|
|
|
|
// Double-Precision Shift
|
|
void SHRD(int bits, OpArg dest, OpArg src, OpArg shift);
|
|
void SHLD(int bits, OpArg dest, OpArg src, OpArg shift);
|
|
|
|
// Extend EAX into EDX in various ways
|
|
void CWD(int bits = 16);
|
|
inline void CDQ() {CWD(32);}
|
|
inline void CQO() {CWD(64);}
|
|
void CBW(int bits = 8);
|
|
inline void CWDE() {CBW(16);}
|
|
inline void CDQE() {CBW(32);}
|
|
|
|
// Load effective address
|
|
void LEA(int bits, X64Reg dest, OpArg src);
|
|
|
|
// Integer arithmetic
|
|
void NEG (int bits, OpArg src);
|
|
void ADD (int bits, const OpArg &a1, const OpArg &a2);
|
|
void ADC (int bits, const OpArg &a1, const OpArg &a2);
|
|
void SUB (int bits, const OpArg &a1, const OpArg &a2);
|
|
void SBB (int bits, const OpArg &a1, const OpArg &a2);
|
|
void AND (int bits, const OpArg &a1, const OpArg &a2);
|
|
void CMP (int bits, const OpArg &a1, const OpArg &a2);
|
|
|
|
// Bit operations
|
|
void NOT (int bits, OpArg src);
|
|
void OR (int bits, const OpArg &a1, const OpArg &a2);
|
|
void XOR (int bits, const OpArg &a1, const OpArg &a2);
|
|
void MOV (int bits, const OpArg &a1, const OpArg &a2);
|
|
void TEST(int bits, const OpArg &a1, const OpArg &a2);
|
|
|
|
// Are these useful at all? Consider removing.
|
|
void XCHG(int bits, const OpArg &a1, const OpArg &a2);
|
|
void XCHG_AHAL();
|
|
|
|
// Byte swapping (32 and 64-bit only).
|
|
void BSWAP(int bits, X64Reg reg);
|
|
|
|
// Sign/zero extension
|
|
void MOVSX(int dbits, int sbits, X64Reg dest, OpArg src); //automatically uses MOVSXD if necessary
|
|
void MOVZX(int dbits, int sbits, X64Reg dest, OpArg src);
|
|
|
|
// Available only on Atom or >= Haswell so far. Test with cpu_info.bMOVBE.
|
|
void MOVBE(int dbits, const OpArg& dest, const OpArg& src);
|
|
|
|
// Available only on AMD >= Phenom or Intel >= Haswell
|
|
void LZCNT(int bits, X64Reg dest, OpArg src);
|
|
// Note: this one is actually part of BMI1
|
|
void TZCNT(int bits, X64Reg dest, OpArg src);
|
|
|
|
// WARNING - These two take 11-13 cycles and are VectorPath! (AMD64)
|
|
void STMXCSR(OpArg memloc);
|
|
void LDMXCSR(OpArg memloc);
|
|
|
|
// Prefixes
|
|
void LOCK();
|
|
void REP();
|
|
void REPNE();
|
|
void FSOverride();
|
|
void GSOverride();
|
|
|
|
// x87
|
|
enum x87StatusWordBits {
|
|
x87_InvalidOperation = 0x1,
|
|
x87_DenormalizedOperand = 0x2,
|
|
x87_DivisionByZero = 0x4,
|
|
x87_Overflow = 0x8,
|
|
x87_Underflow = 0x10,
|
|
x87_Precision = 0x20,
|
|
x87_StackFault = 0x40,
|
|
x87_ErrorSummary = 0x80,
|
|
x87_C0 = 0x100,
|
|
x87_C1 = 0x200,
|
|
x87_C2 = 0x400,
|
|
x87_TopOfStack = 0x2000 | 0x1000 | 0x800,
|
|
x87_C3 = 0x4000,
|
|
x87_FPUBusy = 0x8000,
|
|
};
|
|
|
|
void FLD(int bits, OpArg src);
|
|
void FST(int bits, OpArg dest);
|
|
void FSTP(int bits, OpArg dest);
|
|
void FNSTSW_AX();
|
|
void FWAIT();
|
|
|
|
// SSE/SSE2: Floating point arithmetic
|
|
void ADDSS(X64Reg regOp, OpArg arg);
|
|
void ADDSD(X64Reg regOp, OpArg arg);
|
|
void SUBSS(X64Reg regOp, OpArg arg);
|
|
void SUBSD(X64Reg regOp, OpArg arg);
|
|
void MULSS(X64Reg regOp, OpArg arg);
|
|
void MULSD(X64Reg regOp, OpArg arg);
|
|
void DIVSS(X64Reg regOp, OpArg arg);
|
|
void DIVSD(X64Reg regOp, OpArg arg);
|
|
void MINSS(X64Reg regOp, OpArg arg);
|
|
void MINSD(X64Reg regOp, OpArg arg);
|
|
void MAXSS(X64Reg regOp, OpArg arg);
|
|
void MAXSD(X64Reg regOp, OpArg arg);
|
|
void SQRTSS(X64Reg regOp, OpArg arg);
|
|
void SQRTSD(X64Reg regOp, OpArg arg);
|
|
void RCPSS(X64Reg regOp, OpArg& arg);
|
|
void RSQRTSS(X64Reg regOp, OpArg arg);
|
|
|
|
// SSE/SSE2: Floating point bitwise (yes)
|
|
void CMPSS(X64Reg regOp, OpArg arg, u8 compare);
|
|
void CMPSD(X64Reg regOp, OpArg arg, u8 compare);
|
|
|
|
inline void CMPEQSS(X64Reg regOp, OpArg arg) { CMPSS(regOp, arg, CMP_EQ); }
|
|
inline void CMPLTSS(X64Reg regOp, OpArg arg) { CMPSS(regOp, arg, CMP_LT); }
|
|
inline void CMPLESS(X64Reg regOp, OpArg arg) { CMPSS(regOp, arg, CMP_LE); }
|
|
inline void CMPUNORDSS(X64Reg regOp, OpArg arg) { CMPSS(regOp, arg, CMP_UNORD); }
|
|
inline void CMPNEQSS(X64Reg regOp, OpArg arg) { CMPSS(regOp, arg, CMP_NEQ); }
|
|
inline void CMPNLTSS(X64Reg regOp, OpArg arg) { CMPSS(regOp, arg, CMP_NLT); }
|
|
inline void CMPORDSS(X64Reg regOp, OpArg arg) { CMPSS(regOp, arg, CMP_ORD); }
|
|
|
|
// SSE/SSE2: Floating point packed arithmetic (x4 for float, x2 for double)
|
|
void ADDPS(X64Reg regOp, OpArg arg);
|
|
void ADDPD(X64Reg regOp, OpArg arg);
|
|
void SUBPS(X64Reg regOp, OpArg arg);
|
|
void SUBPD(X64Reg regOp, OpArg arg);
|
|
void CMPPS(X64Reg regOp, OpArg arg, u8 compare);
|
|
void CMPPD(X64Reg regOp, OpArg arg, u8 compare);
|
|
void MULPS(X64Reg regOp, OpArg arg);
|
|
void MULPD(X64Reg regOp, OpArg arg);
|
|
void DIVPS(X64Reg regOp, OpArg arg);
|
|
void DIVPD(X64Reg regOp, OpArg arg);
|
|
void MINPS(X64Reg regOp, OpArg arg);
|
|
void MINPD(X64Reg regOp, OpArg arg);
|
|
void MAXPS(X64Reg regOp, OpArg arg);
|
|
void MAXPD(X64Reg regOp, OpArg arg);
|
|
void SQRTPS(X64Reg regOp, OpArg arg);
|
|
void SQRTPD(X64Reg regOp, OpArg arg);
|
|
void RCPPS(X64Reg regOp, OpArg& arg);
|
|
void RSQRTPS(X64Reg regOp, OpArg arg);
|
|
|
|
inline void CMPEQPS(X64Reg regOp, OpArg arg) { CMPPS(regOp, arg, CMP_EQ); }
|
|
inline void CMPLTPS(X64Reg regOp, OpArg arg) { CMPPS(regOp, arg, CMP_LT); }
|
|
inline void CMPLEPS(X64Reg regOp, OpArg arg) { CMPPS(regOp, arg, CMP_LE); }
|
|
inline void CMPUNORDPS(X64Reg regOp, OpArg arg) { CMPPS(regOp, arg, CMP_UNORD); }
|
|
inline void CMPNEQPS(X64Reg regOp, OpArg arg) { CMPPS(regOp, arg, CMP_NEQ); }
|
|
inline void CMPNLTPS(X64Reg regOp, OpArg arg) { CMPPS(regOp, arg, CMP_NLT); }
|
|
inline void CMPORDPS(X64Reg regOp, OpArg arg) { CMPPS(regOp, arg, CMP_ORD); }
|
|
|
|
// SSE/SSE2: Floating point packed bitwise (x4 for float, x2 for double)
|
|
void ANDPS(X64Reg regOp, OpArg arg);
|
|
void ANDPD(X64Reg regOp, OpArg arg);
|
|
void ANDNPS(X64Reg regOp, OpArg arg);
|
|
void ANDNPD(X64Reg regOp, OpArg arg);
|
|
void ORPS(X64Reg regOp, OpArg arg);
|
|
void ORPD(X64Reg regOp, OpArg arg);
|
|
void XORPS(X64Reg regOp, OpArg arg);
|
|
void XORPD(X64Reg regOp, OpArg arg);
|
|
|
|
// SSE/SSE2: Shuffle components. These are tricky - see Intel documentation.
|
|
void SHUFPS(X64Reg regOp, OpArg arg, u8 shuffle);
|
|
void SHUFPD(X64Reg regOp, OpArg arg, u8 shuffle);
|
|
|
|
// SSE/SSE2: Useful alternative to shuffle in some cases.
|
|
void MOVDDUP(X64Reg regOp, OpArg arg);
|
|
|
|
// TODO: Actually implement
|
|
#if 0
|
|
// SSE3: Horizontal operations in SIMD registers. Could be useful for various VFPU things like dot products...
|
|
void ADDSUBPS(X64Reg dest, OpArg src);
|
|
void ADDSUBPD(X64Reg dest, OpArg src);
|
|
void HADDPD(X64Reg dest, OpArg src);
|
|
void HSUBPS(X64Reg dest, OpArg src);
|
|
void HSUBPD(X64Reg dest, OpArg src);
|
|
|
|
// SSE4: Further horizontal operations - dot products. These are weirdly flexible, the arg contains both a read mask and a write "mask".
|
|
void DPPD(X64Reg dest, OpArg src, u8 arg);
|
|
#endif
|
|
|
|
// SSE4: Insert and extract for floats.
|
|
// Note: insert from memory or an XMM.
|
|
void INSERTPS(X64Reg dest, OpArg arg, u8 dstsubreg, u8 srcsubreg = 0, u8 zmask = 0);
|
|
// Extract to memory or GPR.
|
|
void EXTRACTPS(OpArg dest, X64Reg arg, u8 subreg);
|
|
|
|
// SSE3: Horizontal operations in SIMD registers. Very slow! shufps-based code beats it handily on Ivy.
|
|
void HADDPS(X64Reg dest, OpArg src);
|
|
|
|
// SSE4: Further horizontal operations - dot products. These are weirdly flexible, the arg contains both a read mask and a write "mask".
|
|
void DPPS(X64Reg dest, OpArg arg, u8 mask);
|
|
|
|
void UNPCKLPS(X64Reg dest, OpArg src);
|
|
void UNPCKHPS(X64Reg dest, OpArg src);
|
|
void UNPCKLPD(X64Reg dest, OpArg src);
|
|
void UNPCKHPD(X64Reg dest, OpArg src);
|
|
|
|
// SSE/SSE2: Compares.
|
|
void COMISS(X64Reg regOp, OpArg arg);
|
|
void COMISD(X64Reg regOp, OpArg arg);
|
|
void UCOMISS(X64Reg regOp, OpArg arg);
|
|
void UCOMISD(X64Reg regOp, OpArg arg);
|
|
|
|
// SSE/SSE2: Moves. Use the right data type for your data, in most cases.
|
|
void MOVAPS(X64Reg regOp, OpArg arg);
|
|
void MOVAPD(X64Reg regOp, OpArg arg);
|
|
void MOVAPS(OpArg arg, X64Reg regOp);
|
|
void MOVAPD(OpArg arg, X64Reg regOp);
|
|
|
|
void MOVUPS(X64Reg regOp, OpArg arg);
|
|
void MOVUPD(X64Reg regOp, OpArg arg);
|
|
void MOVUPS(OpArg arg, X64Reg regOp);
|
|
void MOVUPD(OpArg arg, X64Reg regOp);
|
|
|
|
void MOVDQA(X64Reg regOp, OpArg arg);
|
|
void MOVDQA(OpArg arg, X64Reg regOp);
|
|
void MOVDQU(X64Reg regOp, OpArg arg);
|
|
void MOVDQU(OpArg arg, X64Reg regOp);
|
|
|
|
void MOVSS(X64Reg regOp, OpArg arg);
|
|
void MOVSD(X64Reg regOp, OpArg arg);
|
|
void MOVSS(OpArg arg, X64Reg regOp);
|
|
void MOVSD(OpArg arg, X64Reg regOp);
|
|
|
|
void MOVLPS(X64Reg regOp, OpArg arg);
|
|
void MOVLPD(X64Reg regOp, OpArg arg);
|
|
void MOVLPS(OpArg arg, X64Reg regOp);
|
|
void MOVLPD(OpArg arg, X64Reg regOp);
|
|
|
|
void MOVHPS(X64Reg regOp, OpArg arg);
|
|
void MOVHPD(X64Reg regOp, OpArg arg);
|
|
void MOVHPS(OpArg arg, X64Reg regOp);
|
|
void MOVHPD(OpArg arg, X64Reg regOp);
|
|
|
|
void MOVHLPS(X64Reg regOp1, X64Reg regOp2);
|
|
void MOVLHPS(X64Reg regOp1, X64Reg regOp2);
|
|
|
|
void MOVD_xmm(X64Reg dest, const OpArg &arg);
|
|
void MOVQ_xmm(X64Reg dest, OpArg arg);
|
|
void MOVD_xmm(const OpArg &arg, X64Reg src);
|
|
void MOVQ_xmm(OpArg arg, X64Reg src);
|
|
|
|
// SSE3: Some additional moves.
|
|
void MOVSHDUP(X64Reg regOp1, OpArg arg);
|
|
void MOVSLDUP(X64Reg regOp1, OpArg arg);
|
|
|
|
// SSE/SSE2: Generates a mask from the high bits of the components of the packed register in question.
|
|
void MOVMSKPS(X64Reg dest, OpArg arg);
|
|
void MOVMSKPD(X64Reg dest, OpArg arg);
|
|
|
|
// SSE2: Selective byte store, mask in src register. EDI/RDI specifies store address. This is a weird one.
|
|
void MASKMOVDQU(X64Reg dest, X64Reg src);
|
|
void LDDQU(X64Reg dest, OpArg src);
|
|
|
|
// SSE/SSE2: Data type conversions.
|
|
void CVTPS2PD(X64Reg dest, OpArg src);
|
|
void CVTPD2PS(X64Reg dest, OpArg src);
|
|
void CVTSS2SD(X64Reg dest, OpArg src);
|
|
void CVTSI2SS(X64Reg dest, OpArg src);
|
|
void CVTSD2SS(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 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);
|
|
void PACKUSDW(X64Reg dest, OpArg arg);
|
|
void PACKUSWB(X64Reg dest, OpArg arg);
|
|
|
|
void PUNPCKLBW(X64Reg dest, const OpArg &arg);
|
|
void PUNPCKLWD(X64Reg dest, const OpArg &arg);
|
|
void PUNPCKLDQ(X64Reg dest, const OpArg &arg);
|
|
void PUNPCKLQDQ(X64Reg dest, const OpArg &arg);
|
|
|
|
void PUNPCKHBW(X64Reg dest, const OpArg &arg);
|
|
void PUNPCKHWD(X64Reg dest, const OpArg &arg);
|
|
void PUNPCKHDQ(X64Reg dest, const OpArg &arg);
|
|
void PUNPCKHQDQ(X64Reg dest, const OpArg &arg);
|
|
|
|
void PTEST(X64Reg dest, OpArg arg);
|
|
void PAND(X64Reg dest, OpArg arg);
|
|
void PANDN(X64Reg dest, OpArg arg);
|
|
void PXOR(X64Reg dest, OpArg arg);
|
|
void POR(X64Reg dest, OpArg arg);
|
|
|
|
void PADDB(X64Reg dest, OpArg arg);
|
|
void PADDW(X64Reg dest, OpArg arg);
|
|
void PADDD(X64Reg dest, OpArg arg);
|
|
void PADDQ(X64Reg dest, OpArg arg);
|
|
|
|
void PADDSB(X64Reg dest, OpArg arg);
|
|
void PADDSW(X64Reg dest, OpArg arg);
|
|
void PADDUSB(X64Reg dest, OpArg arg);
|
|
void PADDUSW(X64Reg dest, OpArg arg);
|
|
|
|
void PSUBB(X64Reg dest, OpArg arg);
|
|
void PSUBW(X64Reg dest, OpArg arg);
|
|
void PSUBD(X64Reg dest, OpArg arg);
|
|
void PSUBQ(X64Reg dest, OpArg arg);
|
|
|
|
void PSUBSB(X64Reg dest, OpArg arg);
|
|
void PSUBSW(X64Reg dest, OpArg arg);
|
|
void PSUBUSB(X64Reg dest, OpArg arg);
|
|
void PSUBUSW(X64Reg dest, OpArg arg);
|
|
|
|
void PAVGB(X64Reg dest, OpArg arg);
|
|
void PAVGW(X64Reg dest, OpArg arg);
|
|
|
|
void PCMPEQB(X64Reg dest, OpArg arg);
|
|
void PCMPEQW(X64Reg dest, OpArg arg);
|
|
void PCMPEQD(X64Reg dest, OpArg arg);
|
|
|
|
void PCMPGTB(X64Reg dest, OpArg arg);
|
|
void PCMPGTW(X64Reg dest, OpArg arg);
|
|
void PCMPGTD(X64Reg dest, OpArg arg);
|
|
|
|
void PEXTRW(X64Reg dest, X64Reg arg, u8 subreg);
|
|
void PINSRW(X64Reg dest, OpArg arg, u8 subreg);
|
|
// SSE4 inserts and extracts.
|
|
void PEXTRB(OpArg dest, X64Reg arg, u8 subreg);
|
|
void PEXTRW(OpArg dest, X64Reg arg, u8 subreg);
|
|
void PEXTRD(OpArg dest, X64Reg arg, u8 subreg);
|
|
void PEXTRQ(OpArg dest, X64Reg arg, u8 subreg);
|
|
void PINSRB(X64Reg dest, OpArg arg, u8 subreg);
|
|
void PINSRD(X64Reg dest, OpArg arg, u8 subreg);
|
|
void PINSRQ(X64Reg dest, OpArg arg, u8 subreg);
|
|
|
|
void PMADDWD(X64Reg dest, OpArg arg);
|
|
void PMADDUBSW(X64Reg dest, OpArg arg);
|
|
void PSADBW(X64Reg dest, OpArg arg);
|
|
|
|
void PMAXSW(X64Reg dest, OpArg arg);
|
|
void PMAXUB(X64Reg dest, OpArg arg);
|
|
void PMINSW(X64Reg dest, OpArg arg);
|
|
void PMINUB(X64Reg dest, OpArg arg);
|
|
// SSE4: More MAX/MIN instructions.
|
|
void PMINSB(X64Reg dest, OpArg arg);
|
|
void PMINSD(X64Reg dest, OpArg arg);
|
|
void PMINUW(X64Reg dest, OpArg arg);
|
|
void PMINUD(X64Reg dest, OpArg arg);
|
|
void PMAXSB(X64Reg dest, OpArg arg);
|
|
void PMAXSD(X64Reg dest, OpArg arg);
|
|
void PMAXUW(X64Reg dest, OpArg arg);
|
|
void PMAXUD(X64Reg dest, OpArg arg);
|
|
|
|
void PMOVMSKB(X64Reg dest, OpArg arg);
|
|
void PSHUFD(X64Reg dest, OpArg arg, u8 shuffle);
|
|
void PSHUFB(X64Reg dest, OpArg arg);
|
|
|
|
void PSHUFLW(X64Reg dest, OpArg arg, u8 shuffle);
|
|
void PSHUFHW(X64Reg dest, OpArg arg, u8 shuffle);
|
|
|
|
void PSRLW(X64Reg dest, X64Reg reg, int shift);
|
|
void PSRLW(X64Reg reg, int shift) { PSRLW(reg, reg, shift); }
|
|
void PSRLD(X64Reg dest, X64Reg reg, int shift);
|
|
void PSRLD(X64Reg reg, int shift) { PSRLD(reg, reg, shift); }
|
|
void PSRLQ(X64Reg dest, X64Reg reg, int shift);
|
|
void PSRLQ(X64Reg reg, int shift) { PSRLQ(reg, reg, shift); }
|
|
void PSRLDQ(X64Reg dest, X64Reg reg, int shift);
|
|
void PSRLDQ(X64Reg reg, int shift) { PSRLDQ(reg, reg, shift); }
|
|
// Note: all values shifted by lowest 64-bit in XMM arg.
|
|
void PSRLW(X64Reg reg, OpArg arg);
|
|
// Note: all values shifted by lowest 64-bit in XMM arg.
|
|
void PSRLD(X64Reg reg, OpArg arg);
|
|
// Note: both values shifted by lowest 64-bit in XMM arg.
|
|
void PSRLQ(X64Reg reg, OpArg arg);
|
|
|
|
void PSLLW(X64Reg dest, X64Reg reg, int shift);
|
|
void PSLLW(X64Reg reg, int shift) { PSLLW(reg, reg, shift); }
|
|
void PSLLD(X64Reg dest, X64Reg reg, int shift);
|
|
void PSLLD(X64Reg reg, int shift) { PSLLD(reg, reg, shift); }
|
|
void PSLLQ(X64Reg dest, X64Reg reg, int shift);
|
|
void PSLLQ(X64Reg reg, int shift) { PSLLQ(reg, reg, shift); }
|
|
void PSLLDQ(X64Reg dest, X64Reg reg, int shift);
|
|
void PSLLDQ(X64Reg reg, int shift) { PSLLDQ(reg, reg, shift); }
|
|
// Note: all values shifted by lowest 64-bit in XMM arg.
|
|
void PSLLW(X64Reg reg, OpArg arg);
|
|
// Note: all values shifted by lowest 64-bit in XMM arg.
|
|
void PSLLD(X64Reg reg, OpArg arg);
|
|
// Note: both values shifted by lowest 64-bit in XMM arg.
|
|
void PSLLQ(X64Reg reg, OpArg arg);
|
|
|
|
void PSRAW(X64Reg dest, X64Reg reg, int shift);
|
|
void PSRAW(X64Reg reg, int shift) { PSRAW(reg, reg, shift); }
|
|
void PSRAD(X64Reg dest, X64Reg reg, int shift);
|
|
void PSRAD(X64Reg reg, int shift) { PSRAD(reg, reg, shift); }
|
|
// Note: all values shifted by lowest 64-bit in XMM arg.
|
|
void PSRAW(X64Reg reg, OpArg arg);
|
|
// Note: all values shifted by lowest 64-bit in XMM arg.
|
|
void PSRAD(X64Reg reg, OpArg arg);
|
|
|
|
void PMULLW(X64Reg dest, const OpArg &arg);
|
|
void PMULHW(X64Reg dest, const OpArg &arg);
|
|
void PMULHUW(X64Reg dest, const OpArg &arg);
|
|
void PMULUDQ(X64Reg dest, const OpArg &arg);
|
|
|
|
// SSE4: integer multiply
|
|
void PMULLD(X64Reg dest, const OpArg &arg);
|
|
void PMULDQ(X64Reg dest, const OpArg &arg);
|
|
|
|
// SSE4: data type conversions
|
|
void PMOVSXBW(X64Reg dest, OpArg arg);
|
|
void PMOVSXBD(X64Reg dest, OpArg arg);
|
|
void PMOVSXBQ(X64Reg dest, OpArg arg);
|
|
void PMOVSXWD(X64Reg dest, OpArg arg);
|
|
void PMOVSXWQ(X64Reg dest, OpArg arg);
|
|
void PMOVSXDQ(X64Reg dest, OpArg arg);
|
|
void PMOVZXBW(X64Reg dest, OpArg arg);
|
|
void PMOVZXBD(X64Reg dest, OpArg arg);
|
|
void PMOVZXBQ(X64Reg dest, OpArg arg);
|
|
void PMOVZXWD(X64Reg dest, OpArg arg);
|
|
void PMOVZXWQ(X64Reg dest, OpArg arg);
|
|
void PMOVZXDQ(X64Reg dest, OpArg arg);
|
|
|
|
// SSE4: variable blend instructions (xmm0 implicit argument)
|
|
void PBLENDVB(X64Reg dest, OpArg arg);
|
|
void BLENDVPS(X64Reg dest, OpArg arg);
|
|
void BLENDVPD(X64Reg dest, OpArg arg);
|
|
|
|
// SSE4: constant blend instructions
|
|
void PBLENDW(X64Reg dest, OpArg arg, u8 mask);
|
|
void BLENDPS(X64Reg dest, OpArg arg, u8 mask);
|
|
void BLENDPD(X64Reg dest, OpArg arg, u8 mask);
|
|
|
|
// SSE4: rounding (see FloatRound for mode or use ROUNDNEARSS, etc. helpers.)
|
|
void ROUNDSS(X64Reg dest, OpArg arg, u8 mode);
|
|
void ROUNDSD(X64Reg dest, OpArg arg, u8 mode);
|
|
void ROUNDPS(X64Reg dest, OpArg arg, u8 mode);
|
|
void ROUNDPD(X64Reg dest, OpArg arg, u8 mode);
|
|
|
|
inline void ROUNDNEARSS(X64Reg dest, OpArg arg) { ROUNDSS(dest, arg, FROUND_NEAREST); }
|
|
inline void ROUNDFLOORSS(X64Reg dest, OpArg arg) { ROUNDSS(dest, arg, FROUND_FLOOR); }
|
|
inline void ROUNDCEILSS(X64Reg dest, OpArg arg) { ROUNDSS(dest, arg, FROUND_CEIL); }
|
|
inline void ROUNDZEROSS(X64Reg dest, OpArg arg) { ROUNDSS(dest, arg, FROUND_ZERO); }
|
|
|
|
inline void ROUNDNEARSD(X64Reg dest, OpArg arg) { ROUNDSD(dest, arg, FROUND_NEAREST); }
|
|
inline void ROUNDFLOORSD(X64Reg dest, OpArg arg) { ROUNDSD(dest, arg, FROUND_FLOOR); }
|
|
inline void ROUNDCEILSD(X64Reg dest, OpArg arg) { ROUNDSD(dest, arg, FROUND_CEIL); }
|
|
inline void ROUNDZEROSD(X64Reg dest, OpArg arg) { ROUNDSD(dest, arg, FROUND_ZERO); }
|
|
|
|
inline void ROUNDNEARPS(X64Reg dest, OpArg arg) { ROUNDPS(dest, arg, FROUND_NEAREST); }
|
|
inline void ROUNDFLOORPS(X64Reg dest, OpArg arg) { ROUNDPS(dest, arg, FROUND_FLOOR); }
|
|
inline void ROUNDCEILPS(X64Reg dest, OpArg arg) { ROUNDPS(dest, arg, FROUND_CEIL); }
|
|
inline void ROUNDZEROPS(X64Reg dest, OpArg arg) { ROUNDPS(dest, arg, FROUND_ZERO); }
|
|
|
|
inline void ROUNDNEARPD(X64Reg dest, OpArg arg) { ROUNDPD(dest, arg, FROUND_NEAREST); }
|
|
inline void ROUNDFLOORPD(X64Reg dest, OpArg arg) { ROUNDPD(dest, arg, FROUND_FLOOR); }
|
|
inline void ROUNDCEILPD(X64Reg dest, OpArg arg) { ROUNDPD(dest, arg, FROUND_CEIL); }
|
|
inline void ROUNDZEROPD(X64Reg dest, OpArg arg) { ROUNDPD(dest, arg, FROUND_ZERO); }
|
|
|
|
// AVX
|
|
void VADDPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VADDPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VADDSS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VADDSD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VADDSUBPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VADDSUBPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VCMPPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 compare);
|
|
void VCMPPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 compare);
|
|
void VCMPSS(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 compare);
|
|
void VCMPSD(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 compare);
|
|
void VCOMISS(X64Reg regOp1, OpArg arg);
|
|
void VCOMISD(X64Reg regOp1, OpArg arg);
|
|
void VDIVPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VDIVPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VDIVSS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VDIVSD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
// 256 is just two DPPS, not a single dot product.
|
|
void VDPPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 mask);
|
|
void VDPPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 mask);
|
|
void VHADDPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VHADDPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VHSUBPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VHSUBPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMAXPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMAXPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMAXSS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMAXSD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMINPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMINPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMINSS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMINSD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMULPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMULPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMULSS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMULSD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VRCPPS(int bits, X64Reg regOp1, OpArg arg);
|
|
void VRCPSS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VRSQRTPS(int bits, X64Reg regOp1, OpArg arg);
|
|
void VRSQRTSS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VSQRTPS(int bits, X64Reg regOp1, OpArg arg);
|
|
void VSQRTPD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VSQRTSS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VSQRTSD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VSUBPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VSUBPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VSUBSS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VSUBSD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VUCOMISS(X64Reg regOp1, OpArg arg);
|
|
void VUCOMISD(X64Reg regOp1, OpArg arg);
|
|
|
|
void VBLENDPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 mask);
|
|
void VBLENDPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 mask);
|
|
void VBLENDVPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, X64Reg mask);
|
|
void VBLENDVPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, X64Reg mask);
|
|
void VCVTDQ2PS(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTDQ2PD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTPS2DQ(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTPD2DQ(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTPS2PD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTPD2PS(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTSS2SI(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTSS2SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VCVTSD2SI(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTSD2SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VCVTSI2SS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VCVTSI2SD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VCVTTPS2DQ(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTTPD2DQ(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTTSS2SI(int bits, X64Reg regOp1, OpArg arg);
|
|
void VCVTTSD2SI(int bits, X64Reg regOp1, OpArg arg);
|
|
// Can only extract from the low 128 bits.
|
|
void VEXTRACTPS(OpArg arg, X64Reg regOp1, u8 subreg);
|
|
// Can only insert into the low 128 bits, zeros upper bits. Inserts from XMM.
|
|
void VINSERTPS(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 dstsubreg, u8 srcsubreg = 0, u8 zmask = 0);
|
|
void VLDDQU(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVAPS(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVAPD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVAPS(int bits, OpArg arg, X64Reg regOp1);
|
|
void VMOVAPD(int bits, OpArg arg, X64Reg regOp1);
|
|
void VMOVD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVD(int bits, OpArg arg, X64Reg regOp1);
|
|
void VMOVD(X64Reg regOp1, OpArg arg);
|
|
void VMOVD(OpArg arg, X64Reg regOp1);
|
|
void VMOVQ(X64Reg regOp1, OpArg arg);
|
|
void VMOVQ(OpArg arg, X64Reg regOp1);
|
|
void VMOVDDUP(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVDQA(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVDQA(int bits, OpArg arg, X64Reg regOp1);
|
|
void VMOVDQU(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVDQU(int bits, OpArg arg, X64Reg regOp1);
|
|
void VMOVHLPS(X64Reg regOp1, X64Reg regOp2, X64Reg arg);
|
|
void VMOVLHPS(X64Reg regOp1, X64Reg regOp2, X64Reg arg);
|
|
void VMOVHPS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMOVHPS(OpArg arg, X64Reg regOp1);
|
|
void VMOVHPD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMOVHPD(OpArg arg, X64Reg regOp1);
|
|
void VMOVLPS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMOVLPS(OpArg arg, X64Reg regOp1);
|
|
void VMOVLPD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VMOVLPD(OpArg arg, X64Reg regOp1);
|
|
void VMOVMSKPS(int bits, X64Reg genReg, X64Reg xmmReg);
|
|
void VMOVMSKPD(int bits, X64Reg genReg, X64Reg xmmReg);
|
|
void VMOVNTDQ(int bits, OpArg arg, X64Reg regOp1);
|
|
void VMOVNTPS(int bits, OpArg arg, X64Reg regOp1);
|
|
void VMOVNTPD(int bits, OpArg arg, X64Reg regOp1);
|
|
// Between XMM regs, zeros upper bits.
|
|
void VMOVQ(X64Reg regOp1, X64Reg arg);
|
|
void VMOVSS(X64Reg regOp1, X64Reg regOp2, X64Reg arg);
|
|
void VMOVSS(OpArg arg, X64Reg regOp1);
|
|
void VMOVSS(X64Reg regOp1, OpArg arg);
|
|
void VMOVSD(X64Reg regOp1, X64Reg regOp2, X64Reg arg);
|
|
void VMOVSD(OpArg arg, X64Reg regOp1);
|
|
void VMOVSD(X64Reg regOp1, OpArg arg);
|
|
void VMOVSHDUP(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVSLDUP(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVUPS(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVUPD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VMOVUPS(int bits, OpArg arg, X64Reg regOp1);
|
|
void VMOVUPD(int bits, OpArg arg, X64Reg regOp1);
|
|
void VROUNDPS(int bits, X64Reg dest, OpArg arg, u8 mode);
|
|
void VROUNDPD(int bits, X64Reg dest, OpArg arg, u8 mode);
|
|
void VROUNDSS(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 mode);
|
|
void VROUNDSD(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 mode);
|
|
void VSHUFPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 shuffle);
|
|
void VSHUFPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 shuffle);
|
|
void VUNPCKHPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VUNPCKHPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VUNPCKLPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VUNPCKLPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
|
|
void VANDPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VANDPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VANDNPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VANDNPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VORPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VORPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VXORPS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VXORPD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
|
|
void VPTEST(int bits, X64Reg regOp1, OpArg arg);
|
|
|
|
void VMOVNTDQA(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPACKSSWB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPACKSSDW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPACKUSWB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPACKUSDW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
// 256 performs two PALIGNRs (high and low), not a single large one.
|
|
void VPALIGNR(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 shift);
|
|
void VPBLENDVB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, X64Reg maskReg);
|
|
void VPBLENDW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 mask);
|
|
void VPEXTRB(OpArg arg, X64Reg regOp1, u8 subreg);
|
|
void VPEXTRW(OpArg arg, X64Reg regOp1, u8 subreg);
|
|
void VPEXTRD(OpArg arg, X64Reg regOp1, u8 subreg);
|
|
void VPEXTRQ(OpArg arg, X64Reg regOp1, u8 subreg);
|
|
void VPINSRB(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 subreg);
|
|
void VPINSRW(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 subreg);
|
|
void VPINSRD(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 subreg);
|
|
void VPINSRQ(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 subreg);
|
|
void VPMOVMSKB(int bits, X64Reg genReg, X64Reg arg);
|
|
void VPMOVSXBW(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVSXBD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVSXBQ(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVSXWD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVSXWQ(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVSXDQ(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVZXBW(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVZXBD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVZXBQ(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVZXWD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVZXWQ(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPMOVZXDQ(int bits, X64Reg regOp1, OpArg arg);
|
|
// 256 performs two PSHUFBs, can't shuffle between.
|
|
void VPSHUFB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
// 256 performs two PSHUFDs with same shuffle mask, can't shuffle between.
|
|
void VPSHUFD(int bits, X64Reg regOp1, OpArg arg, u8 shuffle);
|
|
// 256 performs two PSHUFHWs with same shuffle mask.
|
|
void VPSHUFHW(int bits, X64Reg regOp1, OpArg arg, u8 shuffle);
|
|
// 256 performs two PSHUFLWs with same shuffle mask.
|
|
void VPSHUFLW(int bits, X64Reg regOp1, OpArg arg, u8 shuffle);
|
|
void VPUNPCKHBW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPUNPCKHWD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPUNPCKHDQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPUNPCKHQDQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPUNPCKLBW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPUNPCKLWD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPUNPCKLDQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPUNPCKLQDQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
|
|
void VPABSB(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPABSW(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPABSD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPADDB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPADDW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPADDD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPADDQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPADDSB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPADDSW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPADDUSB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPADDUSW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPAVGB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPAVGW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPCMPEQB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPCMPEQW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPCMPEQD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPCMPEQQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPCMPGTB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPCMPGTW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPCMPGTD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPCMPGTQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPHADDW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPHADDD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPHADDSW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VHMINPOSUW(X64Reg regOp1, OpArg arg);
|
|
void VPHSUBW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPHSUBD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPHSUBSW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMADDUBSW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMADDWD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMAXSB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMAXSW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMAXSD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMAXUB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMAXUW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMAXUD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMINSB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMINSW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMINSD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMINUB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMINUW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMINUD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMULDQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMULHRS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMULHUW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMULHW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMULLD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMULLW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPMULUDQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSADBW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSIGNB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSIGNW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSIGND(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSUBB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSUBW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSUBD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSUBQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSUBSB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSUBSW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSUBUSB(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSUBUSW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
|
|
void VPAND(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPANDN(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPOR(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSLLDQ(int bits, X64Reg regOp2, X64Reg arg, u8 shift);
|
|
void VPSLLW(int bits, X64Reg regOp2, X64Reg arg, u8 shift);
|
|
void VPSLLW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSLLD(int bits, X64Reg regOp2, X64Reg arg, u8 shift);
|
|
void VPSLLD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSLLQ(int bits, X64Reg regOp2, X64Reg arg, u8 shift);
|
|
void VPSLLQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSRAW(int bits, X64Reg regOp2, X64Reg arg, u8 shift);
|
|
void VPSRAW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSRAD(int bits, X64Reg regOp2, X64Reg arg, u8 shift);
|
|
void VPSRAD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSRLDQ(int bits, X64Reg regOp2, X64Reg arg, u8 shift);
|
|
void VPSRLW(int bits, X64Reg regOp2, X64Reg arg, u8 shift);
|
|
void VPSRLW(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSRLD(int bits, X64Reg regOp2, X64Reg arg, u8 shift);
|
|
void VPSRLD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSRLQ(int bits, X64Reg regOp2, X64Reg arg, u8 shift);
|
|
void VPSRLQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPXOR(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
|
|
void VBROADCASTSS(int bits, X64Reg regOp1, OpArg arg);
|
|
void VBROADCASTSD(X64Reg regOp1, OpArg arg);
|
|
void VBROADCASTF128(X64Reg regOp1, OpArg arg);
|
|
void VEXTRACTF128(OpArg arg, X64Reg regOp1, u8 subreg);
|
|
void VINSERTF128(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 subreg);
|
|
void VPERM2F128(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 mask);
|
|
void VPERMILPS(int bits, X64Reg dest, X64Reg src, OpArg mask);
|
|
void VPERMILPS(int bits, X64Reg dest, OpArg src, u8 mask);
|
|
void VPERMILPD(int bits, X64Reg dest, X64Reg src, OpArg mask);
|
|
void VPERMILPD(int bits, X64Reg dest, OpArg src, u8 mask);
|
|
|
|
void VMASKMOVPS(int bits, X64Reg dest, X64Reg maskReg, OpArg src);
|
|
void VMASKMOVPS(int bits, OpArg dest, X64Reg maskReg, X64Reg src);
|
|
void VMASKMOVPD(int bits, X64Reg dest, X64Reg maskReg, OpArg src);
|
|
void VMASKMOVPD(int bits, OpArg dest, X64Reg maskReg, X64Reg src);
|
|
|
|
void VTESTPS(int bits, X64Reg regOp1, OpArg arg);
|
|
void VTESTPD(int bits, X64Reg regOp1, OpArg arg);
|
|
|
|
void VZEROALL();
|
|
void VZEROUPPER();
|
|
|
|
// AVX2
|
|
void VEXTRACTI128(OpArg arg, X64Reg regOp1, u8 subreg);
|
|
void VINSERTI128(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 subreg);
|
|
void VPBLENDD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 mask);
|
|
void VPBROADCASTB(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPBROADCASTW(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPBROADCASTD(int bits, X64Reg regOp1, OpArg arg);
|
|
void VPBROADCASTQ(int bits, X64Reg regOp1, OpArg arg);
|
|
// Must be memory for this one.
|
|
void VBROADCASTI128(X64Reg regOp1, OpArg arg);
|
|
void VPERM2I128(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 mask);
|
|
void VPERMD(X64Reg dest, X64Reg shuffle, OpArg src);
|
|
void VPERMPS(X64Reg dest, X64Reg shuffle, OpArg src);
|
|
void VPERMPD(X64Reg dest, OpArg src, u8 shuffle);
|
|
void VPERMQ(X64Reg dest, OpArg src, u8 shuffle);
|
|
|
|
void VPMASKMOVD(int bits, X64Reg regOp1, X64Reg mask, OpArg arg);
|
|
void VPMASKMOVD(int bits, OpArg arg, X64Reg mask, X64Reg regOp2);
|
|
void VPMASKMOVQ(int bits, X64Reg regOp1, X64Reg mask, OpArg arg);
|
|
void VPMASKMOVQ(int bits, OpArg arg, X64Reg mask, X64Reg regOp2);
|
|
|
|
// Use an XMM for the scaled reg in MComplex.
|
|
void VGATHERDPS(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void VGATHERDPD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void VGATHERQPS(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void VGATHERQPD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void VPGATHERDD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void VPGATHERQD(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void VPGATHERDQ(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void VPGATHERQQ(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
|
|
void VPSLLVD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSLLVQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSRAVD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSRLVD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VPSRLVQ(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
|
|
// FMA3
|
|
void VFMADD132PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD213PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD231PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD132PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD213PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD231PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD132SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD213SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD231SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD132SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD213SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADD231SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB132PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB213PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB231PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB132PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB213PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB231PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB132SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB213SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB231SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB132SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB213SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUB231SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD132PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD213PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD231PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD132PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD213PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD231PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD132SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD213SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD231SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD132SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD213SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMADD231SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB132PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB213PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB231PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB132PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB213PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB231PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB132SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB213SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB231SS(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB132SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB213SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFNMSUB231SD(X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADDSUB132PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADDSUB213PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADDSUB231PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADDSUB132PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADDSUB213PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMADDSUB231PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUBADD132PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUBADD213PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUBADD231PS(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUBADD132PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUBADD213PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void VFMSUBADD231PD(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
|
|
// VEX GPR instructions
|
|
void SARX(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void SHLX(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void SHRX(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void RORX(int bits, X64Reg regOp, OpArg arg, u8 rotate);
|
|
void PEXT(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void PDEP(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void MULX(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
void BZHI(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void BLSR(int bits, X64Reg regOp, OpArg arg);
|
|
void BLSMSK(int bits, X64Reg regOp, OpArg arg);
|
|
void BLSI(int bits, X64Reg regOp, OpArg arg);
|
|
void BEXTR(int bits, X64Reg regOp1, OpArg arg, X64Reg regOp2);
|
|
void ANDN(int bits, X64Reg regOp1, X64Reg regOp2, OpArg arg);
|
|
|
|
void RDTSC();
|
|
|
|
// Utility functions
|
|
// The difference between this and CALL is that this aligns the stack
|
|
// where appropriate.
|
|
void ABI_CallFunction(const void *func);
|
|
template <typename T>
|
|
void ABI_CallFunction(T (*func)()) {
|
|
ABI_CallFunction((const void *)func);
|
|
}
|
|
|
|
void ABI_CallFunction(const u8 *func) {
|
|
ABI_CallFunction((const void *)func);
|
|
}
|
|
void ABI_CallFunctionC16(const void *func, u16 param1);
|
|
void ABI_CallFunctionCC16(const void *func, u32 param1, u16 param2);
|
|
|
|
|
|
// These only support u32 parameters, but that's enough for a lot of uses.
|
|
// These will destroy the 1 or 2 first "parameter regs".
|
|
void ABI_CallFunctionC(const void *func, u32 param1);
|
|
void ABI_CallFunctionCC(const void *func, u32 param1, u32 param2);
|
|
void ABI_CallFunctionCCC(const void *func, u32 param1, u32 param2, u32 param3);
|
|
void ABI_CallFunctionCP(const void *func, u32 param1, void *param2);
|
|
void ABI_CallFunctionCCP(const void *func, u32 param1, u32 param2, void *param3);
|
|
void ABI_CallFunctionCCCP(const void *func, u32 param1, u32 param2, u32 param3, void *param4);
|
|
void ABI_CallFunctionP(const void *func, void *param1);
|
|
void ABI_CallFunctionPA(const void *func, void *param1, const Gen::OpArg &arg2);
|
|
void ABI_CallFunctionPAA(const void *func, void *param1, const Gen::OpArg &arg2, const Gen::OpArg &arg3);
|
|
void ABI_CallFunctionPP(const void *func, void *param1, void *param2);
|
|
void ABI_CallFunctionPPC(const void *func, void *param1, void *param2, u32 param3);
|
|
void ABI_CallFunctionAC(const void *func, const Gen::OpArg &arg1, u32 param2);
|
|
void ABI_CallFunctionACC(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3);
|
|
void ABI_CallFunctionACCP(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3, void *param4);
|
|
void ABI_CallFunctionA(const void *func, const Gen::OpArg &arg1);
|
|
void ABI_CallFunctionAA(const void *func, const Gen::OpArg &arg1, const Gen::OpArg &arg2);
|
|
|
|
// Pass a register as a parameter.
|
|
void ABI_CallFunctionR(const void *func, X64Reg reg1);
|
|
void ABI_CallFunctionRR(const void *func, X64Reg reg1, X64Reg reg2);
|
|
|
|
template <typename Tr, typename T1>
|
|
void ABI_CallFunctionC(Tr (*func)(T1), u32 param1) {
|
|
ABI_CallFunctionC((const void *)func, param1);
|
|
}
|
|
|
|
// A function that doesn't have any control over what it will do to regs,
|
|
// such as the dispatcher, should be surrounded by these.
|
|
void ABI_PushAllCalleeSavedRegsAndAdjustStack();
|
|
void ABI_PopAllCalleeSavedRegsAndAdjustStack();
|
|
|
|
// A function that doesn't know anything about it's surroundings, should
|
|
// be surrounded by these to establish a safe environment, where it can roam free.
|
|
// An example is a backpatch injected function.
|
|
void ABI_PushAllCallerSavedRegsAndAdjustStack();
|
|
void ABI_PopAllCallerSavedRegsAndAdjustStack();
|
|
|
|
unsigned int ABI_GetAlignedFrameSize(unsigned int frameSize);
|
|
void ABI_AlignStack(unsigned int frameSize);
|
|
void ABI_RestoreStack(unsigned int frameSize);
|
|
|
|
// Sets up a __cdecl function.
|
|
// Only x64 really needs the parameter count.
|
|
void ABI_EmitPrologue(int maxCallParams);
|
|
void ABI_EmitEpilogue(int maxCallParams);
|
|
|
|
#if PPSSPP_ARCH(X86)
|
|
inline int ABI_GetNumXMMRegs() { return 8; }
|
|
#else
|
|
inline int ABI_GetNumXMMRegs() { return 16; }
|
|
#endif
|
|
}; // class XEmitter
|
|
|
|
|
|
// Everything that needs to generate X86 code should inherit from this.
|
|
// You get memory management for free, plus, you can use all the MOV etc functions without
|
|
// having to prefix them with gen-> or something similar.
|
|
|
|
class XCodeBlock : public CodeBlock<XEmitter> {
|
|
public:
|
|
void PoisonMemory(int offset) override;
|
|
bool RipAccessible(const void *ptr) const {
|
|
// For debugging
|
|
// return false;
|
|
#if PPSSPP_ARCH(X86)
|
|
return true;
|
|
#else
|
|
ptrdiff_t diff = GetCodePtr() - (const uint8_t *)ptr;
|
|
return diff > -0x7FFFFFE0 && diff < 0x7FFFFFE0;
|
|
#endif
|
|
}
|
|
};
|
|
|
|
} // namespace
|