https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128175
Bug ID: 128175
Summary: [x86-64] Failure to vectorize a conditional integer
multiply reduction with two updates to the reduction
variable
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: ---
GCC 16.1.0 fails to vectorize a conditional integer multiply reduction loop on
x86-64 when the reduction variable is updated by two multiply statements in the
same iteration, reporting `not vectorized: unsupported use in stmt.`.
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.
GCC can vectorize the loop when the second update to the reduction variable is
removed. The failure therefore appears to be associated with the presence of
two multiply updates to the same reduction variable within one loop iteration.
## Test case
```c
typedef struct {
int v;
} elem;
int foo(const elem *a, const elem *b, int n)
{
int p = 1;
for (int i = 0; i < n; i += 1) {
if (b[i].v != 0) {
p *= a[i].v;
p *= b[i].v;
}
}
return p;
}
```
## 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>:10:23: missed: couldn't vectorize loop
<source>:6:1: missed: not vectorized: unsupported use in stmt.
<source>:6:1: note: vectorized 0 loops in function.
<source>:18:12: note: ***** Analysis failed with vector mode V8SI
<source>:18:12: note: ***** Skipping vector mode V32QI, which would repeat the
analysis for V8SI
```
GCC generates a scalar loop with a conditional branch and scalar multiply
chain:
```asm
.LBB0_13:
mov r8d, dword ptr [rsi + 4*rdx]
test r8d, r8d
je .LBB0_15
imul r8d, eax
imul r8d, dword ptr [rdi + 4*rdx]
mov eax, r8d
jmp .LBB0_15
```
## 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>:10:5: remark: vectorized loop (vectorization width: 8, interleaved
count: 4) [-Rpass=loop-vectorize]
```
The generated assembly uses AVX2 instructions to process multiple iterations
using SIMD operations. Relevant excerpt — the vectorized reduction loop and its
horizontal reduction epilogue:
```asm
; main vectorized loop: 8 x i32 per iteration, accumulating into
ymm0/ymm3/ymm4/ymm5
.LBB0_7:
vmovdqu ymm6, ymmword ptr [rsi + r9]
vpcmpeqd ymm7, ymm6, ymm1
vpxor ymm8, ymm7, ymm2
vpmaskmovd ymm8, ymm8, ymmword ptr [r8 + r9 - 96]
vpmulld ymm6, ymm6, ymm0
vpmulld ymm6, ymm6, ymm8
vblendvps ymm0, ymm6, ymm0, ymm7
...
sub r9, -128
cmp rax, r9
jne .LBB0_7
; horizontal reduction of the four partial products
vpmulld ymm0, ymm3, ymm0
vpmulld ymm1, ymm5, ymm4
vpmulld ymm0, ymm1, ymm0
vextracti128 xmm1, ymm0, 1
vpmulld xmm0, xmm0, xmm1
vpshufd xmm1, xmm0, 238
vpmulld xmm0, xmm0, xmm1
vpshufd xmm1, xmm0, 85
vpmulld xmm0, xmm0, xmm1
vmovd eax, xmm0
```
The `vpmulld` inside the loop and the `vpshufd`/`vpmulld` horizontal-reduction
chain after it show that Clang has recognized `p` as a vectorizable multiply
reduction (V8SI, interleaved 4).
## Additional observations
Two modifications allow GCC to vectorize the loop.
**1. Removing the second update to the reduction variable while retaining the
conditional:**
```c
int foo(const elem *a, const elem *b, int n)
{
int p = 1;
for (int i = 0; i < n; i += 1) {
if (b[i].v != 0)
p *= a[i].v;
}
return p;
}
```
GCC successfully vectorizes this loop (`loop vectorized using 32 byte vectors
and unroll factor 16`).
**2. Reducing the reduction type width is irrelevant:**
Replacing the `int` reduction with a `short` reduction (with `(short)` casts)
still reproduces the miss. The failure is not specific to the reduction type.
**3. `__restrict__` is irrelevant:**
Removing `__restrict__` from both pointer parameters still reproduces the miss,
and Clang still vectorizes. Alias analysis is not the cause.
**4. Two distinct input arrays are irrelevant:**
Changing the second update to read the same array as the first (`p *= a[i].v; p
*= a[i].v;`) still reproduces the miss.
These observations suggest that GCC's failure is related to handling a
reduction variable that is updated by two multiply statements within one
iteration, rather than to the reduction type, pointer aliasing, or the number
of distinct input streams.
## Live examples
GCC:
https://godbolt.org/z/qrf4PGdv4
Clang:
https://godbolt.org/z/ccxax9T5E