Commit Graph
49047 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 d4a2cd3e43 RISC-V and LoongArch64 IR JITs: Name the fixed code routines, and profile on Linux
Both now list their routines (GetFixedCodeLabels), so the disassembly
names jump targets and --jit-profile charges routine samples to the
calling block. The profiler samples them on Linux, and its report
disassembles their host code per instruction (RISC-V's in 2- and 4-byte
steps).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 15:17:50 +02:00
Henrik RydgårdandClaude Opus 5.5 08b398bbd1 LoongArch64 IR JIT: Vec2 packs with LSX, and join two mapped pairs into a quad
With LSX, the Vec2 pack and unpack ops went to the interpreter, as they
addressed the lanes of a pair one by one. They're now an interleave or a
pick of halfwords in the pair's register.

That makes pairs, so mapping a quad whose halves are mapped pairs (two
vs2i.s feeding a vi2f.q) joins them with VILVL_D rather than storing both
and loading the quad back, like the ARM64 JIT's ZIP1 (3d20d51c70).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 15:17:50 +02:00
Henrik RydgårdandClaude Opus 5.5 a80cea2b9e RISC-V and LoongArch64 IR JITs: Vec4 vmin, vmax, vslt, vsge and vi2f
The ops from 108e66f2d1, so both now get moreVec4Ops. With LSX they're
vector code: vfpu_minmax_key() of each lane picked by VBITSEL, a compare
ANDed with 1.0, and VFFINT with a scale. RISC-V and LoongArch without LSX
do them lane by lane, vslt and vsge as a compare into a GPR rather than
through fpcond.

Vec4VfpuMultiDot stays ARM64-only, so the frontend still doesn't emit it
for these.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 15:17:50 +02:00
Henrik RydgårdandClaude Opus 5.5 59b6b4de78 RISC-V and LoongArch64 IR JITs: Look up the block for a jump to a register at the jump
Like the ARM64 JIT (e33f38afa6), each jr and jalr does the dispatcher's
lookup itself, for an indirect branch of its own, and goes to the
dispatcher when out of downcount or with no block yet.

A bad target now faults in the block rather than at dispatchFetch, so the
fault handler takes any fault in the block shadow as a bad jump, with the
masked target from the address. That covers ARM64's too, which was handled
as a bad memory access.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 15:17:37 +02:00
Henrik RydgårdandClaude Opus 5.5 f8ed53b652 CPU fuzzer: PPSSPP_CPUFUZZ_BLOCKS, and docs for the RISC-V and LoongArch dot products
PPSSPP_CPUFUZZ_BLOCKS prints the IR and host code of the blocks the JITs ran in each case, which
is what found the vmv.s.x encoding.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:42:00 +02:00
Henrik RydgårdandClaude Opus 5.5 4c4746ece1 LoongArch64 IR JIT: Run Vec4VfpuDot through a shared routine
As on RISC-V: the accurate dot products called the IR interpreter. Now the JIT calls a routine
generated at startup, vfpu_dot_lsx() with LSX (the operands go in v8 and v9) and vfpu_dot_scalar()
without (through mips->vfpuDotArgs), with inf and NaN going to vfpu_dot_reference(). With LSX, a
result that lands in one of the operands' Vec4s goes in with VEXTRINS.

VFPUDotJit covers both versions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:42:00 +02:00
Henrik RydgårdandClaude Opus 5.5 c6c9e8ff85 RISC-V IR JIT: Run Vec4VfpuDot through a shared routine
The accurate dot products went to the IR interpreter, flushing everything around each one. Now
the JIT stores the operands to mips->vfpuDotArgs and calls a routine generated at startup, which
clobbers only registers late in the allocation order. Inf and NaN go to vfpu_dot_reference().

The routine is vfpu_dot_scalar() in RV64, using Zbb and Zicond where it has them, or with the V
extension, vfpu_dot_sse2() in RVV. The rounding at the end comes from FCVT.S.LU with a static
round-to-nearest-even, which also gives the exponent, so there's no need for Zbb's CLZ.

