diff mbox series

Split gimple statement range folding into a stand alone class.

Message ID 763bb85a-8190-5401-d48a-090c60f5ea12@redhat.com
State New
Headers show
Series Split gimple statement range folding into a stand alone class. | expand

Commit Message

Andrew MacLeod May 19, 2021, 8:23 p.m. UTC
When ranger was first written, it processed all the range-ops 
statements, and the remainder of the statements we slowly added, and 
shared as much code with vr_values as we could.

We are now at a point where it makes sense to split this out into its 
own class. There are a number of places where range-ops is treated 
"special" because it follows  a specific formula, and the other kinds of 
statements have becoming second class citizens.  By pulling all the 
statement processing out of gimple-ranger and into a class which handles 
the statements and uses general range queries, we can treat all the 
statements the same way. There are numerous benefits to this.

   1) with the upcoming relational work, I want to be able to query 
relations in builtin function processing (and other places) as well as  
range-ops.
   2) GORI is going to be enhanced so we can look back thru things other 
than range-ops statements... this will help with overflow analysis in 
builtins especially.
   3) simplified interface for anyone wanting to calculate a range.      
a new routine:
         bool fold_stmt (irange&r, gimple *s, range_query *q=NULL)
will do look-ups to and provide a range for the stmt.  This is now being 
used internally in ranger and can work with global ranges, or any kind 
of range_query.

All the various stmt kind processing is pulled out of gimple_ranger and 
moved into class fold_using_range, which has a single external API.  A 
helper class fur_source (FoldUsingRange_source) is used to provide a 
mechanism for the statement folder to resolve any ssa_names.  It can be 
set up to provide range queries from the original stmt location, on an 
edge, or from any arbitrary location.

This allows us to not only fold a stmt where it sits in the IL, but also 
gives us the power to recalculate a stmt as if it occurs somewhere 
else.  A second function is provided which specifies an edge, and the 
statement can be recalculated as if it was sitting on that edge.  It is 
also possible (and ranger uses this) to calculate the statement as it 
occurs elsewhere in the program.

This is useful for things like

    b_2 = a_1 + 10
    if (a_1 > 20 && a_1 < 40)
          c_5 = b_2 + 4

When ranger calculates a range for c_5, it recognizes that b_2 is 
dependant on the value of a_1, and a_1 has been "refined" since b_2 was 
defined.  So rather than using the original value of b_2, SSA allows us 
to recalculate b_2 at the location of the use... ie,  we know a_1 is 
[21, 39], so b_2 is recalculated at that use as [21,39] + 10 = [31,49] . 
The value of c_5 is then calculated as [31,49] + 4 = [35, 53].

This works with an arbitrary amount of IL between the statements since 
its driven by uses and defs.

This is the first of a set of cleanup/generalizations. Bootstraps on 
x86_64-pc-linux-gnu with no regressions.

Pushed.

Andrew
diff mbox series

Patch

commit dc6758f03effbf7d6946d8c314576c7a6c0003af
Author: Andrew MacLeod <amacleod@redhat.com>
Date:   Tue May 18 20:33:09 2021 -0400

    Split gimple range folding with ranges into a stand alone class.
    
    Introduces fold_using_range which folds any kind of gimple statement by
    querying argument ranges thru a generic range_query.
    This pulls all the statement processing into a client neutral location.
    
            * gimple-range.cc (fur_source::get_operand): New.
            (gimple_range_fold): Delete.
            (fold_using_range::fold_stmt): Move from gimple_ranger::calc_stmt.
            (fold_using_range::range_of_range_op): Move from gimple_ranger.
            (fold_using_range::range_of_address): Ditto.
            (fold_using_range::range_of_phi): Ditto.
            (fold_using_range::range_of_call): Ditto.
            (fold_using_range::range_of_builtin_ubsan_call): Move from
            range_of_builtin_ubsan_call.
            (fold_using_range::range_of_builtin_call): Move from
            range_of_builtin_call.
            (gimple_ranger::range_of_builtin_call): Delete.
            (fold_using_range::range_of_cond_expr): Move from gimple_ranger.
            (gimple_ranger::fold_range_internal): New.
            (gimple_ranger::range_of_stmt): Use new fold_using_range API.
            (fold_using_range::range_of_ssa_name_with_loop_info): Move from
            gimple_ranger.  Improve ranges of SSA_NAMES when possible.
            * gimple-range.h (gimple_ranger): Remove various range_of routines.
            (class fur_source): New.
            (class fold_using_range): New.
            (fur_source::fur_source): New.
            (fold_range): New.
            * vr-values.c (vr_values::extract_range_basic): Use fold_using_range
            instead of range_of_builtin_call.

diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc
index 710bc7f9632..06e9804494b 100644
--- a/gcc/gimple-range.cc
+++ b/gcc/gimple-range.cc
@@ -47,6 +47,31 @@  along with GCC; see the file COPYING3.  If not see
 #include "vr-values.h"
 #include "gimple-range.h"
 
+// Evaluate expression EXPR using the source information the class was
+// instantiated with.  Place the result in R, and return TRUE.  If a range
+// cannot be calcluated, return FALSE.
+
+bool
+fur_source::get_operand (irange &r, tree expr)
+{
+  if (!gimple_range_ssa_p (expr))
+    return get_tree_range (r, expr);
+
+  // If no query engine is present, simply get the global value.
+  if (!m_query)
+    {
+       r = gimple_range_global (expr);
+       return true;
+    }
+
+  // First look for a stmt.
+  if (m_stmt)
+    return m_query->range_of_expr (r, expr, m_stmt);
+
+  // Finally must be on an edge.
+  return m_query->range_on_edge (r, m_edge, expr);
+}
+
 
 // Adjust the range for a pointer difference where the operands came
 // from a memchr.
@@ -193,41 +218,9 @@  get_tree_range (irange &r, tree expr)
   return true;
 }
 
-// Fold this unary statement using R1 as operand1's range, returning
-// the result in RES.  Return false if the operation fails.
-
-bool
-gimple_range_fold (irange &res, const gimple *stmt, const irange &r1)
-{
-  gcc_checking_assert (gimple_range_handler (stmt));
-
-  tree type = gimple_expr_type (stmt);
-  // Unary SSA operations require the LHS type as the second range.
-  int_range<2> r2 (type);
-
-  return gimple_range_fold (res, stmt, r1, r2);
-}
-
-// Fold this binary statement using R1 and R2 as the operands ranges,
-// returning the result in RES.  Return false if the operation fails.
-
-bool
-gimple_range_fold (irange &res, const gimple *stmt,
-		   const irange &r1, const irange &r2)
-{
-  gcc_checking_assert (gimple_range_handler (stmt));
-
-  gimple_range_handler (stmt)->fold_range (res, gimple_expr_type (stmt),
-					   r1, r2);
-
-  // If there are any gimple lookups, do those now.
-  gimple_range_adjustment (res, stmt);
-  return true;
-}
-
 // Return the base of the RHS of an assignment.
 
