Repository navigation
fix(shader): round v_fma_f64 once for subnormal operands and results - #1017
Conversation
|
Heads-up: this PR is on hold, not rejected. It relies on how |
|
Hardware comparison for the subnormal cases, measured on RDNA2 (gfx1036, Ryzen 7950X3D iGPU) with the hardware oracle:
Rows, results and the script: https://gist.github.com/SleglJan/30456e5687240f0b639a78e5477f2f91 ( AI-assisted: yes (Claude Code). |
A subnormal operand sent v_fma_f64 to a separate multiply and add, and a subnormal result was rounded twice: once to 53 bits in the scaled domain and again by the power-of-two rescale. Both differ from IEEE 754 fusedMultiplyAdd in the last bit. Subnormal operands are now widened by 2^64 and enter the exact path with their exponent lowered by 64. The exact path keeps the sign of the residual between the 53-bit result and the exact sum; when the result lands on a midpoint of the subnormal grid it is moved one ulp towards the exact sum before the single final rounding. Normal results are rebuilt by setting the exponent, so only a subnormal result rounds. v_div_fmas_f64 shares the path.
…results Expected values measured on RDNA2 (gfx1035) with the HW oracle at IEEE=0 and IEEE=1, f64 denormals kept. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
With OOB_SELECT 0 and stride 0 every access is out of range now that the buffer bounds follow OOB_SELECT, so the shader never read or wrote. Co-Authored-By: Claude <noreply@anthropic.com>
|
Measured on RDNA2 (gfx1035) with the HW oracle ( I rebased onto
Merging once CI is green. |
32a7c34 to
e4a911f
Compare
…bnormals fix(shader): round v_fma_f64 once for subnormal operands and results
What
v_fma_f64 sent any subnormal operand to a separate multiply and add, so it rounded twice. Results in the subnormal range were also rounded twice even with normal operands: once to 53 bits in the scaled domain and again by the power-of-two rescale. Both cases can differ from IEEE 754 fusedMultiplyAdd in the last bit. The second case was not in the technical debt entry.
Subnormal operands are now widened by 2^64 and go through the exact Boldo and Melquiond path with their exponent lowered by 64. That path now also keeps the sign of the residual between the 53-bit result and the exact sum. When the result lands exactly on a midpoint of the subnormal grid, it is moved one ulp towards the exact sum before the single final rounding. Normal results are rebuilt by setting the exponent, so only a subnormal result rounds. v_div_fmas_f64 shares this path, so its subnormal cases change too.
Source of the semantics is IEEE 754 fusedMultiplyAdd with f64 denormals kept, which the existing f64 path already assumes. I could not compare these cases with RDNA hardware (my GPU is GCN), so the technical debt entry now says that instead of describing the old gap.
Note: the TechnicalDebt.md hunk conflicts textually with #697 and #944, which add lines right above and below the old v_fma_f64 entry. The shader code does not overlap with any open PR. Whichever lands second only needs to keep both lines.
Tested
Linux (GCC 15), no title. New test agc_driver_f64_fma_subnormals with 32 vectors, expected values from glibc fma: 17 of them fail on main and all pass with this change. Full ctest with ANYPS5_REQUIRE_VULKAN=1 passes 370/370 on RADV (R9 380) and on lavapipe, same skips as main.
Outside the repo I modelled the emitted sequence in C++ and checked about 200M random and near-boundary inputs against std::fma, plus the scaled v_div_fmas_f64 path against exact rational arithmetic, with no mismatches. A throwaway GPU fuzz of 320k vectors per driver also had no mismatches; the old code failed 322 of 64k.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)