codegen: PS2-accurate divide-by-zero semantics for COP1 and VU0 FP + RSQRT operand fix#113
Closed
jlsandri wants to merge 1 commit intoran-j:mainfrom
Closed
codegen: PS2-accurate divide-by-zero semantics for COP1 and VU0 FP + RSQRT operand fix#113jlsandri wants to merge 1 commit intoran-j:mainfrom
jlsandri wants to merge 1 commit intoran-j:mainfrom
Conversation
… plus RSQRT operand fix Five related floating-point codegen corrections for EE COP1 and VU0: 1. COP1_S_RSQRT was emitting `fd = 1.0f / sqrtf(fs)`, which is the formula for reciprocal-sqrt of fs. The actual PS2 rsqrt.s operation is `fd = fs / sqrt(ft)` — the old codegen ignored ft entirely and used the wrong source operand. This operand fix is a prerequisite for the div-by-zero handling below. 2. COP1_S_DIV on the PS2 EE does not produce IEEE infinity on divide by zero. It returns 0x7F7FFFFF (max finite float) with the sign bit set to (sign(fs) XOR sign(ft)). Replace the prior `copysignf(INFINITY, ...)` path with the correct max-float form. 3. COP1_S_RSQRT div-by-zero: returns 0x7F7FFFFF with sign preserved from fs. Also guards the sqrt input with std::max(0.0f, ft) to avoid producing NaN for negative operands (matches EE behaviour of ignoring the sign bit when reading the radicand). 4. VU0 vdiv div-by-zero: same 0x7F7FFFFF + sign semantics, writing to vu0_q. 5. VU0 vrsqrt div-by-zero: same 0x7F7FFFFF semantics (unsigned). All five use std::bit_cast<float>(uint32_t) for the bit-level construction. Commented in-line so the magic constant 0x7F7FFFFF is self-documenting.
732f316 to
51b3b95
Compare
Author
|
Closing as part of a batch cleanup after #107 landed. The runtime ecosystem refactor in #107 substantially reworked the files this PR touched, and I would like to re-audit the underlying fix against the new code structure before putting it back in front of you. If the fix is still needed after that re-audit, I will re-open as a focused PR rebased onto current main. Thanks for your patience. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Five related floating-point codegen corrections for EE COP1 and VU0. All five are mis-emulations of documented PS2 hardware semantics.
1.
COP1_S_RSQRTwas using the wrong source operandThe previous emitter produced
which is the formula for reciprocal-sqrt of
fs. The actual PS2rsqrt.soperation isThe emitter ignored
ftentirely and usedfsas the radicand. Fixed to useftas the radicand andfsas the numerator. (This operand fix is also a prerequisite for the div-by-zero handling below, which rewrites the same line.)2.
COP1_S_DIVdivide-by-zeroThe PS2 EE does not produce IEEE infinity on divide-by-zero. It returns
0x7F7FFFFF(the largest finite float) with the sign bit set tosign(fs) XOR sign(ft). Previously the codegen emittedcopysignf(INFINITY, fs * 0.0f)which produces IEEE ±∞.3.
COP1_S_RSQRTdivide-by-zeroSame
0x7F7FFFFF+ sign semantics, with sign taken fromfs. Also guards thesqrtinput withstd::max(0.0f, ft)to match the EE behaviour of ignoring the sign bit on the radicand instead of producing a NaN.4.
VU0 vdivdivide-by-zeroSame
0x7F7FFFFF+ sign semantics, writing tovu0_q. Previously returned0.0fon div-by-zero, which is visibly wrong for game code that relies on the max-float fallback.5.
VU0 vrsqrtdivide-by-zeroSame
0x7F7FFFFFsemantics (unsigned, per the VU0 ISA). Previously returned0.0f.Implementation
All five use
std::bit_cast<float>(uint32_t)for the bit-level construction of the max-float sentinel, and each site has an inline comment documenting the0x7F7FFFFFmagic constant so it is self-explanatory.Scope
ps2xRecomp/src/lib/code_generator.cppRationale
All five changes bring the generated code into line with documented PS2 floating-point semantics. The div-by-zero behaviour in particular is well-known to differ from IEEE and is frequently relied upon by EE code that intentionally divides by zero as a cheap max-float construction.