Files
ARMSX2/pcsx2/x86/ix86-32/iR5900Templates.cpp

515 lines
14 KiB
C++

// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "Common.h"
#include "Memory.h"
#include "R5900OpcodeTables.h"
#include "VU.h"
#include "VUmicro.h"
#include "vtlb.h"
#include "x86/iCOP0.h"
#include "x86/iFPU.h"
#include "x86/iMMI.h"
#include "x86/iR5900.h"
using namespace x86Emitter;
////////////////////
// Code Templates //
////////////////////
void _eeOnWriteReg(int reg, int signext)
{
GPR_DEL_CONST(reg);
}
void _deleteEEreg(int reg, int flush)
{
if (!reg)
return;
if (flush && GPR_IS_CONST1(reg))
{
_flushConstReg(reg);
}
GPR_DEL_CONST(reg);
_deleteGPRtoXMMreg(reg, flush ? DELETE_REG_FREE : DELETE_REG_FLUSH_AND_FREE);
_deleteGPRtoX86reg(reg, flush ? DELETE_REG_FREE : DELETE_REG_FLUSH_AND_FREE);
}
void _deleteEEreg128(int reg)
{
if (!reg)
return;
GPR_DEL_CONST(reg);
_deleteGPRtoXMMreg(reg, DELETE_REG_FREE_NO_WRITEBACK);
_deleteGPRtoX86reg(reg, DELETE_REG_FREE_NO_WRITEBACK);
}
void _flushEEreg(int reg, bool clear)
{
if (!reg)
return;
if (GPR_IS_DIRTY_CONST(reg))
_flushConstReg(reg);
if (clear)
GPR_DEL_CONST(reg);
_deleteGPRtoXMMreg(reg, clear ? DELETE_REG_FLUSH_AND_FREE : DELETE_REG_FLUSH);
_deleteGPRtoX86reg(reg, clear ? DELETE_REG_FLUSH_AND_FREE : DELETE_REG_FLUSH);
}
int _eeTryRenameReg(int to, int from, int fromx86, int other, int xmminfo)
{
// can't rename when in form Rd = Rs op Rt and Rd == Rs or Rd == Rt
if ((xmminfo & XMMINFO_NORENAME) || fromx86 < 0 || to == from || to == other || !EEINST_RENAMETEST(from))
return -1;
RALOG("Renaming %s to %s\n", R3000A::disRNameGPR[from], R3000A::disRNameGPR[to]);
// flush back when it's been modified
if (x86regs[fromx86].mode & MODE_WRITE && EEINST_LIVETEST(from))
_writebackX86Reg(fromx86);
// remove all references to renamed-to register
_deleteGPRtoX86reg(to, DELETE_REG_FREE_NO_WRITEBACK);
_deleteGPRtoXMMreg(to, DELETE_REG_FLUSH_AND_FREE);
GPR_DEL_CONST(to);
// and do the actual rename, new register has been modified.
x86regs[fromx86].reg = to;
x86regs[fromx86].mode |= MODE_READ | MODE_WRITE;
return fromx86;
}
static bool FitsInImmediate(int reg, int fprinfo)
{
if (fprinfo & XMMINFO_64BITOP)
return (s32)g_cpuConstRegs[reg].SD[0] == g_cpuConstRegs[reg].SD[0];
else
return true; // all 32bit ops fit
}
void eeRecompileCodeRC0(R5900FNPTR constcode, R5900FNPTR_INFO constscode, R5900FNPTR_INFO consttcode, R5900FNPTR_INFO noconstcode, int xmminfo)
{
if (!_Rd_ && (xmminfo & XMMINFO_WRITED))
return;
if (GPR_IS_CONST2(_Rs_, _Rt_))
{
if (_Rd_ && (xmminfo & XMMINFO_WRITED))
{
_deleteGPRtoX86reg(_Rd_, DELETE_REG_FREE_NO_WRITEBACK);
_deleteGPRtoXMMreg(_Rd_, DELETE_REG_FLUSH_AND_FREE);
GPR_SET_CONST(_Rd_);
}
constcode();
return;
}
// this function should not be used for lo/hi.
pxAssert(!(xmminfo & (XMMINFO_READLO | XMMINFO_READHI | XMMINFO_WRITELO | XMMINFO_WRITEHI)));
// we have to put these up here, because the register allocator below will wipe out const flags
// for the destination register when/if it switches it to write mode.
const bool s_is_const = GPR_IS_CONST1(_Rs_);
const bool t_is_const = GPR_IS_CONST1(_Rt_);
const bool d_is_const = GPR_IS_CONST1(_Rd_);
const bool s_is_used = EEINST_USEDTEST(_Rs_);
const bool t_is_used = EEINST_USEDTEST(_Rt_);
const bool s_in_xmm = _hasXMMreg(XMMTYPE_GPRREG, _Rs_);
const bool t_in_xmm = _hasXMMreg(XMMTYPE_GPRREG, _Rt_);
// regular x86
if ((xmminfo & XMMINFO_READS) && !s_is_const)
_addNeededGPRtoX86reg(_Rs_);
if ((xmminfo & XMMINFO_READT) && !t_is_const)
_addNeededGPRtoX86reg(_Rt_);
if ((xmminfo & XMMINFO_READD) && !d_is_const)
_addNeededGPRtoX86reg(_Rd_);
// when it doesn't fit in an immediate, we'll flush it to a reg early to save code
u32 info = 0;
int regs = -1, regt = -1;
if (xmminfo & XMMINFO_READS)
{
regs = _checkX86reg(X86TYPE_GPR, _Rs_, MODE_READ);
if (regs < 0 && (!s_is_const || !FitsInImmediate(_Rs_, xmminfo)) && (s_is_used || s_in_xmm || ((xmminfo & XMMINFO_WRITED) && _Rd_ == _Rs_) || (xmminfo & XMMINFO_FORCEREGS)))
{
regs = _allocX86reg(X86TYPE_GPR, _Rs_, MODE_READ);
}
if (regs >= 0)
info |= PROCESS_EE_SET_S(regs);
}
if (xmminfo & XMMINFO_READT)
{
regt = _checkX86reg(X86TYPE_GPR, _Rt_, MODE_READ);
if (regt < 0 && (!t_is_const || !FitsInImmediate(_Rt_, xmminfo)) && (t_is_used || t_in_xmm || ((xmminfo & XMMINFO_WRITED) && _Rd_ == _Rt_) || (xmminfo & XMMINFO_FORCEREGT)))
{
regt = _allocX86reg(X86TYPE_GPR, _Rt_, MODE_READ);
}
if (regt >= 0)
info |= PROCESS_EE_SET_T(regt);
}
if (xmminfo & (XMMINFO_WRITED | XMMINFO_READD))
{
// _eeTryRenameReg() sets READ | WRITE already, so this is only needed when allocating.
const int moded = ((xmminfo & XMMINFO_WRITED) ? MODE_WRITE : 0) | ((xmminfo & XMMINFO_READD) ? MODE_READ : 0);
// If S is no longer live, swap D for S. Saves the move.
int regd = (_Rd_ && xmminfo & XMMINFO_WRITED) ? _eeTryRenameReg(_Rd_, (xmminfo & XMMINFO_READS) ? _Rs_ : 0, regs, (xmminfo & XMMINFO_READT) ? _Rt_ : 0, xmminfo) : 0;
if (regd < 0)
regd = _allocX86reg(X86TYPE_GPR, _Rd_, moded);
pxAssert(regd >= 0);
info |= PROCESS_EE_SET_D(regd);
}
if (xmminfo & XMMINFO_WRITED)
GPR_DEL_CONST(_Rd_);
_validateRegs();
if (s_is_const && regs < 0)
{
constscode(info /*| PROCESS_CONSTS*/);
return;
}
if (t_is_const && regt < 0)
{
consttcode(info /*| PROCESS_CONSTT*/);
return;
}
noconstcode(info);
}
void eeRecompileCodeRC1(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode, int xmminfo)
{
pxAssert((xmminfo & (XMMINFO_READS | XMMINFO_WRITET)) == (XMMINFO_READS | XMMINFO_WRITET));
if (!_Rt_)
return;
if (GPR_IS_CONST1(_Rs_))
{
_deleteGPRtoXMMreg(_Rt_, DELETE_REG_FLUSH_AND_FREE);
_deleteGPRtoX86reg(_Rt_, DELETE_REG_FREE_NO_WRITEBACK);
GPR_SET_CONST(_Rt_);
constcode();
return;
}
const bool s_is_used = EEINST_USEDTEST(_Rs_);
const bool s_in_xmm = _hasXMMreg(XMMTYPE_GPRREG, _Rs_);
u32 info = 0;
int regs = _checkX86reg(X86TYPE_GPR, _Rs_, MODE_READ);
if (regs < 0 && (s_is_used || s_in_xmm || _Rt_ == _Rs_ || (xmminfo & XMMINFO_FORCEREGS)))
regs = _allocX86reg(X86TYPE_GPR, _Rs_, MODE_READ);
if (regs >= 0)
info |= PROCESS_EE_SET_S(regs);
// If S is no longer live, swap D for S. Saves the move.
int regt = _eeTryRenameReg(_Rt_, _Rs_, regs, 0, xmminfo);
if (regt < 0)
regt = _allocX86reg(X86TYPE_GPR, _Rt_, MODE_WRITE);
info |= PROCESS_EE_SET_T(regt);
_validateRegs();
GPR_DEL_CONST(_Rt_);
noconstcode(info);
}
// rd = rt op sa
void eeRecompileCodeRC2(R5900FNPTR constcode, R5900FNPTR_INFO noconstcode, int xmminfo)
{
pxAssert((xmminfo & (XMMINFO_READT | XMMINFO_WRITED)) == (XMMINFO_READT | XMMINFO_WRITED));
if (!_Rd_)
return;
if (GPR_IS_CONST1(_Rt_))
{
_deleteGPRtoXMMreg(_Rd_, DELETE_REG_FLUSH_AND_FREE);
_deleteGPRtoX86reg(_Rd_, DELETE_REG_FREE_NO_WRITEBACK);
GPR_SET_CONST(_Rd_);
constcode();
return;
}
const bool t_is_used = EEINST_USEDTEST(_Rt_);
const bool t_in_xmm = _hasXMMreg(XMMTYPE_GPRREG, _Rt_);
u32 info = 0;
int regt = _checkX86reg(X86TYPE_GPR, _Rt_, MODE_READ);
if (regt < 0 && (t_is_used || t_in_xmm || (_Rd_ == _Rt_) || (xmminfo & XMMINFO_FORCEREGT)))
regt = _allocX86reg(X86TYPE_GPR, _Rt_, MODE_READ);
if (regt >= 0)
info |= PROCESS_EE_SET_T(regt);
// If S is no longer live, swap D for T. Saves the move.
int regd = _eeTryRenameReg(_Rd_, _Rt_, regt, 0, xmminfo);
if (regd < 0)
regd = _allocX86reg(X86TYPE_GPR, _Rd_, MODE_WRITE);
info |= PROCESS_EE_SET_D(regd);
_validateRegs();
GPR_DEL_CONST(_Rd_);
noconstcode(info);
}
// EE XMM allocation code
int eeRecompileCodeXMM(int xmminfo)
{
int info = PROCESS_EE_XMM;
// add needed
if (xmminfo & (XMMINFO_READLO | XMMINFO_WRITELO))
_addNeededGPRtoXMMreg(XMMGPR_LO);
if (xmminfo & (XMMINFO_READHI | XMMINFO_WRITEHI))
_addNeededGPRtoXMMreg(XMMGPR_HI);
if (xmminfo & XMMINFO_READS)
_addNeededGPRtoXMMreg(_Rs_);
if (xmminfo & XMMINFO_READT)
_addNeededGPRtoXMMreg(_Rt_);
if (xmminfo & XMMINFO_WRITED)
_addNeededGPRtoXMMreg(_Rd_);
// TODO: we could do memory operands here if not live. but the MMI implementations aren't hooked up to that at the moment.
if (xmminfo & XMMINFO_READS)
{
const int reg = _allocGPRtoXMMreg(_Rs_, MODE_READ);
info |= PROCESS_EE_SET_S(reg);
}
if (xmminfo & XMMINFO_READT)
{
const int reg = _allocGPRtoXMMreg(_Rt_, MODE_READ);
info |= PROCESS_EE_SET_T(reg);
}
if (xmminfo & XMMINFO_WRITED)
{
int readd = MODE_WRITE | ((xmminfo & XMMINFO_READD) ? MODE_READ : 0);
int regd = _checkXMMreg(XMMTYPE_GPRREG, _Rd_, readd);
if (regd < 0)
{
if (!(xmminfo & XMMINFO_READD) && (xmminfo & XMMINFO_READT) && EEINST_RENAMETEST(_Rt_))
{
_deleteEEreg128(_Rd_);
_reallocateXMMreg(EEREC_T, XMMTYPE_GPRREG, _Rd_, readd, EEINST_LIVETEST(_Rt_));
regd = EEREC_T;
}
else if (!(xmminfo & XMMINFO_READD) && (xmminfo & XMMINFO_READS) && EEINST_RENAMETEST(_Rs_))
{
_deleteEEreg128(_Rd_);
_reallocateXMMreg(EEREC_S, XMMTYPE_GPRREG, _Rd_, readd, EEINST_LIVETEST(_Rs_));
regd = EEREC_S;
}
else
{
regd = _allocGPRtoXMMreg(_Rd_, readd);
}
}
info |= PROCESS_EE_SET_D(regd);
}
if (xmminfo & (XMMINFO_READLO | XMMINFO_WRITELO))
{
info |= PROCESS_EE_SET_LO(_allocGPRtoXMMreg(XMMGPR_LO, ((xmminfo & XMMINFO_READLO) ? MODE_READ : 0) | ((xmminfo & XMMINFO_WRITELO) ? MODE_WRITE : 0)));
}
if (xmminfo & (XMMINFO_READHI | XMMINFO_WRITEHI))
{
info |= PROCESS_EE_SET_HI(_allocGPRtoXMMreg(XMMGPR_HI, ((xmminfo & XMMINFO_READHI) ? MODE_READ : 0) | ((xmminfo & XMMINFO_WRITEHI) ? MODE_WRITE : 0)));
}
if (xmminfo & XMMINFO_WRITED)
GPR_DEL_CONST(_Rd_);
_validateRegs();
return info;
}
// EE COP1(FPU) XMM allocation code
#define _Ft_ _Rt_
#define _Fs_ _Rd_
#define _Fd_ _Sa_
// rd = rs op rt
void eeFPURecompileCode(R5900FNPTR_INFO xmmcode, R5900FNPTR fpucode, int xmminfo)
{
int mmregs = -1, mmregt = -1, mmregd = -1, mmregacc = -1;
int info = PROCESS_EE_XMM;
if (xmminfo & XMMINFO_READS)
_addNeededFPtoXMMreg(_Fs_);
if (xmminfo & XMMINFO_READT)
_addNeededFPtoXMMreg(_Ft_);
if (xmminfo & (XMMINFO_WRITED | XMMINFO_READD))
_addNeededFPtoXMMreg(_Fd_);
if (xmminfo & (XMMINFO_WRITEACC | XMMINFO_READACC))
_addNeededFPACCtoXMMreg();
if (xmminfo & XMMINFO_READT)
{
if (g_pCurInstInfo->fpuregs[_Ft_] & EEINST_LASTUSE)
mmregt = _checkXMMreg(XMMTYPE_FPREG, _Ft_, MODE_READ);
else
mmregt = _allocFPtoXMMreg(_Ft_, MODE_READ);
}
if (xmminfo & XMMINFO_READS)
{
if ((!(xmminfo & XMMINFO_READT) || (mmregt >= 0)) && (g_pCurInstInfo->fpuregs[_Fs_] & EEINST_LASTUSE))
{
mmregs = _checkXMMreg(XMMTYPE_FPREG, _Fs_, MODE_READ);
}
else
{
mmregs = _allocFPtoXMMreg(_Fs_, MODE_READ);
// if we just allocated S and Fs == Ft, share it
if ((xmminfo & XMMINFO_READT) && _Fs_ == _Ft_)
mmregt = mmregs;
}
}
if (xmminfo & XMMINFO_READD)
{
pxAssert(xmminfo & XMMINFO_WRITED);
mmregd = _allocFPtoXMMreg(_Fd_, MODE_READ);
}
if (xmminfo & XMMINFO_READACC)
{
if (!(xmminfo & XMMINFO_WRITEACC) && (g_pCurInstInfo->fpuregs[XMMFPU_ACC] & EEINST_LASTUSE))
mmregacc = _checkXMMreg(XMMTYPE_FPACC, 0, MODE_READ);
else
mmregacc = _allocFPACCtoXMMreg(MODE_READ);
}
if (xmminfo & XMMINFO_WRITEACC)
{
// check for last used, if so don't alloc a new XMM reg
int readacc = MODE_WRITE | ((xmminfo & XMMINFO_READACC) ? MODE_READ : 0);
mmregacc = _checkXMMreg(XMMTYPE_FPACC, 0, readacc);
if (mmregacc < 0)
{
if ((xmminfo & XMMINFO_READT) && mmregt >= 0 && FPUINST_RENAMETEST(_Ft_))
{
if (EE_WRITE_DEAD_VALUES && xmmregs[mmregt].mode & MODE_WRITE)
_writebackXMMreg(mmregt);
xmmregs[mmregt].reg = 0;
xmmregs[mmregt].mode = readacc;
xmmregs[mmregt].type = XMMTYPE_FPACC;
mmregacc = mmregt;
}
else if ((xmminfo & XMMINFO_READS) && mmregs >= 0 && FPUINST_RENAMETEST(_Fs_))
{
if (EE_WRITE_DEAD_VALUES && xmmregs[mmregs].mode & MODE_WRITE)
_writebackXMMreg(mmregs);
xmmregs[mmregs].reg = 0;
xmmregs[mmregs].mode = readacc;
xmmregs[mmregs].type = XMMTYPE_FPACC;
mmregacc = mmregs;
}
else
mmregacc = _allocFPACCtoXMMreg(readacc);
}
xmmregs[mmregacc].mode |= MODE_WRITE;
}
else if (xmminfo & XMMINFO_WRITED)
{
// check for last used, if so don't alloc a new XMM reg
int readd = MODE_WRITE | ((xmminfo & XMMINFO_READD) ? MODE_READ : 0);
if (xmminfo & XMMINFO_READD)
mmregd = _allocFPtoXMMreg(_Fd_, readd);
else
mmregd = _checkXMMreg(XMMTYPE_FPREG, _Fd_, readd);
if (mmregd < 0)
{
if ((xmminfo & XMMINFO_READT) && mmregt >= 0 && FPUINST_RENAMETEST(_Ft_))
{
if (EE_WRITE_DEAD_VALUES && xmmregs[mmregt].mode & MODE_WRITE)
_writebackXMMreg(mmregt);
xmmregs[mmregt].reg = _Fd_;
xmmregs[mmregt].mode = readd;
mmregd = mmregt;
}
else if ((xmminfo & XMMINFO_READS) && mmregs >= 0 && FPUINST_RENAMETEST(_Fs_))
{
if (EE_WRITE_DEAD_VALUES && xmmregs[mmregs].mode & MODE_WRITE)
_writebackXMMreg(mmregs);
xmmregs[mmregs].inuse = 1;
xmmregs[mmregs].reg = _Fd_;
xmmregs[mmregs].mode = readd;
mmregd = mmregs;
}
else if ((xmminfo & XMMINFO_READACC) && mmregacc >= 0 && FPUINST_RENAMETEST(XMMFPU_ACC))
{
if (EE_WRITE_DEAD_VALUES && xmmregs[mmregacc].mode & MODE_WRITE)
_writebackXMMreg(mmregacc);
xmmregs[mmregacc].reg = _Fd_;
xmmregs[mmregacc].mode = readd;
xmmregs[mmregacc].type = XMMTYPE_FPREG;
mmregd = mmregacc;
}
else
mmregd = _allocFPtoXMMreg(_Fd_, readd);
}
}
pxAssert(mmregs >= 0 || mmregt >= 0 || mmregd >= 0 || mmregacc >= 0);
if (xmminfo & XMMINFO_WRITED)
{
pxAssert(mmregd >= 0);
info |= PROCESS_EE_SET_D(mmregd);
}
if (xmminfo & (XMMINFO_WRITEACC | XMMINFO_READACC))
{
if (mmregacc >= 0)
info |= PROCESS_EE_SET_ACC(mmregacc) | PROCESS_EE_ACC;
else
pxAssert(!(xmminfo & XMMINFO_WRITEACC));
}
if (xmminfo & XMMINFO_READS)
{
if (mmregs >= 0)
info |= PROCESS_EE_SET_S(mmregs);
}
if (xmminfo & XMMINFO_READT)
{
if (mmregt >= 0)
info |= PROCESS_EE_SET_T(mmregt);
}
// at least one must be in xmm
if ((xmminfo & (XMMINFO_READS | XMMINFO_READT)) == (XMMINFO_READS | XMMINFO_READT))
{
pxAssert(mmregs >= 0 || mmregt >= 0);
}
xmmcode(info);
}