Split out the FPU reg cache into its own file too.

This commit is contained in:
Henrik Rydgard 2013-01-25 23:09:11 +01:00
parent ad5e2b58c6
commit 68991511ee
18 changed files with 433 additions and 290 deletions

View file

@ -618,7 +618,9 @@ elseif(X86)
Core/MIPS/x86/JitCache.cpp
Core/MIPS/x86/JitCache.h
Core/MIPS/x86/RegCache.cpp
Core/MIPS/x86/RegCache.h)
Core/MIPS/x86/RegCache.h
Core/MIPS/x86/RegCacheFPU.cpp
Core/MIPS/x86/RegCacheFPU.h)
endif()
# 'ppsspp_jni' on ANDROID, 'Core' everywhere else

View file

@ -222,6 +222,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmRegCacheFPU.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmJit.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -257,6 +263,7 @@
<ClCompile Include="MIPS\x86\CompFPU.cpp" />
<ClCompile Include="MIPS\x86\CompLoadStore.cpp" />
<ClCompile Include="MIPS\x86\CompVFPU.cpp" />
<ClCompile Include="MIPS\x86\RegCacheFPU.cpp" />
<ClCompile Include="MIPS\x86\Jit.cpp" />
<ClCompile Include="MIPS\x86\JitCache.cpp" />
<ClCompile Include="MIPS\x86\RegCache.cpp" />
@ -370,6 +377,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="MIPS\ARM\ArmRegCacheFPU.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="MIPS\JitCommon\JitCommon.h" />
<ClInclude Include="Mips\MIPS.h" />
<ClInclude Include="Mips\MIPSAnalyst.h" />
@ -382,6 +395,7 @@
<ClInclude Include="Mips\MIPSTables.h" />
<ClInclude Include="MIPS\MIPSVFPUUtils.h" />
<ClInclude Include="MIPS\x86\Asm.h" />
<ClInclude Include="MIPS\x86\RegCacheFPU.h" />
<ClInclude Include="MIPS\x86\Jit.h" />
<ClInclude Include="MIPS\x86\JitCache.h" />
<ClInclude Include="MIPS\x86\RegCache.h" />

View file

@ -369,6 +369,12 @@
<ClCompile Include="..\ext\disarm.cpp">
<Filter>Ext</Filter>
</ClCompile>
<ClCompile Include="MIPS\x86\RegCacheFPU.cpp">
<Filter>MIPS\x86</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmRegCacheFPU.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@ -683,6 +689,12 @@
<ClInclude Include="..\Ext\disarm.h">
<Filter>Ext</Filter>
</ClInclude>
<ClInclude Include="MIPS\x86\RegCacheFPU.h">
<Filter>MIPS\x86</Filter>
</ClInclude>
<ClInclude Include="MIPS\ARM\ArmRegCacheFPU.h">
<Filter>MIPS\ARM</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />

View file

@ -1,8 +1,8 @@
// Copyright (C) 2003 Dolphin Project.
// 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.
// 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
@ -12,8 +12,8 @@
// 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/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "ArmRegCache.h"
#include "ArmEmitter.h"

View file

@ -1,8 +1,8 @@
// Copyright (C) 2003 Dolphin Project.
// 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.
// 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
@ -12,8 +12,8 @@
// 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/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once

View file

@ -0,0 +1,20 @@
// 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 "Common/ArmEmitter.h"
#include "Core/MIPS/ARM/ArmRegCacheFPU.h"

View file

@ -0,0 +1,29 @@
// 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/.
#pragma once
#include "Common/ArmEmitter.h"
// The PSP has 160 FP registers: 32 FPRs + 128 VFPU registers.
class FPURegCache {
public:
};

View file

