chore(audit): mark P5 PPCBUGs applied; append P5 progress section

P5 phase merged at d39d0ba. Update audit-findings.md status fields
(21 PPCBUGs marked applied) and append the P5 progress section to
audit-report-2026-04-29.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-02 12:39:46 +02:00
parent d39d0bab4d
commit 9f88e275b8
2 changed files with 57 additions and 21 deletions

View File

@@ -1761,7 +1761,7 @@ the ISA-canonical derivation.
### PPCBUG-181 — fmaddsx / fnmaddsx missing VXISI check for add-phase ±∞ collision ### PPCBUG-181 — fmaddsx / fnmaddsx missing VXISI check for add-phase ±∞ collision
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Locations**: interpreter.rs:2339-2348 (fmaddsx), 2383-2392 (fnmaddsx) - **Locations**: interpreter.rs:2339-2348 (fmaddsx), 2383-2392 (fnmaddsx)
- **Symptom**: When `FRA × FRC = +∞` and `FRB = -∞` (or vice versa), PowerISA §4.3.4 - **Symptom**: When `FRA × FRC = +∞` and `FRB = -∞` (or vice versa), PowerISA §4.3.4
requires `FPSCR[VXISI]` to be set and the result to be a QNaN. The double-precision sibling requires `FPSCR[VXISI]` to be set and the result to be a QNaN. The double-precision sibling
@@ -1779,7 +1779,7 @@ the ISA-canonical derivation.
### PPCBUG-182 — fmsubsx / fnmsubsx missing VXISI check for subtract-phase ±∞ collision ### PPCBUG-182 — fmsubsx / fnmsubsx missing VXISI check for subtract-phase ±∞ collision
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Locations**: interpreter.rs:2361-2370 (fmsubsx), 2405-2414 (fnmsubsx) - **Locations**: interpreter.rs:2361-2370 (fmsubsx), 2405-2414 (fnmsubsx)
- **Symptom**: When `FRA × FRC = ±∞` and `FRB = ±∞` with the same sign, `(±∞) (±∞)` - **Symptom**: When `FRA × FRC = ±∞` and `FRB = ±∞` with the same sign, `(±∞) (±∞)`
should fire `FPSCR[VXISI]`. Neither `fmsubsx` nor `fnmsubsx` calls `check_invalid_add`. should fire `FPSCR[VXISI]`. Neither `fmsubsx` nor `fnmsubsx` calls `check_invalid_add`.
@@ -1794,7 +1794,7 @@ the ISA-canonical derivation.
### PPCBUG-183 — fnmaddsx / fnmsubsx NaN sign bit incorrectly flipped by Rust unary `-` ### PPCBUG-183 — fnmaddsx / fnmsubsx NaN sign bit incorrectly flipped by Rust unary `-`
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Locations**: interpreter.rs:2388 (fnmaddsx), 2410 (fnmsubsx) - **Locations**: interpreter.rs:2388 (fnmaddsx), 2410 (fnmsubsx)
- **Symptom**: `to_single(ctx, -(a.mul_add(c, b)))` — Rust's unary `-f64` always flips the - **Symptom**: `to_single(ctx, -(a.mul_add(c, b)))` — Rust's unary `-f64` always flips the
IEEE sign bit, including when the value is NaN. PowerISA §4.3.2 specifies that the final IEEE sign bit, including when the value is NaN. PowerISA §4.3.2 specifies that the final
@@ -1817,7 +1817,7 @@ the ISA-canonical derivation.
### PPCBUG-184 — fresx produces full-precision IEEE 1/b instead of ~12-bit hardware estimate ### PPCBUG-184 — fresx produces full-precision IEEE 1/b instead of ~12-bit hardware estimate
- **Severity**: HIGH - **Severity**: HIGH
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: interpreter.rs:2481-2494 - **Location**: interpreter.rs:2481-2494
- **Symptom**: `fres` on Xenon hardware produces a reciprocal approximation via a 256-entry - **Symptom**: `fres` on Xenon hardware produces a reciprocal approximation via a 256-entry
LUT with linear interpolation, accurate to roughly 1/4096 relative error (~12 mantissa LUT with linear interpolation, accurate to roughly 1/4096 relative error (~12 mantissa
@@ -1916,7 +1916,7 @@ Group 30 summary: **9 findings (PPCBUG-200..208). 2 MEDIUM cross-cutting, 3 MEDI
### PPCBUG-202 — fmaddx: non-FMA `a * c` used in check_invalid_add can spuriously raise/miss VXISI ### PPCBUG-202 — fmaddx: non-FMA `a * c` used in check_invalid_add can spuriously raise/miss VXISI
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `interpreter.rs:2332` - **Location**: `interpreter.rs:2332`
- **Symptom**: `check_invalid_add(ctx, a * c, b, false)` uses a separate two-rounding multiply to approximate the FMA intermediate product. When the true FMA intermediate is finite but the standalone product overflows to ±∞, VXISI fires spuriously. When the true intermediate is ±∞ but the standalone product is finite (extreme cancellation), VXISI is missed. - **Symptom**: `check_invalid_add(ctx, a * c, b, false)` uses a separate two-rounding multiply to approximate the FMA intermediate product. When the true FMA intermediate is finite but the standalone product overflows to ±∞, VXISI fires spuriously. When the true intermediate is ±∞ but the standalone product is finite (extreme cancellation), VXISI is missed.
- **Fix**: Derive VXISI from input-value properties directly: if `(a.is_infinite() || c.is_infinite())` (product is mathematically infinite) and `b.is_infinite()` with opposing sign → VXISI. - **Fix**: Derive VXISI from input-value properties directly: if `(a.is_infinite() || c.is_infinite())` (product is mathematically infinite) and `b.is_infinite()` with opposing sign → VXISI.
@@ -1924,7 +1924,7 @@ Group 30 summary: **9 findings (PPCBUG-200..208). 2 MEDIUM cross-cutting, 3 MEDI
### PPCBUG-203 — fmsubx, fnmaddx, fnmsubx: VXISI never raised for ∞-collision in add/sub step ### PPCBUG-203 — fmsubx, fnmaddx, fnmsubx: VXISI never raised for ∞-collision in add/sub step
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Locations**: `interpreter.rs:2354` (fmsubx), `2376` (fnmaddx), `2398` (fnmsubx) - **Locations**: `interpreter.rs:2354` (fmsubx), `2376` (fnmaddx), `2398` (fnmsubx)
- **Symptom**: Same pattern as PPCBUG-181/182 for the double-precision variants. These three arms call only `check_invalid_mul` and omit `check_invalid_add`. Per ISA, all four FMA variants must raise VXISI when the add step yields ∞+∓∞. Example for fmsub: `A×C = +∞`, `B = +∞` → `+∞ +∞` → VXISI. Currently the result NaN propagates silently with no FPSCR update. The fnmsub pattern is the canonical Newton-Raphson step — the most common FPU path in Xbox 360 graphics code. - **Symptom**: Same pattern as PPCBUG-181/182 for the double-precision variants. These three arms call only `check_invalid_mul` and omit `check_invalid_add`. Per ISA, all four FMA variants must raise VXISI when the add step yields ∞+∓∞. Example for fmsub: `A×C = +∞`, `B = +∞` → `+∞ +∞` → VXISI. Currently the result NaN propagates silently with no FPSCR update. The fnmsub pattern is the canonical Newton-Raphson step — the most common FPU path in Xbox 360 graphics code.
- **Fix**: Add `fpscr::check_invalid_add(ctx, a * c, b, true)` for `fmsubx`/`fnmsubx` and `fpscr::check_invalid_add(ctx, a * c, b, false)` for `fnmaddx` (apply PPCBUG-202 sign-fix simultaneously). - **Fix**: Add `fpscr::check_invalid_add(ctx, a * c, b, true)` for `fmsubx`/`fnmsubx` and `fpscr::check_invalid_add(ctx, a * c, b, false)` for `fnmaddx` (apply PPCBUG-202 sign-fix simultaneously).
@@ -1938,7 +1938,7 @@ Group 30 summary: **9 findings (PPCBUG-200..208). 2 MEDIUM cross-cutting, 3 MEDI
### PPCBUG-205 — fnmaddx / fnmsubx: Rust `` flips NaN sign bit; ISA requires NaN sign preserved ### PPCBUG-205 — fnmaddx / fnmsubx: Rust `` flips NaN sign bit; ISA requires NaN sign preserved
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Locations**: `interpreter.rs:2377` (fnmaddx), `interpreter.rs:2399` (fnmsubx) - **Locations**: `interpreter.rs:2377` (fnmaddx), `interpreter.rs:2399` (fnmsubx)
- **Symptom**: Same pattern as PPCBUG-183 for the double-precision variants. Rust's unary `-` applied to a NaN result flips the IEEE-754 sign bit. PowerISA Book I §4.3.4 states the negation is not applied to NaN results. Title code using NaN sentinels (audio middleware, debug fills) receives sign-flipped NaN payloads. - **Symptom**: Same pattern as PPCBUG-183 for the double-precision variants. Rust's unary `-` applied to a NaN result flips the IEEE-754 sign bit. PowerISA Book I §4.3.4 states the negation is not applied to NaN results. Title code using NaN sentinels (audio middleware, debug fills) receives sign-flipped NaN payloads.
- **Fix**: - **Fix**:
@@ -2008,7 +2008,7 @@ IDs PPCBUG-232..239 unallocated.**
### PPCBUG-221 — `fctidx` / `round_to_i64` NearestEven tie-breaking uses f64::EPSILON; broken for |v| > 2^52 ### PPCBUG-221 — `fctidx` / `round_to_i64` NearestEven tie-breaking uses f64::EPSILON; broken for |v| > 2^52
- **Severity**: HIGH - **Severity**: HIGH
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `fpscr.rs:220238` (`round_to_i64`, `NearestEven` case) - **Location**: `fpscr.rs:220238` (`round_to_i64`, `NearestEven` case)
- **Symptom**: The tie-breaking code computes `diff = (v - v.trunc()).abs()` and tests - **Symptom**: The tie-breaking code computes `diff = (v - v.trunc()).abs()` and tests
`(diff - 0.5).abs() < f64::EPSILON` to detect a half-integer. Above `|v| = 2^52`, `(diff - 0.5).abs() < f64::EPSILON` to detect a half-integer. Above `|v| = 2^52`,
@@ -2040,7 +2040,7 @@ IDs PPCBUG-232..239 unallocated.**
### PPCBUG-223 — `fcmpo` omits FPSCR[VXSNAN] and FPSCR[VXVC] on NaN operands ### PPCBUG-223 — `fcmpo` omits FPSCR[VXSNAN] and FPSCR[VXVC] on NaN operands
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `interpreter.rs:26452675` - **Location**: `interpreter.rs:26452675`
- **Symptom**: `fcmpo` body is identical to `fcmpu` — it sets FPRF and the CR field correctly - **Symptom**: `fcmpo` body is identical to `fcmpu` — it sets FPRF and the CR field correctly
but calls no `fpscr::set_exception`. PowerISA requires: QNaN → `FPSCR[VXVC, VX, FX]`; but calls no `fpscr::set_exception`. PowerISA requires: QNaN → `FPSCR[VXVC, VX, FX]`;
@@ -2064,7 +2064,7 @@ IDs PPCBUG-232..239 unallocated.**
### PPCBUG-224 — `fcfidx` does not set FPSCR[XX/FX] for inexact i64→f64 conversion ### PPCBUG-224 — `fcfidx` does not set FPSCR[XX/FX] for inexact i64→f64 conversion
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `interpreter.rs:25282536` - **Location**: `interpreter.rs:25282536`
- **Symptom**: Only FPRF is updated. Per ISA, `fcfid` sets `FPSCR[XX, FX]` (and FR/FI) when - **Symptom**: Only FPRF is updated. Per ISA, `fcfid` sets `FPSCR[XX, FX]` (and FR/FI) when
the i64 value has more than 53 significant bits and precision is lost. Any i64 with the i64 value has more than 53 significant bits and precision is lost. Any i64 with
@@ -2075,7 +2075,7 @@ IDs PPCBUG-232..239 unallocated.**
### PPCBUG-225 — `frspx` does not set FPSCR[XX/FX/FR/FI] on inexact rounding ### PPCBUG-225 — `frspx` does not set FPSCR[XX/FX/FR/FI] on inexact rounding
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `interpreter.rs:25162527` - **Location**: `interpreter.rs:25162527`
- **Symptom**: `update_after_op` sets OX/UX only. The ISA requires FR/FI/XX/FX on any f64→f32 - **Symptom**: `update_after_op` sets OX/UX only. The ISA requires FR/FI/XX/FX on any f64→f32
rounding that is not exact. `frsp` is the canonical double→single-precision narrowing idiom rounding that is not exact. `frsp` is the canonical double→single-precision narrowing idiom
@@ -2086,7 +2086,7 @@ IDs PPCBUG-232..239 unallocated.**
### PPCBUG-227 — `fctiwx` rounding: `round_to_i32` inherits NearestEven defect via `round_to_i64` ### PPCBUG-227 — `fctiwx` rounding: `round_to_i32` inherits NearestEven defect via `round_to_i64`
- **Severity**: LOW - **Severity**: LOW
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `fpscr.rs:241243` - **Location**: `fpscr.rs:241243`
- **Symptom**: `round_to_i32` calls `round_to_i64` then clamps. The PPCBUG-221 defect in - **Symptom**: `round_to_i32` calls `round_to_i64` then clamps. The PPCBUG-221 defect in
`round_to_i64` does not manifest for i32-range values (the epsilon check accidentally works `round_to_i64` does not manifest for i32-range values (the epsilon check accidentally works
@@ -2109,7 +2109,7 @@ IDs PPCBUG-232..239 unallocated.**
### PPCBUG-229 — `fctidx` / `fctidzx` do not set FPSCR[XX/FX] for inexact inputs ### PPCBUG-229 — `fctidx` / `fctidzx` do not set FPSCR[XX/FX] for inexact inputs
- **Severity**: LOW - **Severity**: LOW
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Locations**: `interpreter.rs:25372574` - **Locations**: `interpreter.rs:25372574`
- **Symptom**: Per ISA, float-to-integer conversions set `FPSCR[XX, FX]` when the source - **Symptom**: Per ISA, float-to-integer conversions set `FPSCR[XX, FX]` when the source
value is not an integer (the fractional part is discarded). Neither opcode sets XX. value is not an integer (the fractional part is discarded). Neither opcode sets XX.
@@ -2118,7 +2118,7 @@ IDs PPCBUG-232..239 unallocated.**
### PPCBUG-230 — `fctiwx` / `fctiwzx` do not set FPSCR[XX/FX] for inexact inputs ### PPCBUG-230 — `fctiwx` / `fctiwzx` do not set FPSCR[XX/FX] for inexact inputs
- **Severity**: LOW - **Severity**: LOW
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Locations**: `interpreter.rs:25752612` - **Locations**: `interpreter.rs:25752612`
- **Symptom**: Same omission as PPCBUG-229 for the word-width conversion pair. - **Symptom**: Same omission as PPCBUG-229 for the word-width conversion pair.
@@ -2624,7 +2624,7 @@ entries in decode_op6.
### PPCBUG-426 — vnmsubfp: two rounding steps instead of fused FMA; NaN sign may be flipped ### PPCBUG-426 — vnmsubfp: two rounding steps instead of fused FMA; NaN sign may be flipped
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `interpreter.rs:1786` (`r[i] = bi - ai * ci`) - **Location**: `interpreter.rs:1786` (`r[i] = bi - ai * ci`)
- **Symptom**: `vmaddfp` uses single-rounded `ai.mul_add(ci, bi)`, but `vnmsubfp` uses - **Symptom**: `vmaddfp` uses single-rounded `ai.mul_add(ci, bi)`, but `vnmsubfp` uses
`bi - ai * ci` (two operations, two rounding steps). ISA specifies a single fused operation. `bi - ai * ci` (two operations, two rounding steps). ISA specifies a single fused operation.
@@ -2635,7 +2635,7 @@ entries in decode_op6.
### PPCBUG-427 — vnmsubfp128: same two-rounding form as vnmsubfp ### PPCBUG-427 — vnmsubfp128: same two-rounding form as vnmsubfp
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `interpreter.rs:1803` (`r[i] = di - ai * bi`) - **Location**: `interpreter.rs:1803` (`r[i] = di - ai * bi`)
- **Symptom**: Same class as PPCBUG-426 for the VMX128 form. - **Symptom**: Same class as PPCBUG-426 for the VMX128 form.
- **Fix**: `r[i] = -ai.mul_add(bi, -di);` - **Fix**: `r[i] = -ai.mul_add(bi, -di);`
@@ -2672,7 +2672,7 @@ entries in decode_op6.
### PPCBUG-432 — vrfin / vrfin128: Rust `round()` is round-half-away-from-zero; ISA requires round-to-nearest-even ### PPCBUG-432 — vrfin / vrfin128: Rust `round()` is round-half-away-from-zero; ISA requires round-to-nearest-even
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `interpreter.rs:2172` (`r[i] = b[i].round()`) - **Location**: `interpreter.rs:2172` (`r[i] = b[i].round()`)
- **Symptom**: `vrfin(0.5)` → ISA = 0.0; Rust = 1.0. `vrfin(2.5)` → ISA = 2.0; Rust = 3.0. - **Symptom**: `vrfin(0.5)` → ISA = 0.0; Rust = 1.0. `vrfin(2.5)` → ISA = 2.0; Rust = 3.0.
Canary uses SSE2 `ROUNDPS` which is round-to-nearest-even. Canary uses SSE2 `ROUNDPS` which is round-to-nearest-even.
@@ -2681,7 +2681,7 @@ entries in decode_op6.
### PPCBUG-433 — vctsxs / vcfpsxws128: NaN input returns 0 instead of saturating to INT_MIN (0x80000000) ### PPCBUG-433 — vctsxs / vcfpsxws128: NaN input returns 0 instead of saturating to INT_MIN (0x80000000)
- **Severity**: MEDIUM - **Severity**: MEDIUM
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `vmx.rs:217` (`if x.is_nan() { return (0, true); }`) - **Location**: `vmx.rs:217` (`if x.is_nan() { return (0, true); }`)
- **Symptom**: AltiVec ISA: NaN in vctsxs saturates to INT_MIN (0x80000000). Xenia-rs returns 0. - **Symptom**: AltiVec ISA: NaN in vctsxs saturates to INT_MIN (0x80000000). Xenia-rs returns 0.
- **Fix**: `if x.is_nan() { return (i32::MIN, true); }` - **Fix**: `if x.is_nan() { return (i32::MIN, true); }`
@@ -2696,7 +2696,7 @@ entries in decode_op6.
### PPCBUG-435 — vaddfp / vsubfp / vmulfp128: subnormal inputs not flushed when VSCR.NJ=1 ### PPCBUG-435 — vaddfp / vsubfp / vmulfp128: subnormal inputs not flushed when VSCR.NJ=1
- **Severity**: MEDIUM (latent — Xbox 360 always boots with NJ=1) - **Severity**: MEDIUM (latent — Xbox 360 always boots with NJ=1)
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `interpreter.rs:1713`, `1729`, `1812` - **Location**: `interpreter.rs:1713`, `1729`, `1812`
- **Symptom**: VSCR.NJ=1 requires flush-to-zero for subnormal inputs. vmaddfp family correctly - **Symptom**: VSCR.NJ=1 requires flush-to-zero for subnormal inputs. vmaddfp family correctly
calls `vmx::flush_denorm()`; plain add/sub/mul do not check VSCR. calls `vmx::flush_denorm()`; plain add/sub/mul do not check VSCR.
@@ -2704,7 +2704,7 @@ entries in decode_op6.
### PPCBUG-436 — vmsum3fp128 / vmsum4fp128: per-product intermediates not individually flushed ### PPCBUG-436 — vmsum3fp128 / vmsum4fp128: per-product intermediates not individually flushed
- **Severity**: MEDIUM (latent) - **Severity**: MEDIUM (latent)
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `interpreter.rs:4076`, `4083` - **Location**: `interpreter.rs:4076`, `4083`
- **Symptom**: `flush_denorm` on final sum only. Per-lane products can be subnormal and - **Symptom**: `flush_denorm` on final sum only. Per-lane products can be subnormal and
accumulate before the final flush. accumulate before the final flush.
@@ -2712,7 +2712,7 @@ entries in decode_op6.
### PPCBUG-437 — vmaddfp / vmaddfp128 / vmaddcfp128 / vnmsubfp128: subnormal output not flushed ### PPCBUG-437 — vmaddfp / vmaddfp128 / vmaddcfp128 / vnmsubfp128: subnormal output not flushed
- **Severity**: MEDIUM (latent) - **Severity**: MEDIUM (latent)
- **Status**: open - **Status**: applied (P5 d39d0ba, 2026-05-02)
- **Location**: `interpreter.rs:17521754`, `17711773`, `40644067`, `18031805` - **Location**: `interpreter.rs:17521754`, `17711773`, `40644067`, `18031805`
- **Symptom**: VSCR.NJ=1 requires flushing subnormal results. Inputs flushed; outputs are not. - **Symptom**: VSCR.NJ=1 requires flushing subnormal results. Inputs flushed; outputs are not.

View File

@@ -400,6 +400,42 @@ After applying Phase 1 alone, run `xenia-rs check sylpheed.iso -n 4B --parallel`
--- ---
### P5 — FPU correctness (merged 2026-05-02, HEAD d39d0ba)
**PPCBUGs fixed**: 21 IDs across the 5a-5f sub-sections.
- 5a (round-to-int): 221+227 (round_to_i64 NearestEven near 2^52, coupled), 432 (vrfin round-to-even)
- 5b (FMA VXISI + NaN sign): 181, 182 (single fmaddsx/fmsubsx VXISI), 202, 203 (double fmaddx/fmsubx/fnmaddx/fnmsubx VXISI), 183, 205 (NaN sign preservation in fnmaddx/fnmsubx and *sx siblings)
- 5c (XX-on-inexact): 223 (verified already correct), 224 (fcfidx XX), 225 (frspx XX), 229 (fctidx/fctidzx XX), 230 (fctiwx/fctiwzx XX)
- 5d (subnormal flush): 435 (vaddfp/vsubfp/vmulfp128 missing flush), 436 (vmsum3fp128/vmsum4fp128 per-product flush), 437 (vmaddfp family output flush)
- 5e (estimate precision): 184 (fresx canary parity via f32 input quantization)
- 5f (saturation + single-FMA): 426 (vnmsubfp single FMA), 427 (vnmsubfp128 single FMA), 433 (vctsxs NaN→INT_MIN)
**Batches**:
- Batch 1 (f6a444b): 5a round-to-int + vrfin
- Batch 2 (26b9897): 5b FMA — new `check_invalid_fma_add` helper in fpscr.rs derives VXISI from input properties
- Batch 3 (49bf74f): 5c XX bit on conversions
- Batch 4 (538fa5a): 5d VSCR.NJ unconditional flush (matches Canary; Xbox 360 always boots NJ=1)
- Batch 5 (6ba8f83): 5e fresx pre-quantize input
- Batch 6 (6fe2cbf): 5f single-FMA + vctsxs NaN
- Review-fix nit (05f2f72): vrfin → stdlib `f32::round_ties_even()`
**Deferred for focused sub-batches** (Status: open in audit-findings.md):
- PPCBUG-201 (FPSCR.RN for double arithmetic) — requires MXCSR set/restore wrappers around 10+ FPU arms
- PPCBUG-185 (FPSCR.NI flush for scalar FPU) — requires NI bit constant + post-op flush wrapper
- PPCBUG-180 + PPCBUG-200 (XX/FR/FI in update_after_op) — requires pre-vs-post-round comparison
**Review findings**:
- Independent reviewer verdict: **MERGE-READY**. No blocking issues.
- Two non-blocking minor follow-ups noted: (a) `check_invalid_fma_add` doesn't catch the finite-product-overflow + infinite-b cancellation half of PPCBUG-202 (audit-acknowledged as rare); (b) vrfin used inline tie-breaker — replaced with stdlib `round_ties_even()` in 05f2f72.
**Gate results**:
- `cargo test --workspace --release`: **498 passed, 0 failed** (up from 494 at P4 merge; 5 new regression tests across the batches)
- **Acid test** `-n 4B --parallel --reservations-table`: deferred per user direction
**Conclusion**: P5 covers the FPU correctness foundation (round-to-int, VXISI, NaN preservation, XX bit, subnormal flush). Three substantive items deferred. Next: P6 — Other MEDIUM correctness (overflow.rs sweep, trap PC-after-advance, sc LEV, twi typed-trap, etc.).
---
## Index — every PPCBUG referenced (in numerical order) ## Index — every PPCBUG referenced (in numerical order)
This list intentionally includes every ID found in `audit-findings.md` so nothing is dropped. For each entry's full description / file:line / fix snippet / test recommendation, see the corresponding `### PPCBUG-NNN` heading in `audit-findings.md`. This list intentionally includes every ID found in `audit-findings.md` so nothing is dropped. For each entry's full description / file:line / fix snippet / test recommendation, see the corresponding `### PPCBUG-NNN` heading in `audit-findings.md`.