On Mon, 25 Aug 2025, dhr...@nvidia.com wrote:

> From: Dhruv Chawla <dhr...@nvidia.com>
> 
> For ==, < and <=, the fold is to 0. For !=, > and >=, the fold is to 1.
> This only applies when C != 0.

So -50 << 1 < 1 is true, so does this only work for unsigned types,
or tree_expr_nonnegative_p in addition to tree_expr_nonzero_p?
I've probably mislead you when asking to simply extend it to other compare
operators.  For == it should of course work when nonzero only.

Or am I missing something?

Why did you restrict this to #if GIMPLE in v2?

Thanks,
Richard.

> Bootstrapped and regtested on aarch64-linux-gnu.
> 
> Signed-off-by: Dhruv Chawla <dhr...@nvidia.com>
> 
> gcc/ChangeLog:
> 
>       * match.pd: New patterns.
> 
> gcc/testsuite/ChangeLog:
> 
>       * gcc.dg/match-constant-shift-1.c: New test.
>       * gcc.dg/match-constant-shift-1.c: Likewise.
> ---
>  gcc/match.pd                                  | 18 ++++++
>  gcc/testsuite/gcc.dg/match-constant-shift-1.c | 46 +++++++++++++++
>  gcc/testsuite/gcc.dg/match-constant-shift-2.c | 59 +++++++++++++++++++
>  3 files changed, 123 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.dg/match-constant-shift-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/match-constant-shift-2.c
> 
> diff --git a/gcc/match.pd b/gcc/match.pd
> index b1d7a3a1b73..4e8fbaa4dd9 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -1316,6 +1316,24 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>      (if (INTEGRAL_TYPE_P (type))
>        (rshift (op @0 @2) @1))))
>  
> +#if GIMPLE
> +/* (y << x) {==,<,<=} x -> 0 when y != 0.  */
> +(for cmp (eq lt le)
> +  (simplify
> +    (cmp:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
> +    (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
> +      && tree_expr_nonzero_p (@0))
> +     { build_zero_cst (type); })))
> +
> +/* (y << x) {!=,>,>=} x -> 1 when y != 0.  */
> +(for cmp (ne gt ge)
> +  (simplify
> +    (cmp:c (nop_convert1? (lshift @0 @1)) (nop_convert2? @1))
> +    (if (INTEGRAL_TYPE_P (TREE_TYPE (@1))
> +      && tree_expr_nonzero_p (@0))
> +     { build_one_cst (type); })))
> +#endif
> +
>  /* Fold (1 << (C - x)) where C = precision(type) - 1
>     into ((1 << C) >> x). */
>  (simplify
> diff --git a/gcc/testsuite/gcc.dg/match-constant-shift-1.c 
> b/gcc/testsuite/gcc.dg/match-constant-shift-1.c
> new file mode 100644
> index 00000000000..8e7a620e951
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/match-constant-shift-1.c
> @@ -0,0 +1,46 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O3 -fdump-tree-optimized" } */
> +
> +#define TEST_ONE_CST(n, op, type, cst)                                       
>   \
> +  bool lshift_cst_##type##n (type x) { return (cst << x) op x; }
> +
> +#define TEST_OP_CST(n, op)                                                   
>   \
> +  TEST_ONE_CST (n, op, unsigned, n)                                          
>   \
> +  TEST_ONE_CST (n, op, int, n)                                               
>   \
> +  TEST_ONE_CST (n, op, bool, n)                                              
>   \
> +  TEST_ONE_CST (n, op, test_enum, n)
> +
> +#define TEST_ONE(n, op, type)                                                
>   \
> +  bool lshift_##type##n (type x, type y)                                     
>   \
> +  {                                                                          
>   \
> +    if (y <= 0)                                                              
>   \
> +      __builtin_unreachable ();                                              
>   \
> +    return (y << x) op x;                                                    
>   \
> +  }
> +
> +#define TEST_OP(n, op)                                                       
>   \
> +  TEST_ONE (n, op, unsigned)                                                 
>   \
> +  TEST_ONE (n, op, int)                                                      
>   \
> +  TEST_ONE (n, op, bool)                                                     
>   \
> +  TEST_ONE (n, op, test_enum)
> +
> +typedef enum
> +{
> +  ZERO
> +} test_enum;
> +
> +TEST_OP_CST (1, ==)
> +TEST_OP_CST (2, !=)
> +TEST_OP_CST (3, <)
> +TEST_OP_CST (4, >)
> +TEST_OP_CST (5, <=)
> +TEST_OP_CST (6, >=)
> +
> +TEST_OP (1, ==)
> +TEST_OP (2, !=)
> +TEST_OP (3, <)
> +TEST_OP (4, >)
> +TEST_OP (5, <=)
> +TEST_OP (6, >=)
> +
> +/* { dg-final { scan-tree-dump-not "<<" optimized } } */
> diff --git a/gcc/testsuite/gcc.dg/match-constant-shift-2.c 
> b/gcc/testsuite/gcc.dg/match-constant-shift-2.c
> new file mode 100644
> index 00000000000..d6a4a3012f2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/match-constant-shift-2.c
> @@ -0,0 +1,59 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +/* The fold (C << x) <op> x -> 0|1 shouldn't trigger when C is 0.  */
> +
> +#define TEST_ONE_CST(n, op, type, cst)                                       
>   \
> +  type lshift_cst_##type##n (type x) { return (type) ((cst << x) op x); }
> +
> +#define TEST_OP_CST(n, op, cst)                                              
>   \
> +  TEST_ONE_CST (n, op, unsigned, cst)                                        
>   \
> +  TEST_ONE_CST (n, op, int, cst)                                             
>   \
> +  TEST_ONE_CST (n, op, bool, cst)                                            
>   \
> +  TEST_ONE_CST (n, op, test_enum, cst)
> +
> +#define TEST_ONE(n, op, type)                                                
>   \
> +  type lshift_##type##n (type x, type y)                                    \
> +  {                                                                          
>   \
> +    if (y != 0)                                                              
>   \
> +      __builtin_unreachable ();                                              
>   \
> +    return (type) ((y << x) op x);                                           
>   \
> +  }
> +
> +#define TEST_OP(n, op)                                                       
>   \
> +  TEST_ONE (n, op, unsigned)                                                 
>   \
> +  TEST_ONE (n, op, int)                                                      
>   \
> +  TEST_ONE (n, op, bool)                                                     
>   \
> +  TEST_ONE (n, op, test_enum)
> +
> +typedef enum
> +{
> +  ZERO
> +} test_enum;
> +
> +TEST_OP_CST (1, ==, 0)
> +TEST_OP_CST (2, !=, 0)
> +TEST_OP_CST (3, <, 0)
> +TEST_OP_CST (4, >, 0)
> +TEST_OP_CST (5, <=, 0)
> +TEST_OP_CST (6, >=, 0)
> +
> +TEST_OP (1, ==)
> +TEST_OP (2, !=)
> +TEST_OP (3, <)
> +TEST_OP (4, >)
> +TEST_OP (5, <=)
> +TEST_OP (6, >=)
> +
> +/* These end up getting folded by other patterns.  */
> +/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) == 0" 10 optimized} } */
> +/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) != 0" 10 optimized } } */
> +/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) > 0" 2 optimized } } */
> +/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) < 0" 2 optimized} } */
> +/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) >= 0" 2 optimized } } */
> +/* { dg-final { scan-tree-dump-times "x_\\d\\(D\\) <= 0" 2 optimized} } */
> +/* { dg-final { scan-tree-dump-times "~x_\\d\\(D\\)" 4 optimized } } */
> +/* { dg-final { scan-tree-dump-times "return x_\\d\\(D\\);" 4 optimized } } 
> */
> +/* { dg-final { scan-tree-dump-times "return 0;" 6 optimized } } */
> +/* { dg-final { scan-tree-dump-times "return 1;" 6 optimized } } */
> +/* Total: 48.  */
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

Reply via email to