#pragma once
#include "Common/x64Emitter.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSVFPUUtils.h"
#undef MAP_NOINIT
#include "ppsspp_config.h"
enum {
TEMP0 = 32 + 128,
NUM_MIPS_FPRS = 32 + 128 + NUM_X86_FPU_TEMPS,
};
#if PPSSPP_ARCH(AMD64)
#define NUM_X_FPREGS 16
#elif PPSSPP_ARCH(X86)
#define NUM_X_FPREGS 8
#endif
namespace MIPSAnalyst {
struct AnalysisResults;
};
struct X64CachedFPReg {
union {
int mipsReg;
int mipsRegs[4];
};
bool dirty;
};
struct MIPSCachedFPReg {
Gen::OpArg location;
int lane;
bool away;
u8 locked;
bool tempLocked;
};
struct FPURegCacheState {
MIPSCachedFPReg regs[NUM_MIPS_FPRS];
X64CachedFPReg xregs[NUM_X_FPREGS];
};
namespace MIPSComp {
struct JitOptions;
struct JitState;
}
enum {
MAP_DIRTY = 1,
MAP_NOINIT = 2 | MAP_DIRTY,
MAP_NOLOCK = 4,
};
class FPURegCache
{
public:
FPURegCache();
~FPURegCache() {}
void Start(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo, MIPSAnalyst::AnalysisResults &stats, bool useRip);
void MapReg(int preg, bool doLoad = true, bool makeDirty = true);
void StoreFromRegister(int preg);
void StoreFromRegisterV(int preg) {
StoreFromRegister(preg + 32);
}
Gen::OpArg GetDefaultLocation(int reg) const;
void DiscardR(int freg);
void DiscardV(int vreg) {
DiscardR(vreg + 32);
}
void DiscardVS(int vreg);
bool IsTempX(Gen::X64Reg xreg);
int GetTempR();
int GetTempV() {
return GetTempR() - 32;
}
int GetTempVS(u8 *v, VectorSize vsz);
void SetEmitter(Gen::XEmitter *emitter) {emit = emitter;}
void FlushRemap(int oldreg, int newreg);
void Flush();
int SanityCheck() const;
const Gen::OpArg &R(int freg) const {return regs[freg].location;}
const Gen::OpArg &V(int vreg) const {
_dbg_assert_msg_(vregs[vreg].lane == 0, "SIMD reg %d used as V reg (use VS instead). pc=%08x", vreg, mips_->pc);
return vregs[vreg].location;
}
const Gen::OpArg &VS(const u8 *vs) const {
_dbg_assert_msg_(vregs[vs[0]].lane != 0, "V reg %d used as VS reg (use V instead). pc=%08x", vs[0], mips_->pc);
return vregs[vs[0]].location;
}
Gen::X64Reg RX(int freg) const {
if (regs[freg].away && regs[freg].location.IsSimpleReg())
return regs[freg].location.GetSimpleReg();
_assert_msg_(false, "Not so simple - f%i", freg);
return (Gen::X64Reg)-1;
}
Gen::X64Reg VX(int vreg) const {
_dbg_assert_msg_(vregs[vreg].lane == 0, "SIMD reg %d used as V reg (use VSX instead). pc=%08x", vreg, mips_->pc);
if (vregs[vreg].away && vregs[vreg].location.IsSimpleReg())
return vregs[vreg].location.GetSimpleReg();
_assert_msg_(false, "Not so simple - v%i", vreg);
return (Gen::X64Reg)-1;
}
Gen::X64Reg VSX(const u8 *vs) const {
_dbg_assert_msg_(vregs[vs[0]].lane != 0, "V reg %d used as VS reg (use VX instead). pc=%08x", vs[0], mips_->pc);
if (vregs[vs[0]].away && vregs[vs[0]].location.IsSimpleReg())
return vregs[vs[0]].location.GetSimpleReg();
_assert_msg_(false, "Not so simple - v%i", vs[0]);
return (Gen::X64Reg)-1;
}
void R(Gen::X64Reg r);
void SpillLock(int p1, int p2=0xff, int p3=0xff, int p4=0xff);
void ReleaseSpillLock(int mipsreg);
void ReleaseSpillLocks();
bool IsMapped(int r) {
return R(r).IsSimpleReg();
}
bool IsMappedV(int v) {
return vregs[v].lane == 0 && V(v).IsSimpleReg();
}
bool IsMappedVS(u8 v) {
return vregs[v].lane != 0 && VS(&v).IsSimpleReg();
}
bool IsMappedVS(const u8 *v, VectorSize vsz);
bool CanMapVS(const u8 *v, VectorSize vsz);
void MapRegV(int vreg, int flags);
void MapRegsV(int vec, VectorSize vsz, int flags);
void MapRegsV(const u8 *v, VectorSize vsz, int flags);
void SpillLockV(int vreg) {
SpillLock(vreg + 32);
}
void SpillLockV(const u8 *v, VectorSize vsz);
void SpillLockV(int vec, VectorSize vsz);
void ReleaseSpillLockV(int vreg) {
ReleaseSpillLock(vreg + 32);
}
void ReleaseSpillLockV(const u8 *vec, VectorSize sz);
void MapRegsVS(const u8 *v, VectorSize vsz, int flags);
bool TryMapRegsVS(const u8 *v, VectorSize vsz, int flags);
bool TryMapDirtyInVS(const u8 *vd, VectorSize vdsz, const u8 *vs, VectorSize vssz, bool avoidLoad = true);
bool TryMapDirtyInInVS(const u8 *vd, VectorSize vdsz, const u8 *vs, VectorSize vssz, const u8 *vt, VectorSize vtsz, bool avoidLoad = true);
void SimpleRegsV(const u8 *v, VectorSize vsz, int flags);
void SimpleRegsV(const u8 *v, MatrixSize msz, int flags);
void SimpleRegV(const u8 v, int flags);
void GetState(FPURegCacheState &state) const;
void RestoreState(const FPURegCacheState& state);
MIPSState *mips_ = nullptr;
void FlushX(Gen::X64Reg reg);
Gen::X64Reg GetFreeXReg();
int GetFreeXRegs(Gen::X64Reg *regs, int n, bool spill = true);
void Invariant() const;
private:
const int *GetAllocationOrder(int &count);
void SetupInitialRegs();
void ReduceSpillLock(int mreg);
void ReduceSpillLockV(int vreg) {
ReduceSpillLock(vreg + 32);
}
void ReduceSpillLockV(const u8 *vec, VectorSize sz);
Gen::X64Reg LoadRegsVS(const u8 *v, int n);
MIPSCachedFPReg regs[NUM_MIPS_FPRS]{};
X64CachedFPReg xregs[NUM_X_FPREGS]{};
MIPSCachedFPReg *vregs;
bool useRip_;
bool pendingFlush;
bool initialReady = false;
MIPSCachedFPReg regsInitial[NUM_MIPS_FPRS];
X64CachedFPReg xregsInitial[NUM_X_FPREGS];
Gen::XEmitter *emit = nullptr;
MIPSComp::JitState *js_;
MIPSComp::JitOptions *jo_;
};