Skip to content

Fix SQRT.S and RSQRT.S operand selection in the FPU translator - #277

Open
yanm1103 wants to merge 1 commit into
ran-j:mainfrom
yanm1103:fix/fpu-sqrt-rsqrt-operands
Open

yanm1103 wants to merge 1 commit into
ran-j:mainfrom
yanm1103:fix/fpu-sqrt-rsqrt-operands

Conversation

@yanm1103

@yanm1103 yanm1103 commented Oct 9, 2026

Copy link
Copy Markdown

Problem

FpuTranslator picks the wrong operands for two COP1.S instructions:

  • SQRT.S was emitted as ctx->f[fd] = FPU_SQRT_S(ctx->f[fs]);
  • RSQRT.S was emitted as ctx->f[fd] = 1.0f / sqrtf(ctx->f[fs]);

R5900 rule

  • SQRT.S fd, ft: the source operand is the ft field. The fs field is 0 in the encoding.
  • RSQRT.S fd, fs, ft: the result is fs / sqrt(ft), not 1 / sqrt(fs).
  • Both use the magnitude of ft, so a negative ft does not give NaN (this is what PCSX2's FPU.cpp does: SQRT_S and RSQRT_S apply sqrt(fabs(ft)) for negative ft, and RSQRT_S divides fs by it).

For example sqrt.s $f2, $f2 is the word 0x46020084 (fmt=S, ft=2, fs=0, fd=2, funct=4). It was translated to ctx->f[2] = FPU_SQRT_S(ctx->f[0]);, i.e. the square root of the wrong register.

Fix

case COP1_S_SQRT:  ctx->f[fd] = FPU_SQRT_S(fabsf(ctx->f[ft]));
case COP1_S_RSQRT: ctx->f[fd] = ctx->f[fs] / sqrtf(fabsf(ctx->f[ft]));

Scope is these two cases only. Zero and denormal handling and the FPU flag updates that PCSX2 models are not touched here.

Verification

  • Found while recompiling a retail PS2 game. Its ELF contains the buggy pattern in 108 functions (sqrt.s) and 76 functions (rsqrt.s).
  • I ran an original function and an independent reimplementation, both recompiled with PS2Recomp, on 300 random states. Without this fix the results disagreed in 300/300 runs (the sum of squares in $f2 was ignored). With the fix they matched 300/300.
  • Added a ps2xTest case in code_generator_tests.cpp that checks the emitted code for sqrt.s $f2, $f2 (0x46020084) and for rsqrt.s $f1, $f3, $f4. I could not run the full CMake test suite locally; I compiled fpu_translator.cpp standalone and checked that it emits exactly the strings the test expects.

🤖 Generated with Claude Code

On the R5900, SQRT.S fd, ft takes its source from the ft field (the fs
field is 0), and RSQRT.S fd, fs, ft computes fs / sqrt(ft). The translator
read fs for SQRT.S (so a real "sqrt.s $f2, $f2", word 0x46020084, became
FPU_SQRT_S(f[0])) and emitted 1/sqrt(fs) for RSQRT.S, ignoring ft and the
numerator.

Use ft for SQRT.S and fs / sqrt(ft) for RSQRT.S. Like PCSX2, take the
magnitude of ft so negative inputs do not produce NaN. Add a code
generator test for both encodings.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant