https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128325
Bug ID: 128325
Summary: wrong code with --param=vect-partial-vector-usage=2:
masked alignment peeling starts a shift induction two
steps ahead
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: wrong-code
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: dohanam2 at illinois dot edu
Target Milestone: ---
With masked (partial-vector) peeling for alignment, a `x <<= 1` induction gets
the wrong start value: lane j of the first vector holds `x << (j + 1)`, but
lane j is iteration j - 1, which needs `x << (j - 1)`.
```c
__attribute__((noipa)) void
f (unsigned *d, unsigned x)
{
d = __builtin_assume_aligned (d, 64, 4);
for (int i = 0; i < 97; i++)
{
d[i] = x;
x <<= 1;
}
}
int
main (void)
{
static unsigned buf[128] __attribute__((aligned (64)));
f (buf + 1, 1);
for (int i = 0; i < 97; i++)
if (buf[i + 1] != (i < 32 ? 1u << i : 0))
__builtin_abort ();
return 0;
}
```
```
gcc -O3 -mavx512f --param=vect-partial-vector-usage=2 -fno-vect-cost-model t.c
```
Trunk at 44c0f794dd4129f279ca9010955095307b981b55 (which has the PR128267 fix)
emits for `f`:
```
leal (%rsi,%rsi), %eax
kmovw .LC2(%rip), %k2
vpbroadcastd %eax, %zmm0
movl $-2, %eax
vpsllvd .LC0(%rip), %zmm0, %zmm0
kmovw %eax, %k1
vmovdqa32 %zmm0, -4(%rdi){%k1}
```
`.LC0` is `{0, 1, ..., 15}`. The store goes to `-4(%rdi)`, i.e. `d - 1`, with
lane 0 masked off, so lane 1 is `d[0]` and gets `(2 * x) << 1 = 4 * x` instead
of `x`. The vect dump shows the same:
```
_6 = {x_9(D) << 1, x_9(D) << 1, ...};
_5 = _6 << { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
vectp_d.8_24 = d_8 + 18446744073709551612;
loop_mask_27 = PHI <_33(5), { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1 }(6)>
```
I could not run it: this machine has no AVX-512. Built with this trunk, the
program passes at -O0 and at -O3 -mavx2. Without
`--param=vect-partial-vector-usage=2`, or with the default cost model, the loop
is not peeled with a mask and the code is fine. `x >>= 1` goes wrong the same
way.
It looks like `vect_peel_nonlinear_iv_init` moves the shl/shr start forward by
step * skip for `LOOP_VINFO_MASK_SKIP_NITERS`, but with masked peeling the
first vector starts skip iterations before iteration 0, so the start would have
to move back instead. A shift cannot be undone exactly, so perhaps such loops
should not be peeled with a mask at all.
Found with an LLM-based testing tool; I used Claude to reduce it and look for
the cause.