FMA, which is short for fused multiply–add, use lots by math in Go compiler and standard library.
I found that Go 1.20 did some riscv64 support for FMA, however, when I'm trying to add test cases for FNMA x * y - z
or FNMS -x * y + z
.
The binary output always different with my expectation and test cases in math always failed.
At first, I thought that is floating point error since floating point number follows IEEE-754, 2008 edition which allows minor errors within 1e-16 i.e. "veryclose" in math test cases.
However, when I implement the same algorithm for 32 bits FP, there is far more error than it should be.
After my carefully search on SSA code generator, I found that FMA SSA for riscv64 will invert FMA into FNMX if multiplier or adder is negative.
(F(MADD|NMADD|MSUB|NMSUB)D neg:(FNEGD x) y z) && neg.Uses == 1 => (F(NMADD|MADD|NMSUB|MSUB)D x y z)
This SSA will convert FMADDD
into FNMADD
, unfortunately according to RISCV manual, this is wrong.
In the manual
FMADD
means
x * y + z
FNMADD
means
- x * y - z
instead of original CL thought FNMADD
should be implemented as
x * y - z
Then I commit a CL that fix this issue for good with some test cases.
https://go-review.googlesource.com/c/go/+/506575