Commit Graph
205 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 39813ffaf1 JITs: Notice a rounding mode that didn't come from a compiled ctc1
A JIT only switched modes around syscalls, and only took the mode into account
in conversions, once it had compiled a ctc1 (hasSetRounding). A mode from an
interpreted ctc1, a savestate made on another core, a core switch or a thread
switch went unnoticed, and HLE then ran in the guest's mode. UpdateFCR31()
now sets the flag when fcr31 isn't the default, and clears the block cache at
the next safe point, since blocks compiled before don't switch either. A new
JIT gets an UpdateFCR31() too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 09:26:28 +02:00
Henrik RydgårdandClaude Opus 5.5 241e781810 JIT: Switch rounding modes around interpreted ctc1 and syscalls only
Interpreted ops run in the guest's mode. A syscall left to the interpreter
(with JitDisable flags, say) is host code and now gets the host's mode, like a
compiled one, and an interpreted ctc1 to fcr31 now takes effect right away:
nothing set hasSetRounding for it, so the mode only reached the host at the
next time slice. Every other interpreted op skips the two calls it used to
make after it. The IR frontend's Comp_Generic, which the IR interpreter and the
IR native backends share, gets the same treatment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 09:26:28 +02:00
Henrik RydgårdandClaude Opus 5.5 7708f30d08 JIT: Run interpreter fallbacks in the guest's rounding mode
Comp_Generic restored the host's default mode around the interpreter call, so
an instruction the JIT doesn't compile rounded and flushed differently than in
the interpreter. Now the guest's mode stays in place, and is reapplied after the
call in case the instruction changed fcr31.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:49:00 +02:00
Henrik RydgårdandClaude Opus 5.5 5fa4a475f9 Rename the replacement pseudo-op, drop the leftover emuhack defines
With block entries out of memory, the only fake opcode left is the one the CPU cores fetch in
place of a hooked instruction. Call it MIPS_CALL_REPLACEMENT_OP, and its handlers
Int_CallReplacement and Dis_CallReplacement, and remove the sub-op defines and the unused
MIPS_IS_REPLACEMENT.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 10:33:30 +02:00
Henrik RydgårdandClaude Opus 5.5 6d122f416e Keep JIT block entries and replacement hooks out of PSP memory
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>
2026-10-11 10:33:30 +02:00
Henrik RydgårdandClaude Opus 5.5 937c100af8 Savestate: Defer operations while the GE debugger holds a list
The list belongs to the sceGe call still in progress, whose end would have
run on the loaded CPU state. Also stop the camera and GPS when a state has
them off, don't restart capture when saving, and fix a double free of the
pmp frame queue (it only holds the media engine's own frame).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:06 -06:00
Henrik RydgårdandClaude Opus 5 35a91b757a Move the temporary breakpoint out of the user's breakpoint list
step-over, step-out and run-until plant a one-shot breakpoint at the address
they want execution to return to. Keeping it in breakPoints_ alongside the
user's own meant the two kept colliding:

- Adding a log-only user breakpoint at the same address hijacked the temporary
  one. AddBreakPoint() didn't match across temp-ness so both existed, and then
  ChangeBreakPoint() looked up "the first enabled breakpoint at this address" -
  a log-only breakpoint isn't enabled, so the temporary one won and had its
  action overwritten to log-only. It lost PAUSE and the step never came back.
- RemoveBreakPoint() erased up to two entries per address to catch an
  overlapping temporary one, so deleting either deleted both - including the
  interpreter's cleanup path in CheckExecBreakpoints() taking the user's
  breakpoint with it.
- ExecBreakPoint() handled one breakpoint per address, so with both at the same
  address only one of them did anything: the step completed but the user's log
  line never printed.
- Nothing dropped it when something *else* stopped us first, so an interrupted
  step left a breakpoint armed at an address nobody was waiting for anymore,
  which later fired as a phantom stop.

It's a single TempBreakPoint member now, invisible to the breakpoint lists and
untouched by user edits. One is enough: step over/out and cross-thread step into
all require the CPU to already be stepping and resume it immediately, so only
one can be in flight, and run-until now replaces rather than stacking (two
pending run-untils had no coherent meaning, and the loser stayed armed).

Behavior follows what other debuggers do. Both breakpoints at an address are
evaluated independently and their actions combine, so a log-only breakpoint
logs without stopping and still lets the step finish. Core_Break() drops the
temporary breakpoint on any stop, whatever the reason - the same way gdb deletes
its step-resume breakpoint and lldb discards the thread plan.

Two things to be careful of, both covered by the new TempBreakpoints test:
HasBreakPoints() has to account for it, or the interpreter's checked run loop
and the JIT skip breakpoint checking entirely and a step with no user
breakpoints set never returns; and IsAddressBreakPoint() (user-facing, for the
lists and disassembly markers) is now separate from NeedsBreakCheckAt() (what
the JIT frontends and interpreter ask), since only the latter should see it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-17 00:29:26 +02:00
Henrik RydgårdandClaude Sonnet 5 ffad0acea1 Plumb an explicit MIPSState *mips through the interpreter's Int_* handlers
All ~82 MIPSInt::Int_* functions (Interpreter.cpp/.h,
InterpreterVFPU.cpp/.h) now take an explicit MIPSState *mips instead
of reaching for the global currentMIPS internally, along with their
file-local helpers (DelayBranchTo, SkipLikely, ApplySwizzleS/T,
ApplyPrefixD/ST, RetainInvalidSwizzleST, EatPrefixes). MIPSInterpretFunc,
Interpret(), ExecInstruction()/InterpreterDispatch.cpp (regenerated),
and RunUntilFast() all thread mips through accordingly.

Deliberately left on currentMIPS for now: MIPSVFPUUtils.cpp's
ReadVector/WriteVector/ReadMatrix/WriteMatrix/VFPURewritePrefix -
these are shared with every JIT backend's compile-time VFPU code, so
parameterizing them would balloon this into a JIT-wide refactor. This
is a partial refactor; that's the next boundary to push on.

Several JIT backends (x86 Jit.cpp, ARM/ArmJit.cpp, ARM64/Arm64Jit.cpp,
x86/X64IRJit.cpp, RiscV/RiscVJit.cpp, LoongArch64/LoongArch64Jit.cpp,
ARM64/Arm64IRJit.cpp) bake the raw interpreter function pointer
directly into JIT-generated machine code as their "fall back to the
interpreter for this one op" mechanism, with only a single argument
register set up for the call. Rather than hand-editing register
allocation across four architectures that can't be build-tested here,
added MIPSInterpretTrampoline(MIPSOpcode op) - a 1-arg wrapper around
MIPSInterpret(currentMIPS, op) - and pointed all 7 such call sites at
it instead, leaving that codegen untouched. Two other call sites
(JitLogMiss, JitBranchLog) were plain C++ calls and just got the
extra argument directly.

Verified (Windows x64): PPSSPPWindows/PPSSPPHeadless/UnitTest all
build clean, 49/49 unit tests pass. `test.py -g --graphics=software`:
interpreter 312/314 (cpu/fpu/fpu is the pre-existing, unrelated
interpreter-vs-JIT denormal difference; gpu/rendertarget/copy passes
standalone, so was cross-test state bleed in the batch run, not a
regression), default JIT 314/314, jit-ir 313/314 (gpu/vertices/morph
is an expected difference from the vertex decoder taking a different
mode with this core change, not a bug).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SKhm9wKEQzRUsx9mTrrtQ
2026-08-12 14:02:19 +02:00
Henrik RydgårdandClaude Sonnet 5 d718bda178 Rename MIPSInt/MIPSIntVFPU to Interpreter/InterpreterVFPU
Just a file rename (plus updating all build configs and includes) to
give the interpreter source files clearer names. No functional change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SKhm9wKEQzRUsx9mTrrtQ
2026-08-12 10:04:40 +02:00
Henrik Rydgård 749e3a02cb Jit: Add missing instruction address check from ARM jits 2026-02-26 10:37:12 +01:00
Henrik Rydgård 505a72bb3f Work around java exception seen in reporting. Buildfix. 2026-02-18 01:34:56 +01:00
Henrik Rydgård 8897b5914a Improve integrity checks in JIT (trying to catch an elusive crash) 2026-02-17 22:42:04 +01:00
Henrik Rydgård f0ba391a4e Remove the whole concept of proxy blocks from the block cache, and experimental uses of it. 2026-02-17 09:40:27 +01:00
Henrik Rydgård 9f5be16663 Jit: Remove the Jal-to-replacement optimization (really not all that benificial) 2026-02-17 09:40:27 +01:00
Henrik Rydgård 2797cfca56 Remove more vestiges of old experiments 2026-02-12 01:23:48 +01:00
Henrik Rydgård 44a2585753 Rename some function, remove an unused return value 2026-02-12 01:23:45 +01:00
Henrik Rydgård 8631e77db2 Delete some outdated profiler integrations 2026-01-01 13:55:30 +01:00
Henrik Rydgård 96c4a10e8c Add two new core states, rename RUNNING to RUNNING_CPU and similar for stepping. 2024-12-01 21:04:21 +01:00
Henrik Rydgård 7992ff4627 Make CBreakpoints an object 2024-11-25 00:22:53 +01:00
Henrik Rydgård e01ca5b057 Logging API change (refactor) (#19324)
* Rename LogType to Log

* Explicitly use the Log:: enum when logging. Allows for autocomplete when editing.

* Mac/ARM64 buildfix

* Do the same with the hle result log macros

* Rename the log names to mixed case while at it.

* iOS buildfix

* Qt buildfix attempt, ARM32 buildfix
2024-07-14 14:42:59 +02:00
Unknown W. Brackets 053831bf4d HLE: Add mechanics for sliced replacements. 2023-12-16 09:08:58 -08:00
Henrik Rydgård 84d0236bf4 Comment fixes 2023-09-27 12:31:17 +02:00
Henrik Rydgård 51d5026792 WriteExit: Assert on bad exit numbers 2023-09-26 19:39:48 +02:00
Henrik Rydgård e64d1e94fe add reporting to the invalid replacement op 2023-09-23 11:39:20 +02:00
Henrik Rydgård 8a90e94e74 Add an assert to try to track down a mysterious reported crash. 2023-09-21 12:08:16 +02:00
Unknown W. Brackets 74e5e43fdc jit: Skip known prefix writes.
If we already know what's in memory and it's default, we can skip
overwriting with default values.  This is common, actually.
2023-08-22 23:26:31 -07:00
Henrik Rydgård 4a4cd3d977 Add logging when loading a save state that has "unknown-prefix-mode" set 2023-06-14 10:23:23 +02:00
Henrik Rydgård 67a35d3476 Merge pull request #17356 from unknownbrackets/minor-cleanup
Cleanup some more string formats, mostly in debugger
2023-05-23 08:29:23 +02:00
Unknown W. Brackets 87217053ef Debugger: Correct PC if replacement breaks.
If a memory breakpoint hits within a replacement (like memcpy, memset) we
would previously move PC back to the jal in cases, which would break
things if you tried to resume.
2023-05-06 13:40:01 -07:00
Unknown W. Brackets 46101581c0 Core: Cleanup disasm buffer usage. 2023-04-29 09:07:25 -07:00
Henrik Rydgård 6945deec01 Replace a LOT of sprintf with snprintf, and a few strcpy with truncate_cpy 2023-04-28 21:04:05 +02:00
Henrik Rydgård 237c3ce128 Apply the fix to avoid jit clearing for rewind savestates to all platforms
Silly oversight made in commit 718cb9e .
2023-04-16 00:02:16 +02:00
Unknown W. Brackets 0f5859510e x86jit: Simplify memcheck handling.
Now it's mostly the same as the other jits.
2023-04-12 01:07:48 -07:00
Unknown W. Brackets 6df939034a Core: Cleanup some sign extensions for clarity. 2023-04-05 17:16:51 -07:00
Unknown W. Brackets b2798c7ada jit: Add more reasonable estimates for RX protect. 2022-11-20 10:55:35 -08:00
Unknown W. Brackets b9de1a44df jit: Reduce some include pollution.
Usually no need for all of MIPSAnalyst.
2022-10-27 23:26:44 -07:00
Unknown W. Brackets fa5f9d5e74 jit: Consistently check range on invalidate.
We did this on x86, we should do it everywhere or nowhere.
2022-10-15 18:30:13 -07:00
Henrik Rydgård 26f6afbfa7 Followup to #16205, fix one more instance of the problem. 2022-10-12 01:02:54 +02:00
Henrik Rydgård df5b51990d ArmJit: Save/restore downcount where needed, we missed a few cases. 2022-10-11 15:50:37 +02:00
Unknown W. Brackets 52e9856b4b Debugger: Fix breakpoints on delay slots. 2021-08-23 23:00:30 -07:00
Unknown W. Brackets d9aecffd72 Build: Remove old ARM define. 2021-03-02 21:26:03 -08:00
Unknown W. Brackets cae0815095 jit: Avoid using mips identifier directly.
Apparently this gets defined on mips systems.
2021-02-26 07:24:58 -08:00
Henrik Rydgård 821817e6d4 Move the profiler to Common 2020-10-04 11:42:16 +02:00
Henrik Rydgård c41f875df4 Remove base/logging.h in a whole lot more places. 2020-08-15 19:09:00 +02:00
Henrik Rydgård 6f1915110f Remove base/logging from UI and more 2020-08-15 19:08:54 +02:00
Unknown W. Brackets 4b4e3432cd SaveState: Split Do() into a separate header. 2020-08-10 08:03:41 +00:00
Unknown W. Brackets ae224b3893 jit: Add some basic checks in armjit/arm64jit.
Just the simple (and unlikely) case so far...
2020-07-13 01:49:19 -07:00
Unknown W. Brackets 7910b4029a arm64jit: Track writable and non-writable pointers.
Switch uses different memory regions.  We can handle this, might as well
cleanup some const abuse.
2020-05-17 00:15:12 -07:00
Unknown W. Brackets 66687c2c82 Core: Fix a format type error. 2019-08-17 13:31:16 -07:00
Unknown W. Brackets 816abce8fc Windows: Fix some warnings.
Also one in armjit.
2019-06-02 09:30:38 -07:00