mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-22 10:49:05 +02:00
Their IR-based replacements stay. The JIT core now creates the IR JIT on both, so "JIT using IR" is the same as "JIT" on every architecture (ARM32 still uses its legacy JIT for both). The settings no longer offer it, a saved JIT_IR loads as JIT, and the JIT harness and CPUEquivalence fuzzer drop the duplicate run. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
313 lines
8.3 KiB
C++
313 lines
8.3 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/.
|
|
|
|
#pragma once
|
|
|
|
#include "ppsspp_config.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Core/Opcode.h"
|
|
|
|
class PointerWrap;
|
|
|
|
typedef Memory::Opcode MIPSOpcode;
|
|
|
|
// A fake opcode the CPU cores fetch in place of an instruction with a replacement function (see
|
|
// ReplaceTables.h), with the replacement's index in the low bits. It's never in PSP memory. Opcode
|
|
// 26, which the Allegrex doesn't have, decodes it as CallRepl.
|
|
#define MIPS_CALL_REPLACEMENT_OP 0x6A000000
|
|
#define MIPS_REPLACEMENT_INDEX_MASK 0x00FFFFFF
|
|
|
|
enum MIPSGPReg {
|
|
MIPS_REG_ZERO=0,
|
|
MIPS_REG_COMPILER_SCRATCH=1,
|
|
|
|
MIPS_REG_V0=2,
|
|
MIPS_REG_V1=3,
|
|
|
|
MIPS_REG_A0=4,
|
|
MIPS_REG_A1=5,
|
|
MIPS_REG_A2=6,
|
|
MIPS_REG_A3=7,
|
|
MIPS_REG_A4=8,
|
|
MIPS_REG_A5=9,
|
|
|
|
MIPS_REG_T0=8, //alternate names for A4/A5
|
|
MIPS_REG_T1=9,
|
|
MIPS_REG_T2=10,
|
|
MIPS_REG_T3=11,
|
|
MIPS_REG_T4=12,
|
|
MIPS_REG_T5=13,
|
|
MIPS_REG_T6=14,
|
|
MIPS_REG_T7=15,
|
|
|
|
MIPS_REG_S0=16,
|
|
MIPS_REG_S1=17,
|
|
MIPS_REG_S2=18,
|
|
MIPS_REG_S3=19,
|
|
MIPS_REG_S4=20,
|
|
MIPS_REG_S5=21,
|
|
MIPS_REG_S6=22,
|
|
MIPS_REG_S7=23,
|
|
MIPS_REG_T8=24,
|
|
MIPS_REG_T9=25,
|
|
MIPS_REG_K0=26,
|
|
MIPS_REG_K1=27,
|
|
MIPS_REG_GP=28,
|
|
MIPS_REG_SP=29,
|
|
MIPS_REG_FP=30,
|
|
MIPS_REG_RA=31,
|
|
|
|
// Not real regs, just for convenience/jit mapping.
|
|
// NOTE: These are not the same as the offsets the IR has to use!
|
|
MIPS_REG_HI = 32,
|
|
MIPS_REG_LO = 33,
|
|
MIPS_REG_FPCOND = 34,
|
|
MIPS_REG_VFPUCC = 35,
|
|
|
|
MIPS_REG_INVALID=-1,
|
|
};
|
|
|
|
enum {
|
|
VFPU_CTRL_SPREFIX,
|
|
VFPU_CTRL_TPREFIX,
|
|
VFPU_CTRL_DPREFIX,
|
|
VFPU_CTRL_CC,
|
|
VFPU_CTRL_INF4,
|
|
VFPU_CTRL_RSV5,
|
|
VFPU_CTRL_RSV6,
|
|
VFPU_CTRL_REV,
|
|
VFPU_CTRL_RCX0,
|
|
VFPU_CTRL_RCX1,
|
|
VFPU_CTRL_RCX2,
|
|
VFPU_CTRL_RCX3,
|
|
VFPU_CTRL_RCX4,
|
|
VFPU_CTRL_RCX5,
|
|
VFPU_CTRL_RCX6,
|
|
VFPU_CTRL_RCX7,
|
|
|
|
VFPU_CTRL_MAX,
|
|
//unknown....
|
|
};
|
|
|
|
enum VCondition
|
|
{
|
|
VC_FL,
|
|
VC_EQ,
|
|
VC_LT,
|
|
VC_LE,
|
|
VC_TR,
|
|
VC_NE,
|
|
VC_GE,
|
|
VC_GT,
|
|
VC_EZ,
|
|
VC_EN,
|
|
VC_EI,
|
|
VC_ES,
|
|
VC_NZ,
|
|
VC_NN,
|
|
VC_NI,
|
|
VC_NS
|
|
};
|
|
|
|
// fcr31, the FPU control/status register, in the standard MIPS layout: bits 0-1 are the
|
|
// rounding mode, 2-6 the sticky Flags, 7-11 the trap Enables, 12-16 the per-instruction Cause,
|
|
// 23 the condition flag (mirrored in fpcond) and 24 flush-to-zero. Each of the three 5-bit
|
|
// fields holds the IEEE exceptions in the same order (inexact, underflow, overflow, divide by
|
|
// zero, invalid operation); only divide by zero is implemented so far, and only in the interpreter.
|
|
enum {
|
|
FCR31_FLAG_DIV_BY_ZERO = 1 << 5,
|
|
FCR31_ENABLE_DIV_BY_ZERO = 1 << 10,
|
|
FCR31_CAUSE_DIV_BY_ZERO = 1 << 15,
|
|
};
|
|
|
|
// In memory, we order the VFPU registers differently.
|
|
// Games use columns a whole lot more than rows, and it would thus be good if columns
|
|
// were contiguous in memory. Also, matrices aren't but should be.
|
|
extern u8 voffset[128];
|
|
extern u8 fromvoffset[128];
|
|
|
|
enum class CPUCore;
|
|
|
|
// Only icache invalidations currently, so no type field.
|
|
struct PendingCacheOperation {
|
|
u32 addr;
|
|
u32 size;
|
|
};
|
|
|
|
class MIPSState {
|
|
public:
|
|
MIPSState();
|
|
~MIPSState();
|
|
|
|
void Init();
|
|
void Shutdown();
|
|
void Reset();
|
|
void UpdateCore(CPUCore desired);
|
|
|
|
void DoState(PointerWrap &p);
|
|
|
|
// MUST start with r and be followed by f, v, and t!
|
|
u32 r[32];
|
|
union {
|
|
float f[32];
|
|
u32 fi[32];
|
|
int fs[32];
|
|
};
|
|
union {
|
|
float v[128];
|
|
u32 vi[128];
|
|
};
|
|
|
|
// Register-allocated JIT Temps don't get flushed so we don't reserve space for them.
|
|
// However, the IR interpreter needs some temps that can stick around between ops.
|
|
// Can be indexed through r[] using indices 192+, thanks to predictable struct layout.
|
|
// Unfortunately, UBSAN isn't too happy about these.
|
|
u32 t[16]; //192
|
|
|
|
// If vfpuCtrl (prefixes) get mysterious values, check the VFPU regcache code.
|
|
u32 vfpuCtrl[16]; // 208
|
|
|
|
float vt[16]; //224 TODO: VFPU temp
|
|
|
|
// ARM64 wants lo/hi to be aligned to 64 bits from the base of this struct.
|
|
u32 padLoHi; // 240
|
|
|
|
union {
|
|
struct {
|
|
u32 pc; //241
|
|
|
|
u32 lo; //242
|
|
u32 hi; //243
|
|
|
|
u32 fcr31; //244 fpu control register
|
|
u32 fpcond; //245 cache the cond flag of fcr31 (& 1 << 23)
|
|
};
|
|
u32 other[6];
|
|
};
|
|
|
|
u32 nextPC;
|
|
int downcount; // This really doesn't belong here, it belongs in CoreTiming. But you gotta do what you gotta do, this needs to be reachable in the JITs without additional pointers.
|
|
|
|
bool inDelaySlot;
|
|
int llBit; // ll/sc
|
|
u32 temp; // can be used to save temporaries during calculations when we need more than R0 and R1
|
|
u32 mxcsrTemp;
|
|
// Temporary used around delay slots and similar.
|
|
u64 saved_flags;
|
|
|
|
// Debug stuff
|
|
u32 debugCount; // can be used to count basic blocks before crashes, etc.
|
|
|
|
// Temps needed for JitBranch.cpp experiments
|
|
u32 intBranchExit;
|
|
u32 jitBranchExit;
|
|
|
|
u32 savedPC;
|
|
|
|
alignas(16) u32 vcmpResult[4];
|
|
|
|
float sincostemp[2];
|
|
|
|
static const u32 FCR0_VALUE = 0x00003351;
|
|
|
|
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
|
|
enum {
|
|
NUM_X86_FPU_TEMPS = 16,
|
|
};
|
|
// FPU TEMP0, etc. are swapped in here if necessary (e.g. on x86.)
|
|
float tempValues[NUM_X86_FPU_TEMPS];
|
|
#endif
|
|
|
|
u8 VfpuWriteMask() const {
|
|
return (vfpuCtrl[VFPU_CTRL_DPREFIX] >> 8) & 0xF;
|
|
}
|
|
bool VfpuWriteMask(int i) const {
|
|
return (vfpuCtrl[VFPU_CTRL_DPREFIX] >> (8 + i)) & 1;
|
|
}
|
|
|
|
bool HasDefaultPrefix() const;
|
|
|
|
void SingleStep();
|
|
int RunLoopUntil(u64 globalTicks);
|
|
|
|
// To clear jit caches, etc.
|
|
|
|
// The immediate functions have the risk of invalidating the currently running block -
|
|
// might not behave correctly in all cases. Use Deferred when possible.
|
|
void InvalidateICacheRangeImmediate(u32 address, u32 length);
|
|
|
|
// Actual clearing is deferred until execution begins again.
|
|
void InvalidateICacheRangeDeferred(u32 address, u32 length);
|
|
void ClearJitCacheDeferred();
|
|
|
|
void ProcessPendingInvalidates();
|
|
|
|
// Don't make private, see the static_assert below.
|
|
// Doesn't need save stating.
|
|
std::vector<PendingCacheOperation> pendingInvalidates_;
|
|
bool invalidateAll_ = false;
|
|
};
|
|
|
|
// Access control ('private') break standard layout. Let's not allow it, to avoid excessive
|
|
// compiler warnings. Hm, this seems to go wrong on MSVC in debug builds...
|
|
#ifndef _MSC_VER
|
|
static_assert(std::is_standard_layout_v<MIPSState>, "the JITs use offsetof on MIPSState");
|
|
#endif
|
|
|
|
class MIPSDebugInterface;
|
|
|
|
// The one we are compiling or running currently
|
|
// TODO: These globals should be refactored away.
|
|
extern MIPSState *currentMIPS;
|
|
extern MIPSDebugInterface *currentDebugMIPS;
|
|
extern MIPSState mipsr4k;
|
|
|
|
// With FS set, the host flushes a denormal input to a float->int conversion on ARM64 and x86 (DAZ),
|
|
// but ARM32's libm rounds with integer code that doesn't, so it's flushed here
|
|
// (cpu/fpu/denormal, cpu/vfpu/fcr31).
|
|
inline float FlushForConversion(const MIPSState *mips, float f) {
|
|
#if PPSSPP_ARCH(ARM)
|
|
u32 bits;
|
|
memcpy(&bits, &f, 4);
|
|
if ((mips->fcr31 & 0x01000000) != 0 && (bits & 0x7F800000) == 0) {
|
|
bits &= 0x80000000;
|
|
memcpy(&f, &bits, 4);
|
|
}
|
|
#endif
|
|
return f;
|
|
}
|
|
|
|
extern const float cst_constants[32];
|
|
|
|
// The guest's rounding mode and flush-to-zero flag (fcr31 bits 0-1 and 24) are emulated by putting
|
|
// the *host* FPU into the matching mode, since we do the arithmetic with plain host float ops.
|
|
// That mode must not be left on while running anything that isn't emulating a guest instruction -
|
|
// HLE syscalls, replacement functions, the GPU - so an emulation loop applies it on entry and
|
|
// restores it before calling out, the way the JITs do (see Jit::ApplyRoundingMode).
|
|
// Both are cheap when the guest is in the default mode (by far the common case): they do nothing.
|
|
// Otherwise apply reads the register once and restore just writes it back.
|
|
void ApplyHostRoundingMode(const MIPSState *mips);
|
|
// force also clears our bits when apply didn't set them, for callers that may run under a native
|
|
// JIT, whose own asm stubs apply the mode.
|
|
void RestoreHostRoundingMode(bool force = false);
|