[gcc r17-3518] middle-end: Teach ranger about POLY_INT_CST ranges for VRP.

Tamar Christina via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:1dd02e1deffddc3b6c17b26d78569d93d8a0dac0

commit r17-3518-g1dd02e1deffddc3b6c17b26d78569d93d8a0dac0
Author: Tamar Christina <[email protected]>
Date:   Fri Aug 21 16:10:31 2026 +0100

    middle-end: Teach ranger about POLY_INT_CST ranges for VRP.
    
    A an area where LLVM generates much better code than GCC today is in
    optimization of VLA branches guarded by VLA constants.
    
    Normally in GIMPLE POLY_INT_CSTs have no upport and lower bound because well,
    they're poly.  However SVE has defined minimum and maximum vector sizes in
    the architecture[1] and so for us we do have bounds on these constants.
    
    [1] https://developer.arm.com/documentation/102476/0101/Introducing-SVE
    
    Today LLVM optimizes these simple expressions
    
    int g (void)
    {
      unsigned int vl = svcntb ();
      return vl < 257;
    }
    
    int h (void)
    {
      return svcntw () <= 64;
    }
    
    away to
    
    g():
            mov     w0, #1
            ret
    
    h():
            mov     w0, #1
            ret
    
    which is right because both are always true for any SVE vector length.
    
    while GCC generates:
    
    g():
            cntb    x0
            cmp     w0, 257
            cset    w0, cc
            ret
    h():
            cntw    x0
            cmp     x0, 65
            cset    w0, cc
            ret
    
    This patch extends ranger with a target hook poly_int_indeterminate_bound which
    is different from the current costing only hooks used in the vectorizer because
    those hooks can't be relied upon for correctness.
    
    This new hook allows us specify the minimum and maximum bounds of a POLY_INT_CST
    and have ranger use it in range_query::get_tree_range though specifying the
    indeterminate bound of a POLY target.
    
    This gets GCC to fold away many known true or known false comparisons in
    codegen today and we generate much simpler loop pre-headers.
    
    gcc/ChangeLog:
    
            * target.def (poly_int_indeterminate_bound): New.
            * doc/tm.texi.in: Document it.
            * doc/tm.texi: Regenerate.
            * value-query.cc (range_query::get_tree_range): Use it.

Diff:
---
 gcc/doc/tm.texi    |  8 +++++
 gcc/doc/tm.texi.in |  2 ++
 gcc/target.def     | 10 ++++++
 gcc/value-query.cc | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 85e175256833..2613b95775bb 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -7491,6 +7491,14 @@ the @code{POLY_VALUE_MIN}, @code{POLY_VALUE_MAX} and
 implementation returns the lowest possible value of @var{val}.
 @end deftypefn
 
+@deftypefn {Target Hook} poly_uint64 TARGET_POLY_INT_INDETERMINATE_BOUND (void)
+Return a @code{poly_uint64} whose coefficients give conservative
+inclusive upper bounds for the corresponding @code{poly_int}
+indeterminates.  The constant coefficient is ignored and should be zero.
+This hook is used for correctness rather than for costing, so the returned
+bounds must be safe for all architecturally-valid runtime values.
+@end deftypefn
+
 @deftypefn {Target Hook} bool TARGET_AVOID_STORE_FORWARDING_P (vec<store_fwd_info>, @var{rtx}, @var{int}, @var{bool})
 Given a list of stores and a load instruction that reads from the location
 of the stores, this hook decides if it's profitable to emit additional code
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 1a9edd0635d0..0d2a76ec6b0b 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -4794,6 +4794,8 @@ Define this macro if a non-short-circuit operation produced by
 
 @hook TARGET_ESTIMATED_POLY_VALUE
 
+@hook TARGET_POLY_INT_INDETERMINATE_BOUND
+
 @hook TARGET_AVOID_STORE_FORWARDING_P
 
 @node Scheduling
diff --git a/gcc/target.def b/gcc/target.def
index 884fe1bd57e1..bf2825e991d6 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4217,6 +4217,16 @@ implementation returns the lowest possible value of @var{val}.",
  HOST_WIDE_INT, (poly_int64 val, poly_value_estimate_kind kind),
  default_estimated_poly_value)
 
+DEFHOOK
+(poly_int_indeterminate_bound,
+ "Return a @code{poly_uint64} whose coefficients give conservative\n\
+inclusive upper bounds for the corresponding @code{poly_int}\n\
+indeterminates.  The constant coefficient is ignored and should be zero.\n\
+This hook is used for correctness rather than for costing, so the returned\n\
+bounds must be safe for all architecturally-valid runtime values.",
+ poly_uint64, (void),
+ NULL)
+
 /* Permit speculative instructions in delay slots during delayed-branch
    scheduling.  */
 DEFHOOK
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index 17077eef9e8b..8457ae705fe2 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -27,11 +27,13 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple.h"
 #include "ssa.h"
 #include "tree-pretty-print.h"