-tree
+static tree
 gimple_range_base_of_assignment (const gimple *stmt)
 {
   gcc_checking_assert (gimple_code (stmt) == GIMPLE_ASSIGN);
@@ -364,20 +357,29 @@  gimple_range_calc_op2 (irange &r, const gimple *stmt,
 // be calculated, return false.
 
 bool
-gimple_ranger::calc_stmt (irange &r, gimple *s, tree name)
+fold_using_range::fold_stmt (irange &r, gimple *s, fur_source &src, tree name)
 {
   bool res = false;
-  // If name is specified, make sure it is an LHS of S.
-  gcc_checking_assert (name ? SSA_NAME_DEF_STMT (name) == s : true);
+  // If name and S are specified, make sure it is an LHS of S.
+  gcc_checking_assert (!name || !gimple_get_lhs (s) ||
+		       name == gimple_get_lhs (s));
+
+  if (!name)
+    name = gimple_get_lhs (s);
+
+  // Process addresses.
+  if (gimple_code (s) == GIMPLE_ASSIGN
+      && gimple_assign_rhs_code (s) == ADDR_EXPR)
+    return range_of_address (r, s, src);
 
   if (gimple_range_handler (s))
-    res = range_of_range_op (r, s);
+    res = range_of_range_op (r, s, src);
   else if (is_a<gphi *>(s))
-    res = range_of_phi (r, as_a<gphi *> (s));
+    res = range_of_phi (r, as_a<gphi *> (s), src);
   else if (is_a<gcall *>(s))
-    res = range_of_call (r, as_a<gcall *> (s));
+    res = range_of_call (r, as_a<gcall *> (s), src);
   else if (is_a<gassign *> (s) && gimple_assign_rhs_code (s) == COND_EXPR)
-    res = range_of_cond_expr (r, as_a<gassign *> (s));
+    res = range_of_cond_expr (r, as_a<gassign *> (s), src);
 
   if (!res)
     {
@@ -414,36 +416,45 @@  gimple_ranger::calc_stmt (irange &r, gimple *s, tree name)
 // If a range cannot be calculated, return false.
 
 bool
-gimple_ranger::range_of_range_op (irange &r, gimple *s)
+fold_using_range::range_of_range_op (irange &r, gimple *s, fur_source &src)
 {
   int_range_max range1, range2;
-  tree lhs = gimple_get_lhs (s);
   tree type = gimple_expr_type (s);
+  range_operator *handler = gimple_range_handler (s);
+  gcc_checking_assert (handler);
   gcc_checking_assert (irange::supports_type_p (type));
 
+  tree lhs = gimple_get_lhs (s);
   tree op1 = gimple_range_operand1 (s);
   tree op2 = gimple_range_operand2 (s);
 
-  if (lhs)
-    {
-      // Register potential dependencies for stale value tracking.
-      m_cache.register_dependency (lhs, op1);
-      m_cache.register_dependency (lhs, op2);
-    }
-
-  if (gimple_code (s) == GIMPLE_ASSIGN
-      && gimple_assign_rhs_code (s) == ADDR_EXPR)
-    return range_of_address (r, s);
-
-  if (range_of_expr (range1, op1, s))
+  if (src.get_operand (range1, op1))
     {
       if (!op2)
-	return gimple_range_fold (r, s, range1);
-
-      if (range_of_expr (range2, op2, s))
-	return gimple_range_fold (r, s, range1, range2);
+	{
+	  // Fold range, and register any dependency if available.
+	  int_range<2> r2 (type);
+	  handler->fold_range (r, type, range1, r2);
+	  if (lhs && src.m_cache)
+	    src.m_cache->register_dependency (lhs, op1);
+	}
+      else if (src.get_operand (range2, op2))
+	{
+	  // Fold range, and register any dependency if available.
+	  handler->fold_range (r, type, range1, range2);
+	  if (lhs && src.m_cache)
+	    {
+	      src.m_cache->register_dependency (lhs, op1);
+	      src.m_cache->register_dependency (lhs, op2);
+	    }
+	}
+      else
+	r.set_varying (type);
     }
-  r.set_varying (type);
+  else
+    r.set_varying (type);
+  // Make certain range-op adjustments that aren't handled any other way.
+  gimple_range_adjustment (r, s);
   return true;
 }
 
@@ -452,7 +463,7 @@  gimple_ranger::range_of_range_op (irange &r, gimple *s)
 // If a range cannot be calculated, set it to VARYING and return true.
 
 bool
-gimple_ranger::range_of_address (irange &r, gimple *stmt)
+fold_using_range::range_of_address (irange &r, gimple *stmt, fur_source &src)
 {
   gcc_checking_assert (gimple_code (stmt) == GIMPLE_ASSIGN);
   gcc_checking_assert (gimple_assign_rhs_code (stmt) == ADDR_EXPR);
@@ -473,8 +484,11 @@  gimple_ranger::range_of_address (irange &r, gimple *stmt)
       && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME)
     {
       tree ssa = TREE_OPERAND (base, 0);
+      tree lhs = gimple_get_lhs (stmt);
+      if (src.m_cache && lhs && gimple_range_ssa_p (ssa))
+	src.m_cache->register_dependency (lhs, ssa);
       gcc_checking_assert (irange::supports_type_p (TREE_TYPE (ssa)));
-      range_of_expr (r, ssa, stmt);
+      src.get_operand (r, ssa);
       range_cast (r, TREE_TYPE (gimple_assign_rhs1 (stmt)));
 
       poly_offset_int off = 0;
@@ -531,7 +545,7 @@  gimple_ranger::range_of_address (irange &r, gimple *stmt)
 // If a range cannot be calculated, return false.
 
 bool
-gimple_ranger::range_of_phi (irange &r, gphi *phi)
+fold_using_range::range_of_phi (irange &r, gphi *phi, fur_source &src)
 {
   tree phi_def = gimple_phi_result (phi);
   tree type = TREE_TYPE (phi_def);
@@ -549,9 +563,19 @@  gimple_ranger::range_of_phi (irange &r, gphi *phi)
       edge e = gimple_phi_arg_edge (phi, x);
 
       // Register potential dependencies for stale value tracking.
-      m_cache.register_dependency (phi_def, arg);
-
-      range_on_edge (arg_range, e, arg);
+      if (src.m_cache && gimple_range_ssa_p (arg))
+	src.m_cache->register_dependency (phi_def, arg);
+
+      // Get the range of the argument on its edge.
+      fur_source e_src (src.m_query, e);
+      e_src.get_operand (arg_range, arg);
+      // If we're recomputing the argument elsewhere, try to refine it.
+      if (src.m_stmt != phi)
+	{
+	  int_range_max tmp;
+	  e_src.get_operand (tmp, arg);
+	  arg_range.intersect (tmp);
+	}
       r.union_ (arg_range);
       // Once the value reaches varying, stop looking.
       if (r.varying_p ())
@@ -565,7 +589,7 @@  gimple_ranger::range_of_phi (irange &r, gphi *phi)
       class loop *l = loop_containing_stmt (phi);
       if (l && loop_outer (l))
         {
-	  range_of_ssa_name_with_loop_info (loop_range, phi_def, l, phi);
+	  range_of_ssa_name_with_loop_info (loop_range, phi_def, l, phi, src);
 	  if (!loop_range.varying_p ())
 	    {
 	      if (dump_file && (dump_flags & TDF_DETAILS))
@@ -590,7 +614,7 @@  gimple_ranger::range_of_phi (irange &r, gphi *phi)
 // If a range cannot be calculated, return false.
 
 bool
-gimple_ranger::range_of_call (irange &r, gcall *call)
+fold_using_range::range_of_call (irange &r, gcall *call, fur_source &src)
 {
   tree type = gimple_call_return_type (call);
   tree lhs = gimple_call_lhs (call);
@@ -599,7 +623,7 @@  gimple_ranger::range_of_call (irange &r, gcall *call)
   if (!irange::supports_type_p (type))
     return false;
 
-  if (range_of_builtin_call (r, call))
+  if (range_of_builtin_call (r, call, src))
     ;
   else if (gimple_stmt_nonnegative_warnv_p (call, &strict_overflow_p))
     r.set (build_int_cst (type, 0), TYPE_MAX_VALUE (type));
@@ -623,9 +647,9 @@  gimple_ranger::range_of_call (irange &r, gcall *call)
 // CODE is the type of ubsan call (PLUS_EXPR, MINUS_EXPR or
 // MULT_EXPR).
 
-static void
-range_of_builtin_ubsan_call (range_query &query, irange &r, gcall *call,
-			     tree_code code)
+void
+fold_using_range::range_of_builtin_ubsan_call (irange &r, gcall *call,
+					       tree_code code, fur_source &src)
 {
   gcc_checking_assert (code == PLUS_EXPR || code == MINUS_EXPR
 		       || code == MULT_EXPR);
@@ -635,8 +659,8 @@  range_of_builtin_ubsan_call (range_query &query, irange &r, gcall *call,
   int_range_max ir0, ir1;
   tree arg0 = gimple_call_arg (call, 0);
   tree arg1 = gimple_call_arg (call, 1);
-  query.range_of_expr (ir0, arg0, call);
-  query.range_of_expr (ir1, arg1, call);
+  src.get_operand (ir0, arg0);
+  src.get_operand (ir1, arg1);
 
   bool saved_flag_wrapv = flag_wrapv;
   // Pretend the arithmetic is wrapping.  If there is any overflow,
@@ -656,7 +680,8 @@  range_of_builtin_ubsan_call (range_query &query, irange &r, gcall *call,
 // TRUE.  Otherwise return FALSE.
 
 bool
-range_of_builtin_call (range_query &query, irange &r, gcall *call)
+fold_using_range::range_of_builtin_call (irange &r, gcall *call,
+					 fur_source &src)
 {
   combined_fn func = gimple_call_combined_fn (call);
   if (func == CFN_LAST)
@@ -677,7 +702,7 @@  range_of_builtin_call (range_query &query, irange &r, gcall *call)
 	  return true;
 	}
       arg = gimple_call_arg (call, 0);
-      if (query.range_of_expr (r, arg, call) && r.singleton_p ())
+      if (src.get_operand (r, arg) && r.singleton_p ())
 	{
 	  r.set (build_one_cst (type), build_one_cst (type));
 	  return true;
@@ -691,7 +716,7 @@  range_of_builtin_call (range_query &query, irange &r, gcall *call)
       prec = TYPE_PRECISION (TREE_TYPE (arg));
       mini = 0;
       maxi = prec;
-      query.range_of_expr (r, arg, call);
+      src.get_operand (r, arg);
       // If arg is non-zero, then ffs or popcount are non-zero.
       if (!range_includes_zero_p (&r))
 	mini = 1;
@@ -735,7 +760,7 @@  range_of_builtin_call (range_query &query, irange &r, gcall *call)
 	    }
 	}
 
-      query.range_of_expr (r, arg, call);
+      src.get_operand (r, arg);
       // From clz of minimum we can compute result maximum.
       if (r.constant_p () && !r.varying_p ())
 	{
@@ -800,7 +825,7 @@  range_of_builtin_call (range_query &query, irange &r, gcall *call)
 		mini = -2;
 	    }
 	}
-      query.range_of_expr (r, arg, call);
+      src.get_operand (r, arg);
       if (!r.undefined_p ())
 	{
 	  if (r.lower_bound () != 0)
@@ -838,13 +863,13 @@  range_of_builtin_call (range_query &query, irange &r, gcall *call)
       r.set (build_int_cst (type, 0), build_int_cst (type, prec - 1));
       return true;
     case CFN_UBSAN_CHECK_ADD:
-      range_of_builtin_ubsan_call (query, r, call, PLUS_EXPR);
+      range_of_builtin_ubsan_call (r, call, PLUS_EXPR, src);
       return true;
     case CFN_UBSAN_CHECK_SUB:
-      range_of_builtin_ubsan_call (query, r, call, MINUS_EXPR);
+      range_of_builtin_ubsan_call (r, call, MINUS_EXPR, src);
       return true;
     case CFN_UBSAN_CHECK_MUL:
-      range_of_builtin_ubsan_call (query, r, call, MULT_EXPR);
+      range_of_builtin_ubsan_call (r, call, MULT_EXPR, src);
       return true;
 
     case CFN_GOACC_DIM_SIZE:
@@ -894,17 +919,11 @@  range_of_builtin_call (range_query &query, irange &r, gcall *call)
 }
 
 
-bool
-gimple_ranger::range_of_builtin_call (irange &r, gcall *call)
-{
-  return ::range_of_builtin_call (*this, r, call);
-}
-
 // Calculate a range for COND_EXPR statement S and return it in R.
 // If a range cannot be calculated, return false.
 
 bool
-gimple_ranger::range_of_cond_expr  (irange &r, gassign *s)
+fold_using_range::range_of_cond_expr  (irange &r, gassign *s, fur_source &src)
 {
   int_range_max cond_range, range1, range2;
   tree cond = gimple_assign_rhs1 (s);
@@ -917,9 +936,9 @@  gimple_ranger::range_of_cond_expr  (irange &r, gassign *s)
   if (!irange::supports_type_p (TREE_TYPE (op1)))
     return false;
 
-  range_of_expr (cond_range, cond, s);
-  range_of_expr (range1, op1, s);
-  range_of_expr (range2, op2, s);
+  src.get_operand (cond_range, cond);
+  src.get_operand (range1, op1);
+  src.get_operand (range2, op2);
 
   // If the condition is known, choose the appropriate expression.
   if (cond_range.singleton_p ())
@@ -1047,6 +1066,16 @@  gimple_ranger::range_on_edge (irange &r, edge e, tree name)
   return true;
 }
 
+// fold_range wrapper for range_of_stmt to use as an internal client.
+
+bool
+gimple_ranger::fold_range_internal (irange &r, gimple *s, tree name)
+{
+  fold_using_range f;
+  fur_source src (this, &m_cache, NULL, s);
+  return f.fold_stmt (r, s, src, name);
+}
+
 // Calculate a range for statement S and return it in R.  If NAME is
 // provided it represents the SSA_NAME on the LHS of the statement.
 // It is only required if there is more than one lhs/output.  Check
@@ -1063,7 +1092,7 @@  gimple_ranger::range_of_stmt (irange &r, gimple *s, tree name)
 
   // If no name, simply call the base routine.
   if (!name)
-    return calc_stmt (r, s, NULL_TREE);
+    return fold_range_internal (r, s, NULL_TREE);
 
   if (!gimple_range_ssa_p (name))
     return false;
@@ -1074,7 +1103,7 @@  gimple_ranger::range_of_stmt (irange &r, gimple *s, tree name)
 
   // Otherwise calculate a new value.
   int_range_max tmp;
-  calc_stmt (tmp, s, name);
+  fold_range_internal (tmp, s, name);
 
   // Combine the new value with the old value.  This is required because
   // the way value propagation works, when the IL changes on the fly we
@@ -1216,20 +1245,32 @@  gimple_ranger::dump (FILE *f)
 // If SCEV has any information about phi node NAME, return it as a range in R.
 
 void
-gimple_ranger::range_of_ssa_name_with_loop_info (irange &r, tree name,
-						 class loop *l, gphi *phi)
+fold_using_range::range_of_ssa_name_with_loop_info (irange &r, tree name,
+						    class loop *l, gphi *phi,
+						    fur_source &src)
 {
   gcc_checking_assert (TREE_CODE (name) == SSA_NAME);
   tree min, max, type = TREE_TYPE (name);
-  if (bounds_of_var_in_loop (&min, &max, this, l, phi, name))
+  if (bounds_of_var_in_loop (&min, &max, src.m_query, l, phi, name))
     {
-      // ?? We could do better here.  Since MIN/MAX can only be an
-      // SSA, SSA +- INTEGER_CST, or INTEGER_CST, we could easily call
-      // the ranger and solve anything not an integer.
       if (TREE_CODE (min) != INTEGER_CST)
-	min = vrp_val_min (type);
+	{
+	  if (src.m_query
+	      && src.m_query->range_of_expr (r, min, phi)
+	      && !r.undefined_p ())
+	    min = wide_int_to_tree (type, r.lower_bound ());
+	  else
+	    min = vrp_val_min (type);
+	}
       if (TREE_CODE (max) != INTEGER_CST)
-	max = vrp_val_max (type);
+	{
+	  if (src.m_query
+	      && src.m_query->range_of_expr (r, max, phi)
+	      && !r.undefined_p ())
+	    max = wide_int_to_tree (type, r.upper_bound ());
+	  else
+	    max = vrp_val_max (type);
+	}
       r.set (min, max);
     }
   else
diff --git a/gcc/gimple-range.h b/gcc/gimple-range.h
index f33156181bf..53205066ab4 100644
--- a/gcc/gimple-range.h
+++ b/gcc/gimple-range.h
@@ -30,6 +30,18 @@  along with GCC; see the file COPYING3.  If not see
 #include "gimple-range-cache.h"
 #include "value-query.h"
 
+// This file is the main include point for gimple ranges.
+// There are two fold_range routines of interest:
+//   bool fold_range (irange &r, gimple *s, range_query *q)
+//   bool fold_range (irange &r, gimple *s, edge on_edge, range_query *q)
+// These routines will fold stmt S into the result irange R.
+// Any ssa_names on the stmt will be calculated using the range_query
+// parameter via a call to range_of_expr.
+// If no range_query is provided, current global range info will be used.
+// The second variation specifies an edge, and stmt S is recalculated as if
+// it appeared on that edge.
+
+
 // This is the basic range generator interface.
 //
 // This base class provides all the API entry points, but only provides
@@ -55,32 +67,116 @@  public:
   void export_global_ranges ();
   void dump (FILE *f);
 protected:
-  bool calc_stmt (irange &r, gimple *s, tree name = NULL_TREE);
-  bool range_of_range_op (irange &r, gimple *s);
-  bool range_of_call (irange &r, gcall *call);
-  bool range_of_cond_expr (irange &r, gassign* cond);
+  bool fold_range_internal (irange &r, gimple *s, tree name);
   ranger_cache m_cache;
-private:
-  bool range_of_phi (irange &r, gphi *phi);
-  bool range_of_address (irange &r, gimple *s);
-  bool range_of_builtin_call (irange &r, gcall *call);
-  bool range_with_loop_info (irange &r, tree name);
-  void range_of_ssa_name_with_loop_info (irange &, tree, class loop *,
-					 gphi *);
 };
 
-// Calculate a basic range for a tree expression.
+// Source of an operand for fold_using_range.
+// It can specify a stmt or and edge, or thru an internal API which uses
+// the ranger cache.
+// Its primary function is to retreive an operand from the source via a
+// call thru the range_query object.
+
+class fur_source
+{
+  friend class fold_using_range;
+public:
+  inline fur_source (range_query *q, edge e);
+  inline fur_source (range_query *q, gimple *s);
+  inline fur_source (range_query *q, class ranger_cache *g, edge e, gimple *s);
+  bool get_operand (irange &r, tree expr);
+protected:
+  ranger_cache *m_cache;
+  range_query *m_query;
+  edge m_edge;
+  gimple *m_stmt;
+};
+
+
+// This class uses ranges to fold a gimple statement producinf a range for
+// the LHS.  The source of all operands is supplied via the fur_source class
+// which provides a range_query as well as a source location and any other
+// required information.
+
+class fold_using_range
+{
+public:
+  bool fold_stmt (irange &r, gimple *s, class fur_source &src,
+		  tree name = NULL_TREE);
+protected:
+  bool range_of_range_op (irange &r, gimple *s, fur_source &src);
+  bool range_of_call (irange &r, gcall *call, fur_source &src);
+  bool range_of_cond_expr (irange &r, gassign* cond, fur_source &src);
+  bool range_of_address (irange &r, gimple *s, fur_source &src);
+  bool range_of_builtin_call (irange &r, gcall *call, fur_source &src);
+  void range_of_builtin_ubsan_call (irange &r, gcall *call, tree_code code,
+				    fur_source &src);
+  bool range_of_phi (irange &r, gphi *phi, fur_source &src);
+  void range_of_ssa_name_with_loop_info (irange &, tree, class loop *, gphi *,
+					 fur_source &src);
+};
+
+
+// Create a source for a query on an edge.
+
+inline
+fur_source::fur_source (range_query *q, edge e)
+{
+  m_query = q;
+  m_cache = NULL;
+  m_edge = e;
+  m_stmt = NULL;
+}
+
+// Create a source for a query at a statement.
+
+inline
+fur_source::fur_source (range_query *q, gimple *s)
+{
+  m_query = q;
+  m_cache = NULL;
+  m_edge = NULL;
+  m_stmt = s;
+}
+
+// Create a source for Ranger.  THis can recalculate from a different location
+// and can also set the dependency information as appropriate when invoked.
+
+inline
+fur_source::fur_source (range_query *q, ranger_cache *g, edge e, gimple *s)
+{
+  m_query = q;
+  m_cache = g;
+  m_edge = e;
+  m_stmt = s;
+}
+
+// Fold stmt S into range R using range query Q.
+
+inline bool
+fold_range (irange &r, gimple *s, range_query *q = NULL)
+{
+  fold_using_range f;
+  fur_source src (q, s);
+  return f.fold_stmt (r, s, src);
+}
+
+// Recalculate stmt S into R using range query Q as if it were on edge ON_EDGE.
+
+inline bool
+fold_range (irange &r, gimple *s, edge on_edge, range_query *q = NULL)
+{
+  fold_using_range f;
+  fur_source src (q, on_edge);
+  return f.fold_stmt (r, s, src);
+}
+
+// Calculate a basic range for a tree node expression.
 extern bool get_tree_range (irange &r, tree expr);
 
 // These routines provide a GIMPLE interface to the range-ops code.
 extern tree gimple_range_operand1 (const gimple *s);
 extern tree gimple_range_operand2 (const gimple *s);
-extern tree gimple_range_base_of_assignment (const gimple *s);
-extern bool gimple_range_fold (irange &res, const gimple *s,
-			       const irange &r1);
-extern bool gimple_range_fold (irange &res, const gimple *s,
-			       const irange &r1,
-			       const irange &r2);
 extern bool gimple_range_calc_op1 (irange &r, const gimple *s,
 				   const irange &lhs_range);
 extern bool gimple_range_calc_op1 (irange &r, const gimple *s,
@@ -199,7 +295,4 @@  private:
 // Flag to enable debugging the various internal Caches.
 #define DEBUG_RANGE_CACHE (dump_file && (param_evrp_mode & EVRP_MODE_DEBUG))
 
-// Temporary external interface to share with vr_values.
-bool range_of_builtin_call (range_query &query, irange &r, gcall *call);
-
 #endif // GCC_GIMPLE_RANGE_STMT_H
diff --git a/gcc/vr-values.c b/gcc/vr-values.c
index b1bf53af9e0..02bc0db9088 100644
--- a/gcc/vr-values.c
+++ b/gcc/vr-values.c
@@ -1229,7 +1229,7 @@  vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt)
 	    return;
 	  break;
 	default:
-	  if (range_of_builtin_call (*this, *vr, as_a<gcall *> (stmt)))
+	  if (fold_range (*vr, stmt, this))
 	    {
 	      /* The original code nuked equivalences every time a
 		 range was found, so do the same here.  */