On Thu, 27 Nov 2025, Jakub Jelinek wrote:

> On Fri, Nov 21, 2025 at 11:29:38AM +0100, Jakub Jelinek wrote:
> > On Thu, Nov 20, 2025 at 09:33:50AM +0530, Dhruv Chawla wrote:
> > > Thanks, I have attached the updated patch.
> > 
> > >   PR tree-optimization/122733
> > > 
> > > gcc/ChangeLog:
> > > 
> > >   * match.pd ((y << x) {<,<=,>,>=} x): Remove.
> > >   ((y << x) {==,!=} x): Call constant_boolean_node instead of
> > >   build_one_cst/build_zero_cst and combine into one pattern.
> > > 
> > > gcc/testsuite/ChangeLog:
> > > 
> > >   * gcc.dg/match-shift-cmp-1.c: Update test to only check
> > >   equality.
> > >   * gcc.dg/match-shift-cmp-2.c: Likewise.
> > >   * gcc.dg/match-shift-cmp-3.c: Likewise.
> > >   * gcc.dg/match-shift-cmp-4.c: Removed.
> > 
> > LGTM.
> 
> I've committed your patch now.
> 
> On top of that, here is my attempt to implement it using ranger.
> Note also the changes to the equality pattern, first of all, there
> could be e.g. vector << scalar shifts, although they'll likely
> fail on the nop_convert vs. nop_convert, but also it would never
> match for say unsigned long long @0 and unsigned int @1 etc., pretty
> common cases.
> 
> The new simplifier asks the ranger about ranges and bitmasks, verifies
> @0 is non-zero and that clz of the @0 nonzero bits bitmask (i.e. the minimum
> clz of all possible values of @0) is greater than (or greater than or equal
> to) maximum shift count.  Which one of those depends on if the actual
> non-equality comparison is signed or unsigned.
> 
> 2025-11-27  Jakub Jelinek  <[email protected]>
> 
>       PR tree-optimization/122733
>       * match.pd ((y << x) == x, (y << x) != x): Use convert2?
>       instead of nop_convert2? and test INTEGRAL_TYPE_P on TREE_TYPE (@0)
>       rather than TREE_TYPE (@1).
>       ((y << x) {<,<=,>,>=} x): New simplification.
> 
>       * gcc.dg/match-shift-cmp-4.c: New test.
>       * gcc.dg/match-shift-cmp-5.c: New test.
> 
> --- gcc/match.pd.jj   2025-11-27 12:08:34.887423204 +0100
> +++ gcc/match.pd      2025-11-27 13:06:35.494902478 +0100
> @@ -1343,11 +1343,39 @@ (define_operator_list SYNC_FETCH_AND_AND
>  /* (y << x) == x -> false and (y << x) != x -> true when y != 0.  */
>  (for cmp (eq ne)
>   (simplify
> -  (cmp:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
> -  (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
> +  (cmp:c (nop_convert1? (lshift @0 @1)) (convert2? @1))
> +  (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
>         && tree_expr_nonzero_p (@0))
>        { constant_boolean_node (cmp != EQ_EXPR, type); })))
>  
> +#if GIMPLE
> +/* (y << x) {<,<=} x -> false and (y << x) {>,>=} x -> true when y != 0
> +   and (y << x) >> x == y and for signed comparison (y << x) >= 0.  */
> +(for cmp (gt ge lt le)
> + (simplify
> +  (cmp:c (nop_convert1?@3 (lshift@2 @0 @1)) (convert2? @1))
> +  (if (INTEGRAL_TYPE_P (TREE_TYPE (@0)))
> +   (with { bool ok = false;
> +        int_range_max vr0, vr1;
> +        if (gimple_match_range_of_expr (vr0, @0, @2)
> +            && !vr0.varying_p ()
> +            && !vr0.undefined_p ()

I guess the varying and undefined checks could be put into
gimple_match_range_of_expr as well?

Otherwise looks good.

> +            && gimple_match_range_of_expr (vr1, @1, @2)
> +            && !vr1.varying_p ()
> +            && !vr1.undefined_p ()
> +            && !vr0.contains_p (wi::zero (TYPE_PRECISION (TREE_TYPE (@0)))))
> +          {
> +            unsigned lz = wi::clz (vr0.get_nonzero_bits ());
> +            if (!wi::neg_p (vr1.upper_bound (), TYPE_SIGN (TREE_TYPE (@1)))
> +                && wi::ltu_p (vr1.upper_bound (),
> +                              wi::uhwi (lz + TYPE_UNSIGNED (TREE_TYPE (@3)),
> +                                        TYPE_PRECISION (TREE_TYPE (@1)))))
> +              ok = true;
> +          } }
> +    (if (ok)
> +     { constant_boolean_node (cmp == GT_EXPR || cmp == GE_EXPR, type); })))))
> +#endif
> +
>  /* Fold (1 << (C - x)) where C = precision(type) - 1
>     into ((1 << C) >> x). */
>  (simplify
> --- gcc/testsuite/gcc.dg/match-shift-cmp-4.c.jj       2025-11-27 
> 13:28:04.848516222 +0100
> +++ gcc/testsuite/gcc.dg/match-shift-cmp-4.c  2025-11-27 13:29:37.676904590 
> +0100
> @@ -0,0 +1,47 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-final { scan-tree-dump-times "return 0;" 4 "optimized" { target 
> bitint575 } } } */
> +/* { dg-final { scan-tree-dump-times "return 0;" 2 "optimized" { target { ! 
> bitint575 } } } } */
> +/* { dg-final { scan-tree-dump-not " << " "optimized" } } */
> +
> +bool
> +foo (unsigned long long x, unsigned y)
> +{
> +  if (x >= 64 || x == 0)
> +    __builtin_unreachable ();
> +  if (y > sizeof (unsigned long long) * __CHAR_BIT__ - 6)
> +    __builtin_unreachable ();
> +  return (x << y) <= y;
> +}
> +
> +#if __BITINT_MAXWIDTH__ >= 575
> +bool
> +bar (unsigned _BitInt(575) x, unsigned y)
> +{
> +  if (x >= 1361129467683753853853498429727072845823uwb || x == 0)
> +    __builtin_unreachable ();
> +  if (y > 575 - 130)
> +    __builtin_unreachable ();
> +  return (x << y) < y;
> +}
> +
> +bool
> +baz (unsigned _BitInt(575) x, unsigned y)
> +{
> +  if (x >= 1361129467683753853853498429727072845823uwb || x == 0)
> +    __builtin_unreachable ();
> +  if (y >= 575 - 130)
> +    __builtin_unreachable ();
> +  return ((signed _BitInt(575)) (x << y)) < y;
> +}
> +#endif
> +
> +bool
> +qux (int x, int y)
> +{
> +  if (x >= 128 || x <= 0)
> +    __builtin_unreachable ();
> +  if (y >= sizeof (int) * __CHAR_BIT__ - 7)
> +    __builtin_unreachable ();
> +  return (x << y) <= y;
> +}
> --- gcc/testsuite/gcc.dg/match-shift-cmp-5.c.jj       2025-11-27 
> 13:28:13.585364537 +0100
> +++ gcc/testsuite/gcc.dg/match-shift-cmp-5.c  2025-11-27 13:34:15.448082029 
> +0100
> @@ -0,0 +1,47 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-final { scan-tree-dump-times "return 1;" 4 "optimized" { target 
> bitint575 } } } */
> +/* { dg-final { scan-tree-dump-times "return 1;" 2 "optimized" { target { ! 
> bitint575 } } } } */
> +/* { dg-final { scan-tree-dump-not " << " "optimized" } } */
> +
> +bool
> +foo (unsigned long long x, unsigned y)
> +{
> +  if (x >= 64 || x == 0)
> +    __builtin_unreachable ();
> +  if (y > sizeof (unsigned long long) * __CHAR_BIT__ - 6)
> +    __builtin_unreachable ();
> +  return (x << y) >= y;
> +}
> +
> +#if __BITINT_MAXWIDTH__ >= 575
> +bool
> +bar (unsigned _BitInt(575) x, unsigned y)
> +{
> +  if (x >= 1361129467683753853853498429727072845823uwb || x == 0)
> +    __builtin_unreachable ();
> +  if (y > 575 - 130)
> +    __builtin_unreachable ();
> +  return (x << y) > y;
> +}
> +
> +bool
> +baz (unsigned _BitInt(575) x, unsigned y)
> +{
> +  if (x >= 1361129467683753853853498429727072845823uwb || x == 0)
> +    __builtin_unreachable ();
> +  if (y >= 575 - 130)
> +    __builtin_unreachable ();
> +  return ((signed _BitInt(575)) (x << y)) > y;
> +}
> +#endif
> +
> +bool
> +qux (int x, int y)
> +{
> +  if (x >= 128 || x <= 0)
> +    __builtin_unreachable ();
> +  if (y >= sizeof (int) * __CHAR_BIT__ - 7)
> +    __builtin_unreachable ();
> +  return (x << y) >= y;
> +}
> 
> 
>       Jakub
> 
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

Reply via email to