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)
