2013-01-25 22:09:11 +00:00
|
|
|
// 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"
|
|
|
|
|
2013-11-26 02:18:15 +00:00
|
|
|
#undef MAP_NOINIT
|
|
|
|
|
2013-01-25 22:09:11 +00:00
|
|
|
using namespace Gen;
|
|
|
|
|
|
|
|
// GPRs are numbered 0 to 31
|
2013-02-16 18:18:13 +00:00
|
|
|
// VFPU regs are numbered 32 to 159.
|
2013-02-18 07:14:22 +00:00
|
|
|
// Then we have some temp regs for VFPU handling from 160 to 175.
|
|
|
|
|
|
|
|
// Temp regs: 4 from S prefix, 4 from T prefix, 4 from D mask, and 4 for work (worst case.)
|
|
|
|
// But most of the time prefixes aren't used that heavily so we won't use all of them.
|
2013-01-25 22:09:11 +00:00
|
|
|
|
2013-10-08 18:09:43 +00:00
|
|
|
// PLANS FOR PROPER SIMD
|
|
|
|
// 1, 2, 3, and 4-vectors will be loaded into single XMM registers
|
|
|
|
// Matrices will be loaded into pairs, triads, or quads of XMM registers - simply by loading
|
|
|
|
// the columns or the rows one by one.
|
|
|
|
|
|
|
|
// On x86 this means that only one 4x4 matrix can be fully loaded at once but that's alright.
|
|
|
|
// We might want to keep "linearized" columns in memory.
|
|
|
|
|
|
|
|
// Implement optimized vec/matrix multiplications of all types and transposes that
|
|
|
|
// take into account in which XMM registers the values are. Fallback: Just dump out the values
|
|
|
|
// and do it the old way.
|
|
|
|
|
2013-02-16 11:15:22 +00:00
|
|
|
enum {
|
2013-02-18 07:14:22 +00:00
|
|
|
NUM_TEMPS = 16,
|
2013-02-16 11:15:22 +00:00
|
|
|
TEMP0 = 32 + 128,
|
|
|
|
NUM_MIPS_FPRS = 32 + 128 + NUM_TEMPS,
|
|
|
|
};
|
2013-01-25 22:09:11 +00:00
|
|
|
|
|
|
|
#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;
|
2013-02-18 08:32:42 +00:00
|
|
|
// Only for temp regs.
|
|
|
|
bool tempLocked;
|
2013-01-25 22:09:11 +00:00
|
|
|
};
|
|
|
|
|
2013-08-16 06:13:40 +00:00
|
|
|
struct FPURegCacheState {
|
|
|
|
MIPSCachedFPReg regs[NUM_MIPS_FPRS];
|
|
|
|
X64CachedFPReg xregs[NUM_X_FPREGS];
|
|
|
|
};
|
|
|
|
|
2013-01-26 00:33:32 +00:00
|
|
|
enum {
|
|
|
|
MAP_DIRTY = 1,
|
|
|
|
MAP_NOINIT = 2,
|
|
|
|
};
|
|
|
|
|
2013-01-25 22:09:11 +00:00
|
|
|
// 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);
|
2013-11-09 14:23:31 +00:00
|
|
|
void MapReg(int preg, bool doLoad = true, bool makeDirty = true);
|
2013-01-25 22:09:11 +00:00
|
|
|
void StoreFromRegister(int preg);
|
2013-02-10 11:14:55 +00:00
|
|
|
void StoreFromRegisterV(int preg) {
|
|
|
|
StoreFromRegister(preg + 32);
|
|
|
|
}
|
2013-01-25 22:09:11 +00:00
|
|
|
OpArg GetDefaultLocation(int reg) const;
|
2013-02-16 18:18:13 +00:00
|
|
|
void DiscardR(int freg);
|
|
|
|
void DiscardV(int vreg) {
|
|
|
|
DiscardR(vreg + 32);
|
|
|
|
}
|
2013-02-18 06:37:56 +00:00
|
|
|
bool IsTempX(X64Reg xreg);
|
|
|
|
int GetTempR();
|
|
|
|
int GetTempV() {
|
|
|
|
return GetTempR() - 32;
|
|
|
|
}
|
2013-01-25 22:09:11 +00:00
|
|
|
|
|
|
|
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);
|
2013-02-18 06:18:46 +00:00
|
|
|
void ReleaseSpillLock(int mipsrega);
|
2013-01-25 22:09:11 +00:00
|
|
|
void ReleaseSpillLocks();
|
|
|
|
|
2013-02-10 11:14:55 +00:00
|
|
|
void MapRegV(int vreg, int flags);
|
2013-01-26 00:33:32 +00:00
|
|
|
void MapRegsV(int vec, VectorSize vsz, int flags);
|
|
|
|
void MapRegsV(const u8 *v, VectorSize vsz, int flags);
|
2013-02-16 11:15:22 +00:00
|
|
|
void SpillLockV(int vreg) {
|
|
|
|
SpillLock(vreg + 32);
|
|
|
|
}
|
2013-01-25 22:09:11 +00:00
|
|
|
void SpillLockV(const u8 *v, VectorSize vsz);
|
|
|
|
void SpillLockV(int vec, VectorSize vsz);
|
2013-02-18 06:37:56 +00:00
|
|
|
void ReleaseSpillLockV(int vreg) {
|
|
|
|
ReleaseSpillLock(vreg + 32);
|
|
|
|
}
|
2013-01-25 22:09:11 +00:00
|
|
|
|
2013-08-16 06:13:40 +00:00
|
|
|
void GetState(FPURegCacheState &state) const;
|
|
|
|
void RestoreState(const FPURegCacheState state);
|
|
|
|
|
2013-01-25 22:09:11 +00:00
|
|
|
MIPSState *mips;
|
|
|
|
|
2013-02-16 11:15:22 +00:00
|
|
|
void FlushX(X64Reg reg);
|
2013-09-28 12:01:26 +00:00
|
|
|
X64Reg GetFreeXReg();
|
2013-11-27 21:45:17 +00:00
|
|
|
|
2013-09-28 12:01:26 +00:00
|
|
|
private:
|
2013-01-25 22:09:11 +00:00
|
|
|
const int *GetAllocationOrder(int &count);
|
2013-10-24 15:23:33 +00:00
|
|
|
void SetupInitialRegs();
|
2013-01-25 22:09:11 +00:00
|
|
|
|
|
|
|
MIPSCachedFPReg regs[NUM_MIPS_FPRS];
|
|
|
|
X64CachedFPReg xregs[NUM_X_FPREGS];
|
|
|
|
MIPSCachedFPReg *vregs;
|
|
|
|
|
2014-03-30 03:34:17 +00:00
|
|
|
bool pendingFlush;
|
2013-10-24 15:23:33 +00:00
|
|
|
bool initialReady;
|
|
|
|
MIPSCachedFPReg regsInitial[NUM_MIPS_FPRS];
|
|
|
|
X64CachedFPReg xregsInitial[NUM_X_FPREGS];
|
|
|
|
|
2013-02-18 06:18:46 +00:00
|
|
|
// TEMP0, etc. are swapped in here if necessary (e.g. on x86.)
|
|
|
|
static u32 tempValues[NUM_TEMPS];
|
|
|
|
|
2013-01-25 22:09:11 +00:00
|
|
|
XEmitter *emit;
|
|
|
|
};
|