On Fri, Mar 13, 2026 at 10:43 PM Philipp Tomsich
<[email protected]> wrote:
>
> The single_use restriction on the X +- C1 CMP C2 -> X CMP C2 -+ C1
> simplification (for eq/ne) prevents folding patterns like (++*a == 1)
> into (*a == 0) when the defining SSA value has multiple uses.
>
> Comparing against zero is cheaper on most targets (beqz on RISC-V,
> cbz on AArch64), so the transform is profitable even when the
> defining SSA has multiple uses. Relax single_use when the folded
> comparison constant is zero.
>
> For example, given:
> _1 = *a;
> _2 = _1 + 1;
> *a = _2;
> if (_2 == 1)
>
> match.pd now produces:
> if (_1 == 0)
>
> which generates beqz/cbz instead of li+beq/cmp+b.eq.
>
> This is a partial fix towards the issue described in PR120283.
One reason for the single-use check is that we want to avoid the
transform for a loop exit check where the result prevents coalescing
of the in-loop IV before/after update and thus requires a non-empty
latch block. IIRC there's code that tries to
fixup during out-of-SSA, but please double-check this actually works
(you still get a copy then, of course).
That said, the check was added on purpose.
Richard.
>
> gcc/ChangeLog:
>
> * match.pd: Relax single_use for eq/ne when folded constant
> is zero.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/tree-ssa/forwprop-pre-incr-cmp.c: New test.
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 7f16fd4e0814..c8601ea6878a 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -9864,7 +9864,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> (if (TREE_OVERFLOW (res)
> && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0)))
> { constant_boolean_node (cmp == NE_EXPR, type); }
> - (if (single_use (@3))
> + (if (single_use (@3) || wi::to_wide (res) == 0)
> (cmp @0 { TREE_OVERFLOW (res)
> ? drop_tree_overflow (res) : res; }))))))))
> (for cmp (lt le gt ge)
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-pre-incr-cmp.c
> b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-pre-incr-cmp.c
> new file mode 100644
> index 000000000000..cfa6afe304c9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-pre-incr-cmp.c
> @@ -0,0 +1,33 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-forwprop2" } */
> +
> +/* Verify that the X +- C1 CMP C2 -> X CMP C2 -+ C1 pattern fires
> + when the folded constant is zero, even without single use.
> + PR tree-optimization/120283. */
> +
> +void g ();
> +
> +/* Unsigned EQ: ++*a == 1 -> *a == 0. */
> +void f1 (unsigned int *a)
> +{
> + if (++*a == 1)
> + g ();
> +}
> +
> +/* Unsigned NE: ++*a != 1 -> *a != 0. */
> +void f2 (unsigned int *a)
> +{
> + if (++*a != 1)
> + g ();
> +}
> +
> +/* Unsigned EQ with addend > 1: (*a += 3) == 3 -> *a == 0. */
> +void f3 (unsigned int *a)
> +{
> + if ((*a += 3) == 3)
> + g ();
> +}
> +
> +/* Positive: f1, f3 should fold to == 0, f2 should fold to != 0. */
> +/* { dg-final { scan-tree-dump-times "== 0" 2 "forwprop2" } } */
> +/* { dg-final { scan-tree-dump-times "!= 0" 1 "forwprop2" } } */
> --
> 2.34.1
>