VFPUDotJit runs each variant the host can run against the reference on VFPUDot's inputs, the RVV
one with poisoned vector registers. PPSSPP_CPUFUZZ_HOSTEXT=- now turns V off too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:42:00 +02:00
Henrik RydgårdandClaude Opus 5.5 d71599ae85 RISC-V emitter: Fix the vmv.s.x and vfmv.s.f encodings
They used the OPMVV and OPFVV funct3, which makes them vmv.x.s and vfmv.f.s: the scalar register
was written instead of the vector. Nothing used them until now. Checked against GNU as.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:42:00 +02:00
Henrik RydgårdandClaude Opus 5.5 fe2c9a3231 VFPU: Add scalar and LSX versions of vfpu_dot
Hosts without NEON or SSE2 ran vfpu_dot_reference() for every dot product. vfpu_dot_scalar() is
the same computation without its branches, finished like the SIMD versions, and is what RISC-V
gets. LoongArch builds with LSX (GCC's default la64v1.0 has it) get vfpu_dot_lsx(), the SSE2
version in LSX.

VFPUDot checks vfpu_dot_scalar() on every host, and its inputs come from ForEachVFPUDotTestCase(),
for the JITs' routines to use too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:42:00 +02:00
Henrik RydgårdandClaude Opus 5.5 2af81e0499 docs: CPU equivalence on RISC-V and LoongArch64
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:41:51 +02:00
Henrik RydgårdandClaude Opus 5.5 f083720577 CPU fuzzer: Options for host extensions and unknown prefixes
PPSSPP_CPUFUZZ_HOSTEXT=+ turns on the RISC-V extensions that detection only
reports for a few SoCs, and - turns them off (LSX on LoongArch), to test both
paths of the JIT. PPSSPP_CPUFUZZ_UNKNOWNPREFIX compiles the way the IR
frontend does after any block has left a prefix uneaten, which in a run
happens at the first case that does it. Without it, such a later case
doesn't reproduce on its own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:41:51 +02:00
Henrik RydgårdandClaude Opus 5.5 6d8f744ae6 Interpreter: Don't rely on the host to keep a NaN's sign in vsgn
vsgn takes the sign of s - t, and a NaN's sign counts (cpu/vfpu/specials).
RISC-V's subtraction always makes the positive canonical NaN, so there vsgn
of a negative NaN gave 1 instead of -1, unlike the PSP and the IR cores.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:41:51 +02:00
Henrik RydgårdandClaude Opus 5.5 a6193abcd7 IR: Fix vmmov between transposed matrices whose rows wrap around
For a matrix like E020, the register layout checks pass, but transposing
the register names and taking their columns gives unaligned Vec4s. The IR
interpreter crashed on x86 on the aligned store (vmscl.p M000, M702, S301
then vmmov.q E020, E100). Take the Vec4s from the checked layout instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:41:51 +02:00
Henrik RydgårdandClaude Opus 5.5 ff42928673 RISC-V and LoongArch64 JITs: Don't fuse the multiply-adds in Vec4Dot
A fused multiply-add rounds once where the IR interpreter rounds twice, so
vdot, vhdp, vtfm and vmmul gave different low bits. Multiply and add
separately, in the IR interpreter's order. (LoongArch's LSX path already
did, in the pairwise order of the x86 and ARM64 JITs.)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:41:51 +02:00
Henrik RydgårdandClaude Opus 5.5 21010a497f LoongArch64 JIT: Keep NaNs through vsat0/vsat1, and make vcmp NE true for them
FMIN/FMAX return the other operand for a NaN, so the saturating ops clamped
a NaN to the bound. They now compare and select like vfpu_clamp(). vcmp NE
used the ordered not-equal, false for a NaN.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:41:51 +02:00
Henrik RydgårdandClaude Opus 5.5 ae32504fa1 RISC-V emitter: Fix Zcb byte offsets, and FLI of a NaN
c.lbu and c.sb keep the offset's bit 0 in bit 6 and bit 1 in bit 5, so
offsets 1 and 2 were swapped, and the JIT loaded and stored the wrong byte.

FLI's only NaN is the canonical one, but CanFLI took any NaN for it: the
pattern went through a float to double conversion that quieted it, which on
RISC-V also drops the sign and payload. vfim of a NaN lost its sign.

Neither is detected on real hardware yet (we never set Zcb or Zfa), and the
CPU fuzzer found both with the extensions forced on under qemu.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 14:41:51 +02:00
Henrik Rydgård 253029c083 Merge pull request #22583 from hrydgard/ctr-delayed-loadmodule
sceKernelLoadModule: Resolve a relative path when the load is requested
2026-10-15 14:14:02 +02:00
Henrik Rydgård 79fbc7e5db Merge pull request #22582 from hrydgard/resistance-muzzle-light-20420
Resistance: Retribution: Bring back the muzzle light flicker at 60fps
2026-10-15 14:12:55 +02:00
Henrik RydgårdandClaude Opus 5.5 8087ad652b sceKernelLoadModule: Resolve a relative path when the load is requested
- Fixes #22578

