https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126550

            Bug ID: 126550
           Summary: RISC-V: auto-vectorization miscompiles duplicated
                    deinterleave store via vcompress/vrgather
                    (rv64gcv_zvl256b)
           Product: gcc
           Version: 16.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: [email protected]
  Target Milestone: ---

Created attachment 65195
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65195&action=edit
preprocessed file triggering the bug

At `-O3` for a RISC-V target with `zvl256b` GCC miscompiles a loop that stores
each source pair into two adjacent 2-element `uint16_t` fields of a 12-byte
struct (a duplicate deinterleave, `dst[k] = {first, second, first, second}`).

The loop is compiled to a `vcompress.vm` / `vrgather.vv` combination that
reads only every 4th source pair and broadcasts it across four destinations.
Element 1 of the result wrongly receives `src[0]` instead of `src[1]`.

The source is well-defined C (no UB: `-fsanitize=address,undefined` is clean,
`-fno-strict-aliasing -fwrapv` makes no difference).

wrong code:
  riscv64-unknown-elf-gcc -O3 -march=rv64gcv_zvl256b -mabi=lp64d bug.c -o bug

correct:
  riscv64-unknown-elf-gcc -O3 -fno-tree-vectorize -march=rv64gcv_zvl256b
-mabi=lp64d bug.c -o bug
  riscv64-unknown-elf-gcc -O3 -march=rv64gcv -mabi=lp64d bug.c -o bug # VLEN
not fixed


```
$ riscv64-unknown-elf-gcc -O3 -march=rv64gcv_zvl256b -mabi=lp64d bug.c -o bug
$ qemu-riscv64 -cpu rv64,v=true,vlen=256 ./bug
FAIL at 1: got {7,123,7,123} want {2661,40626,2661,40626}
$ spike --isa=rv64gcbv_zvl256b pk ./bug
FAIL at 1: got {7,123,7,123} want {2661,40626,2661,40626}
```

Tested with qemu-riscv64 8.2.2 and spike (riscv-isa-sim) commit
`3d8eb089bd289c59dcb506f197a172e02beb7b5b` (1.1.1-dev).

Compiled with the gcc from riscv-collab/riscv-gnu-toolchain most current
nightly July 15, 2026:
$ riscv64-unknown-elf-gcc -v
Target: riscv64-unknown-elf
Configured with:
/home/runner/work/riscv-gnu-toolchain/riscv-gnu-toolchain/gcc/configure
--target=riscv64-unknown-elf --prefix=/mnt/riscv --disable-shared
--disable-threads --enable-languages=c,c++ --with-pkgversion=g6afcc4f6d
--with-system-zlib --enable-tls --with-newlib
--with-sysroot=/mnt/riscv/riscv64-unknown-elf
--with-native-system-header-dir=/include --disable-libmudflap --disable-libssp
--disable-libquadmath --disable-libgomp --disable-nls
--disable-tm-clone-registry --src=.././gcc --disable-multilib --with-abi=lp64d
--with-arch=rv64gc --with-isa-spec=20191213
--with-specs='%{!mcmodel*:-mcmodel=medlow}' 'CFLAGS_FOR_TARGET=-Os   
-mcmodel=medlow' 'CXXFLAGS_FOR_TARGET=-Os    -mcmodel=medlow'
gcc version 16.1.0 (g6afcc4f6d)


$ cat bug.c#include <stdint.h>
#include <stdio.h>

struct Pair { uint16_t first, second; };
struct S { uint16_t a[2], b[2]; uint32_t r; };

__attribute__((noipa))
void load(struct S *dst, const struct Pair *src, unsigned long n)
{
  for (unsigned long k = 0; k < n; k++) {
    dst[k].a[0] = src[k].first;
    dst[k].a[1] = src[k].second;
    dst[k].b[0] = src[k].first;
    dst[k].b[1] = src[k].second;
  }
}

#define N 40
static struct Pair src[N];
static struct S    out[N];

int main(void)
{
  for (unsigned i = 0; i < N; i++) {
    src[i].first = (uint16_t)(i * 2u + 1);
    src[i].second = (uint16_t)(i * 3u + 2);
  }
  load(out, src, N);

  for (unsigned i = 0; i < N; i++) {
    uint16_t f = src[i].first, s = src[i].second;
    if (out[i].a[0] != f || out[i].a[1] != s ||
      out[i].b[0] != f || out[i].b[1] != s) {
      printf("FAIL at %u: got {%u,%u,%u,%u} want {%u,%u,%u,%u}\n",
        i, out[i].a[0], out[i].a[1], out[i].b[0], out[i].b[1], f, s, f, s);
      return 1;
    }
  }
  printf("OK\n");
  return 0;
}

Reply via email to