+#include "tree-ssanames.h"
 #include "fold-const.h"
 #include "value-query.h"
 #include "alloc-pool.h"
 #include "gimple-range.h"
 #include "value-range-storage.h"
+#include "target.h"
 
 // range_query default methods.
 
@@ -415,8 +417,92 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt,
       if (POLY_INT_CST_P (expr))
 	{
 	  unsigned int precision = TYPE_PRECISION (type);
-	  r.set_varying (type);
-	  r.update_bitmask ({ wi::zero (precision), get_nonzero_bits (expr) });
+	  signop sign = TYPE_SIGN (type);
+	  bool have_poly_bound = targetm.poly_int_indeterminate_bound;
+	  poly_uint64 indeterminate_bound;
+
+	  if (have_poly_bound)
+	    indeterminate_bound = targetm.poly_int_indeterminate_bound ();
+
+	  auto val = wi::to_poly_wide (expr);
+	  auto type_min = wi::to_wide (TYPE_MIN_VALUE (type));
+	  auto type_max = wi::to_wide (TYPE_MAX_VALUE (type));
+
+	  /* Start with the invariant part of the poly-int, then account
+	     for each coefficient below.
+
+	     The target hook gives a per-coefficient upper bound for the
+	     indeterminate.  Since those indeterminates are unsigned and
+	     nonnegative, a positive coefficient can only increase the upper
+	     bound and a negative coefficient can only decrease the lower
+	     bound.  The opposite bound is unaffected by that coefficient:
+
+		[A, +C] with C >= 0  => max += C * bound
+		[A, -C] with C >= 0  => min -= C * bound.  */
+	  wide_int bounds[2] = { val.coeffs[0], val.coeffs[0] };
+	  bool ovf[2] = { false, false };
+
+	  for (unsigned int i = 1; i < NUM_POLY_INT_COEFFS; ++i)
+	    {
+	      const auto &coeff = val.coeffs[i];
+	      if (wi::eq_p (coeff, 0))
+		continue;
+
+	      /* Select the only bound affected by this coefficient.  A
+		 negative coefficient contributes to the minimum and a positive
+		 coefficient contributes to the maximum.  */
+	      bool coeff_neg = wi::neg_p (coeff, sign);
+	      wide_int &bound = bounds[coeff_neg ? 0 : 1];
+	      bool &bound_ovf = ovf[coeff_neg ? 0 : 1];
+
+	      if (bound_ovf)
+		continue;
+
+	      /* A missing hook, or a -1 bound for this coefficient, means the
+		 indeterminate has no finite target-specific limit.  Treat that
+		 like an overflow of the affected bound.  */
+	      if (!have_poly_bound
+		  || indeterminate_bound.coeffs[i] == HOST_WIDE_INT_M1U)
+		bound_ovf = true;
+	      else
+		{
+		  auto indeterminate
+		    = wi::uhwi (indeterminate_bound.coeffs[i], precision);
+		  wi::overflow_type mul_ovf = wi::OVF_NONE;
+		  auto term = wi::mul (coeff, indeterminate, sign, &mul_ovf);
+		  wi::overflow_type add_ovf = wi::OVF_NONE;
+		  bound = wi::add (bound, term, sign, &add_ovf);
+		  bound_ovf = (mul_ovf != wi::OVF_NONE
+			       || add_ovf != wi::OVF_NONE);
+		}
+
+	      if (TYPE_OVERFLOW_WRAPS (type) && bound_ovf)
+		{
+		  r.set_varying (type);
+		  return true;
+		}
+
+	      if (bound_ovf)
+		{
+		  if (coeff_neg)
+		    bounds[0] = type_min;
+		  else
+		    bounds[1] = type_max;
+		}
+	    }
+
+	  /* Check that the target filled in sensible bounds information.  */
+	  gcc_assert (wi::le_p (bounds[0], bounds[1], sign));
+
+	  irange &ir = as_a <irange> (r);
+	  ir.set (type, bounds[0], bounds[1]);
+
+	  /* Preserve alignment/step information that is not visible in the
+	     intervals.  For example, a poly-int like [8, 8] can
+	     only produce multiples of 8, but the interval range might be
+	     [8, 136], which also contains values with low bits set.  */
+	  ir.update_bitmask (irange_bitmask (wi::zero (precision),
+					     get_nonzero_bits (expr)));
 	  return true;
 	}
       break;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.