On Tue, Jan 6, 2026 at 1:46 PM Daniel Barboza
<[email protected]> wrote:
>
> Add a transformation for a nested lshift/rshift inside a plus that compares
> for
> equality with the same operand of the plus. In other words:
>
> ((a OP b) + c EQ|NE c) ? x : y
>
> becomes, for OP = (lshift, rshift) and in a scenario without overflows:
>
> a !=/== 0 ? x : y
I think we want the transformation even if it is used outside of a cond_expr.
Also the above is valid even for types that wrap; just not valid for
types that trap on overflow.
As we already have a pattern for `a + C == C`:
```
/* For equality, this is also true with wrapping overflow. */
(for op (eq ne)
(simplify
(op:c (nop_convert?@3 (plus:c@2 @0 (convert1? @1))) (convert2? @1))
(if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0))
&& (TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0))
|| TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))
&& (CONSTANT_CLASS_P (@0) || (single_use (@2) && single_use (@3)))
&& tree_nop_conversion_p (TREE_TYPE (@3), TREE_TYPE (@2))
&& tree_nop_conversion_p (TREE_TYPE (@3), TREE_TYPE (@1)))
(op @0 { build_zero_cst (TREE_TYPE (@0)); })))
```
The problem is the above does not work as there is an single_use check
on the plus. The single use check was there since the original match
pattern was added.
I am not sure if we should add a special case where in the above
pattern @0 is a shift.
Though changing that will have to wait for GCC 17 I think.
Jeff/Richard,
What are your thoughts on this? I know Richard had thoughts on some
of the single_use in the match patterns before but I have not tracked
them that well.
Thanks,
Andrew
>
> This optimizes the following code, seen in perlbench:
>
> long
> frob(long x, long y, long z)
> {
> long ret = (y << 2) + z;
> long cond = ret != z;
> if (cond == 0)
> ret = 0;
> return ret;
> }
>
> gcc/ChangeLog:
>
> * match.pd (`((a OP b) + c eq|ne c) ? x : y`): New pattern.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/torture/ifcond-plus-shift-czero.c: New test.
>
> Signed-off-by: Daniel Barboza <[email protected]>
> ---
> gcc/match.pd | 17 +++++++++++++
> .../gcc.dg/torture/ifcond-plus-shift-czero.c | 25 +++++++++++++++++++
> 2 files changed, 42 insertions(+)
> create mode 100644 gcc/testsuite/gcc.dg/torture/ifcond-plus-shift-czero.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index ccdc1129e23..fdc18b1ef41 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -2824,6 +2824,23 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> (le (minus (convert:etype @0) { lo; }) { hi; })
> (gt (minus (convert:etype @0) { lo; }) { hi; })))))))))
>
> +/* Reduces ((A OP B) + C EQ|NE C) ? X : Y to
> + A EQ|NE 0 ? x : y, for OP = (lshift, rshift). */
> +(for cmp (eq ne)
> + (for op (lshift rshift)
> + (simplify
> + (cond (cmp:c (plus:c (op @1 @2) @0) @0) @3 @4)
> + (if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (@1))
> + && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@1)))
> + (with
> + {
> + fold_overflow_warning ("assuming signed overflow does not occur "
> + "when changing (X << imm) + C1 cmp C1 to "
> + "X << imm cmp 0 and X << imm cmp 0 "
> + "to X cmp 0",
> + WARN_STRICT_OVERFLOW_COMPARISON); }
> + (cond (cmp @1 { build_zero_cst (type); }) @3 @4))))))
> +
> /* X + Z < Y + Z is the same as X < Y when there is no overflow. */
> (for op (lt le ge gt)
> (simplify
> diff --git a/gcc/testsuite/gcc.dg/torture/ifcond-plus-shift-czero.c
> b/gcc/testsuite/gcc.dg/torture/ifcond-plus-shift-czero.c
> new file mode 100644
> index 00000000000..769412202c3
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/torture/ifcond-plus-shift-czero.c
> @@ -0,0 +1,25 @@
> +/* { dg-do compile } */
> +/* { dg-additional-options "-fdump-tree-phiopt2" } */
> +/* { dg-skip-if "" { *-*-* } { "-O0" "-fno-fat-lto-objects" } { "" } } */
> +
> +long
> +f1 (long x, long y, long z)
> +{
> + long ret = (y << 2) + z;
> + long cond = ret != z;
> + if (cond == 0)
> + ret = 0;
> + return ret;
> +}
> +
> +long
> +f2 (long x, long y, long z)
> +{
> + long ret = (y >> 2) + z;
> + long cond = ret != z;
> + if (cond == 0)
> + ret = 0;
> + return ret;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "== 0" 2 "phiopt2" } } */
> --
> 2.43.0
>