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

Reply via email to