Since loads became deferred (#22507), the file is read from the event at the
end of the wait, where there is no current thread and so no current
directory. Crash Tag Team Racing loads its PRXs as "prx/sc_sascore.prx" and
so on, got NOCWD for all of them and never started. Make the path absolute
with the caller's current directory before waiting.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 13:58:40 +02:00
Henrik RydgårdandClaude Opus 5.5 43aef0ddc6 Resistance: Retribution: Bring back the muzzle light flicker at 60fps
- Fixes #20420

Each shot's light lasts 0.1s of effect time. At 30fps the 303 assault rifle
fires every 0.1s, and the wall goes dark for a frame per shot. At 60fps it
fires every 1/12s, the lights overlap and the wall stays lit. Hook the flash
effect's constructor and shorten the light to 0.075s, which keeps the 30fps
pattern and leaves one dark frame in five at 60fps.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 13:28:17 +02:00
Henrik Rydgård edc6620348 Merge pull request #22579 from hrydgard/ir-jit-hot-blocks
IR JIT: Multi-dot, compiling past branches, more Vec4 ops, and a sampling profiler
2026-10-15 12:43:18 +02:00
Henrik Rydgård 719b713813 Merge pull request #22581 from hrydgard/resistance-crash-19755
Resistance: Retribution: Fix the crash after the angel boss
2026-10-15 12:42:34 +02:00
Henrik RydgårdandClaude Opus 5.5 20063fe526 IR JITs: Name the fixed code routines that jumps go to in the disassembly
DescribeCodePtr() called everything in the fixed code other than a few
routines "fixedCode". It now uses the routine list the profiler has
(moved to the backend as GetAllFixedCodeLabels()), with an offset when
not at the start, so a block ends in "b dispatcherCheckCoreState".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 12:22:35 +02:00
Henrik RydgårdandClaude Opus 5.5 3258bc2c8e Resistance: Retribution: Add smoke trail nodes at most every 1/30s
The debris smoke trails add a node on every update, so at 60fps the smoke was twice as dense
as at 30fps and spawned twice as many trails. Keep the nodes 1/30 of the effect's clock apart
instead. In the angel boss explosion the display list now peaks at 0x483ec bytes without the
overflow guard's help, against 0x637f4 before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:42:18 +02:00
Henrik RydgårdandClaude Opus 5.5 6d9c85f366 Resistance: Retribution: Stop smoke trails overflowing the display list
- Fixes #19755

The game adds smoke trail nodes per update but expires them by time, so the faster we run, the
more trails are alive. After the angel boss in Bonn, the debris trails grow the display list
past its 0x5DC40 byte buffer at 60fps. The overflow lands in the music stream's Atrac buffer
(the "Invalid start bit" errors), corrupts textures, and crashes the game. Locking the CPU to
222MHz, the known workaround, slows the frame rate enough to avoid it.

Hook the trail draw and skip a trail when the current list buffer, whose limit the game keeps
next to its write pointer, is nearly full.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:42:17 +02:00
Henrik Rydgård 25ab83766b Merge pull request #22580 from eraycep/make-scrollbar-fully-opaque-while-held-down
UI: Make scrollbar opaque while dragging
2026-10-15 11:38:40 +02:00
Henrik RydgårdandClaude Opus 5.5 a738fe9b86 Scan modules that only exist in a savestate for functions
A module the game loaded before the state was saved, but that this run never loaded, was never
scanned, so loading such a state left all of its code without function replacements and hooks
(and without functions in the debugger). Scan any loaded module with no analyzed functions in its
text range before rehashing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:22:06 +02:00
Eray Cepni f3cde93bc5 UI: Make scrollbar opaque while dragging
Preserve the theme's scrollbar color while forcing full opacity when
the scrollbar is held. Restore theme opacity on release.
2026-10-15 12:13:17 +03:00
Henrik RydgårdandClaude Opus 5.5 2718010a71 IR: Build a dot operand of one quad's lanes and constants as a vector
vhdp's s, [x y z 1], went lane by lane, splitting the quad apart in the
register cache only to zip it back together for the dot. It's now a
Vec4Init and a Vec4Blend.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:02:17 +02:00
Henrik RydgårdandClaude Opus 5.5 3d20d51c70 ARM64 IR JIT: Join two mapped pairs into a quad with a ZIP1
Mapping a quad whose halves were in registers as pairs (two vs2i.p
feeding a vi2f.q) flushed both and loaded the quad back, a store
forwarding stall.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:02:17 +02:00
Henrik RydgårdandClaude Opus 5.5 108e66f2d1 IR: Vec4 vmin, vmax, vslt, vsge and vi2f
New IR ops do them four lanes at a time where the backend has them, the
ARM64 JIT and the IR interpreter so far. vmin and vmax compare by
vfpu_minmax_key() in NEON, which took about twelve instructions a lane
through the integer registers before; vslt and vsge are a compare and an
AND with 1.0.

vi2f becomes one SCVTF with fractional bits.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:02:17 +02:00
Henrik RydgårdandClaude Opus 5.5 e33f38afa6 ARM64 IR JIT: Look up the block for a jump to a register at the jump
Each jr and jalr now does the dispatcher's lookup itself, so each has its
own indirect branch, which the CPU predicts much better than the one in
the dispatcher that every one of them shared. Out of downcount or with no
block yet, it goes to the dispatcher as before.

Cycles over 10 emulated seconds: -2.3% in ATV, -1.4% Tekken 6 and Ridge
Racer, -1.2% Daxter, -1.1% Killzone, and -0.3% to -0.8% in the rest.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:02:17 +02:00
Henrik RydgårdandClaude Opus 5.5 f247df5e4d IR: Compile on past conditional branches
A non-likely conditional branch now exits to its target when taken, and
the block goes on with the not-taken path, up to 8 branches and 96
instructions. Loops written as a few blocks become one with one exit per
iteration, and code around branches gets optimized together. Branches
decided by their operands, like b, still end the block.

Not with a prefix pending, as blocks assume default prefixes on entry
and only the end of a block checks that it leaves them so.

10 emulated seconds, Vulkan, ARM64: cycles -4.6% in God of War, -3.5%
Killzone, -3.0% God Eater, -2.8% Daxter, -2.3% Tekken 6, -2.2% GTA,
-2.1% ATV, about -1% in the rest.

docs/cpu-equivalence.md gets the fuzzer's new classes and what it found
on this branch and in #22576.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:02:17 +02:00
Henrik RydgårdandClaude Opus 5.5 48be692972 CPU fuzzer: Generate forward branches, and keep them intact when minimizing
Class b turns some instructions into conditional branches to a later
one. Minimizing code with branches makes instructions nops rather than
removing them, so no branch moves its target or lands in a delay slot,
and the reports leave the nops out.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:02:17 +02:00
Henrik RydgårdandClaude Opus 5.5 533baffb24 IR: Compute runs of VFPU dot products four at a time
A new IR op, Vec4VfpuMultiDot, does four vfpu_dot()s against one vector,
the other operands being the rows or Vec4s of a 4x4 block of registers.
The frontend emits it for vmmul and vtfm/vhtfm, and for runs of vdot that
fit it, like a matrix times a vector written out.

On ARM64 it calls a routine that has the dots in lanes rather than the
terms, so nothing is horizontal and nothing goes through the integer
registers. The block comes in with LD1, or LD4 for the transposed form,
when none of it is in host registers. The IR interpreter runs it through
vfpu_dot(); the other backends don't get it yet.

The CPU fuzzer gets runs of vdot shaped for it, and finds no difference
between the cores on them.

There's a routine per number of terms, so vdot.t runs don't pay for a
fourth. The vector's significands are worked out once and multiplied by
element, and the rounding at the end is UCVTF's in round-to-nearest,
which is what games run in; the integer version stays for the other
modes. The routine saves nothing, and the call site keeps only those
registers it clobbers that hold guest state.

JitDotBench, ns per dot: vtfm4.q 3.40, vmmul.q 3.12, a run of vdot.t
2.83, against 4.6 for single dots.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:02:17 +02:00
Henrik RydgårdandClaude Opus 5.5 4a28589be4 Unit tests: Add JitDotBench, timing the JIT's VFPU dot products
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:02:17 +02:00
Henrik RydgårdandClaude Opus 5.5 9b1395db8e ARM64 emitter: Add vector CLZ, LD4, UMULL by element, ABS and SLI
The disassembler decodes the widening integer multiplies by element.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:02:17 +02:00
Henrik RydgårdandClaude Opus 5.5 1d5e2689b5 JIT: Add --jit-profile, a sampling profiler for the CPU cores
The native IR JITs get the emulator thread's host PC sampled every 50 us (a
signal on macOS and Linux, SuspendThread on Windows), mapped to blocks, the
routines they call and host functions. The report has each hot block's MIPS,
IR and host code with the samples per host instruction. The IR interpreter's
block timing, previously behind IR_PROFILING, now runs on the same switch.
Both cost nothing when off.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 11:01:52 +02:00
Henrik Rydgård 51f619d876 Merge pull request #22577 from hrydgard/gpu-cull-speed
GPU: Faster cull test and depth raster, fix an ARM32 alignment crash
2026-10-15 10:59:17 +02:00
Henrik RydgårdandClaude Opus 5.5 75877d8ece CrossSIMD: The C++ fallback's AsVec3ByMatrix44 computes W
It returned 1 for W, where the SIMD versions return the matrix's fourth
row times the vector, and added in a different order. The depth raster
and the culling test (compared with the old version in a unit test,
which failed on RISC-V) read W.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 10:33:44 +02:00
Henrik Rydgård 3e41ecba63 Merge pull request #22576 from hrydgard/cpu-generic-fixes
CPU equivalence fixes, headless vertex JIT, and Mac W^X switching per thread
2026-10-15 10:17:04 +02:00
Henrik RydgårdandClaude Opus 5.5 0edfd93a3a Depth raster: Stop a row once it has left the triangle
The triangle is convex, so once a row has been entered and left, the
rest of it is outside. This cut cycles by 5% in Wipeout Pure, where depth
raster is on for the lens flares.

