https://gcc.gnu.org/g:08255c1267e6d25e517f987cae76ca6eaf6908d7

commit r17-5339-g08255c1267e6d25e517f987cae76ca6eaf6908d7
Author: Kyrylo Tkachov <[email protected]>
Date:   Fri Oct 9 10:50:11 2026 -0700

    match.pd: Fix the condition of the MIN of counts fold
    
    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.
    
    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]>

Diff:
---
 gcc/match.pd                                       |  3 +-
 .../gcc.dg/tree-ssa/clz-ctz-min-value-at-zero-1.c  | 70 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/vect/vect-clz-ctz-min-1.c     | 27 +++++++++
 3 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/gcc/match.pd b/gcc/match.pd
index bdd874079631..a341669bf69a 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -11298,8 +11298,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 000000000000..6b8736fb51eb
--- /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 clzsi } */
+/* { dg-require-effective-target ctzsi } */
+/* { 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 000000000000..bb7dbbdd680d
--- /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;
+    }
+}

Reply via email to