On Tue, 13 Oct 2026, [email protected] wrote:

> From: Kyrylo Tkachov <[email protected]>
> 
> The fold
> 
>   min (.CLZ (x, V), .CLZ (y, V)) -> .CLZ (x | y, V)
> 
> and the same fold for .CTZ, added in r17-2815 for PR123311, are correct
> when V is not smaller than the count of any nonzero value.  The
> condition has two problems:
> 
> - It uses TYPE_PRECISION (TREE_TYPE (x)), but X can be a vector.  At
>   -O3 on AArch64 the loop body
> 
>     int ca = __builtin_clzg (a[i], 32);
>     int cb = __builtin_clzg (b[i], 32);
>     r[i] = ca < cb ? ca : cb;
>     s[i] = ca + cb;
> 
>   gives an ICE with "tree check: expected none of vector_type, have
>   vector_type" when the vectorized counts reach the fold.
> - It checks that V is at least the precision with wi::geu_p, which
>   accepts V = -1, but the MIN is signed.  For x = 0 and y = 5
> 
>     _1 = .CLZ (x_4(D), -1);
>     _2 = .CLZ (y_5(D), -1);
>     _3 = MIN_EXPR <_1, _2>;
> 
>   gives -1, but the fold gives .CLZ (5, -1), which is 29.  gcn defines
>   -1 as the value at zero for CLZ and CTZ, so __builtin_clzg (x, -1)
>   and __builtin_ctzg (x, -1) make such calls there.  rs6000 without
>   TARGET_CTZ and TARGET_POPCNTD does the same for
>   __builtin_ctzg (x, -1).
> 
> Compare V with element_precision as a signed value.  The fold is
> correct for each lane, so it now also applies to vector counts.
> 
> Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux.

OK.

Richard.

> gcc/ChangeLog:
> 
>       * match.pd (min (clz (x), clz (y)) -> clz (x | y)): Compare the
>       value at zero with element_precision as a signed value.
>       (min (ctz (x), ctz (y)) -> ctz (x | y)): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
>       * gcc.dg/tree-ssa/clz-ctz-min-value-at-zero-1.c: New test.
>       * gcc.dg/vect/vect-clz-ctz-min-1.c: New test.
> 
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/match.pd                                  |  3 +-
>  .../tree-ssa/clz-ctz-min-value-at-zero-1.c    | 70 +++++++++++++++++++
>  .../gcc.dg/vect/vect-clz-ctz-min-1.c          | 27 +++++++
>  3 files changed, 98 insertions(+), 2 deletions(-)
>  create mode 100644 
> gcc/testsuite/gcc.dg/tree-ssa/clz-ctz-min-value-at-zero-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/vect/vect-clz-ctz-min-1.c
> 
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 6984af44582..68b910e337b 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -11275,8 +11275,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>   (simplify
>    (min (func:s @0 INTEGER_CST@2) (func:s @1 @2))
>    (if (types_match (@0, @1)
> -       && wi::geu_p (wi::to_wide (@2),
> -                  TYPE_PRECISION (TREE_TYPE (@0))))
> +       && wi::to_widest (@2) >= element_precision (@0))
>     (func (bit_ior @0 @1) @2))))
>  
>  /* CLZ simplifications.  */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/clz-ctz-min-value-at-zero-1.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/clz-ctz-min-value-at-zero-1.c
> new file mode 100644
> index 00000000000..85aed347f62
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/clz-ctz-min-value-at-zero-1.c
> @@ -0,0 +1,70 @@
> +/* MIN of two .CLZ or two .CTZ calls becomes one call on the IOR of the
> +   arguments only if the value at zero is at least the precision.  */
> +/* { dg-do compile } */
> +/* { dg-require-effective-target int32 } */
> +/* { dg-require-effective-target clz_ifn } */
> +/* { dg-require-effective-target ctz_ifn } */
> +/* { dg-options "-O2 -fgimple -fdump-tree-optimized" } */
> +
> +/* For X = 0 and Y = 5 the MIN is -1, but .CLZ (X | Y, -1) is 29.  */
> +
> +int __GIMPLE (ssa)
> +clzm1 (unsigned int x, unsigned int y)
> +{
> +  int _1;
> +  int _2;
> +  int _3;
> +
> +  __BB(2):
> +  _1 = .CLZ (x_4(D), -1);
> +  _2 = .CLZ (y_5(D), -1);
> +  _3 = __MIN (_1, _2);
> +  return _3;
> +}
> +
> +int __GIMPLE (ssa)
> +ctzm1 (unsigned int x, unsigned int y)
> +{
> +  int _1;
> +  int _2;
> +  int _3;
> +
> +  __BB(2):
> +  _1 = .CTZ (x_4(D), -1);
> +  _2 = .CTZ (y_5(D), -1);
> +  _3 = __MIN (_1, _2);
> +  return _3;
> +}
> +
> +/* 32 is not smaller than any count of a nonzero value.  */
> +
> +int __GIMPLE (ssa)
> +clz32 (unsigned int x, unsigned int y)
> +{
> +  int _1;
> +  int _2;
> +  int _3;
> +
> +  __BB(2):
> +  _1 = .CLZ (x_4(D), 32);
> +  _2 = .CLZ (y_5(D), 32);
> +  _3 = __MIN (_1, _2);
> +  return _3;
> +}
> +
> +int __GIMPLE (ssa)
> +ctz32 (unsigned int x, unsigned int y)
> +{
> +  int _1;
> +  int _2;
> +  int _3;
> +
> +  __BB(2):
> +  _1 = .CTZ (x_4(D), 32);
> +  _2 = .CTZ (y_5(D), 32);
> +  _3 = __MIN (_1, _2);
> +  return _3;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 2 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " \\| " 2 "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/vect/vect-clz-ctz-min-1.c 
> b/gcc/testsuite/gcc.dg/vect/vect-clz-ctz-min-1.c
> new file mode 100644
> index 00000000000..bb7dbbdd680
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vect/vect-clz-ctz-min-1.c
> @@ -0,0 +1,27 @@
> +/* MIN of two vector counts with a value at zero.  */
> +/* { dg-do compile } */
> +/* { dg-additional-options "-O3" } */
> +
> +void
> +fclz (unsigned *a, unsigned *b, int *r, int *s, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    {
> +      int ca = __builtin_clzg (a[i], 32);
> +      int cb = __builtin_clzg (b[i], 32);
> +      r[i] = ca < cb ? ca : cb;
> +      s[i] = ca + cb;
> +    }
> +}
> +
> +void
> +fctz (unsigned *a, unsigned *b, int *r, int *s, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    {
> +      int ca = __builtin_ctzg (a[i], 32);
> +      int cb = __builtin_ctzg (b[i], 32);
> +      r[i] = ca < cb ? ca : cb;
> +      s[i] = ca + cb;
> +    }
> +}
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to