https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126597
Bug ID: 126597
Summary: [15/16/17 Regression] Wrong aarch64 code with -O0 and
aarch64_expand_vec_perm_const_1
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: wrong-code
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: ktkachov at gcc dot gnu.org
Target Milestone: ---
Target: aarch64
/* Candidate config/aarch64/aarch64.cc:28708 - wrong code.
aarch64_expand_vec_perm_const_1 canonicalises a selector whose first
index picks the second operand:
poly_int64 nelt = d->perm.length ();
if (known_ge (d->perm[0], nelt))
{
d->perm.rotate_inputs (1);
std::swap (d->op0, d->op1);
}
but it does not swap d->zero_op0_p / d->zero_op1_p, which were computed
from the pre-swap operands in aarch64_vectorize_vec_perm_const. The two
recognisers that consume those flags then use the wrong operand:
aarch64_evpc_and: rtx in = d->zero_op0_p ? d->op1 : d->op0;
aarch64_evpc_tbl: if (d->zero_op0_p) d->op0 = d->op1;
Both end up permuting the all-zero operand instead of the data operand,
so every function below returns { 0, 0, ... }.
Flags (aborts):
gcc -O0 */
typedef int v4si __attribute__ ((vector_size (16)));
typedef unsigned char v16qi __attribute__ ((vector_size (16)));
/* aarch64_evpc_and, zero operand is op1, perm[0] = 4 >= nelt.
Expected { 0, x1, x2, x3 }; emits "movi v.4s, 0; fmov s, s". */
v4si
f_and_op1 (v4si x)
{
const v4si m = { 4, 1, 2, 3 };
return __builtin_shuffle (x, (v4si) { 0, 0, 0, 0 }, m);
}
/* aarch64_evpc_and, zero operand is op0, perm[0] = 4 >= nelt.
Expected { x0, 0, x2, 0 }; ANDs the zero operand with the mask. */
v4si
f_and_op0 (v4si x)
{
const v4si m = { 4, 1, 6, 3 };
return __builtin_shuffle ((v4si) { 0, 0, 0, 0 }, x, m);
}
/* aarch64_evpc_tbl, perm[0] = 16 >= nelt.
Expected { 0, x1 .. x14, 0 }; emits "tbl v, {zero}, idx". */
v16qi
f_tbl (v16qi x)
{
const v16qi m = { 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17 };
return __builtin_shuffle (x, (v16qi) { 0 }, m);
}
/* Control: identical shape but perm[0] = 0 < nelt, so no operand swap
happens and the same AND recogniser produces correct code. */
v4si
g_canonical (v4si x)
{
const v4si m = { 0, 1, 2, 7 };
return __builtin_shuffle (x, (v4si) { 0, 0, 0, 0 }, m);
}
int
main (void)
{
v4si x = { 11, 22, 33, 44 };
v4si r = f_and_op1 (x);
if (r[0] != 0 || r[1] != 22 || r[2] != 33 || r[3] != 44)
__builtin_abort ();
v4si r2 = f_and_op0 (x);
if (r2[0] != 11 || r2[1] != 0 || r2[2] != 33 || r2[3] != 0)
__builtin_abort ();
v16qi y;
for (int i = 0; i < 16; i++)
y[i] = i + 1;
v16qi t = f_tbl (y);
if (t[0] != 0 || t[15] != 0)
__builtin_abort ();
for (int i = 1; i < 15; i++)
if (t[i] != i + 1)
__builtin_abort ();
v4si c = g_canonical (x);
if (c[0] != 11 || c[1] != 22 || c[2] != 33 || c[3] != 0)
__builtin_abort ();
return 0;
}
aborts on aarch64 at -O0 and passes with optimisation