https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128199
Bug ID: 128199
Summary: [x86-64] Failure to vectorize a stride-2 reduction of
a float struct member
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 stride-2 reduction loop that accumulates a
`float` struct member into an `int` accumulator on x86-64, reporting `no
vectype for stmt: _5 = *_4.f0; scalar_type: const float`.
Clang 22.1.0 successfully vectorizes the same loop with a vectorization width
of 8 and an interleaved count of 4, without requiring an explicit vectorization
pragma.
The failure depends on both the struct layout and the non-unit stride: removing
any field from the struct, or changing `i * 2` to `i`, makes GCC vectorize the
loop.
## Test case
```c
#include <stdint.h>
#include <stddef.h>
typedef struct {
float f0;
double d1;
float f2;
float f3;
int s4;
} elem;
int
foo(const elem * __restrict__ data, int n)
{
int sum = 0;
for (int i = 0; i < n; i += 1)
{
sum += (int)data[(i * 2)].f0;
}
return sum;
}
```
## 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>:17:23: missed: couldn't vectorize loop
<source>:19:33: missed: not vectorized: no vectype for stmt: _5 = *_4.f0;
scalar_type: const float
<source>:13:1: note: vectorized 0 loops in function.
<source>:19:13: note: ***** Analysis failed with vector mode V8SF
<source>:19:13: note: ***** The result for vector mode V32QI would be the same
<source>:19:13: note: ***** Re-trying analysis with vector mode V16QI
<source>:19:13: note: ***** Analysis failed with vector mode V16QI
<source>:19:13: note: ***** Re-trying analysis with vector mode V8QI
<source>:19:13: note: ***** Analysis failed with vector mode V8QI
<source>:19:13: note: ***** Re-trying analysis with vector mode V4QI
<source>:19:13: note: ***** Analysis failed with vector mode V4QI
```
GCC generates scalar code for the loop (no loop vectorization, no SLP
vectorization of the body).
## 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>:17:5: remark: vectorized loop (vectorization width: 8, interleaved
count: 4) [-Rpass=loop-vectorize]
```
The generated assembly uses AVX2 instructions to gather the stride-2 `float`
field values, convert them to `int`, and accumulate them with SIMD operations.
Relevant excerpt — the vectorized reduction loop and its horizontal reduction:
```asm
; main vectorized loop: gathers data[i*2].f0 with stride 2, 8 lanes,
interleaved 4
.LBB0_7:
vmovss xmm4, dword ptr [rdi + rsi]
vmovss xmm5, dword ptr [rdi + rsi + 256]
vinsertps xmm5, xmm5, dword ptr [rdi + rsi + 320], 16
vinsertps xmm5, xmm5, dword ptr [rdi + rsi + 384], 32
vinsertps xmm5, xmm5, dword ptr [rdi + rsi + 448], 48
vinsertps xmm4, xmm4, dword ptr [rdi + rsi + 64], 16
vinsertps xmm4, xmm4, dword ptr [rdi + rsi + 128], 32
vinsertps xmm4, xmm4, dword ptr [rdi + rsi + 192], 48
vinsertf128 ymm4, ymm4, xmm5, 1
vcvttps2dq ymm4, ymm4
vpaddd ymm0, ymm0, ymm4
...
add rsi, 2048
cmp rax, rsi
jne .LBB0_7
; horizontal reduction of the partial sums
vpaddd ymm0, ymm1, ymm0
vpaddd ymm0, ymm2, ymm0
vpaddd ymm0, ymm3, ymm0
vextracti128 xmm1, ymm0, 1
vpaddd xmm0, xmm0, xmm1
vpshufd xmm1, xmm0, 238
vpaddd xmm0, xmm0, xmm1
vpshufd xmm1, xmm0, 85
vpaddd xmm0, xmm0, xmm1
vmovd eax, xmm0
```
The `vmovss` + `vinsertps` sequence gathers the stride-2 `float` loads,
`vcvttps2dq` converts them to `int`, and `vpaddd` accumulates 8 lanes at a
time, followed by a horizontal `vpaddd` reduction.
## Additional observations
1. **The struct layout is essential.** With this AoS layout (`float / double /
float / float / int`, struct size 24 bytes with 8-byte alignment), GCC cannot
find a vector type for the strided `float` load. Removing any single field from
the struct makes GCC vectorize the loop.
2. **A non-unit stride is essential.** Changing `data[i * 2].f0` to
`data[i].f0` makes GCC vectorize the loop. The stride-2 access is required to
reproduce the `no vectype for stmt ... scalar_type: const float` failure.
3. **Neither the accumulation type nor the conversion is relevant.** An `int`
accumulator with a direct `(int)` conversion reproduces the failure identically
to a `char` accumulator with a `(char)` conversion; no extra casts are needed.
The failure appears to be in the vectorizer's vectype determination for a
strided load of a `float` struct member with this AoS layout.
## Live examples
GCC:
https://godbolt.org/z/x3PPo5zKq
Clang:
https://godbolt.org/z/MexxoenfW