https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126932
Bug ID: 126932
Summary: GCC's RISC-V auto-vectorizer emits FMA instructions
with -ffp-contract=off
Product: gcc
Version: 16.1.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c
Assignee: unassigned at gcc dot gnu.org
Reporter: grezindanil at gmail dot com
Target Milestone: ---
Created attachment 65362
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65362&action=edit
Preprocessed file that triggers -ffp-contract=off auto-vectorizer bug
-ffp-contract=off should stop GCC from fusing mul and add into a single FMA
instruction. Scalar code generator obeys this rule, but GCC's RISC-V
auto-vectorizer doesn't. It generates FMA vector instructions regardless of the
fp-contract setting.
gcc -v output:
```
Using built-in specs.
COLLECT_GCC=/home/danil/x-tools/riscv64-unknown-linux-gnu/bin/riscv64-unknown-linux-gnu-gcc
COLLECT_LTO_WRAPPER=/home/danil/x-tools/riscv64-unknown-linux-gnu/libexec/gcc/riscv64-unknown-linux-gnu/16.1.0/lto-wrapper
Target: riscv64-unknown-linux-gnu
Configured with:
/home/danil/Proj/crosstool-ng/.build/riscv64-unknown-linux-gnu/src/gcc/configure
--build=x86_64-build_pc-linux-gnu --host=x86_64-build_pc-linux-gnu
--target=riscv64-unknown-linux-gnu
--prefix=/home/danil/x-tools/riscv64-unknown-linux-gnu
--exec_prefix=/home/danil/x-tools/riscv64-unknown-linux-gnu
--with-sysroot=/home/danil/x-tools/riscv64-unknown-linux-gnu/riscv64-unknown-linux-gnu/sysroot
--enable-languages=c,c++
--with-arch=rv64imafdcv_zicbom_zicboz_zicntr_zicond_zicsr_zifencei_zihintpause_zihpm_zfh_zfhmin_zca_zcd_zba_zbb_zbc_zbs_zkt_zve32f_zve32x_zve64d_zve64f_zve64x_zvfh_zvfhmin_zvkt_sscofpmf_sstc_svinval_svnapot_svpbmt
--with-pkgversion='crosstool-NG 1.28.0.58_27cd838' --enable-__cxa_atexit
--disable-libmudflap --disable-libgomp --disable-libssp --disable-libquadmath
--disable-libquadmath-support --disable-libmpx
--with-gmp=/home/danil/Proj/crosstool-ng/.build/riscv64-unknown-linux-gnu/buildtools
--with-mpfr=/home/danil/Proj/crosstool-ng/.build/riscv64-unknown-linux-gnu/buildtools
--with-mpc=/home/danil/Proj/crosstool-ng/.build/riscv64-unknown-linux-gnu/buildtools
--with-isl=/home/danil/Proj/crosstool-ng/.build/riscv64-unknown-linux-gnu/buildtools
--enable-lto --enable-threads=posix --enable-target-optspace --disable-plugin
--disable-nls --disable-multilib
--with-local-prefix=/home/danil/x-tools/riscv64-unknown-linux-gnu/riscv64-unknown-linux-gnu/sysroot
--enable-long-long
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 16.1.0 (crosstool-NG 1.28.0.58_27cd838)
```
## Source code
```
typedef struct {
float x, y, z, w;
} vec4;
vec4 mul_add(vec4 a, vec4 b, vec4 c) {
vec4 v;
v.x = a.x + b.x * c.x;
v.y = a.y + b.y * c.y;
v.z = a.z + b.z * c.z;
v.w = a.w + b.w * c.w;
return v;
}
vec4 neg_mul_add(vec4 a, vec4 b, vec4 c) {
vec4 v;
v.x = c.x - a.x * b.x;
v.y = c.y - a.y * b.y;
v.z = c.z - a.z * b.z;
v.w = c.w - a.w * b.w;
return v;
}
vec4 mul_sub(vec4 a, vec4 b, vec4 c) {
vec4 v;
v.x = a.x * b.x - c.x;
v.y = a.y * b.y - c.y;
v.z = a.z * b.z - c.z;
v.w = a.w * b.w - c.w;
return v;
}
vec4 neg_mul_sub(vec4 a, vec4 b, vec4 c) {
vec4 v;
v.x = -(a.x * b.x) - c.x;
v.y = -(a.y * b.y) - c.y;
v.z = -(a.z * b.z) - c.z;
v.w = -(a.w * b.w) - c.w;
return v;
}
int main(void) {
return 0;
}
```
Compiled it with:
* -march=rv64gcv -O3 -ffp-contract=off. Auto-vectorizer is ON and vector FMA is
present. Not OK.
* -march=rv64gcv -O3 -ffp-contract=off -fno-tree-vectorize. Auto-vectorizer is
OFF and no scalar FMA. OK.
`mul_add` version with auto-vectorizer ON:
```
vle32.v v1,0(a5)
...
vle32.v v2,0(a5)
vle32.v v3,0(sp)
...
vfmadd.vv v1,v3,v2
```
`mul_add` version with auto-vectorizer OFF:
```
fmul.s fa2,fa5,fa4
...
fadd.s fa2,fa2,fa1
```
The same pattern appears in neg_mul_add, mul_sub and neg_mul_sub. Full
comparison: https://godbolt.org/z/aqT6M9hzq
Expected: no FMA fusion in either compilation since -ffp-contract=off was
passed.
Actual: compilation with the auto-vectorizer ignores the flag, compilation
without the auto-vectorizer respects it correctly.
Command line that triggers the bug:
```
riscv64-unknown-linux-gnu-gcc -ffp-contract=off -O3 -march=rv64gcv test.c -o
test
```
Preprocessed file is in attach.
Related Box2D issue:
https://github.com/erincatto/box2d/issues/1086#issuecomment-5143830277