@ -46,7 +46,7 @@ void Jit::CompFPTriArith(u32 op, void (XEmitter::*arith)(X64Reg reg, OpArg), boo
int ft = _FT;
int fs = _FS;
int fd = _FD;
fpr.Lock(ft, fs, fd);
fpr.SpillLock(ft, fs, fd);
if (false && fs == fd)
{
@ -66,7 +66,7 @@ void Jit::CompFPTriArith(u32 op, void (XEmitter::*arith)(X64Reg reg, OpArg), boo
(this->*arith)(XMM0, R(XMM1));
MOVSS(fpr.RX(fd), R(XMM0));
}
fpr.UnlockAll();
fpr.ReleaseSpillLocks();
}
void Jit::Comp_FPU3op(u32 op)
@ -97,7 +97,7 @@ void Jit::Comp_FPULS(u32 op)
{
case 49: //FI(ft) = Memory::Read_U32(addr); break; //lwc1
gpr.Lock(rs);
fpr.Lock(ft);
fpr.SpillLock(ft);
fpr.BindToRegister(ft, false, true);
if (gpr.R(rs).IsImm())
@ -181,11 +181,11 @@ void Jit::Comp_FPULS(u32 op)
}
gpr.UnlockAll();
fpr.UnlockAll();
fpr.ReleaseSpillLocks();
break;
case 57: //Memory::Write_U32(FI(ft), addr); break; //swc1
gpr.Lock(rs);
fpr.Lock(ft);
fpr.SpillLock(ft);
fpr.BindToRegister(ft, true, false);
if (gpr.R(rs).IsImm())
@ -263,7 +263,7 @@ void Jit::Comp_FPULS(u32 op)
}
gpr.UnlockAll();
fpr.UnlockAll();
fpr.ReleaseSpillLocks();
break;
default:
@ -285,28 +285,28 @@ void Jit::Comp_FPU2op(u32 op)
switch (op & 0x3f)
{
case 5: //F(fd) = fabsf(F(fs)); break; //abs
fpr.Lock(fd, fs);
fpr.SpillLock(fd, fs);
fpr.BindToRegister(fd, fd == fs, true);
MOVSS(fpr.RX(fd), fpr.R(fs));
PAND(fpr.RX(fd), M((void *)ssNoSignMask));
fpr.UnlockAll();
fpr.ReleaseSpillLocks();
break;
case 6: //F(fd) = F(fs); break; //mov
if (fd != fs) {
fpr.Lock(fd, fs);
fpr.SpillLock(fd, fs);
fpr.BindToRegister(fd, fd == fs, true);
MOVSS(fpr.RX(fd), fpr.R(fs));
fpr.UnlockAll();
fpr.ReleaseSpillLocks();
}
break;
case 7: //F(fd) = -F(fs); break; //neg
fpr.Lock(fd, fs);
fpr.SpillLock(fd, fs);
fpr.BindToRegister(fd, fd == fs, true);
MOVSS(fpr.RX(fd), fpr.R(fs));
PXOR(fpr.RX(fd), M((void *)ssSignBits2));
fpr.UnlockAll();
fpr.ReleaseSpillLocks();
break;
case 12: //FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s
@ -321,11 +321,11 @@ void Jit::Comp_FPU2op(u32 op)
return;
case 13: //FsI(fd) = F(fs)>=0 ? (int)floorf(F(fs)) : (int)ceilf(F(fs)); break;//trunc.w.s
fpr.Lock(fs, fd);
fpr.SpillLock(fs, fd);
fpr.StoreFromRegister(fd);
CVTTSS2SI(EAX, fpr.R(fs));
MOV(32, fpr.R(fd), R(EAX));
fpr.UnlockAll();
fpr.ReleaseSpillLocks();
break;
case 14: //FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s
@ -363,10 +363,10 @@ void Jit::Comp_mxc1(u32 op)
case 4: //FI(fs) = R(rt); break; //mtc1
// Cross move! slightly tricky
gpr.StoreFromRegister(rt);
fpr.Lock(fs);
fpr.SpillLock(fs);
fpr.BindToRegister(fs, false, true);
MOVSS(fpr.RX(fs), gpr.R(rt));
fpr.UnlockAll();
fpr.ReleaseSpillLocks();
return;
case 6: //currentMIPS->WriteFCR(fs, R(rt)); break; //ctc1

View file

@ -58,7 +58,7 @@ void Jit::Comp_SVQ(u32 op)
if (!g_Config.bFastMemory) {
DISABLE;
}
fpr.Flush(FLUSH_ALL);
fpr.Flush();
gpr.BindToRegister(rs, true, true);
u8 vregs[4];
@ -92,7 +92,7 @@ void Jit::Comp_SVQ(u32 op)
if (!g_Config.bFastMemory) {
DISABLE;
}
fpr.Flush(FLUSH_ALL);
fpr.Flush();
gpr.BindToRegister(rs, true, true);
u8 vregs[4];

View file

@ -106,8 +106,8 @@ Jit::Jit(MIPSState *mips) : blocks(mips), mips_(mips)
void Jit::FlushAll()
{
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
gpr.Flush();
fpr.Flush();
}
void Jit::WriteDowncount(int offset)

View file

@ -17,17 +17,18 @@
#pragma once
#include "../../../Globals.h"
#include "../../../Common/Thunk.h"
#include "Globals.h"
#include "Common/Thunk.h"
#include "Asm.h"
#if defined(ARM)
#error DO NOT BUILD X86 JIT ON ARM
#endif
#include "x64Emitter.h"
#include "Common/x64Emitter.h"
#include "JitCache.h"
#include "RegCache.h"
#include "RegCacheFPU.h"
namespace MIPSComp
{

View file

@ -15,12 +15,12 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "../MIPS.h"
#include "../MIPSTables.h"
#include "../MIPSAnalyst.h"
#include "Jit.h"
#include "Asm.h"
#include "RegCache.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/x86/Jit.h"
#include "Core/MIPS/x86/Asm.h"
#include "Core/MIPS/x86/RegCache.h"
using namespace Gen;
@ -59,7 +59,7 @@ void GPRRegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) {
// todo: sort to find the most popular regs
/*
int maxPreload = 2;
for (int i = 0; i < 32; i++)
for (int i = 0; i < NUM_MIPS_GPRS; i++)
{
if (stats.numReads[i] > 2 || stats.numWrites[i] >= 2)
{
@ -94,7 +94,7 @@ void GPRRegCache::LockX(int x1, int x2, int x3, int x4) {
}
void GPRRegCache::UnlockAll() {
for (int i = 0; i < 32; i++)
for (int i = 0; i < NUM_MIPS_GPRS; i++)
regs[i].locked = false;
}
@ -144,7 +144,7 @@ void GPRRegCache::FlushR(X64Reg reg)
}
int GPRRegCache::SanityCheck() const {
for (int i = 0; i < 32; i++) {
for (int i = 0; i < NUM_MIPS_GPRS; i++) {
if (regs[i].away) {
if (regs[i].location.IsSimpleReg()) {
Gen::X64Reg simple = regs[i].location.GetSimpleReg();
@ -276,13 +276,13 @@ void GPRRegCache::StoreFromRegister(int i) {
}
}
void GPRRegCache::Flush(FlushMode mode)
void GPRRegCache::Flush()
{
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++) {
for (int i = 0; i < NUM_MIPS_GPRS; i++) {
if (regs[i].locked) {
PanicAlert("Somebody forgot to unlock MIPS reg %i.", i);
}
@ -299,174 +299,4 @@ void GPRRegCache::Flush(FlushMode mode)
}
}
}
}
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) {
// Reg is at home in the memory register file. Let's pull it out.
X64Reg xr = GetFreeXReg();
_assert_msg_(DYNA_REC, xr < NUM_X_REGS, "WTF - load - invalid reg");
xregs[xr].mipsReg = i;
xregs[xr].free = false;
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].away = true;
} else {
// 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;
}
}
void FPURegCache::StoreFromRegister(int i) {
_assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - store - imm");
if (regs[i].away) {
X64Reg xr = regs[i].location.GetSimpleReg();
_assert_msg_(DYNA_REC, xr < NUM_X_REGS, "WTF - store - invalid reg");
xregs[xr].free = true;
xregs[xr].dirty = false;
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::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);
}
}
}
}
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

@ -21,36 +21,20 @@
#include "../MIPSAnalyst.h"
using namespace Gen;
enum FlushMode
{
FLUSH_ALL
};
enum GrabMode
{
M_READ = 1,
M_WRITE = 2,
M_READWRITE = 3,
};
struct MIPSCachedReg
{
struct MIPSCachedReg {
OpArg location;
bool away; // value not in source register
bool locked;
};
struct X64CachedReg
{
struct X64CachedReg {
int mipsReg;
bool dirty;
bool free;
bool allocLocked;
};
typedef int XReg;
typedef int PReg;
#ifdef _M_X64
#define NUM_X_REGS 16
#elif _M_IX86
@ -59,12 +43,10 @@ typedef int PReg;
// TODO: Add more cachable regs, like HI, LO
#define NUM_MIPS_GPRS 32
#define NUM_MIPS_FPRS 32
class GPRRegCache
{
public:
MIPSState *mips;
GPRRegCache();
~GPRRegCache() {}
void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats);
@ -81,7 +63,7 @@ public:
FlushR(reg1); FlushR(reg2);
LockX(reg1); LockX(reg2);
}
void Flush(FlushMode mode);
void Flush();
int SanityCheck() const;
void KillImmediate(int preg, bool doLoad, bool makeDirty);
@ -104,69 +86,17 @@ public:
void UnlockAll();
void UnlockAllX();
X64Reg GetFreeXReg();
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;
};
// 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);
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];
MIPSCachedReg regs[NUM_MIPS_GPRS];
X64CachedReg xregs[NUM_X_REGS];
XEmitter *emit;

View file

@ -0,0 +1,192 @@
// 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/.
#pragma once
#include "Common/Log.h"
#include "Common/x64Emitter.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/x86/RegCacheFPU.h"
FPURegCache::FPURegCache() : emit(0), mips(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;
for (int i = 0; i < NUM_X_FPREGS; i++) {
xregs[i].mipsReg = -1;
xregs[i].dirty = false;
}
for (int i = 0; i < NUM_MIPS_FPRS; i++) {
regs[i].location = GetDefaultLocation(i);
regs[i].away = false;
regs[i].locked = false;
}
}
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 *v, VectorSize vsz) {
for (int i = 0; i < GetNumVectorElements(vsz); i++) {
vregs[i].locked = true;
}
}
void FPURegCache::SpillLockV(int vec, VectorSize vsz) {
u8 v[4];
GetVectorRegs(v, vsz, vec);
SpillLockV(v, vsz);
}
void FPURegCache::ReleaseSpillLocks() {
for (int i = 0; i < NUM_MIPS_FPRS; i++)
regs[i].locked = 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) {
// Reg is at home in the memory register file. Let's pull it out.
X64Reg xr = GetFreeXReg();
_assert_msg_(DYNA_REC, 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].away = true;
} else {
// 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;
}
}
void FPURegCache::StoreFromRegister(int i) {
_assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - store - imm");
if (regs[i].away) {
X64Reg xr = regs[i].location.GetSimpleReg();
_assert_msg_(DYNA_REC, xr < NUM_X_FPREGS, "WTF - store - invalid reg");
xregs[xr].dirty = false;
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::Flush() {
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_(DYNA_REC,0,"Jit64 - Flush unhandled case, reg %i PC: %08x", i, mips->pc);
}
}
}
}
OpArg FPURegCache::GetDefaultLocation(int reg) const {
if (reg < 32) {
return M(&mips->f[reg]);
} else {
return M(&mips->v[reg - 32]);
}
}
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::GetFreeXReg() {
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;
}
}
//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];
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_FPREGS)
PanicAlert("Flushing non existent reg");
if (xregs[reg].mipsReg != -1) {
StoreFromRegister(xregs[reg].mipsReg);
}
}

107
Core/MIPS/x86/RegCacheFPU.h Normal file
View file

@ -0,0 +1,107 @@
// 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/.
#pragma once
#include "Common/x64Emitter.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/MIPSVFPUUtils.h"
using namespace Gen;
// GPRs are numbered 0 to 31
// VFPU regs are numbered 32 to 160.
#define NUM_MIPS_FPRS (32 + 128)
#ifdef _M_X64
#define NUM_X_FPREGS 16
#elif _M_IX86
#define NUM_X_FPREGS 8
#endif
struct X64CachedFPReg {
int mipsReg;
bool dirty;
};
struct MIPSCachedFPReg {
OpArg location;
bool away; // value not in source register
bool locked;
};
// The PSP has 160 FP registers: 32 FPRs + 128 VFPU registers.
// Soon we will support them all.
class FPURegCache
{
public:
FPURegCache();
~FPURegCache() {}
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;
void SetEmitter(XEmitter *emitter) {emit = emitter;}
void Flush();
int SanityCheck() const;
const OpArg &R(int freg) const {return regs[freg].location;}
const OpArg &V(int vreg) const {return regs[32 + vreg].location;}
X64Reg RX(int freg) const
{
if (regs[freg].away && regs[freg].location.IsSimpleReg())
return regs[freg].location.GetSimpleReg();
PanicAlert("Not so simple - f%i", freg);
return (X64Reg)-1;
}
X64Reg VX(int vreg) const
{
if (regs[vreg + 32].away && regs[vreg + 32].location.IsSimpleReg())
return regs[vreg + 32].location.GetSimpleReg();
PanicAlert("Not so simple - v%i", vreg);
return (X64Reg)-1;
}
// Register locking. Prevents them from being spilled.
void SpillLock(int p1, int p2=0xff, int p3=0xff, int p4=0xff);
void ReleaseSpillLocks();
void SpillLockV(const u8 *v, VectorSize vsz);
void SpillLockV(int vec, VectorSize vsz);
MIPSState *mips;
private:
X64Reg GetFreeXReg();
void FlushR(X64Reg reg);
const int *GetAllocationOrder(int &count);
MIPSCachedFPReg regs[NUM_MIPS_FPRS];
X64CachedFPReg xregs[NUM_X_FPREGS];
MIPSCachedFPReg *vregs;
XEmitter *emit;
};

View file

@ -18,12 +18,13 @@ arm {
../Core/MIPS/ARM/ArmJit.cpp \
../Core/MIPS/ARM/ArmJitCache.cpp \
../Core/MIPS/ARM/ArmRegCache.cpp \
../Core/MIPS/ARM/ArmRegCacheFPU.cpp \
../ext/disarm.cpp
HEADERS += ../Core/MIPS/ARM/ArmAsm.h \
../Core/MIPS/ARM/ArmJit.h \
../Core/MIPS/ARM/ArmJitCache.h \
../Core/MIPS/ARM/ArmRegCache.h
../Core/MIPS/ARM/ArmRegCacheFPU.h
}
x86 {
SOURCES += ../Core/MIPS/x86/Asm.cpp \
@ -34,11 +35,13 @@ x86 {
../Core/MIPS/x86/CompVFPU.cpp \
../Core/MIPS/x86/Jit.cpp \
../Core/MIPS/x86/JitCache.cpp \
../Core/MIPS/x86/RegCache.cpp
../Core/MIPS/x86/RegCache.cpp \
../Core/MIPS/x86/RegCacheFPU.cpp
HEADERS += ../Core/MIPS/x86/Asm.h \
../Core/MIPS/x86/Jit.h \
../Core/MIPS/x86/JitCache.h \
../Core/MIPS/x86/RegCache.h
../Core/MIPS/x86/RegCache.h \
../Core/MIPS/x86/RegCacheFPU.h
}
SOURCES += ../Core/CPU.cpp \ # Core

View file

@ -177,6 +177,7 @@ LOCAL_SRC_FILES := \
$(SRC)/Core/MIPS/ARM/ArmAsm.cpp \
$(SRC)/Core/MIPS/ARM/ArmJit.cpp \
$(SRC)/Core/MIPS/ARM/ArmRegCache.cpp \
$(SRC)/Core/MIPS/ARM/ArmRegCacheFPU.cpp \
$(SRC)/Core/Util/BlockAllocator.cpp \
$(SRC)/Core/Util/ppge_atlas.cpp \
$(SRC)/Core/Util/PPGeDraw.cpp

View file

@ -52,9 +52,11 @@ typedef uint64_t uint64;
typedef std::string string;
#ifndef DISALLOW_COPY_AND_ASSIGN
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
#endif
} // namespace snappy