mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-21 10:19:04 +02:00
Kernel code's loads and stores may reach hardware registers, which only the interpreter handled, so the IR frontend sent every one of them (in kernel blocks, only run by the VSH boot) to it. They now become LoadKernel*/StoreKernel* IR ops (one per size, like the plain loads and stores), which check at run time whether the address is memory or a hardware register (MMIO). After constant propagation, ResolveKernelMemory turns the ones at a constant address that's plain memory into ordinary loads and stores, so only the hardware registers, and addresses only known at run time, stay checked. The native JITs run the two ops through their IR fallback. They're barriers, since a register access has side effects, but can't change the GPRs, so constants stay known across. The kernel mode check for MMIO moves to the interpreter, the only caller that can run user code there. TestMMIO covers the new paths and which accesses stay checked. Under --vsh, the JIT and the interpreter now make the same unhandled register accesses, in the same numbers. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
173 lines
4.7 KiB
C++
173 lines
4.7 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 "Core/MemMap.h"
|
|
#include "Core/MIPS/MIPS.h"
|
|
#include "Core/MIPS/MIPSAnalyst.h"
|
|
#include "Core/MIPS/MIPSCodeUtils.h"
|
|
#include "Core/MIPS/IR/IRFrontend.h"
|
|
#include "Core/MIPS/IR/IRRegCache.h"
|
|
|
|
#define _RS MIPS_GET_RS(op)
|
|
#define _RT MIPS_GET_RT(op)
|
|
#define _RD MIPS_GET_RD(op)
|
|
#define _FS MIPS_GET_FS(op)
|
|
#define _FT MIPS_GET_FT(op)
|
|
#define _FD MIPS_GET_FD(op)
|
|
#define _SA MIPS_GET_SA(op)
|
|
#define _POS ((op>> 6) & 0x1F)
|
|
#define _SIZE ((op>>11) & 0x1F)
|
|
#define _IMM16 (signed short)(op & 0xFFFF)
|
|
#define _IMM26 (op & 0x03FFFFFF)
|
|
|
|
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
|
|
// Currently known non working ones should have DISABLE.
|
|
|
|
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
|
|
#define CONDITIONAL_DISABLE(flag) if (opts.disableFlags & (uint32_t)JitDisable::flag) { Comp_Generic(op); return; }
|
|
#define DISABLE { Comp_Generic(op); return; }
|
|
#define INVALIDOP { Comp_Generic(op); return; }
|
|
|
|
namespace MIPSComp {
|
|
void IRFrontend::Comp_ITypeMem(MIPSOpcode op) {
|
|
CONDITIONAL_DISABLE(LSU);
|
|
|
|
int offset = _IMM16;
|
|
MIPSGPReg rt = _RT;
|
|
MIPSGPReg rs = _RS;
|
|
int o = op >> 26;
|
|
|
|
CheckMemoryBreakpoint(rs, offset);
|
|
|
|
if (((op >> 29) & 1) == 0 && rt == MIPS_REG_ZERO) {
|
|
// Don't load anything into $zr
|
|
return;
|
|
}
|
|
|
|
if (js.kernelMode) {
|
|
// Kernel code may reach hardware registers. ResolveKernelMemory turns the plain memory
|
|
// accesses back into ordinary ones where it can tell. The rest go to the interpreter.
|
|
switch (o) {
|
|
case 32: ir.Write(IROp::LoadKernel8Ext, rt, rs, 0, offset); return;
|
|
case 33: ir.Write(IROp::LoadKernel16Ext, rt, rs, 0, offset); return;
|
|
case 35: ir.Write(IROp::LoadKernel32, rt, rs, 0, offset); return;
|
|
case 36: ir.Write(IROp::LoadKernel8, rt, rs, 0, offset); return;
|
|
case 37: ir.Write(IROp::LoadKernel16, rt, rs, 0, offset); return;
|
|
case 40: ir.Write(IROp::StoreKernel8, rt, rs, 0, offset); return;
|
|
case 41: ir.Write(IROp::StoreKernel16, rt, rs, 0, offset); return;
|
|
case 43: ir.Write(IROp::StoreKernel32, rt, rs, 0, offset); return;
|
|
default:
|
|
DISABLE;
|
|
}
|
|
}
|
|
|
|
switch (o) {
|
|
// Load
|
|
case 35:
|
|
ir.Write(IROp::Load32, rt, rs, 0, offset);
|
|
break;
|
|
case 37:
|
|
ir.Write(IROp::Load16, rt, rs, 0, offset);
|
|
break;
|
|
case 33:
|
|
ir.Write(IROp::Load16Ext, rt, rs, 0, offset);
|
|
break;
|
|
case 36:
|
|
ir.Write(IROp::Load8, rt, rs, 0, offset);
|
|
break;
|
|
case 32:
|
|
ir.Write(IROp::Load8Ext, rt, rs, 0, offset);
|
|
break;
|
|
// Store
|
|
case 43:
|
|
ir.Write(IROp::Store32, rt, rs, 0, offset);
|
|
break;
|
|
case 41:
|
|
ir.Write(IROp::Store16, rt, rs, 0, offset);
|
|
break;
|
|
case 40:
|
|
ir.Write(IROp::Store8, rt, rs, 0, offset);
|
|
break;
|
|
|
|
case 34: //lwl
|
|
ir.Write(IROp::Load32Left, rt, rs, 0, offset);
|
|
break;
|
|
case 38: //lwr
|
|
ir.Write(IROp::Load32Right, rt, rs, 0, offset);
|
|
break;
|
|
case 42: //swl
|
|
ir.Write(IROp::Store32Left, rt, rs, 0, offset);
|
|
break;
|
|
case 46: //swr
|
|
ir.Write(IROp::Store32Right, rt, rs, 0, offset);
|
|
break;
|
|
|
|
default:
|
|
INVALIDOP;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void IRFrontend::Comp_StoreSync(MIPSOpcode op) {
|
|
CONDITIONAL_DISABLE(LSU);
|
|
|
|
int offset = _IMM16;
|
|
MIPSGPReg rt = _RT;
|
|
MIPSGPReg rs = _RS;
|
|
// Note: still does something even if loading to zero.
|
|
|
|
CheckMemoryBreakpoint(rs, offset);
|
|
|
|
if (js.kernelMode) {
|
|
// Send all memory accesses to the interpreter in kernel mode.
|
|
// TODO: Do something faster - but it hardly matters, currently this is VSH-only.
|
|
DISABLE;
|
|
return;
|
|
}
|
|
|
|
switch (op >> 26) {
|
|
case 48: // ll
|
|
ir.Write(IROp::Load32Linked, rt, rs, 0, offset);
|
|
break;
|
|
|
|
case 56: // sc
|
|
ir.Write(IROp::Store32Conditional, rt, rs, 0, offset);
|
|
break;
|
|
|
|
default:
|
|
INVALIDOP;
|
|
}
|
|
}
|
|
|
|
void IRFrontend::Comp_Cache(MIPSOpcode op) {
|
|
CONDITIONAL_DISABLE(LSU);
|
|
|
|
int func = (op >> 16) & 0x1F;
|
|
|
|
// See Int_Cache for the definitions.
|
|
switch (func) {
|
|
case 24: break;
|
|
case 25: break;
|
|
case 27: break;
|
|
case 30: break;
|
|
default:
|
|
// Fall back to the interpreter.
|
|
DISABLE;
|
|
}
|
|
}
|
|
}
|