]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
niter: Use ranger to query ctz range.
authorRobin Dapp <rdapp@ventanamicro.com>
Fri, 17 Oct 2025 09:07:17 +0000 (11:07 +0200)
committerRobin Dapp <rdapp@ventanamicro.com>
Thu, 30 Oct 2025 14:54:29 +0000 (15:54 +0100)
When niter runs after the copy-header pass it sometimes fails to
simplify assumptions in a ctz loop.
As the assumption is a simple nonzero test here we can have
ranger get us the range of the shifted expression, then verify that
this range is nonzero.

This helps recognize a ctz loop in 502.gcc's compute_transp.

PR/tree-optimization 122207

gcc/ChangeLog:

* tree-ssa-loop-niter.cc (shifted_range_nonzero_p): New
function.
(number_of_iterations_cltz): Call new function.
* tree-ssa-loop.cc (pass_scev_cprop::execute): Enable ranger.

gcc/testsuite/ChangeLog:

* gcc.dg/tree-ssa/ctz-char.c: Remove -fno-tree-ch.
* gcc.dg/tree-ssa/ctz-complement-char.c: Ditto.
* gcc.dg/tree-ssa/ctz-complement-int.c: Ditto.
* gcc.dg/tree-ssa/ctz-complement-long-long.c: Ditto.
* gcc.dg/tree-ssa/ctz-complement-long.c: Ditto.
* gcc.dg/tree-ssa/ctz-int.c: Ditto.
* gcc.dg/tree-ssa/ctz-long-long.c: Ditto.
* gcc.dg/tree-ssa/ctz-long.c: Ditto.
* gcc.dg/tree-ssa/ctz-ch.c: New test.
* gcc.dg/pr41488.c: Add -fno-tree-scev-cprop.

12 files changed:
gcc/testsuite/gcc.dg/pr41488.c
gcc/testsuite/gcc.dg/tree-ssa/ctz-ch.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/ctz-char.c
gcc/testsuite/gcc.dg/tree-ssa/ctz-complement-char.c
gcc/testsuite/gcc.dg/tree-ssa/ctz-complement-int.c
gcc/testsuite/gcc.dg/tree-ssa/ctz-complement-long-long.c
gcc/testsuite/gcc.dg/tree-ssa/ctz-complement-long.c
gcc/testsuite/gcc.dg/tree-ssa/ctz-int.c
gcc/testsuite/gcc.dg/tree-ssa/ctz-long-long.c
gcc/testsuite/gcc.dg/tree-ssa/ctz-long.c
gcc/tree-ssa-loop-niter.cc
gcc/tree-ssa-loop.cc

