https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128206
Bug ID: 128206
Summary: [x86-64] Failure to vectorize a loop with a carried
dependence and a converted scalar operand
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 loads `out[i - 8]` (a carried
dependence with distance 8), adds a `char` struct member converted to `long`,
and stores the result back to a `long` array, on x86-64. GCC reports `no
vectype for stmt: ... scalar_type: long int`.
Clang 22.1.0 successfully vectorizes the same loop with a vectorization width
of 4 and an interleaved count of 1, without requiring an explicit vectorization
pragma.
The failure is not specific to floating point: the reproducer uses only integer
types. Removing the converted scalar operand, or making the struct field the
same type as the arithmetic, makes GCC vectorize the loop.
## Test case
```c
typedef struct {
char v;
} elem;
long
foo(const elem * __restrict__ data, long * __restrict__ out, int n)
{
for (int i = 0; i < n; i += 1)
{
out[i] = (i >= 8 ? out[i - 8] : 0L)
+ (long)data[i].v;
}
return 0;
}
```
## 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: iftmp.0_35 = *_36;
scalar_type: long int
<source>:8:23: missed: couldn't vectorize loop
<source>:10:16: missed: not vectorized: no vectype for stmt: *_9 = _7;
scalar_type: long int
<source>:6:1: note: vectorized 0 loops in function.
<source>:11:16: note: ***** Analysis failed with vector mode V32QI
<source>:11:16: note: ***** The result for vector mode V32QI would be the same
<source>:11:16: note: ***** Re-trying analysis with vector mode V16QI
<source>:11:16: note: ***** Analysis failed with vector mode V16QI
<source>:11:16: note: ***** Re-trying analysis with vector mode V8QI
<source>:11:16: note: ***** Analysis failed with vector mode V8QI
<source>:11:16: note: ***** Re-trying analysis with vector mode V4QI
<source>:11:16: note: ***** Analysis failed with vector mode V4QI
```
GCC generates scalar code: the first eight iterations are peeled, then a scalar
loop from `i = 8`:
```asm
foo(elem const*, long*, int):
mov ecx, edx
test edx, edx
jle .L2
movsx rax, BYTE PTR [rdi]
mov QWORD PTR [rsi], rax
cmp edx, 1
je .L2
movsx rax, BYTE PTR [rdi+1]
mov QWORD PTR [rsi+8], rax
...
movsx rax, BYTE PTR [rdi+7]
mov QWORD PTR [rsi+56], rax
cmp edx, 8
je .L2
mov eax, 8
.L3:
movsx rdx, BYTE PTR [rdi+rax]
add rdx, QWORD PTR [rsi-64+rax*8]
mov QWORD PTR [rsi+rax*8], rdx
add rax, 1
cmp ecx, eax
jg .L3
.L2:
xor eax, eax
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: the cost-model indicates that interleaving is not
beneficial [-Rpass-analysis=loop-vectorize]
<source>:8:5: remark: vectorized loop (vectorization width: 4, interleaved
count: 1) [-Rpass=loop-vectorize]
```
The generated assembly uses AVX2 instructions: `vpcmpgtq` + `vpmaskmovq` handle
the `i >= 8` guard for the carried load of `out[i - 8]`, `vpmovsxbq`
sign-extends the `char` member to `long`, and `vpaddq` / `vmovdqu` compute and
store four results at a time:
```asm
; main vectorized loop: masked load of out[i-8], sign-extend char, add, store
.LBB0_4:
vpxor ymm4, ymm0, ymm1
vpcmpgtq ymm4, ymm4, ymm2
vpmaskmovq ymm4, ymm4, ymmword ptr [rsi + 8*rdx - 64]
vpmovsxbq ymm5, dword ptr [rdi + rdx]
vpaddq ymm4, ymm4, ymm5
vmovdqu ymmword ptr [rsi + 8*rdx], ymm4
add rdx, 4
vpaddq ymm0, ymm0, ymm3
cmp rcx, rdx
jne .LBB0_4
```
The `vpcmpgtq` / `vpmaskmovq` sequence implements the `i >= 8` guard for the
carried load, `vpmovsxbq` performs the `char` to `long` conversion, and
`vmovdqu` stores 4 results at a time.
## Additional observations
The following variants were tested:
1. **The failure is not specific to floating point.** The reproducer uses only
`char` and `long`; GCC reports `no vectype for stmt ... scalar_type: long int`.
The same failure reproduces with `double` (`scalar_type: double`), so
floating-point arithmetic and strict-fp settings are not the cause.
2. **The converted scalar operand is essential.** Removing the `+
(long)data[i].v` term (leaving only the carried load `out[i - 8]`) makes GCC
vectorize the loop.
3. **The conversion is essential.** If the struct field is changed to `long`
(so no conversion is needed), i.e. `out[i] = (i >= 8 ? out[i - 8] : 0L) +
data[i].v;`, GCC vectorizes the loop.
4. **The dependence distance is not essential.** A distance of 4 instead of 8
reproduces the failure identically.
5. **A constant addend is not essential.** Removing an extra `+ 2` constant
term from the original test case does not change the divergence.
The failure appears to be in the vectorizer's vectype determination for the
carried load / store when the stored value combines a dependence-carried load
with a type-converted scalar operand.
## Live examples
GCC:
https://godbolt.org/z/vaK8T1rb9
Clang:
https://godbolt.org/z/6r3dKqWen