ppsspp/Core/MIPS/x86/RegCacheFPU.cpp
Unknown W. Brackets 2862367927 x86jit: Add force-non-simd to all current ops.
Unless they already use MapRegs, because that will automatically handle
it.
2014-11-16 13:33:12 -08:00

375 lines
10 KiB
C++

// Copyright (c) 2012- PPSSPP 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 git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <cstring>
#include "Common/Log.h"
#include "Common/x64Emitter.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/x86/RegCache.h"
#include "Core/MIPS/x86/RegCacheFPU.h"
u32 FPURegCache::tempValues[NUM_TEMPS];
FPURegCache::FPURegCache() : mips(0), initialReady(false), emit(0) {
memset(regs, 0, sizeof(regs));
memset(xregs, 0, sizeof(xregs));
vregs = regs + 32;
}
void FPURegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) {
this->mips = mips;
if (!initialReady) {
SetupInitialRegs();
initialReady = true;
}
memcpy(xregs, xregsInitial, sizeof(xregs));
memcpy(regs, regsInitial, sizeof(regs));
pendingFlush = false;
}
void FPURegCache::SetupInitialRegs() {
for (int i = 0; i < NUM_X_FPREGS; i++) {
memset(xregsInitial[i].mipsRegs, -1, sizeof(xregsInitial[i].mipsRegs));
xregsInitial[i].dirty = false;
}
memset(regsInitial, 0, sizeof(regsInitial));
OpArg base = GetDefaultLocation(0);
for (int i = 0; i < 32; i++) {
regsInitial[i].location = base;
base.IncreaseOffset(sizeof(float));
}
for (int i = 32; i < 32 + 128; i++) {
regsInitial[i].location = GetDefaultLocation(i);
}
base = GetDefaultLocation(32 + 128);
for (int i = 32 + 128; i < NUM_MIPS_FPRS; i++) {
regsInitial[i].location = base;
base.IncreaseOffset(sizeof(float));
}
}
void FPURegCache::SpillLock(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::SpillLockV(const u8 *vec, VectorSize sz) {
for (int i = 0; i < GetNumVectorElements(sz); i++) {
vregs[vec[i]].locked = true;
}
}
void FPURegCache::SpillLockV(int vec, VectorSize sz) {
u8 r[4];
GetVectorRegs(r, sz, vec);
SpillLockV(r, sz);
}
void FPURegCache::MapRegV(int vreg, int flags) {
MapReg(vreg + 32, (flags & MAP_NOINIT) == 0, (flags & MAP_DIRTY) != 0);
}
void FPURegCache::MapRegsV(int vec, VectorSize sz, int flags) {
u8 r[4];
GetVectorRegs(r, sz, vec);
SpillLockV(r, sz);
for (int i = 0; i < GetNumVectorElements(sz); i++) {
MapReg(r[i] + 32, (flags & MAP_NOINIT) == 0, (flags & MAP_DIRTY) != 0);
}
}
void FPURegCache::MapRegsV(const u8 *r, VectorSize sz, int flags) {
SpillLockV(r, sz);
for (int i = 0; i < GetNumVectorElements(sz); i++) {
MapReg(r[i] + 32, (flags & MAP_NOINIT) == 0, (flags & MAP_DIRTY) != 0);
}
}
void FPURegCache::MapRegsVS(const u8 *r, VectorSize sz, int flags) {
SpillLockV(r, sz);
// TODO: Basically TryMapRegsVS, if that fails, flush hard and try again, or else die.
}
bool FPURegCache::TryMapRegsVS(const u8 *v, VectorSize vsz, int flags) {
const int n = GetNumVectorElements(vsz);
// First, check if it's already mapped. Might be used in a row.
if (vregs[v[0]].lane != 0) {
const MIPSCachedFPReg &v0 = vregs[v[0]];
_dbg_assert_msg_(JIT, v0.away, "Must be away when lane != 0");
_dbg_assert_msg_(JIT, v0.location.IsSimpleReg(), "Must be is register when lane != 0");
// Already in another simd set.
if (v0.lane != 1) {
return false;
}
const X64Reg xr = VSX(v[0]);
// We have to check for extra regs too (might trash them.)
// TODO: Might be worth trying to store them off.
for (int i = 1; i < 4; ++i) {
if (i < n && xregs[xr].mipsRegs[i] != v[i] + 32) {
return false;
}
if (i >= n && xregs[xr].mipsRegs[i] != -1) {
return false;
}
}
// Already mapped then, perfect. Just mark dirty.
if ((flags & MAP_DIRTY) != 0)
xregs[xr].dirty = true;
return true;
}
// Next, fail if any of the other regs are in simd currently.
// TODO: Only if locked? Not sure if it will be worth breaking them anyway.
for (int i = 1; i < n; ++i) {
if (vregs[v[i]].lane != 0) {
return false;
}
}
// TODO: Single is easy, otherwise look for a cheap load.
// We can use free regs if there are any to assemble.
return false;
}
void FPURegCache::SimpleRegsV(const u8 *v, VectorSize vsz, int flags) {
// TODO
}
void FPURegCache::SimpleRegsV(const u8 *v, MatrixSize msz, int flags) {
// TODO
}
void FPURegCache::SimpleRegV(const u8 v, int flags) {
// TODO: If the reg has lane != 0, store or map it? Discard if noinit?
}
void FPURegCache::ReleaseSpillLock(int mipsreg)
{
regs[mipsreg].locked = false;
}
void FPURegCache::ReleaseSpillLocks() {
for (int i = 0; i < NUM_MIPS_FPRS; i++)
regs[i].locked = false;
for (int i = TEMP0; i < TEMP0 + NUM_TEMPS; ++i)
DiscardR(i);
}
void FPURegCache::MapReg(const int i, bool doLoad, bool makeDirty) {
pendingFlush = true;
_assert_msg_(JIT, !regs[i].location.IsImm(), "WTF - load - imm");
if (!regs[i].away) {
// Reg is at home in the memory register file. Let's pull it out.
X64Reg xr = GetFreeXReg();
_assert_msg_(JIT, xr >= 0 && xr < NUM_X_FPREGS, "WTF - load - invalid reg");
xregs[xr].mipsReg = i;
xregs[xr].dirty = makeDirty;
OpArg newloc = ::Gen::R(xr);
if (doLoad) {
if (!regs[i].location.IsImm() && (regs[i].location.offset & 0x3)) {
PanicAlert("WARNING - misaligned fp register location %i", i);
}
emit->MOVSS(xr, regs[i].location);
}
regs[i].location = newloc;
regs[i].lane = 0;
regs[i].away = true;
} else {
// TODO: If it's in a lane, need to pull it out.
// There are no immediates in the FPR reg file, so we already had this in a register. Make dirty as necessary.
xregs[RX(i)].dirty |= makeDirty;
_assert_msg_(JIT, regs[i].location.IsSimpleReg(), "not loaded and not simple.");
}
}
void FPURegCache::StoreFromRegister(int i) {
_assert_msg_(JIT, !regs[i].location.IsImm(), "WTF - store - imm");
if (regs[i].away) {
X64Reg xr = regs[i].location.GetSimpleReg();
_assert_msg_(JIT, xr >= 0 && xr < NUM_X_FPREGS, "WTF - store - invalid reg");
xregs[xr].dirty = false;
// TODO: Handle simd. Need to store the rest. Hmm messy.
xregs[xr].mipsReg = -1;
OpArg newLoc = GetDefaultLocation(i);
emit->MOVSS(newLoc, xr);
regs[i].location = newLoc;
regs[i].away = false;
} else {
// _assert_msg_(DYNA_REC,0,"already stored");
}
}
void FPURegCache::DiscardR(int i) {
_assert_msg_(JIT, !regs[i].location.IsImm(), "FPU can't handle imm yet.");
if (regs[i].away) {
X64Reg xr = regs[i].location.GetSimpleReg();
_assert_msg_(JIT, xr >= 0 && xr < NUM_X_FPREGS, "DiscardR: MipsReg had bad X64Reg");
// Note that we DO NOT write it back here. That's the whole point of Discard.
xregs[xr].dirty = false;
// TODO: Handle simd. Need to store the rest. Hmm messy.
xregs[xr].mipsReg = -1;
regs[i].location = GetDefaultLocation(i);
regs[i].away = false;
regs[i].tempLocked = false;
} else {
// _assert_msg_(DYNA_REC,0,"already stored");
regs[i].tempLocked = false;
}
}
bool FPURegCache::IsTempX(X64Reg xr) {
return xregs[xr].mipsReg >= TEMP0;
}
int FPURegCache::GetTempR() {
pendingFlush = true;
for (int r = TEMP0; r < TEMP0 + NUM_TEMPS; ++r) {
if (!regs[r].away && !regs[r].tempLocked) {
regs[r].tempLocked = true;
return r;
}
}
_assert_msg_(JIT, 0, "Regcache ran out of temp regs, might need to DiscardR() some.");
return -1;
}
void FPURegCache::Flush() {
if (!pendingFlush) {
return;
}
for (int i = 0; i < NUM_MIPS_FPRS; 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_(JIT,0,"Jit64 - Flush unhandled case, reg %i PC: %08x", i, mips->pc);
}
}
}
pendingFlush = false;
}
OpArg FPURegCache::GetDefaultLocation(int reg) const {
if (reg < 32) {
return MDisp(CTXREG, reg * 4);
} else if (reg < 32 + 128) {
return M(&mips->v[voffset[reg - 32]]);
} else {
return M(&tempValues[reg - 32 - 128]);
}
}
int FPURegCache::SanityCheck() const {
for (int i = 0; i < NUM_MIPS_FPRS; i++) {
if (regs[i].away) {
if (regs[i].location.IsSimpleReg()) {
Gen::X64Reg simple = regs[i].location.GetSimpleReg();
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::GetFreeXRegNoSpill() {
pendingFlush = true;
int aCount;
const int *aOrder = GetAllocationOrder(aCount);
for (int i = 0; i < aCount; i++) {
X64Reg xr = (X64Reg)aOrder[i];
if (xregs[xr].mipsReg == -1) {
return (X64Reg)xr;
}
}
return INVALID_REG;
}
X64Reg FPURegCache::GetFreeXReg() {
X64Reg noSpill = GetFreeXRegNoSpill();
if (noSpill != INVALID_REG) {
return noSpill;
}
// Okay, not found :(... Force grab one.
int aCount;
const int *aOrder = GetAllocationOrder(aCount);
// 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];
int preg = xregs[xr].mipsReg;
if (!regs[preg].locked) {
StoreFromRegister(preg);
return xr;
}
}
// Still no dice? Die!
_assert_msg_(JIT, 0, "Regcache ran out of regs");
return INVALID_REG;
}
void FPURegCache::FlushX(X64Reg reg) {
if (reg >= NUM_X_FPREGS) {
PanicAlert("Flushing non existent reg");
} else if (xregs[reg].mipsReg != -1) {
StoreFromRegister(xregs[reg].mipsReg);
}
}
void FPURegCache::GetState(FPURegCacheState &state) const {
memcpy(state.regs, regs, sizeof(regs));
memcpy(state.xregs, xregs, sizeof(xregs));
}
void FPURegCache::RestoreState(const FPURegCacheState state) {
memcpy(regs, state.regs, sizeof(regs));
memcpy(xregs, state.xregs, sizeof(xregs));
pendingFlush = true;
}