dolphin/Source/Core/Common/x64Emitter.h
mitaclaw e8d5fb89e4 C++20: Synthesize operator!= From operator==
The inequality operator is automatically generated by the compiler if `operator==` is defined.
2024-10-10 20:23:55 -07:00

1214 lines
42 KiB
C++

// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// WARNING - THIS LIBRARY IS NOT THREAD SAFE!!!
#pragma once
#include <cstddef>
#include <cstring>
#include <functional>
#include <tuple>
#include <type_traits>
#include "Common/Assert.h"
#include "Common/BitSet.h"
#include "Common/CodeBlock.h"
#include "Common/CommonTypes.h"
#include "Common/x64ABI.h"
namespace Gen
{
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 SSECompare
{
CMP_EQ = 0,
CMP_LT = 1,
CMP_LE = 2,
CMP_UNORD = 3,
CMP_NEQ = 4,
CMP_NLT = 5,
CMP_NLE = 6,
CMP_ORD = 7,
};
class XEmitter;
enum class FloatOp;
enum class NormalOp;
// Information about a generated MOV op
struct MovInfo final
{
u8* address;
bool nonAtomicSwapStore;
// valid iff nonAtomicSwapStore is true
X64Reg nonAtomicSwapStoreSrc;
};
// RIP addressing does not benefit from micro op fusion on Core arch
struct OpArg
{
// For accessing offset and operandReg.
// This also allows us to keep the op writing functions private.
friend class XEmitter;
// dummy op arg, used for storage
constexpr OpArg() = default;
constexpr OpArg(u64 offset_, int scale_, X64Reg rm_reg = RAX, X64Reg scaled_reg = RAX)
: scale{static_cast<u8>(scale_)}, offsetOrBaseReg{static_cast<u16>(rm_reg)},
indexReg{static_cast<u16>(scaled_reg)}, offset{offset_}
{
}
constexpr bool operator==(const OpArg& b) const
{
return std::tie(scale, offsetOrBaseReg, indexReg, offset, operandReg) ==
std::tie(b.scale, b.offsetOrBaseReg, b.indexReg, b.offset, b.operandReg);
}
u64 Imm64() const
{
DEBUG_ASSERT(scale == SCALE_IMM64);
return (u64)offset;
}
u32 Imm32() const
{
DEBUG_ASSERT(scale == SCALE_IMM32);
return (u32)offset;
}
u16 Imm16() const
{
DEBUG_ASSERT(scale == SCALE_IMM16);
return (u16)offset;
}
u8 Imm8() const
{
DEBUG_ASSERT(scale == SCALE_IMM8);
return (u8)offset;
}
s64 SImm64() const
{
DEBUG_ASSERT(scale == SCALE_IMM64);
return (s64)offset;
}
s32 SImm32() const
{
DEBUG_ASSERT(scale == SCALE_IMM32);
return (s32)offset;
}
s16 SImm16() const
{
DEBUG_ASSERT(scale == SCALE_IMM16);
return (s16)offset;
}
s8 SImm8() const
{
DEBUG_ASSERT(scale == SCALE_IMM8);
return (s8)offset;
}
OpArg AsImm64() const
{
DEBUG_ASSERT(IsImm());
return OpArg((u64)offset, SCALE_IMM64);
}
OpArg AsImm32() const
{
DEBUG_ASSERT(IsImm());
return OpArg((u32)offset, SCALE_IMM32);
}
OpArg AsImm16() const
{
DEBUG_ASSERT(IsImm());
return OpArg((u16)offset, SCALE_IMM16);
}
OpArg AsImm8() const
{
DEBUG_ASSERT(IsImm());
return OpArg((u8)offset, SCALE_IMM8);
}
constexpr bool IsImm() const
{
return scale == SCALE_IMM8 || scale == SCALE_IMM16 || scale == SCALE_IMM32 ||
scale == SCALE_IMM64;
}
constexpr bool IsSimpleReg() const { return scale == SCALE_NONE; }
constexpr bool IsSimpleReg(X64Reg reg) const { return IsSimpleReg() && GetSimpleReg() == reg; }
constexpr bool IsZero() const { return IsImm() && offset == 0; }
constexpr 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;
}
}
constexpr X64Reg GetSimpleReg() const
{
if (scale == SCALE_NONE)
return static_cast<X64Reg>(offsetOrBaseReg);
return INVALID_REG;
}
void AddMemOffset(int val)
{
DEBUG_ASSERT_MSG(DYNA_REC, scale == SCALE_RIP || (scale <= SCALE_ATREG && scale > SCALE_NONE),
"Tried to increment an OpArg which doesn't have an offset");
offset += val;
}
private:
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);
void WriteNormalOp(XEmitter* emit, bool toRM, NormalOp op, const OpArg& operand, int bits) const;
u8 scale = 0;
u16 offsetOrBaseReg = 0;
u16 indexReg = 0;
u64 offset = 0; // Also used to store immediates.
u16 operandReg = 0;
};
template <typename T>
inline OpArg M(const T* ptr)
{
return OpArg((u64)(const void*)ptr, (int)SCALE_RIP);
}
constexpr OpArg R(X64Reg value)
{
return OpArg(0, SCALE_NONE, value);
}
constexpr OpArg MatR(X64Reg value)
{
return OpArg(0, SCALE_ATREG, value);
}
constexpr OpArg MDisp(X64Reg value, int offset)
{
return OpArg(static_cast<u32>(offset), SCALE_ATREG, value);
}
constexpr OpArg MComplex(X64Reg base, X64Reg scaled, int scale, int offset)
{
return OpArg(offset, scale, base, scaled);
}
constexpr OpArg MScaled(X64Reg scaled, int scale, int offset)
{
if (scale == SCALE_1)
return OpArg(offset, SCALE_ATREG, scaled);
return OpArg(offset, scale | 0x20, RAX, scaled);
}
constexpr OpArg MRegSum(X64Reg base, X64Reg offset)
{
return MComplex(base, offset, 1, 0);
}
constexpr OpArg Imm8(u8 imm)
{
return OpArg(imm, SCALE_IMM8);
}
constexpr OpArg Imm16(u16 imm)
{
return OpArg(imm, SCALE_IMM16);
} // rarely used
constexpr OpArg Imm32(u32 imm)
{
return OpArg(imm, SCALE_IMM32);
}
constexpr OpArg Imm64(u64 imm)
{
return OpArg(imm, SCALE_IMM64);
}
inline OpArg ImmPtr(const void* imm)
{
return Imm64(reinterpret_cast<u64>(imm));
}
inline u32 PtrOffset(const void* ptr, const void* base = nullptr)
{
s64 distance = (s64)ptr - (s64)base;
if (distance >= 0x80000000LL || distance < -0x80000000LL)
{
ASSERT_MSG(DYNA_REC, 0, "pointer offset out of range");
return 0;
}
return (u32)distance;
}
struct FixupBranch
{
enum class Type
{
Branch8Bit,
Branch32Bit
};
u8* ptr;
Type type;
};
class XEmitter
{
friend struct OpArg; // for Write8 etc
private:
// Pointer to memory where code will be emitted to.
u8* code = nullptr;
// Pointer past the end of the memory region we're allowed to emit to.
// Writes that would reach this memory are refused and will set the m_write_failed flag instead.
u8* m_code_end = nullptr;
bool flags_locked = false;
// Set to true when a write request happens that would write past m_code_end.
// Must be cleared with SetCodePtr() afterwards.
bool m_write_failed = false;
void CheckFlags();
void Rex(int w, int r, int x, int b);
void WriteModRM(int mod, int reg, int rm);
void WriteSIB(int scale, int index, int base);
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, const OpArg& shift, int ext);
void WriteBitTest(int bits, const OpArg& dest, const 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, const OpArg& arg, int extrabytes = 0);
void WriteSSE41Op(u8 opPrefix, u16 op, X64Reg regOp, const OpArg& arg, int extrabytes = 0);
void WriteVEXOp(u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int W = 0,
int extrabytes = 0);
void WriteVEXOp4(u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg,
X64Reg regOp3, int W = 0);
void WriteAVXOp(u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int W = 0,
int extrabytes = 0);
void WriteAVXOp4(u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg,
X64Reg regOp3, int W = 0);
void WriteFMA3Op(u8 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int W = 0);
void WriteFMA4Op(u8 op, X64Reg dest, X64Reg regOp1, X64Reg regOp2, const OpArg& arg, int W = 0);
void WriteBMIOp(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg,
int extrabytes = 0);
void WriteBMI1Op(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg,
int extrabytes = 0);
void WriteBMI2Op(int size, u8 opPrefix, u16 op, X64Reg regOp1, X64Reg regOp2, const OpArg& arg,
int extrabytes = 0);
void WriteMOVBE(int bits, u8 op, X64Reg regOp, const OpArg& arg);
void WriteNormalOp(int bits, NormalOp op, const OpArg& a1, const OpArg& a2);
void ABI_CalculateFrameSize(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size,
size_t* shadowp, size_t* subtractionp, size_t* xmm_offsetp);
protected:
void Write8(u8 value);
void Write16(u16 value);
void Write32(u32 value);
void Write64(u64 value);
public:
XEmitter() = default;
explicit XEmitter(u8* code_ptr, u8* code_end) : code(code_ptr), m_code_end(code_end) {}
virtual ~XEmitter() = default;
void SetCodePtr(u8* ptr, u8* end, bool write_failed = false);
void ReserveCodeSpace(int bytes);
u8* AlignCodeTo(size_t alignment);
u8* AlignCode4();
u8* AlignCode16();
u8* AlignCodePage();
const u8* GetCodePtr() const { return code; }
u8* GetWritableCodePtr() { return code; }
const u8* GetCodeEnd() const { return m_code_end; }
u8* GetWritableCodeEnd() { return m_code_end; }
void LockFlags() { flags_locked = true; }
void UnlockFlags() { flags_locked = false; }
// Should be checked after a block of code has been generated to see if the code has been
// successfully written to memory. Do not call the generated code when this returns true!
bool HasWriteFailed() const { return m_write_failed; }
// 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.
// 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& reg);
void POP(int bits, const OpArg& reg);
void PUSHF();
void POPF();
enum class Jump
{
Short,
Near,
};
// Flow control
void RET();
void RET_FAST();
void UD2();
[[nodiscard]] FixupBranch J(Jump jump = Jump::Short);
void JMP(const u8* addr, Jump jump = Jump::Short);
void JMPptr(const OpArg& arg);
void JMPself(); // infinite loop!
#ifdef CALL
#undef CALL
#endif
void CALL(const void* fnptr);
[[nodiscard]] FixupBranch CALL();
void CALLptr(OpArg arg);
[[nodiscard]] FixupBranch J_CC(CCFlags conditionCode, Jump jump = Jump::Short);
void J_CC(CCFlags conditionCode, const u8* addr);
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, const OpArg& src); // Bottom bit to top bit
void BSR(int bits, X64Reg dest, const OpArg& src); // Top bit to bottom bit
// Cache control
enum class PrefetchLevel : u8
{
NTA = 0, // Non-temporal (data used once and only once)
T0 = 1, // All cache levels
T1 = 2, // Levels 2+ (aliased to T0 on AMD)
T2 = 3, // Levels 3+ (aliased to T0 on AMD)
};
void PREFETCH(PrefetchLevel level, OpArg arg);
void MOVNTI(int bits, const OpArg& dest, X64Reg src);
void MOVNTDQ(const OpArg& arg, X64Reg regOp);
void MOVNTPS(const OpArg& arg, X64Reg regOp);
void MOVNTPD(const OpArg& arg, X64Reg regOp);
// Multiplication / division
void MUL(int bits, const OpArg& src); // UNSIGNED
void IMUL(int bits, const OpArg& src); // SIGNED
void IMUL(int bits, X64Reg regOp, const OpArg& src);
void IMUL(int bits, X64Reg regOp, const OpArg& src, const OpArg& imm);
void DIV(int bits, const OpArg& src);
void IDIV(int bits, const OpArg& src);
// Shift
void ROL(int bits, const OpArg& dest, const OpArg& shift);
void ROR(int bits, const OpArg& dest, const OpArg& shift);
void RCL(int bits, const OpArg& dest, const OpArg& shift);
void RCR(int bits, const OpArg& dest, const OpArg& shift);
void SHL(int bits, const OpArg& dest, const OpArg& shift);
void SHR(int bits, const OpArg& dest, const OpArg& shift);
void SAR(int bits, const OpArg& dest, const OpArg& shift);
// Bit Test
void BT(int bits, const OpArg& dest, const OpArg& index);
void BTS(int bits, const OpArg& dest, const OpArg& index);
void BTR(int bits, const OpArg& dest, const OpArg& index);
void BTC(int bits, const OpArg& dest, const OpArg& index);
// Double-Precision Shift
void SHRD(int bits, const OpArg& dest, const OpArg& src, const OpArg& shift);
void SHLD(int bits, const OpArg& dest, const OpArg& src, const 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, const 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, const 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);
void CMP_or_TEST(int bits, const OpArg& a1, const OpArg& a2);
void MOV_sum(int bits, X64Reg dest, 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 bits, X64Reg dest, const OpArg& src);
void MOVBE(int bits, const OpArg& dest, X64Reg src);
void LoadAndSwap(int size, X64Reg dst, const OpArg& src, bool sign_extend = false,
MovInfo* info = nullptr);
void SwapAndStore(int size, const OpArg& dst, X64Reg src, MovInfo* info = nullptr);
// Available only on AMD >= Phenom or Intel >= Haswell
void LZCNT(int bits, X64Reg dest, const OpArg& src);
// Note: this one is actually part of BMI1
void TZCNT(int bits, X64Reg dest, const OpArg& src);
// WARNING - These two take 11-13 cycles and are VectorPath! (AMD64)
void STMXCSR(const OpArg& memloc);
void LDMXCSR(const OpArg& memloc);
// Prefixes
void LOCK();
void REP();
void REPNE();
void FSOverride();
void GSOverride();
// SSE/SSE2: Floating point arithmetic
void ADDSS(X64Reg regOp, const OpArg& arg);
void ADDSD(X64Reg regOp, const OpArg& arg);
void SUBSS(X64Reg regOp, const OpArg& arg);
void SUBSD(X64Reg regOp, const OpArg& arg);
void MULSS(X64Reg regOp, const OpArg& arg);
void MULSD(X64Reg regOp, const OpArg& arg);
void DIVSS(X64Reg regOp, const OpArg& arg);
void DIVSD(X64Reg regOp, const OpArg& arg);
void MINSS(X64Reg regOp, const OpArg& arg);
void MINSD(X64Reg regOp, const OpArg& arg);
void MAXSS(X64Reg regOp, const OpArg& arg);
void MAXSD(X64Reg regOp, const OpArg& arg);
void SQRTSS(X64Reg regOp, const OpArg& arg);
void SQRTSD(X64Reg regOp, const OpArg& arg);
void RCPSS(X64Reg regOp, const OpArg& arg);
void RSQRTSS(X64Reg regOp, const OpArg& arg);
// SSE/SSE2: Floating point bitwise (yes)
void CMPSS(X64Reg regOp, const OpArg& arg, u8 compare);
void CMPSD(X64Reg regOp, const OpArg& arg, u8 compare);
// SSE/SSE2: Floating point packed arithmetic (x4 for float, x2 for double)
void ADDPS(X64Reg regOp, const OpArg& arg);
void ADDPD(X64Reg regOp, const OpArg& arg);
void SUBPS(X64Reg regOp, const OpArg& arg);
void SUBPD(X64Reg regOp, const OpArg& arg);
void CMPPS(X64Reg regOp, const OpArg& arg, u8 compare);
void CMPPD(X64Reg regOp, const OpArg& arg, u8 compare);
void MULPS(X64Reg regOp, const OpArg& arg);
void MULPD(X64Reg regOp, const OpArg& arg);
void DIVPS(X64Reg regOp, const OpArg& arg);
void DIVPD(X64Reg regOp, const OpArg& arg);
void MINPS(X64Reg regOp, const OpArg& arg);
void MINPD(X64Reg regOp, const OpArg& arg);
void MAXPS(X64Reg regOp, const OpArg& arg);
void MAXPD(X64Reg regOp, const OpArg& arg);
void SQRTPS(X64Reg regOp, const OpArg& arg);
void SQRTPD(X64Reg regOp, const OpArg& arg);
void RCPPS(X64Reg regOp, const OpArg& arg);
void RSQRTPS(X64Reg regOp, const OpArg& arg);
// SSE/SSE2: Floating point packed bitwise (x4 for float, x2 for double)
void ANDPS(X64Reg regOp, const OpArg& arg);
void ANDPD(X64Reg regOp, const OpArg& arg);
void ANDNPS(X64Reg regOp, const OpArg& arg);
void ANDNPD(X64Reg regOp, const OpArg& arg);
void ORPS(X64Reg regOp, const OpArg& arg);
void ORPD(X64Reg regOp, const OpArg& arg);
void XORPS(X64Reg regOp, const OpArg& arg);
void XORPD(X64Reg regOp, const OpArg& arg);
// SSE/SSE2: Shuffle components. These are tricky - see Intel documentation.
void SHUFPS(X64Reg regOp, const OpArg& arg, u8 shuffle);
void SHUFPD(X64Reg regOp, const OpArg& arg, u8 shuffle);
// SSE3
void MOVSLDUP(X64Reg regOp, const OpArg& arg);
void MOVSHDUP(X64Reg regOp, const OpArg& arg);
void MOVDDUP(X64Reg regOp, const OpArg& arg);
// SSE/SSE2: Useful alternative to shuffle in some cases.
void UNPCKLPS(X64Reg dest, const OpArg& src);
void UNPCKHPS(X64Reg dest, const OpArg& src);
void UNPCKLPD(X64Reg dest, const OpArg& src);
void UNPCKHPD(X64Reg dest, const OpArg& src);
// SSE/SSE2: Compares.
void COMISS(X64Reg regOp, const OpArg& arg);
void COMISD(X64Reg regOp, const OpArg& arg);
void UCOMISS(X64Reg regOp, const OpArg& arg);
void UCOMISD(X64Reg regOp, const OpArg& arg);
// SSE/SSE2: Moves. Use the right data type for your data, in most cases.
void MOVAPS(X64Reg regOp, const OpArg& arg);
void MOVAPD(X64Reg regOp, const OpArg& arg);
void MOVAPS(const OpArg& arg, X64Reg regOp);
void MOVAPD(const OpArg& arg, X64Reg regOp);
void MOVUPS(X64Reg regOp, const OpArg& arg);
void MOVUPD(X64Reg regOp, const OpArg& arg);
void MOVUPS(const OpArg& arg, X64Reg regOp);
void MOVUPD(const OpArg& arg, X64Reg regOp);
void MOVDQA(X64Reg regOp, const OpArg& arg);
void MOVDQA(const OpArg& arg, X64Reg regOp);
void MOVDQU(X64Reg regOp, const OpArg& arg);
void MOVDQU(const OpArg& arg, X64Reg regOp);
void MOVSS(X64Reg regOp, const OpArg& arg);
void MOVSD(X64Reg regOp, const OpArg& arg);
void MOVSS(const OpArg& arg, X64Reg regOp);
void MOVSD(const OpArg& arg, X64Reg regOp);
void MOVLPS(X64Reg regOp, const OpArg& arg);
void MOVLPD(X64Reg regOp, const OpArg& arg);
void MOVLPS(const OpArg& arg, X64Reg regOp);
void MOVLPD(const OpArg& arg, X64Reg regOp);
void MOVHPS(X64Reg regOp, const OpArg& arg);
void MOVHPD(X64Reg regOp, const OpArg& arg);
void MOVHPS(const OpArg& arg, X64Reg regOp);
void MOVHPD(const OpArg& arg, X64Reg regOp);
void MOVHLPS(X64Reg regOp1, X64Reg regOp2);
void MOVLHPS(X64Reg regOp1, X64Reg regOp2);
// Be careful when using these overloads for reg <--> xmm moves.
// The one you cast to OpArg with R(reg) is the x86 reg, the other
// one is the xmm reg.
// ie: "MOVD_xmm(eax, R(xmm1))" generates incorrect code (movd xmm0, rcx)
// use "MOVD_xmm(R(eax), xmm1)" instead.
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);
// SSE/SSE2: Generates a mask from the high bits of the components of the packed register in
// question.
void MOVMSKPS(X64Reg dest, const OpArg& arg);
void MOVMSKPD(X64Reg dest, const 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, const OpArg& src);
// SSE/SSE2: Data type conversions.
void CVTPS2PD(X64Reg dest, const OpArg& src);
void CVTPD2PS(X64Reg dest, const OpArg& src);
void CVTSS2SD(X64Reg dest, const OpArg& src);
void CVTSI2SS(X64Reg dest, const OpArg& src);
void CVTSD2SS(X64Reg dest, const OpArg& src);
void CVTSI2SD(X64Reg dest, const OpArg& src);
void CVTDQ2PD(X64Reg regOp, const OpArg& arg);
void CVTPD2DQ(X64Reg regOp, const OpArg& arg);
void CVTDQ2PS(X64Reg regOp, const OpArg& arg);
void CVTPS2DQ(X64Reg regOp, const OpArg& arg);
void CVTTPS2DQ(X64Reg regOp, const OpArg& arg);
void CVTTPD2DQ(X64Reg regOp, const OpArg& arg);
// Destinations are X64 regs (rax, rbx, ...) for these instructions.
void CVTSS2SI(X64Reg xregdest, const OpArg& src);
void CVTSD2SI(X64Reg xregdest, const OpArg& src);
void CVTTSS2SI(X64Reg xregdest, const OpArg& arg);
void CVTTSD2SI(X64Reg xregdest, const OpArg& arg);
// SSE2: Packed integer instructions
void PACKSSDW(X64Reg dest, const OpArg& arg);
void PACKSSWB(X64Reg dest, const OpArg& arg);
void PACKUSDW(X64Reg dest, const OpArg& arg);
void PACKUSWB(X64Reg dest, const 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 PTEST(X64Reg dest, const OpArg& arg);
void PAND(X64Reg dest, const OpArg& arg);
void PANDN(X64Reg dest, const OpArg& arg);
void PXOR(X64Reg dest, const OpArg& arg);
void POR(X64Reg dest, const OpArg& arg);
void PADDB(X64Reg dest, const OpArg& arg);
void PADDW(X64Reg dest, const OpArg& arg);
void PADDD(X64Reg dest, const OpArg& arg);
void PADDQ(X64Reg dest, const OpArg& arg);
void PADDSB(X64Reg dest, const OpArg& arg);
void PADDSW(X64Reg dest, const OpArg& arg);
void PADDUSB(X64Reg dest, const OpArg& arg);
void PADDUSW(X64Reg dest, const OpArg& arg);
void PSUBB(X64Reg dest, const OpArg& arg);
void PSUBW(X64Reg dest, const OpArg& arg);
void PSUBD(X64Reg dest, const OpArg& arg);
void PSUBQ(X64Reg dest, const OpArg& arg);
void PSUBSB(X64Reg dest, const OpArg& arg);
void PSUBSW(X64Reg dest, const OpArg& arg);
void PSUBUSB(X64Reg dest, const OpArg& arg);
void PSUBUSW(X64Reg dest, const OpArg& arg);
void PAVGB(X64Reg dest, const OpArg& arg);
void PAVGW(X64Reg dest, const OpArg& arg);
void PCMPEQB(X64Reg dest, const OpArg& arg);
void PCMPEQW(X64Reg dest, const OpArg& arg);
void PCMPEQD(X64Reg dest, const OpArg& arg);
void PCMPGTB(X64Reg dest, const OpArg& arg);
void PCMPGTW(X64Reg dest, const OpArg& arg);
void PCMPGTD(X64Reg dest, const OpArg& arg);
void PEXTRW(X64Reg dest, const OpArg& arg, u8 subreg);
void PINSRW(X64Reg dest, const OpArg& arg, u8 subreg);
void PINSRD(X64Reg dest, const OpArg& arg, u8 subreg);
void PMADDWD(X64Reg dest, const OpArg& arg);
void PSADBW(X64Reg dest, const OpArg& arg);
void PMAXSW(X64Reg dest, const OpArg& arg);
void PMAXUB(X64Reg dest, const OpArg& arg);
void PMINSW(X64Reg dest, const OpArg& arg);
void PMINUB(X64Reg dest, const OpArg& arg);
void PMOVMSKB(X64Reg dest, const OpArg& arg);
void PSHUFD(X64Reg dest, const OpArg& arg, u8 shuffle);
void PSHUFB(X64Reg dest, const OpArg& arg);
void PSHUFLW(X64Reg dest, const OpArg& arg, u8 shuffle);
void PSHUFHW(X64Reg dest, const OpArg& arg, u8 shuffle);
void PSRLW(X64Reg reg, int shift);
void PSRLD(X64Reg reg, int shift);
void PSRLQ(X64Reg reg, int shift);
void PSRLQ(X64Reg reg, const OpArg& arg);
void PSRLDQ(X64Reg reg, int shift);
void PSLLW(X64Reg reg, int shift);
void PSLLD(X64Reg reg, int shift);
void PSLLQ(X64Reg reg, int shift);
void PSLLDQ(X64Reg reg, int shift);
void PSRAW(X64Reg reg, int shift);
void PSRAD(X64Reg reg, int shift);
// SSE4: data type conversions
void PMOVSXBW(X64Reg dest, const OpArg& arg);
void PMOVSXBD(X64Reg dest, const OpArg& arg);
void PMOVSXBQ(X64Reg dest, const OpArg& arg);
void PMOVSXWD(X64Reg dest, const OpArg& arg);
void PMOVSXWQ(X64Reg dest, const OpArg& arg);
void PMOVSXDQ(X64Reg dest, const OpArg& arg);
void PMOVZXBW(X64Reg dest, const OpArg& arg);
void PMOVZXBD(X64Reg dest, const OpArg& arg);
void PMOVZXBQ(X64Reg dest, const OpArg& arg);
void PMOVZXWD(X64Reg dest, const OpArg& arg);
void PMOVZXWQ(X64Reg dest, const OpArg& arg);
void PMOVZXDQ(X64Reg dest, const OpArg& arg);
// SSE4: blend instructions
void PBLENDVB(X64Reg dest, const OpArg& arg);
void BLENDVPS(X64Reg dest, const OpArg& arg);
void BLENDVPD(X64Reg dest, const OpArg& arg);
void BLENDPS(X64Reg dest, const OpArg& arg, u8 blend);
void BLENDPD(X64Reg dest, const OpArg& arg, u8 blend);
// AVX
void VADDSS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VSUBSS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VMULSS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VDIVSS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VADDPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VSUBPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VMULPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VDIVPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VADDSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VSUBSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VMULSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VDIVSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VADDPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VSUBPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VMULPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VDIVPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VSQRTSD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VCMPPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg, u8 compare);
void VSHUFPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg, u8 shuffle);
void VSHUFPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg, u8 shuffle);
void VUNPCKLPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VUNPCKLPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VUNPCKHPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VBLENDVPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg, X64Reg mask);
void VBLENDPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg, u8 blend);
void VBLENDPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg, u8 blend);
void VANDPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VANDPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VANDNPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VANDNPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VORPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VORPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VXORPS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VXORPD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VPAND(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VPANDN(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VPOR(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VPXOR(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
// FMA3
void VFMADD132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADD231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUB231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMADD231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB132SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB213SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB231SS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB132SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB213SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFNMSUB231SD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADDSUB132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADDSUB213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADDSUB231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADDSUB132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADDSUB213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMADDSUB231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUBADD132PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUBADD213PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUBADD231PS(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUBADD132PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUBADD213PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void VFMSUBADD231PD(X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
#define FMA4(name) \
void name(X64Reg dest, X64Reg regOp1, X64Reg regOp2, const OpArg& arg); \
void name(X64Reg dest, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
FMA4(VFMADDSUBPS)
FMA4(VFMADDSUBPD)
FMA4(VFMSUBADDPS)
FMA4(VFMSUBADDPD)
FMA4(VFMADDPS)
FMA4(VFMADDPD)
FMA4(VFMADDSS)
FMA4(VFMADDSD)
FMA4(VFMSUBPS)
FMA4(VFMSUBPD)
FMA4(VFMSUBSS)
FMA4(VFMSUBSD)
FMA4(VFNMADDPS)
FMA4(VFNMADDPD)
FMA4(VFNMADDSS)
FMA4(VFNMADDSD)
FMA4(VFNMSUBPS)
FMA4(VFNMSUBPD)
FMA4(VFNMSUBSS)
FMA4(VFNMSUBSD)
#undef FMA4
// VEX GPR instructions
void SARX(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
void SHLX(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
void SHRX(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
void RORX(int bits, X64Reg regOp, const OpArg& arg, u8 rotate);
void PEXT(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void PDEP(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void MULX(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void BZHI(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
void BLSR(int bits, X64Reg regOp, const OpArg& arg);
void BLSMSK(int bits, X64Reg regOp, const OpArg& arg);
void BLSI(int bits, X64Reg regOp, const OpArg& arg);
void BEXTR(int bits, X64Reg regOp1, const OpArg& arg, X64Reg regOp2);
void ANDN(int bits, X64Reg regOp1, X64Reg regOp2, const OpArg& arg);
void RDTSC();
// Utility functions
// The difference between this and CALL is that this aligns the stack
// where appropriate.
template <typename FunctionPointer>
void ABI_CallFunction(FunctionPointer func)
{
static_assert(std::is_pointer<FunctionPointer>() &&
std::is_function<std::remove_pointer_t<FunctionPointer>>(),
"Supplied type must be a function pointer.");
const void* ptr = reinterpret_cast<const void*>(func);
const u64 address = reinterpret_cast<u64>(ptr);
const u64 distance = address - (reinterpret_cast<u64>(code) + 5);
if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL)
{
// Far call
MOV(64, R(RAX), Imm64(address));
CALLptr(R(RAX));
}
else
{
CALL(ptr);
}
}
template <typename FunctionPointer>
void ABI_CallFunctionC16(FunctionPointer func, u16 param1)
{
MOV(32, R(ABI_PARAM1), Imm32(param1));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionCC16(FunctionPointer func, u32 param1, u16 param2)
{
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32(param2));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionC(FunctionPointer func, u32 param1)
{
MOV(32, R(ABI_PARAM1), Imm32(param1));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionCC(FunctionPointer func, u32 param1, u32 param2)
{
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32(param2));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionCP(FunctionPointer func, u32 param1, const void* param2)
{
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(64, R(ABI_PARAM2), Imm64(reinterpret_cast<u64>(param2)));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionCCC(FunctionPointer func, u32 param1, u32 param2, u32 param3)
{
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32(param2));
MOV(32, R(ABI_PARAM3), Imm32(param3));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionCCP(FunctionPointer func, u32 param1, u32 param2, const void* param3)
{
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32(param2));
MOV(64, R(ABI_PARAM3), Imm64(reinterpret_cast<u64>(param3)));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionCCCP(FunctionPointer func, u32 param1, u32 param2, u32 param3,
const void* param4)
{
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32(param2));
MOV(32, R(ABI_PARAM3), Imm32(param3));
MOV(64, R(ABI_PARAM4), Imm64(reinterpret_cast<u64>(param4)));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionP(FunctionPointer func, const void* param1)
{
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(param1)));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionPP(FunctionPointer func, const void* param1, const void* param2)
{
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(param1)));
MOV(64, R(ABI_PARAM2), Imm64(reinterpret_cast<u64>(param2)));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionPC(FunctionPointer func, const void* param1, u32 param2)
{
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(param1)));
MOV(32, R(ABI_PARAM2), Imm32(param2));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionPPC(FunctionPointer func, const void* param1, const void* param2, u32 param3)
{
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(param1)));
MOV(64, R(ABI_PARAM2), Imm64(reinterpret_cast<u64>(param2)));
MOV(32, R(ABI_PARAM3), Imm32(param3));
ABI_CallFunction(func);
}
// Pass a register as a parameter.
template <typename FunctionPointer>
void ABI_CallFunctionR(FunctionPointer func, X64Reg reg1)
{
if (reg1 != ABI_PARAM1)
MOV(32, R(ABI_PARAM1), R(reg1));
ABI_CallFunction(func);
}
// Pass a pointer and register as a parameter.
template <typename FunctionPointer>
void ABI_CallFunctionPR(FunctionPointer func, const void* ptr, X64Reg reg1)
{
if (reg1 != ABI_PARAM2)
MOV(64, R(ABI_PARAM2), R(reg1));
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(ptr)));
ABI_CallFunction(func);
}
// Pass two registers as parameters.
template <typename FunctionPointer>
void ABI_CallFunctionRR(FunctionPointer func, X64Reg reg1, X64Reg reg2)
{
MOVTwo(64, ABI_PARAM1, reg1, 0, ABI_PARAM2, reg2);
ABI_CallFunction(func);
}
// Pass a pointer and two registers as parameters.
template <typename FunctionPointer>
void ABI_CallFunctionPRR(FunctionPointer func, const void* ptr, X64Reg reg1, X64Reg reg2)
{
MOVTwo(64, ABI_PARAM2, reg1, 0, ABI_PARAM3, reg2);
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(ptr)));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionAC(int bits, FunctionPointer func, const Gen::OpArg& arg1, u32 param2)
{
if (!arg1.IsSimpleReg(ABI_PARAM1))
MOV(bits, R(ABI_PARAM1), arg1);
MOV(32, R(ABI_PARAM2), Imm32(param2));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionPAC(int bits, FunctionPointer func, const void* ptr1, const Gen::OpArg& arg2,
u32 param3)
{
if (!arg2.IsSimpleReg(ABI_PARAM2))
MOV(bits, R(ABI_PARAM2), arg2);
MOV(32, R(ABI_PARAM3), Imm32(param3));
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(ptr1)));
ABI_CallFunction(func);
}
template <typename FunctionPointer>
void ABI_CallFunctionA(int bits, FunctionPointer func, const Gen::OpArg& arg1)
{
if (!arg1.IsSimpleReg(ABI_PARAM1))
MOV(bits, R(ABI_PARAM1), arg1);
ABI_CallFunction(func);
}
// Helper method for ABI functions related to calling functions. May be used by itself as well.
void MOVTwo(int bits, X64Reg dst1, X64Reg src1, s32 offset, X64Reg dst2, X64Reg src2);
// Saves/restores the registers and adjusts the stack to be aligned as
// required by the ABI, where the previous alignment was as specified.
// Push returns the size of the shadow space, i.e. the offset of the frame.
size_t ABI_PushRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment,
size_t needed_frame_size = 0);
void ABI_PopRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment,
size_t needed_frame_size = 0);
// Utility to generate a call to a std::function object.
//
// Unfortunately, calling operator() directly is undefined behavior in C++
// (this method might be a thunk in the case of multi-inheritance) so we
// have to go through a trampoline function.
template <typename T, typename... Args>
static T CallLambdaTrampoline(const std::function<T(Args...)>* f, Args... args)
{
return (*f)(args...);
}
template <typename T, typename... Args>
void ABI_CallLambdaPC(const std::function<T(Args...)>* f, void* p1, u32 p2)
{
auto trampoline = &XEmitter::CallLambdaTrampoline<T, Args...>;
ABI_CallFunctionPPC(trampoline, reinterpret_cast<const void*>(f), p1, p2);
}
}; // class XEmitter
class X64CodeBlock : public Common::CodeBlock<XEmitter>
{
private:
void PoisonMemory() override
{
// x86/64: 0xCC = breakpoint
memset(region, 0xCC, region_size);
}
};
} // namespace Gen