[Bug c/126932] New: GCC's RISC-V auto-vectorizer emits FMA instructions with -ffp-contract=off

grezindanil at gmail dot com via Gcc-bugs <[email protected]>
Newsgroups gmane.comp.gcc.bugs
Message-ID <[email protected]/bugzilla/>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.