Separate the two regcaches before doing major surgery to FPURegCache.

This commit is contained in:
Henrik Rydgard 2013-01-25 22:11:51 +01:00
parent be12d2efe3
commit ad5e2b58c6
2 changed files with 267 additions and 215 deletions

View file

@ -38,30 +38,22 @@ static const int allocationOrder[] =
#endif
};
RegCache::RegCache() : emit(0), mips(0) {
memset(locks, 0, sizeof(locks));
memset(xlocks, 0, sizeof(xlocks));
memset(saved_locks, 0, sizeof(saved_locks));
memset(saved_xlocks, 0, sizeof(saved_xlocks));
GPRRegCache::GPRRegCache() : emit(0), mips(0) {
memset(regs, 0, sizeof(regs));
memset(xregs, 0, sizeof(xregs));
memset(saved_regs, 0, sizeof(saved_regs));
memset(saved_xregs, 0, sizeof(saved_xregs));
}
void RegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats)
{
void GPRRegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) {
this->mips = mips;
for (int i = 0; i < NUMXREGS; i++)
{
for (int i = 0; i < NUM_X_REGS; i++) {
xregs[i].free = true;
xregs[i].dirty = false;
xlocks[i] = false;
xregs[i].allocLocked = false;
}
for (int i = 0; i < 32; i++)
{
for (int i = 0; i < NUM_MIPS_GPRS; i++) {
regs[i].location = GetDefaultLocation(i);
regs[i].away = false;
regs[i].locked = false;
}
// todo: sort to find the most popular regs
@ -81,66 +73,58 @@ void RegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats)
//But only preload IF written OR reads >= 3
}
// these are MIPS reg indices
void RegCache::Lock(int p1, int p2, int p3, int p4)
{
locks[p1] = true;
if (p2 != 0xFF) locks[p2] = true;
if (p3 != 0xFF) locks[p3] = true;
if (p4 != 0xFF) locks[p4] = true;
void GPRRegCache::Lock(int p1, int p2, int p3, int p4) {
regs[p1].locked = true;
if (p2 != 0xFF) regs[p2].locked = true;
if (p3 != 0xFF) regs[p3].locked = true;
if (p4 != 0xFF) regs[p4].locked = true;
}
// these are x64 reg indices
void RegCache::LockX(int x1, int x2, int x3, int x4)
{
if (xlocks[x1]) {
void GPRRegCache::LockX(int x1, int x2, int x3, int x4) {
if (xregs[x1].allocLocked) {
PanicAlert("RegCache: x %i already locked!", x1);
}
xlocks[x1] = true;
if (x2 != 0xFF) xlocks[x2] = true;
if (x3 != 0xFF) xlocks[x3] = true;
if (x4 != 0xFF) xlocks[x4] = true;
xregs[x1].allocLocked = true;
if (x2 != 0xFF) xregs[x2].allocLocked = true;
if (x3 != 0xFF) xregs[x3].allocLocked = true;
if (x4 != 0xFF) xregs[x4].allocLocked = true;
}
bool RegCache::IsFreeX(int xreg) const
{
return xregs[xreg].free && !xlocks[xreg];
}
void RegCache::UnlockAll()
{
void GPRRegCache::UnlockAll() {
for (int i = 0; i < 32; i++)
locks[i] = false;
regs[i].locked = false;
}
void RegCache::UnlockAllX()
{
for (int i = 0; i < NUMXREGS; i++)
xlocks[i] = false;
void GPRRegCache::UnlockAllX() {
for (int i = 0; i < NUM_X_REGS; i++)
xregs[i].allocLocked = false;
}
X64Reg RegCache::GetFreeXReg()
X64Reg GPRRegCache::GetFreeXReg()
{
int aCount;
const int *aOrder = GetAllocationOrder(aCount);
for (int i = 0; i < aCount; i++)
{
X64Reg xr = (X64Reg)aOrder[i];
if (!xlocks[xr] && xregs[xr].free)
if (!xregs[xr].allocLocked && xregs[xr].free)
{
return (X64Reg)xr;
}
}
//Okay, not found :( Force grab one
//TODO - add a pass to grab xregs whose ppcreg is not used in the next 3 instructions
//TODO - add a pass to grab xregs whose mipsreg is not used in the next 3 instructions
for (int i = 0; i < aCount; i++)
{
X64Reg xr = (X64Reg)aOrder[i];
if (xlocks[xr])
if (xregs[xr].allocLocked)
continue;
int preg = xregs[xr].mipsReg;
if (!locks[preg])
if (!regs[preg].locked)
{
StoreFromRegister(preg);
return xr;
@ -151,39 +135,20 @@ X64Reg RegCache::GetFreeXReg()
return (X64Reg) -1;
}
void RegCache::SaveState()
void GPRRegCache::FlushR(X64Reg reg)
{
memcpy(saved_locks, locks, sizeof(locks));
memcpy(saved_xlocks, xlocks, sizeof(xlocks));
memcpy(saved_regs, regs, sizeof(regs));
memcpy(saved_xregs, xregs, sizeof(xregs));
}
void RegCache::LoadState()
{
memcpy(xlocks, saved_xlocks, sizeof(xlocks));
memcpy(locks, saved_locks, sizeof(locks));
memcpy(regs, saved_regs, sizeof(regs));
memcpy(xregs, saved_xregs, sizeof(xregs));
}
void RegCache::FlushR(X64Reg reg)
{
if (reg >= NUMXREGS)
if (reg >= NUM_X_REGS)
PanicAlert("Flushing non existent reg");
if (!xregs[reg].free)
{
StoreFromRegister(xregs[reg].mipsReg);
}
}
int RegCache::SanityCheck() const
{
int GPRRegCache::SanityCheck() const {
for (int i = 0; i < 32; i++) {
if (regs[i].away) {
if (regs[i].location.IsSimpleReg()) {
Gen::X64Reg simple = regs[i].location.GetSimpleReg();
if (xlocks[simple])
if (xregs[simple].allocLocked)
return 1;
if (xregs[simple].mipsReg != i)
return 2;
@ -195,10 +160,8 @@ int RegCache::SanityCheck() const
return 0;
}
void RegCache::DiscardRegContentsIfCached(int preg)
{
if (regs[preg].away && regs[preg].location.IsSimpleReg())
{
void GPRRegCache::DiscardRegContentsIfCached(int preg) {
if (regs[preg].away && regs[preg].location.IsSimpleReg()) {
X64Reg xr = regs[preg].location.GetSimpleReg();
xregs[xr].free = true;
xregs[xr].dirty = false;
@ -209,8 +172,7 @@ void RegCache::DiscardRegContentsIfCached(int preg)
}
void GPRRegCache::SetImmediate32(int preg, u32 immValue)
{
void GPRRegCache::SetImmediate32(int preg, u32 immValue) {
// ZERO is always zero. Let's just make sure.
if (preg == 0)
immValue = 0;
@ -220,16 +182,14 @@ void GPRRegCache::SetImmediate32(int preg, u32 immValue)
regs[preg].location = Imm32(immValue);
}
bool GPRRegCache::IsImmediate(int preg) const
{
bool GPRRegCache::IsImmediate(int preg) const {
// Always say yes for ZERO, even if it's in a temp reg.
if (preg == 0)
return true;
return regs[preg].location.IsImm();
}
u32 GPRRegCache::GetImmediate32(int preg) const
{
u32 GPRRegCache::GetImmediate32(int preg) const {
_dbg_assert_msg_(JIT, IsImmediate(preg), "Reg %d must be an immediate.", preg);
// Always 0 for ZERO.
if (preg == 0)
@ -237,50 +197,19 @@ u32 GPRRegCache::GetImmediate32(int preg) const
return regs[preg].location.GetImmValue();
}
void GPRRegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats)
{
RegCache::Start(mips, stats);
}
void FPURegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats)
{
RegCache::Start(mips, stats);
}
const int *GPRRegCache::GetAllocationOrder(int &count)
{
const int *GPRRegCache::GetAllocationOrder(int &count) {
count = sizeof(allocationOrder) / sizeof(const int);
return allocationOrder;
}
const int *FPURegCache::GetAllocationOrder(int &count)
{
static const int allocationOrder[] =
{
#ifdef _M_X64
XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM2, XMM3, XMM4, XMM5
#elif _M_IX86
XMM2, XMM3, XMM4, XMM5, XMM6, XMM7,
#endif
};
count = sizeof(allocationOrder) / sizeof(int);
return allocationOrder;
}
OpArg GPRRegCache::GetDefaultLocation(int reg) const
{
OpArg GPRRegCache::GetDefaultLocation(int reg) const {
return M(&mips->r[reg]);
}
OpArg FPURegCache::GetDefaultLocation(int reg) const
{
return M(&mips->f[reg]);
}
void RegCache::KillImmediate(int preg, bool doLoad, bool makeDirty)
{
if (regs[preg].away)
{
void GPRRegCache::KillImmediate(int preg, bool doLoad, bool makeDirty) {
if (regs[preg].away) {
if (regs[preg].location.IsImm())
BindToRegister(preg, doLoad, makeDirty);
else if (regs[preg].location.IsSimpleReg())
@ -288,65 +217,53 @@ void RegCache::KillImmediate(int preg, bool doLoad, bool makeDirty)
}
}
void GPRRegCache::BindToRegister(int i, bool doLoad, bool makeDirty)
{
void GPRRegCache::BindToRegister(int i, bool doLoad, bool makeDirty) {
if (!regs[i].away && regs[i].location.IsImm())
PanicAlert("Bad immediate");
if (!regs[i].away || (regs[i].away && regs[i].location.IsImm()))
{
if (!regs[i].away || (regs[i].away && regs[i].location.IsImm())) {
X64Reg xr = GetFreeXReg();
if (xregs[xr].dirty) PanicAlert("Xreg already dirty");
if (xlocks[xr]) PanicAlert("GetFreeXReg returned locked register");
if (xregs[xr].allocLocked) PanicAlert("GetFreeXReg returned locked register");
xregs[xr].free = false;
xregs[xr].mipsReg = i;
xregs[xr].dirty = makeDirty || regs[i].location.IsImm();
OpArg newloc = ::Gen::R(xr);
if (doLoad)
{
if (doLoad) {
// Force ZERO to be 0.
if (i == 0)
emit->MOV(32, newloc, Imm32(0));
else
emit->MOV(32, newloc, regs[i].location);
}
for (int j = 0; j < 32; j++)
{
if (i != j && regs[j].location.IsSimpleReg() && regs[j].location.GetSimpleReg() == xr)
{
for (int j = 0; j < 32; j++) {
if (i != j && regs[j].location.IsSimpleReg() && regs[j].location.GetSimpleReg() == xr) {
ERROR_LOG(JIT, "BindToRegister: Strange condition");
Crash();
}
}
regs[i].away = true;
regs[i].location = newloc;
}
else
{
} else {
// reg location must be simplereg; memory locations
// and immediates are taken care of above.
xregs[RX(i)].dirty |= makeDirty;
}
if (xlocks[RX(i)]) {
if (xregs[RX(i)].allocLocked) {
PanicAlert("Seriously WTF, this reg should have been flushed");
}
}
void GPRRegCache::StoreFromRegister(int i)
{
if (regs[i].away)
{
void GPRRegCache::StoreFromRegister(int i) {
if (regs[i].away) {
bool doStore;
if (regs[i].location.IsSimpleReg())
{
if (regs[i].location.IsSimpleReg()) {
X64Reg xr = RX(i);
xregs[xr].free = true;
xregs[xr].mipsReg = -1;
doStore = xregs[xr].dirty;
xregs[xr].dirty = false;
}
else
{
} else {
//must be immediate - do nothing
doStore = true;
}
@ -359,14 +276,73 @@ void GPRRegCache::StoreFromRegister(int i)
}
}
void FPURegCache::BindToRegister(int i, bool doLoad, bool makeDirty)
void GPRRegCache::Flush(FlushMode mode)
{
for (int i = 0; i < NUM_X_REGS; i++) {
if (xregs[i].allocLocked)
PanicAlert("Someone forgot to unlock X64 reg %i.", i);
}
for (int i = 0; i < 32; i++) {
if (regs[i].locked) {
PanicAlert("Somebody forgot to unlock MIPS reg %i.", i);
}
if (regs[i].away) {
if (regs[i].location.IsSimpleReg()) {
X64Reg xr = RX(i);
StoreFromRegister(i);
xregs[xr].dirty = false;
}
else if (regs[i].location.IsImm()) {
StoreFromRegister(i);
} else {
_assert_msg_(DYNA_REC,0,"Jit64 - Flush unhandled case, reg %i PC: %08x", i, mips->pc);
}
}
}
}
FPURegCache::FPURegCache() : emit(0), mips(0) {
memset(regs, 0, sizeof(regs));
memset(xregs, 0, sizeof(xregs));
}
void FPURegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) {
this->mips = mips;
for (int i = 0; i < NUM_X_REGS; i++) {
xregs[i].free = true;
xregs[i].dirty = false;
xregs[i].allocLocked = false;
}
for (int i = 0; i < 32; i++) {
regs[i].location = GetDefaultLocation(i);
regs[i].away = false;
regs[i].locked = false;
}
}
void FPURegCache::Lock(int p1, int p2, int p3, int p4) {
regs[p1].locked = true;
if (p2 != 0xFF) regs[p2].locked = true;
if (p3 != 0xFF) regs[p3].locked = true;
if (p4 != 0xFF) regs[p4].locked = true;
}
void FPURegCache::UnlockAll() {
for (int i = 0; i < 32; i++)
regs[i].locked = false;
}
void FPURegCache::UnlockAllX() {
for (int i = 0; i < NUM_X_REGS; i++)
xregs[i].allocLocked = false;
}
void FPURegCache::BindToRegister(int i, bool doLoad, bool makeDirty) {
_assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - load - imm");
if (!regs[i].away)
{
if (!regs[i].away) {
// Reg is at home in the memory register file. Let's pull it out.
X64Reg xr = GetFreeXReg();
_assert_msg_(DYNA_REC, xr < NUMXREGS, "WTF - load - invalid reg");
_assert_msg_(DYNA_REC, xr < NUM_X_REGS, "WTF - load - invalid reg");
xregs[xr].mipsReg = i;
xregs[xr].free = false;
xregs[xr].dirty = makeDirty;
@ -387,13 +363,11 @@ void FPURegCache::BindToRegister(int i, bool doLoad, bool makeDirty)
}
}
void FPURegCache::StoreFromRegister(int i)
{
void FPURegCache::StoreFromRegister(int i) {
_assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - store - imm");
if (regs[i].away)
{
if (regs[i].away) {
X64Reg xr = regs[i].location.GetSimpleReg();
_assert_msg_(DYNA_REC, xr < NUMXREGS, "WTF - store - invalid reg");
_assert_msg_(DYNA_REC, xr < NUM_X_REGS, "WTF - store - invalid reg");
xregs[xr].free = true;
xregs[xr].dirty = false;
xregs[xr].mipsReg = -1;
@ -401,41 +375,98 @@ void FPURegCache::StoreFromRegister(int i)
emit->MOVSS(newLoc, xr);
regs[i].location = newLoc;
regs[i].away = false;
}
else
{
} else {
// _assert_msg_(DYNA_REC,0,"already stored");
}
}
void RegCache::Flush(FlushMode mode)
{
for (int i = 0; i < NUMXREGS; i++) {
if (xlocks[i])
void FPURegCache::Flush(FlushMode mode) {
for (int i = 0; i < NUM_X_REGS; i++) {
if (xregs[i].allocLocked)
PanicAlert("Someone forgot to unlock X64 reg %i.", i);
}
for (int i = 0; i < 32; i++)
{
if (locks[i])
{
PanicAlert("Somebody forgot to unlock PPC reg %i.", i);
for (int i = 0; i < 32; i++) {
if (regs[i].locked) {
PanicAlert("Somebody forgot to unlock MIPS reg %i.", i);
}
if (regs[i].away)
{
if (regs[i].location.IsSimpleReg())
{
if (regs[i].away) {
if (regs[i].location.IsSimpleReg()) {
X64Reg xr = RX(i);
StoreFromRegister(i);
xregs[xr].dirty = false;
}
else if (regs[i].location.IsImm())
{
} else if (regs[i].location.IsImm()) {
StoreFromRegister(i);
}
else
{
} else {
_assert_msg_(DYNA_REC,0,"Jit64 - Flush unhandled case, reg %i PC: %08x", i, mips->pc);
}
}
}
}
OpArg FPURegCache::GetDefaultLocation(int reg) const {
return M(&mips->f[reg]);
}
int FPURegCache::SanityCheck() const {
for (int i = 0; i < 32; i++) {
if (regs[i].away) {
if (regs[i].location.IsSimpleReg()) {
Gen::X64Reg simple = regs[i].location.GetSimpleReg();
if (xregs[simple].allocLocked)
return 1;
if (xregs[simple].mipsReg != i)
return 2;
}
else if (regs[i].location.IsImm())
return 3;
}
}
return 0;
}
const int *FPURegCache::GetAllocationOrder(int &count) {
static const int allocationOrder[] = {
#ifdef _M_X64
XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM2, XMM3, XMM4, XMM5
#elif _M_IX86
XMM2, XMM3, XMM4, XMM5, XMM6, XMM7,
#endif
};
count = sizeof(allocationOrder) / sizeof(int);
return allocationOrder;
}
X64Reg FPURegCache::GetFreeXReg() {
int aCount;
const int *aOrder = GetAllocationOrder(aCount);
for (int i = 0; i < aCount; i++) {
X64Reg xr = (X64Reg)aOrder[i];
if (!xregs[xr].allocLocked && xregs[xr].free) {
return (X64Reg)xr;
}
}
//Okay, not found :( Force grab one
//TODO - add a pass to grab xregs whose mipsreg is not used in the next 3 instructions
for (int i = 0; i < aCount; i++) {
X64Reg xr = (X64Reg)aOrder[i];
if (xregs[xr].allocLocked)
continue;
int preg = xregs[xr].mipsReg;
if (!regs[preg].locked) {
StoreFromRegister(preg);
return xr;
}
}
//Still no dice? Die!
_assert_msg_(DYNA_REC, 0, "Regcache ran out of regs");
return (X64Reg) -1;
}
void FPURegCache::FlushR(X64Reg reg) {
if (reg >= NUM_X_REGS)
PanicAlert("Flushing non existent reg");
if (!xregs[reg].free) {
StoreFromRegister(xregs[reg].mipsReg);
}
}

View file

@ -19,6 +19,7 @@
#include "x64Emitter.h"
#include "../MIPSAnalyst.h"
using namespace Gen;
enum FlushMode
{
@ -36,6 +37,7 @@ struct MIPSCachedReg
{
OpArg location;
bool away; // value not in source register
bool locked;
};
struct X64CachedReg
@ -43,48 +45,34 @@ struct X64CachedReg
int mipsReg;
bool dirty;
bool free;
bool allocLocked;
};
typedef int XReg;
typedef int PReg;
#ifdef _M_X64
#define NUMXREGS 16
#define NUM_X_REGS 16
#elif _M_IX86
#define NUMXREGS 8
#define NUM_X_REGS 8
#endif
class RegCache
// TODO: Add more cachable regs, like HI, LO
#define NUM_MIPS_GPRS 32
#define NUM_MIPS_FPRS 32
class GPRRegCache
{
private:
bool locks[32];
bool saved_locks[32];
bool saved_xlocks[NUMXREGS];
protected:
bool xlocks[NUMXREGS];
MIPSCachedReg regs[32];
X64CachedReg xregs[NUMXREGS];
MIPSCachedReg saved_regs[32];
X64CachedReg saved_xregs[NUMXREGS];
virtual const int *GetAllocationOrder(int &count) = 0;
XEmitter *emit;
public:
MIPSState *mips;
RegCache();
virtual ~RegCache() {}
virtual void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) = 0;
GPRRegCache();
~GPRRegCache() {}
void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats);
void DiscardRegContentsIfCached(int preg);
void SetEmitter(XEmitter *emitter) {emit = emitter;}
void FlushR(X64Reg reg);
void FlushR(X64Reg reg, X64Reg reg2) {FlushR(reg); FlushR(reg2);}
void FlushLockX(X64Reg reg) {
FlushR(reg);
LockX(reg);
@ -93,15 +81,12 @@ public:
FlushR(reg1); FlushR(reg2);
LockX(reg1); LockX(reg2);
}
virtual void Flush(FlushMode mode);
// virtual void Flush(PPCAnalyst::CodeOp *op) {Flush(FLUSH_ALL);}
void Flush(FlushMode mode);
int SanityCheck() const;
void KillImmediate(int preg, bool doLoad, bool makeDirty);
//TODO - instead of doload, use "read", "write"
//read only will not set dirty flag
virtual void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true) = 0;
virtual void StoreFromRegister(int preg) = 0;
void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true);
void StoreFromRegister(int preg);
const OpArg &R(int preg) const {return regs[preg].location;}
X64Reg RX(int preg) const
@ -111,7 +96,7 @@ public:
PanicAlert("Not so simple - %i", preg);
return (X64Reg)-1;
}
virtual OpArg GetDefaultLocation(int reg) const = 0;
OpArg GetDefaultLocation(int reg) const;
// Register locking.
void Lock(int p1, int p2=0xff, int p3=0xff, int p4=0xff);
@ -119,34 +104,70 @@ public:
void UnlockAll();
void UnlockAllX();
bool IsFreeX(int xreg) const;
X64Reg GetFreeXReg();
void SaveState();
void LoadState();
};
class GPRRegCache : public RegCache
{
public:
void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats);
void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true);
void StoreFromRegister(int preg);
OpArg GetDefaultLocation(int reg) const;
const int *GetAllocationOrder(int &count);
void SetImmediate32(int preg, u32 immValue);
bool IsImmediate(int preg) const;
u32 GetImmediate32(int preg) const;
private:
MIPSCachedReg regs[NUM_MIPS_GPRS];
X64CachedReg xregs[NUM_X_REGS];
const int *GetAllocationOrder(int &count);
XEmitter *emit;
};
class FPURegCache : public RegCache
// Now also with VFPU support!
class FPURegCache
{
public:
MIPSState *mips;
FPURegCache();
~FPURegCache() {}
void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats);
void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true);
void StoreFromRegister(int preg);
const int *GetAllocationOrder(int &count);
OpArg GetDefaultLocation(int reg) const;
void SetEmitter(XEmitter *emitter) {emit = emitter;}
void FlushLockX(X64Reg reg) {
FlushR(reg);
LockX(reg);
}
void FlushLockX(X64Reg reg1, X64Reg reg2) {
FlushR(reg1); FlushR(reg2);
LockX(reg1); LockX(reg2);
}
void Flush(FlushMode mode);
int SanityCheck() const;
const OpArg &R(int preg) const {return regs[preg].location;}
X64Reg RX(int preg) const
{
if (regs[preg].away && regs[preg].location.IsSimpleReg())
return regs[preg].location.GetSimpleReg();
PanicAlert("Not so simple - %i", preg);
return (X64Reg)-1;
}
// Register locking. Prevents them from being spilled.
void Lock(int p1, int p2=0xff, int p3=0xff, int p4=0xff);
void LockX(int x1, int x2=0xff, int x3=0xff, int x4=0xff);
void UnlockAll();
void UnlockAllX();
private:
X64Reg GetFreeXReg();
void FlushR(X64Reg reg);
const int *GetAllocationOrder(int &count);
MIPSCachedReg regs[NUM_MIPS_FPRS];
X64CachedReg xregs[NUM_X_REGS];
XEmitter *emit;
};