On Fri, 18 Oct 2024, Richard Sandiford wrote:

> This patch adds a rule to simplify (X >> C1) << (C1 + C2) -> X << C2
> when the low C1 bits of X are known to be zero.
> 
> Any single conversion can take place between the shifts.  E.g. for
> a truncating conversion, any extra bits of X that are preserved by
> truncating after the shift are immediately lost by the shift left.
> And the sign bits used for an extending conversion are the same as
> the sign bits used for the rshift.  (A double conversion of say
> int->unsigned->uint64_t would be wrong though.)

OK.

Thanks,
Richard.

> gcc/
>       * match.pd: Simplify (X >> C1) << (C1 + C2) -> X << C2 if the
>       low C1 bits of X are zero.
> 
> gcc/testsuite/
>       * gcc.dg/tree-ssa/shifts-1.c: New test.
>       * gcc.dg/tree-ssa/shifts-2.c: Likewise.
> ---
>  gcc/match.pd                             | 13 +++++
>  gcc/testsuite/gcc.dg/tree-ssa/shifts-1.c | 61 ++++++++++++++++++++++++
>  gcc/testsuite/gcc.dg/tree-ssa/shifts-2.c | 21 ++++++++
>  3 files changed, 95 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/shifts-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/shifts-2.c
> 
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 268316456c3..540582dc984 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -4902,6 +4902,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>                       - TYPE_PRECISION (TREE_TYPE (@2)))))
>    (bit_and (convert @0) (lshift { build_minus_one_cst (type); } @1))))
>  
> +#if GIMPLE
> +/* (X >> C1) << (C1 + C2) -> X << C2 if the low C1 bits of X are zero.  */
> +(simplify
> + (lshift (convert? (rshift (with_possible_nonzero_bits2 @0) INTEGER_CST@1))
> +         INTEGER_CST@2)
> + (if (INTEGRAL_TYPE_P (type)
> +      && wi::ltu_p (wi::to_wide (@1), element_precision (type))
> +      && wi::ltu_p (wi::to_wide (@2), element_precision (type))
> +      && wi::to_widest (@2) >= wi::to_widest (@1)
> +      && wi::to_widest (@1) <= wi::ctz (get_nonzero_bits (@0)))
> +  (lshift (convert @0) (minus @2 @1))))
> +#endif
> +
>  /* For (x << c) >> c, optimize into x & ((unsigned)-1 >> c) for
>     unsigned x OR truncate into the precision(type) - c lowest bits
>     of signed x (if they have mode precision or a precision of 1).  */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/shifts-1.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/shifts-1.c
> new file mode 100644
> index 00000000000..d88500ca8dd
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/shifts-1.c
> @@ -0,0 +1,61 @@
> +/* { dg-options "-O2 -fdump-tree-optimized-raw" } */
> +
> +unsigned int
> +f1 (unsigned int x)
> +{
> +  if (x & 3)
> +    __builtin_unreachable ();
> +  x >>= 2;
> +  return x << 3;
> +}
> +
> +unsigned int
> +f2 (unsigned int x)
> +{
> +  if (x & 3)
> +    __builtin_unreachable ();
> +  unsigned char y = x;
> +  y >>= 2;
> +  return y << 3;
> +}
> +
> +unsigned long
> +f3 (unsigned int x)
> +{
> +  if (x & 3)
> +    __builtin_unreachable ();
> +  x >>= 2;
> +  return (unsigned long) x << 3;
> +}
> +
> +int
> +f4 (int x)
> +{
> +  if (x & 15)
> +    __builtin_unreachable ();
> +  x >>= 4;
> +  return x << 5;
> +}
> +
> +unsigned int
> +f5 (int x)
> +{
> +  if (x & 31)
> +    __builtin_unreachable ();
> +  x >>= 5;
> +  return x << 6;
> +}
> +
> +unsigned int
> +f6 (unsigned int x)
> +{
> +  if (x & 1)
> +    __builtin_unreachable ();
> +  x >>= 1;
> +  return x << (sizeof (int) * __CHAR_BIT__ - 1);
> +}
> +
> +/* { dg-final { scan-tree-dump-not {<[a-z]*_div_expr,} "optimized" } } */
> +/* { dg-final { scan-tree-dump-not {<rshift_expr,} "optimized" } } */
> +/* { dg-final { scan-tree-dump-times {<lshift_expr, [^,]*, [^,]*, 1,} 5 
> "optimized" } } */
> +/* { dg-final { scan-tree-dump {<lshift_expr, [^,]*, [^,]*, 30,} "optimized" 
> { target int32 } } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/shifts-2.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/shifts-2.c
> new file mode 100644
> index 00000000000..67ba4a75aec
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/shifts-2.c
> @@ -0,0 +1,21 @@
> +/* { dg-options "-O2 -fdump-tree-optimized-raw" } */
> +
> +unsigned int
> +f1 (unsigned int x)
> +{
> +  if (x & 3)
> +    __builtin_unreachable ();
> +  x >>= 3;
> +  return x << 4;
> +}
> +
> +unsigned int
> +f2 (unsigned int x)
> +{
> +  if (x & 3)
> +    __builtin_unreachable ();
> +  x >>= 2;
> +  return x << 1;
> +}
> +
> +/* { dg-final { scan-tree-dump-times {<rshift_expr,} 2 "optimized" } } */
> 

-- 
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