https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128208
Bug ID: 128208
Summary: [x86-64] Failure to vectorize a conditional reduction
with a unit-stride test and a stride-8 accumulate
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 loop that tests `data[i].a != 0` (unit stride)
and conditionally accumulates `data[i * 8].a` (stride 8) into a `char`
reduction, on x86-64. GCC reports `unsupported SLP instances` and generates
scalar code.
Clang 22.1.0 successfully vectorizes the same loop with a vectorization width
of 8 and an interleaved count of 4, when `#pragma clang loop vectorize(enable)`
is present (without the pragma, Clang's cost model declines to vectorize).
The failure depends on the combination of a unit-stride load in the condition
and a stride-8 load in the reduction body: changing the stride-8 access to unit
stride, or making the condition also use the stride-8 access, makes GCC
vectorize the loop.
## Test case
```c
typedef struct {
unsigned int a;
} elem;
char
foo(const elem * __restrict__ data, int n)
{
char sum = 0;
#pragma clang loop vectorize(enable)
for (int i = 0; i < n; i += 1)
{
if (data[i].a != 0) {
sum += data[(i * 8)].a;
}
}
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>:11:23: missed: couldn't vectorize loop
<source>:11:23: missed: unsupported SLP instances
<source>:6:1: note: vectorized 0 loops in function.
<source>:14:17: note: ***** Analysis failed with vector mode V8SI
<source>:14:17: note: ***** The result for vector mode V32QI would be the same
<source>:14:17: note: ***** Re-trying analysis with vector mode V16QI
<source>:14:17: note: ***** Analysis failed with vector mode V16QI
<source>:14:17: note: ***** Re-trying analysis with vector mode V8QI
<source>:14:17: note: ***** Analysis failed with vector mode V8QI
<source>:14:17: note: ***** Re-trying analysis with vector mode V4QI
<source>:14:17: note: ***** Analysis failed with vector mode V4QI
```
GCC generates scalar code for the loop:
```asm
foo(elem const*, int):
test esi, esi
jle .L5
mov esi, esi
xor eax, eax
xor edx, edx
.L4:
mov ecx, DWORD PTR [rdi+rax*4]
test ecx, ecx
je .L3
lea ecx, [0+rax*8]
add dl, BYTE PTR [rdi+rcx*4]
.L3:
add rax, 1
cmp rsi, rax
jne .L4
mov eax, edx
ret
.L5:
xor edx, edx
mov eax, edx
ret
```
The scalar loop tests `data[i].a` (`mov ecx, DWORD PTR [rdi+rax*4]`) and
conditionally accumulates `data[i*8].a` (`add dl, BYTE PTR [rdi+rcx*4]`), with
no vectorization.
## 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>:11:5: remark: vectorized loop (vectorization width: 8, interleaved
count: 4) [-Rpass=loop-vectorize]
```
Without `#pragma clang loop vectorize(enable)`, Clang reports:
```text
<source>:10:5: remark: the cost-model indicates that vectorization is not
beneficial [-Rpass-missed=loop-vectorize]
<source>:10:5: remark: the cost-model indicates that interleaving is not
beneficial [-Rpass-missed=loop-vectorize]
```
so the divergence depends on Clang being allowed to override its cost model via
the pragma.
Clang vectorizes the loop with width 8 and interleaved count 4. The generated
code is large because it uses masked gathers and byte-wise accumulation for the
stride-8 access, but it is genuinely vectorized. Relevant excerpt — mask
construction and masked `char` accumulation:
```asm
; build the byte mask for data[i].a != 0
vpcmpeqd ymm3, ymm14, ymm5
vpxor ymm3, ymm3, ymm6
vpackssdw xmm3, xmm3, xmm4
vpacksswb xmm14, xmm3, xmm3
; gather the stride-8 char values and accumulate with the mask
vpshufb xmm5, xmm10, xmm6
...
vpand xmm4, xmm4, xmm5
vpaddb xmm9, xmm9, xmm4
```
The `vpcmpeqd` / `vpackssdw` / `vpacksswb` sequence builds the `data[i].a != 0`
predicate as a byte mask, `vpshufb` / `vpunpckldq` / `vpinsrb` collect the
stride-8 `char` values, and `vpand` / `vpaddb` conditionally accumulate them.
## Additional observations
The following variants were tested (each recompiled and re-checked; the
criterion was that GCC remains unvectorized — the missed reason in the log may
vary — while Clang still vectorizes with vectorize(enable)):
1. **The struct layout is not essential.** Removing the unused `short` and
`long` fields, leaving only `unsigned int a`, reproduces the failure
identically.
2. **The outer stride is not essential.** Changing `i += 67` to `i += 1`
reproduces the failure identically.
3. **The `idx` temporary is not essential.** Using `i` directly reproduces the
failure identically.
4. **An explicit `(char)` cast is not essential.** Assigning `data[(i * 8)].a`
to `char sum` without an explicit cast reproduces the failure identically.
5. **The stride-8 access is essential.** Changing `data[(i * 8)].a` to
`data[i].a` makes GCC vectorize the loop.
6. **The unit-stride condition is essential.** Changing the condition to also
read the stride-8 access (`data[(i * 8)].a != 0`) makes GCC vectorize the loop.
The failure requires a unit-stride test combined with a stride-8 accumulate.
7. **Clang needs `vectorize(enable)`.** Without the pragma, Clang's cost model
declines to vectorize. The GCC side uses `-fvect-cost-model=unlimited` as the
corresponding relaxation.
## Live examples
GCC:
https://godbolt.org/z/fzzv5q8Wa
Clang:
https://godbolt.org/z/9YPGhhh83