mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-15 07:20:31 +02:00
The JITs used to find a block by overwriting its first instruction with an "emuhack" holding the offset into the code space, and replacement functions installed their own emuhack the same way. Anything that read code (savestates, the debugger, memory dumps, cheats, the function hasher, games reading their own code) had to translate them back. Blocks now live in a block shadow: one u32 per guest instruction, indexed by the PC masked with BLOCK_SHADOW_MASK (1GB on 64-bit, folding the kernel and uncached mirrors; 256MB on 32-bit, iOS and Switch, where address space is short), with the low bits cleared so a misaligned PC can't read across two entries. Only the ranges under memory views are committed, so a bad PC still faults in the dispatcher, and the shadow is reserved before the memory map so it can't land in a gap of the 4GB window. Entries hold full 32-bit offsets, lifting the 16MB limit. The dispatchers on every backend, and the IR interpreter's loop, look blocks up there. A hooked instruction without a compiled block gets the JIT's hook value in its entry: the offset of the dispatcher's compile path (1 for the interpreters, the IR arena keeping that offset empty), so dispatching there compiles a block, and the frontends substitute the hook. Compiling replaces the entry with the block's, and destroying the block puts the hook value back. The hook value is rewritten when the JIT changes or regenerates its dispatcher. Hooks live in a map keyed by the masked address, and are dropped when the game overwrites the hooked instruction, or when an icache invalidation finds the function's hash (immediates masked, as when matching it) changed. Those checks run when queued invalidations are processed, since cheats and the debugger queue them before writing. Installing and removing hooks invalidates deferred, as it happens in HLE calls that can run inside a block. The cache instruction goes through MIPSState, so it drops changed hooks too. With clean memory, the save/clear/restore around savestates, memory reads and dumps goes away, as do the source-range invalidations in the memcpy replacements, sceDmac and the cheats. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
146 lines
4.8 KiB
C++
146 lines
4.8 KiB
C++
#pragma once
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Core/MIPS/JitCommon/JitCommon.h"
|
|
#include "Core/MIPS/JitCommon/JitState.h"
|
|
#include "Core/MIPS/MIPSVFPUUtils.h"
|
|
#include "Core/MIPS/IR/IRInst.h"
|
|
|
|
namespace MIPSComp {
|
|
|
|
class IRFrontend : public MIPSFrontendInterface {
|
|
public:
|
|
IRFrontend(bool startDefaultPrefix);
|
|
void Comp_Generic(MIPSOpcode op) override;
|
|
|
|
void Comp_ReplacementFunc(MIPSOpcode op) override;
|
|
|
|
// Ops
|
|
void Comp_ITypeMem(MIPSOpcode op) override;
|
|
void Comp_StoreSync(MIPSOpcode op) override;
|
|
void Comp_Cache(MIPSOpcode op) override;
|
|
|
|
void Comp_RelBranch(MIPSOpcode op) override;
|
|
void Comp_RelBranchRI(MIPSOpcode op) override;
|
|
void Comp_FPUBranch(MIPSOpcode op) override;
|
|
void Comp_FPULS(MIPSOpcode op) override;
|
|
void Comp_FPUComp(MIPSOpcode op) override;
|
|
void Comp_Jump(MIPSOpcode op) override;
|
|
void Comp_JumpReg(MIPSOpcode op) override;
|
|
void Comp_Syscall(MIPSOpcode op) override;
|
|
void Comp_Break(MIPSOpcode op) override;
|
|
|
|
void Comp_IType(MIPSOpcode op) override;
|
|
void Comp_RType2(MIPSOpcode op) override;
|
|
void Comp_RType3(MIPSOpcode op) override;
|
|
void Comp_ShiftType(MIPSOpcode op) override;
|
|
void Comp_Allegrex(MIPSOpcode op) override;
|
|
void Comp_Allegrex2(MIPSOpcode op) override;
|
|
void Comp_VBranch(MIPSOpcode op) override;
|
|
void Comp_MulDivType(MIPSOpcode op) override;
|
|
void Comp_Special3(MIPSOpcode op) override;
|
|
|
|
void Comp_FPU3op(MIPSOpcode op) override;
|
|
void Comp_FPU2op(MIPSOpcode op) override;
|
|
void Comp_mxc1(MIPSOpcode op) override;
|
|
|
|
void Comp_DoNothing(MIPSOpcode op) override;
|
|
|
|
void Comp_SV(MIPSOpcode op) override;
|
|
void Comp_SVQ(MIPSOpcode op) override;
|
|
void Comp_VPFX(MIPSOpcode op) override;
|
|
void Comp_VVectorInit(MIPSOpcode op) override;
|
|
void Comp_VMatrixInit(MIPSOpcode op) override;
|
|
void Comp_VDot(MIPSOpcode op) override;
|
|
void Comp_VecDo3(MIPSOpcode op) override;
|
|
void Comp_VV2Op(MIPSOpcode op) override;
|
|
void Comp_Mftv(MIPSOpcode op) override;
|
|
void Comp_Vmfvc(MIPSOpcode op) override;
|
|
void Comp_Vmtvc(MIPSOpcode op) override;
|
|
void Comp_Vmmov(MIPSOpcode op) override;
|
|
void Comp_VScl(MIPSOpcode op) override;
|
|
void Comp_Vmmul(MIPSOpcode op) override;
|
|
void Comp_Vmscl(MIPSOpcode op) override;
|
|
void Comp_Vtfm(MIPSOpcode op) override;
|
|
void Comp_VHdp(MIPSOpcode op) override;
|
|
void Comp_VCrs(MIPSOpcode op) override;
|
|
void Comp_VDet(MIPSOpcode op) override;
|
|
void Comp_Vi2x(MIPSOpcode op) override;
|
|
void Comp_Vx2i(MIPSOpcode op) override;
|
|
void Comp_Vf2i(MIPSOpcode op) override;
|
|
void Comp_Vi2f(MIPSOpcode op) override;
|
|
void Comp_Vh2f(MIPSOpcode op) override;
|
|
void Comp_Vcst(MIPSOpcode op) override;
|
|
void Comp_Vhoriz(MIPSOpcode op) override;
|
|
void Comp_VRot(MIPSOpcode op) override;
|
|
void Comp_VIdt(MIPSOpcode op) override;
|
|
void Comp_Vcmp(MIPSOpcode op) override;
|
|
void Comp_Vcmov(MIPSOpcode op) override;
|
|
void Comp_Viim(MIPSOpcode op) override;
|
|
void Comp_Vfim(MIPSOpcode op) override;
|
|
void Comp_VCrossQuat(MIPSOpcode op) override;
|
|
void Comp_Vsgn(MIPSOpcode op) override;
|
|
void Comp_Vocp(MIPSOpcode op) override;
|
|
void Comp_ColorConv(MIPSOpcode op) override;
|
|
void Comp_Vbfy(MIPSOpcode op) override;
|
|
|
|
int Replace_fabsf() override;
|
|
void DoState(PointerWrap &p);
|
|
bool CheckRounding(u32 blockAddress); // returns true if we need a do-over
|
|
|
|
void DoJit(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes);
|
|
|
|
void EatPrefix() override {
|
|
js.EatPrefix();
|
|
}
|
|
|
|
void SetOptions(const IROptions &o) {
|
|
opts = o;
|
|
}
|
|
|
|
private:
|
|
void RestoreRoundingMode(bool force = false);
|
|
void ApplyRoundingMode(bool force = false);
|
|
void UpdateRoundingMode();
|
|
|
|
void FlushAll();
|
|
void FlushPrefixV();
|
|
|
|
u32 GetCompilerPC();
|
|
void CompileDelaySlot();
|
|
void EatInstruction(MIPSOpcode op);
|
|
MIPSOpcode GetOffsetInstruction(int offset);
|
|
|
|
void CheckBreakpoint(u32 addr);
|
|
void CheckMemoryBreakpoint(int rs, int offset);
|
|
|
|
// Utility compilation functions
|
|
void BranchFPFlag(MIPSOpcode op, IRComparison cc, bool likely);
|
|
void BranchVFPUFlag(MIPSOpcode op, IRComparison cc, bool likely);
|
|
void BranchRSZeroComp(MIPSOpcode op, IRComparison cc, bool andLink, bool likely);
|
|
void BranchRSRTComp(MIPSOpcode op, IRComparison cc, bool likely);
|
|
|
|
// Utilities to reduce duplicated code
|
|
void CompShiftImm(MIPSOpcode op, IROp shiftType, int sa);
|
|
void CompShiftVar(MIPSOpcode op, IROp shiftType);
|
|
|
|
void ApplyPrefixST(u8 *vregs, u32 prefix, VectorSize sz, int tempReg);
|
|
void ApplyPrefixD(u8 *vregs, VectorSize sz, int vectorReg);
|
|
void ApplyPrefixDMask(u8 *vregs, VectorSize sz, int vectorReg);
|
|
void GetVectorRegsPrefixS(u8 *regs, VectorSize sz, int vectorReg);
|
|
void GetVectorRegsPrefixT(u8 *regs, VectorSize sz, int vectorReg);
|
|
void GetVectorRegsPrefixD(u8 *regs, VectorSize sz, int vectorReg);
|
|
void GetVectorRegs(u8 regs[4], VectorSize N, int vectorReg);
|
|
void GetMatrixRegs(u8 regs[16], MatrixSize N, int matrixReg);
|
|
|
|
// State
|
|
JitState js;
|
|
IRWriter ir;
|
|
IROptions opts{};
|
|
|
|
int dontLogBlocks = 0;
|
|
int logBlocks = 0;
|
|
};
|
|
|
|
} // namespace
|