Skip to content

riscv: don't leak spurious NX from the FMA error-recovery path - #316

Open
carlosqwqqwq wants to merge 1 commit into
LekKit:stagingfrom
carlosqwqqwq:fix/fma-exact-nx
Open

riscv: don't leak spurious NX from the FMA error-recovery path#316
carlosqwqqwq wants to merge 1 commit into
LekKit:stagingfrom
carlosqwqqwq:fix/fma-exact-nx

Conversation

@carlosqwqqwq

@carlosqwqqwq carlosqwqqwq commented Sep 6, 2026

Copy link
Copy Markdown

riscv: don't leak spurious NX from the FMA error-recovery path

Fixes #310

Commit message

riscv: fix spurious NX on exact fused multiply-add results

The software FMA fallback (fpu_lib.h, used on hosts without the
builtin fused op) runs the TwoSum error-recovery intermediates on the
host f64 unit.  For exactly representable results those intermediates
can set the host inexact flag, which is then reported to the guest as
fflags.NX even though no rounding occurred.  Additionally the odd-round
helpers treat a -0.0 error term as a nonzero negative error and nudge
the value by one ulp, making an exact f32 grid-point result inexact in
the subsequent conversion.

Fix both: mask the sign bit in the odd-round nonzero test, and clear only
newly raised host NX immediately before the single final rounding of the
software fallback (the f64->f32 conversion for fmadd.s and the final
sum+err add for fmadd.d), preserving any NX already sticky before the
instruction.

Description

fpu_fma32/fpu_fma64_raw in src/util/fpu_lib.h compute the fused value with host f64 arithmetic plus a TwoSum error-recovery pass. When the exact result is representable in the destination format, the recovery intermediates can still raise the host inexact flag, and fpu_odd_round32/fpu_odd_round64 also treat a -0.0 error term (returned for an exact sum) as a nonzero negative error, nudging the value off the exact grid point. Both effects produce a spurious guest fflags.NX on exact results.

The change masks the sign bit in the odd-round nonzero test and clears only newly raised NX around the single final rounding step of each software fallback. In fpu_fma32 it preserves NX raised by the first f64 sum; in fpu_fma64_raw the final add establishes the instruction's NX, with a residual check covering hosts whose excess precision does not set the host flag. Both paths preserve NX that was already set before the instruction. The host-flag reconciliation is gated to the SSE2 fallback used by the affected x86-64 configuration; the legacy x87 path keeps its existing platform-specific exception handling. Result bits are unchanged in all cases (verified by differential testing).

Validation

  • Witnesses fmadd.s rdn (exact -2^-46) and fmadd.d/fnmadd.d (exact +/-2^-104): unmodified RVVM reports fflags=1; the patched build reports fflags=0 with identical result bits, matching native RISC-V hardware and QEMU.
  • Both interpreter and x86 JIT lanes exercised.
  • 152-case fixed-input differential: only the three witness cases change (all become correct); zero new differences.
  • 200000-case random differential of fpu_fma64 with finite normal-range operands: zero result-bit and zero NX-flag differences.

The patch is independently applicable to the staging branch and does not
depend on an unmerged FMA change.

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.

FMA raises spurious NX for exact fused results

1 participant