The test checks the covered pixels against a per-pixel edge test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 10:14:54 +02:00
Henrik RydgårdandClaude Opus 5.5 83639a73e8 GPU: Cull-test draws four vertices at a time
TestBoundingBoxFast transformed and tested one vertex per iteration,
with a scalar divide and a min/max chain each vertex had to wait on.
It now transposes four vertices into lanes and does it all as vectors,
with the same operations in the same order, so the results don't change.
It was 16.6% of the emulator thread in Wipeout Pure (with depth raster
off) and 5% in GTA: Vice City Stories.

Per vertex, it's 1.8x faster on an M1, 2x on x86-64, 1.7x on a Galaxy S8+
and 10% faster in 32-bit mode there.

The test compares it with the old version on random draws in every
position and index format.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 10:14:51 +02:00
Henrik RydgårdandClaude Opus 5.5 50ef294c97 CrossSIMD: Load 8-bit vertex components without assuming alignment
LoadS8Norm and friends read four bytes through a u32 pointer. On ARM32
the compiler turns that into a NEON load that requires 4-byte alignment,
and 8-bit positions can sit at any byte offset in a vertex, so
TestBoundingBoxFast could crash with SIGBUS (seen in a unit test on an
S8+ in 32-bit mode).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 10:14:43 +02:00
Henrik RydgårdandClaude Opus 5.5 ee5f480fc9 Mac ARM64: Switch JIT memory between writable and executable per thread
JIT memory is now mapped with MAP_JIT, and pthread_jit_write_protect_np()
switches the calling thread's view of it, in place of two mprotect() calls
for every block compiled and every exit linked. This makes compiling
cheaper, which matters mostly in the first seconds after a game or state
loads. A hardened runtime without the JIT entitlement refuses the mapping,
and then it's mprotect() as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 09:55:35 +02:00
Henrik RydgårdandClaude Opus 5.5 9de0f6e45d bltzal and friends test rs from before they link
As on hardware (cpu/cpu_alu/branch_link_ra), with ra as rs the branch
sees its old value, not the return address. Every core linked first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 09:55:35 +02:00
Henrik RydgårdandClaude Opus 5.5 38322d12ad IR: vmtvc to a masked control register reads vs
It read the VFPU register numbered like the control register instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 09:55:35 +02:00
Henrik RydgårdandClaude Opus 5.5 8c3f929615 IR: Keep d in vcmov's lanes that don't move under a D prefix
With a D prefix the result goes through temps, and a lane the condition
doesn't move kept whatever the temp held. They now start as d. That
exposed OptimizeFPMoves renaming the src3 of FCmovVfpuCC, which is also
its destination, so it no longer replaces a register an op reads as src3
and writes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 09:55:35 +02:00
Henrik RydgårdandClaude Opus 5.5 be09c1b25b IR: Compare store offsets as signed in OptimizeLoadsAfterStores
A store at a negative offset whose end wraps past zero, like 16 bytes at
-16, counted as ending before a load at -12, so the load reused an older
value from before the store.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 09:55:35 +02:00
Henrik RydgårdandClaude Opus 5.5 91a0ac7567 IR JITs: Flushing a host register that holds zero unmaps it
FlushNativeReg left a register caching MIPS zero mapped, as there's
nothing to store, but its callers flush to clobber the register. After
the dot product routine's call, a store of zero stored its result.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 09:55:35 +02:00