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

            Bug ID: 128200
           Summary: [x86-64] Failure to vectorize a double-to-long
                    converting store loop
           Product: gcc
           Version: 16.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: bug_hunters at yeah dot net
  Target Milestone: ---

## Description

GCC 16.1.0 fails to vectorize a simple loop that loads `double` values from an
array of structs, converts them to `long`, and stores them to a `long` array,
on x86-64, reporting `no vectype for stmt: _4 = _3->v; scalar_type: const
double`.

Clang 22.1.0 successfully vectorizes the same loop with a vectorization width
of 4 and an interleaved count of 4, without requiring an explicit vectorization
pragma.

## Test case

```c
typedef struct {
    double v;
} elem;

void
foo(const elem *data, long *out, int n)
{
    for (int i = 0; i < n; i += 1)
    {
        out[i] = data[i].v;
    }
}
```

## GCC version

```text
gcc version 16.1.0
```

## GCC options

```text
-S -O3 -ftree-vectorize -ftree-slp-vectorize -march=x86-64-v3
-fopt-info-vec-all -fvect-cost-model=unlimited 
```

## GCC output

```text
<source>:8:23: missed: couldn't vectorize loop
<source>:10:26: missed: not vectorized: no vectype for stmt: _4 = _3->v;
 scalar_type: const double
<source>:6:1: note: vectorized 0 loops in function.
<source>:12:1: note: ***** Analysis failed with vector mode V4DF
<source>:12:1: note: ***** Skipping vector mode V32QI, which would repeat the
analysis for V4DF
```

GCC generates a scalar loop with a scalar `vcvttsd2si` and a scalar store per
iteration:

```asm
foo(elem const*, long*, int):
        test    edx, edx
        jle     .L5
        mov     edx, edx
        xor     eax, eax
        sal     rdx, 3
.L3:
        vcvttsd2si      rcx, QWORD PTR [rdi+rax]
        mov     QWORD PTR [rsi+rax], rcx
        add     rax, 8
        cmp     rax, rdx
        jne     .L3
.L5:
        ret
```

## Comparison with Clang

### Clang version

```text
clang version 22.1.0
LLVM commit:
4434dabb69916856b824f68a64b029c67175e532
```

Clang options:

```text
-S -O3 -ftree-vectorize -ftree-slp-vectorize --target=x86_64-linux-gnu
-march=x86-64-v3 -Rpass=.*vectorize.* -Rpass-missed=.*vectorize.*
-Rpass-analysis=.*vectorize.*
```

Clang reports:

```text
<source>:8:5: remark: vectorized loop (vectorization width: 4, interleaved
count: 4) [-Rpass=loop-vectorize]
```

The generated assembly uses AVX2 instructions to load four `double` values at a
time, convert them with `vcvttsd2si`, pack them into vector registers with
`vpunpcklqdq`, and store them with `vmovdqu`. Relevant excerpt — the vectorized
main loop:

```asm
; main vectorized loop: 16 doubles per iteration (4x4 interleaved),
; converted with vcvttsd2si and stored as long with vmovdqu
.LBB0_6:
        vcvttsd2si      r9, qword ptr [rdi + r8 + 8]
        vcvttsd2si      r10, qword ptr [rdi + r8]
        vmovq   xmm0, r9
        vcvttsd2si      r9, qword ptr [rdi + r8 + 24]
        vmovq   xmm1, r10
        vcvttsd2si      r10, qword ptr [rdi + r8 + 16]
        vmovq   xmm2, r9
        vpunpcklqdq     xmm0, xmm1, xmm0
        vmovq   xmm1, r10
        vpunpcklqdq     xmm1, xmm1, xmm2
        ...
        vmovdqu xmmword ptr [rsi + r8 + 16], xmm1
        vmovdqu xmmword ptr [rsi + r8], xmm0
        vmovdqu xmmword ptr [rsi + r8 + 32], xmm3
        vmovdqu xmmword ptr [rsi + r8 + 48], xmm2
        vmovdqu xmmword ptr [rsi + r8 + 64], xmm5
        vmovdqu xmmword ptr [rsi + r8 + 80], xmm4
        vmovdqu xmmword ptr [rsi + r8 + 96], xmm7
        vmovdqu xmmword ptr [rsi + r8 + 112], xmm6
        sub     r8, -128
        cmp     rdx, r8
        jne     .LBB0_6
```

The `vcvttsd2si` / `vpunpcklqdq` / `vmovdqu` sequence shows Clang vectorizing
the `double -> long` conversion and the packed store, processing 16 elements
per iteration (vectorization width 4, interleaved count 4).

## Additional observations

The following variants were tested (each recompiled and re-checked; the
criterion was that GCC remains unvectorized and does not fall back to SLP
vectorization of the loop body, while Clang still vectorizes):

1. **The loop direction is irrelevant.** A forward loop (`i += 1`) reproduces
the failure identically to the original reverse loop (`i -= 1`).
2. **`__restrict__` is irrelevant.** Removing `__restrict__` from both pointers
does not change the GCC/Clang divergence.
3. **The explicit `(long)` cast is irrelevant.** Using an implicit `double ->
long` conversion in the assignment reproduces the failure identically.
4. **The function signature is irrelevant.** The failure reproduces with a
`void` return type, so no return value is needed in the reproducer.
5. The failure maybe appears to occur early in the vectorizer, during data-ref
analysis

## Live examples

GCC:
https://godbolt.org/z/s9h8sPYrs

Clang:
https://godbolt.org/z/4zsehfvPE

Reply via email to