index 1e4bf19c7da947a134be00864ed244a089d09ec6..a7ba3672efefe873a9808935f42d76024e47bb42 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-ivcanon-scev" } */
+/* { dg-options "-O2 -fno-tree-scev-cprop -fdump-tree-ivcanon-scev" } */
 
 struct struct_t
 {
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ctz-ch.c b/gcc/testsuite/gcc.dg/tree-ssa/ctz-ch.c
new file mode 100644 (file)
index 0000000..5d72597
--- /dev/null
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+typedef unsigned long BITMAP_WORD;
+
+bool
+bmp_iter_set (BITMAP_WORD bits, unsigned *bit_no)
+{
+  /* If our current word is nonzero, it contains the bit we want.  */
+  if (bits)
+    {
+      while (!(bits & 1))
+       {
+         bits >>= 1;
+         *bit_no += 1;
+       }
+      return true;
+    }
+
+  return false;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_ctz|\\.CTZ" 1 "optimized" } } */
index 3cd166acbd4670e175d79a2403de2d5a4fd38665..fa8b7f39de4b96dcac24694c0f57aa8b62894ae4 100644 (file)
@@ -1,6 +1,6 @@
 /* { dg-do run } */
 /* { dg-require-effective-target ctz } */
-/* { dg-options "-O2 -fno-tree-ch -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
 
 #define PREC (__CHAR_BIT__)
 
index b9afe8852d8ffbc7ee9a0760cf04b8f98af293a2..5ebc321316925bddd2ef9a11364e8fe2b754e054 100644 (file)
@@ -1,6 +1,6 @@
 /* { dg-do run } */
 /* { dg-require-effective-target ctz } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-tree-ch -fdump-tree-optimized" } */
 
 #define PREC (__CHAR_BIT__)
 
index d2702a65daf34db66550d2255395db68a29a4797..0ce4b6beaa7d169d6f9126a42334a6cecd2f96ab 100644 (file)
@@ -1,6 +1,6 @@
 /* { dg-do run } */
 /* { dg-require-effective-target ctz } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-tree-ch -fdump-tree-optimized" } */
 
 #define PREC (__CHAR_BIT__ * __SIZEOF_INT__)
 
index 1ea0d5d7d9f8be1824c4177c33edd91e66b4ddab..f98bec039b35b5c2114a3a3c4146690beb777a4d 100644 (file)
@@ -1,6 +1,6 @@
 /* { dg-do run } */
 /* { dg-require-effective-target ctzll } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-tree-ch -fdump-tree-optimized" } */
 
 #define PREC (__CHAR_BIT__ * __SIZEOF_LONG_LONG__)
 
index 80fb02dcfa68bc022ae69b26fb189323e01fc6fc..8edb3728131c1df33c6764e6f77278064e07e2d9 100644 (file)
@@ -1,6 +1,6 @@
 /* { dg-do run } */
 /* { dg-require-effective-target ctzl } */
-/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fno-tree-ch -fdump-tree-optimized" } */
 
 #define PREC (__CHAR_BIT__ * __SIZEOF_LONG__)
 
index 7f63493eb7389a18516f8f126c3c55dc80f0bde6..2bf3ae69b93868791f68dec1082c6cfd8b775831 100644 (file)
@@ -1,6 +1,6 @@
 /* { dg-do run } */
 /* { dg-require-effective-target ctz } */
-/* { dg-options "-O2 -fno-tree-ch -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
 
 #define PREC (__CHAR_BIT__ * __SIZEOF_INT__)
 
index 924f61b76f01c77a40b9fff64af3b629ab1418c0..2e159485cb9836dbba311bee823953ed3dd0b90f 100644 (file)
@@ -1,6 +1,6 @@
 /* { dg-do run } */
 /* { dg-require-effective-target ctzll } */
-/* { dg-options "-O2 -fno-tree-ch -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
 
 #define PREC (__CHAR_BIT__ * __SIZEOF_LONG_LONG__)
 
index 178945daa8a2697989f1a1a0804ce33d768dcc55..2e3be652a0bc9f7c0d1600cc263463a5d3f770e9 100644 (file)
@@ -1,6 +1,6 @@
 /* { dg-do run } */
 /* { dg-require-effective-target ctzl } */
-/* { dg-options "-O2 -fno-tree-ch -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
 
 #define PREC (__CHAR_BIT__ * __SIZEOF_LONG__)
 
index 6e1308625491392f1ed2fdbcb5ab947c42f8aaab..cc763839edcd273e6abc4f715d176db182c4900d 100644 (file)
@@ -2321,6 +2321,48 @@ is_rshift_by_1 (gassign *stmt)
   return false;
 }
 
+/* Helper for number_of_iterations_cltz that uses ranger to determine
+   if SRC's range, shifted left (when LEFT_SHIFT is true) or right
+   by NUM_IGNORED_BITS, is guaranteed to be != 0 on LOOP's preheader
+   edge.
+   Return true if so or false otherwise.  */
+
+static bool
+shifted_range_nonzero_p (loop_p loop, tree src,
+                        bool left_shift, int num_ignored_bits)
+{
+  int_range_max r (TREE_TYPE (src));
+  gcc_assert (num_ignored_bits >= 0);
+
+  if (get_range_query (cfun)->range_on_edge
+      (r, loop_preheader_edge (loop), src)
+      && !r.varying_p ()
+      && !r.undefined_p ())
+    {
+      if (num_ignored_bits)
+       {
+         range_op_handler op (left_shift ? LSHIFT_EXPR : RSHIFT_EXPR);
+         int_range_max shifted_range (TREE_TYPE (src));
+         wide_int shift_count = wi::shwi (num_ignored_bits,
+                                          TYPE_PRECISION (TREE_TYPE
+                                                          (src)));
+         int_range_max shift_amount
+           (TREE_TYPE (src), shift_count, shift_count);
+
+         if (op.fold_range (shifted_range, TREE_TYPE (src), r,
+                            shift_amount))
+           r = shifted_range;
+       }
+
+      /* If the range does not contain zero we are good.  */
+      if (!range_includes_zero_p (r))
+       return true;
+    }
+
+  return false;
+}
+
+
 /* See comment below for number_of_iterations_bitcount.
    For c[lt]z, we have:
 
@@ -2438,6 +2480,9 @@ number_of_iterations_cltz (loop_p loop, edge exit,
   tree src = gimple_phi_arg_def (phi, loop_preheader_edge (loop)->dest_idx);
   int src_precision = TYPE_PRECISION (TREE_TYPE (src));
 
+  /* Save the original SSA name before preprocessing for ranger queries.  */
+  tree unshifted_src = src;
+
   /* Apply any needed preprocessing to src.  */
   int num_ignored_bits;
   if (left_shift)
@@ -2463,10 +2508,52 @@ number_of_iterations_cltz (loop_p loop, edge exit,
 
   expr = fold_convert (unsigned_type_node, expr);
 
-  tree assumptions = fold_build2 (NE_EXPR, boolean_type_node, src,
-                                 build_zero_cst (TREE_TYPE (src)));
+  /* If the copy-header (ch) pass peeled one iteration we're shifting
+     SRC by preprocessing it above.
+
+     A loop like
+      if (bits)
+       {
+         while (!(bits & 1))
+           {
+             bits >>= 1;
+             cnt += 1;
+           }
+         return cnt;
+       }
+     ch (roughly) transforms into:
+      if (bits)
+       {
+         if (!(bits & 1)
+           {
+             do
+               {
+                 bits >>= 1;
+                 cnt += 1;
+               } while (!(bits & 1));
+           }
+          else
+            cnt = 1;
+         return cnt;
+       }
+
+     Then, our preprocessed SRC (that is used for c[tl]z computation)
+     will be bits >> 1, and the assumption is bits >> 1 != 0.  */
+
+  tree assumptions;
+  if (shifted_range_nonzero_p (loop, unshifted_src,
+                              left_shift, num_ignored_bits))
+    assumptions = boolean_true_node;
+  else
+    {
+      /* If ranger couldn't prove the assumption, try
+        simplify_using_initial_conditions.  */
+      assumptions = fold_build2 (NE_EXPR, boolean_type_node, src,
+                                build_zero_cst (TREE_TYPE (src)));
+      assumptions = simplify_using_initial_conditions (loop, assumptions);
+    }
 
-  niter->assumptions = simplify_using_initial_conditions (loop, assumptions);
+  niter->assumptions = assumptions;
   niter->may_be_zero = boolean_false_node;
   niter->niter = simplify_using_initial_conditions (loop, expr);
 
index 5629524afb2f595abf8ab8ec4c1e8148f808f735..dc4b560e9245f490c58533ade2d7eaa0837cd2fd 100644 (file)
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm_p.h"
 #include "fold-const.h"
 #include "gimple-iterator.h"
+#include "gimple-range.h"
 #include "tree-ssa-loop-ivopts.h"
 #include "tree-ssa-loop-manip.h"
 #include "tree-ssa-loop-niter.h"
@@ -404,11 +405,15 @@ pass_scev_cprop::execute (function *)
 {
   bool any = false;
 
+  enable_ranger (cfun);
+
   /* Perform final value replacement in loops, in case the replacement
      expressions are cheap.  */
   for (auto loop : loops_list (cfun, LI_FROM_INNERMOST))
     any |= final_value_replacement_loop (loop);
 
+  disable_ranger (cfun);
+
   return any ? TODO_cleanup_cfg | TODO_update_ssa_only_virtuals : 0;
 }