middle-end: correctly identify the edge taken when condition is true. [PR113287]

The vectorizer needs to know during early break vectorization whether the edge
that will be taken if the condition is true stays or leaves the loop.

This is because the code assumes that if you take the true branch you exit the
loop.  If you don't exit the loop it has to generate a different condition.

Basically it uses this information to decide whether it's generating a
"any element" or an "all element" check.

Bootstrapped Regtested on aarch64-none-linux-gnu, x86_64-pc-linux-gnu
and no issues with --enable-lto --with-build-config=bootstrap-O3
--enable-checking=release,yes,rtl,extra.

gcc/ChangeLog:

	PR tree-optimization/113287
	* tree-vect-stmts.cc (vectorizable_early_exit): Check the flags on edge
	instead of using BRANCH_EDGE to determine true edge.

gcc/testsuite/ChangeLog:

	PR tree-optimization/113287
	* gcc.dg/vect/vect-early-break_100-pr113287.c: New test.
	* gcc.dg/vect/vect-early-break_99-pr113287.c: New test.
This commit is contained in:
Tamar Christina 2024-01-10 14:31:02 +00:00
parent cac9d2d234
commit 91fd5c9496
3 changed files with 74 additions and 2 deletions

View file

@ -0,0 +1,35 @@
/* { dg-add-options vect_early_break } */
/* { dg-require-effective-target vect_early_break } */
/* { dg-require-effective-target vect_int } */
/* { dg-require-effective-target bitint } */
__attribute__((noipa)) void
bar (unsigned long *p)
{
__builtin_memset (p, 0, 142 * sizeof (unsigned long));
p[17] = 0x50000000000UL;
}
__attribute__((noipa)) int
foo (void)
{
unsigned long r[142];
bar (r);
unsigned long v = ((long) r[0] >> 31);
if (v + 1 > 1)
return 1;
for (unsigned long i = 1; i <= 140; ++i)
if (r[i] != v)
return 1;
unsigned long w = r[141];
if ((unsigned long) (((long) (w << 60)) >> 60) != v)
return 1;
return 0;
}
int
main ()
{
if (foo () != 1)
__builtin_abort ();
}

View file

@ -0,0 +1,32 @@
/* { dg-add-options vect_early_break } */
/* { dg-require-effective-target vect_early_break } */
/* { dg-require-effective-target vect_int } */
/* { dg-require-effective-target bitint } */
_BitInt(998) b;
char c;
char d;
char e;
char f;
char g;
char h;
char i;
char j;
void
foo(char y, _BitInt(9020) a, char *r)
{
char x = __builtin_mul_overflow_p(a << sizeof(a), y, 0);
x += c + d + e + f + g + h + i + j + b;
*r = x;
}
int
main(void)
{
char x;
foo(5, 5, &x);
if (x != 1)
__builtin_abort();
return 0;
}

View file

@ -12870,13 +12870,18 @@ vectorizable_early_exit (vec_info *vinfo, stmt_vec_info stmt_info,
rewrite conditions to always be a comparison against 0. To do this it
sometimes flips the edges. This is fine for scalar, but for vector we
then have to flip the test, as we're still assuming that if you take the
branch edge that we found the exit condition. */
branch edge that we found the exit condition. i.e. we need to know whether
we are generating a `forall` or an `exist` condition. */
auto new_code = NE_EXPR;
auto reduc_optab = ior_optab;
auto reduc_op = BIT_IOR_EXPR;
tree cst = build_zero_cst (vectype);
edge exit_true_edge = EDGE_SUCC (gimple_bb (cond_stmt), 0);
if (exit_true_edge->flags & EDGE_FALSE_VALUE)
exit_true_edge = EDGE_SUCC (gimple_bb (cond_stmt), 1);
gcc_assert (exit_true_edge->flags & EDGE_TRUE_VALUE);
if (flow_bb_inside_loop_p (LOOP_VINFO_LOOP (loop_vinfo),
BRANCH_EDGE (gimple_bb (cond_stmt))->dest))
exit_true_edge->dest))
{
new_code = EQ_EXPR;
reduc_optab = and_optab;