diff mbox series

[COMMITTED,2/7] Add relational support to range-op.

Message ID 9cdb3a62-6be5-4d71-651f-bcd19a3ab454@redhat.com
State New
Headers show
Series [COMMITTED,1/7] Initial value-relation code. | expand

Commit Message

Andrew MacLeod June 22, 2021, 1:17 p.m. UTC
THis patch adds relation support to range-ops.

a relation_kind is added to all fold and op1/2_range operations, which 
will allow any known relation to be applied during calculations.

4 more routines are provided which enable range-ops to indicate when a 
relation is caused by an expression/result and what effects it may have. 
    All relations are driven from these routines.

virtual enum tree_code lhs_op1_relation (const irange &lhs, const irange 
&op1, const irange &op2) const;
virtual enum tree_code lhs_op2_relation (const irange &lhs, const irange 
&op1, const irange &op2) const;
virtual enum tree_code op1_op2_relation (const irange &lhs) const;
virtual bool op1_op2_relation_effect (irange &lhs_range, tree type, 
const irange &op1_range, const irange &op2_range, relation_kind rel) const;

This initial patch provides the basic of the relation opcodes. Ie,  
operator_equal has added

enum tree_code
operator_equal::op1_op2_relation (const irange &lhs) const
{
   if (lhs.undefined_p ())
     return VREL_EMPTY;

   // FALSE = op1 == op2 indicates NE_EXPR.
   if (lhs.zero_p ())
     return NE_EXPR;

   // TRUE = op1 == op2 indicates EQ_EXPR.
   if (!lhs.contains_p (build_zero_cst (lhs.type ())))
     return EQ_EXPR;
   return VREL_NONE;
}

This teaches range-ops that there is a relation between op1 and op2 is 
the LHS has a range of FALSE, or TRUE.  Otherwise, we don't know what 
the relation is.

All 6 basic relations are implemented, as well as folding of these 
opcodes when a relation is to be applied.  Ie, how to fold it ( if a_2 
== b_2 is being folded., and the known relation coming into this 
expression is a_2 > b_2, it will fold to [0,0] or false.

Again, this patch on its own will not cause anything to actually happen, 
that will be enabled in the next patch. Virtually all relation 
generation and application is handled via these range-ops routines.

  Bootstraps on x86_64-pc-linux-gnu with no regressions.  Pushed.

Andrew
diff mbox series

Patch

From 80dd13f5c3bdc7899ee6e863e05b254815ec0cef Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacleod@redhat.com>
Date: Thu, 17 Jun 2021 11:49:21 -0400
Subject: [PATCH 2/7] Add relational support to range-op.

This patch integrates relations with range-op functionality so that any
known relations can be used to help reduce or resolve ranges.
Initially handle  EQ_EXPR, NE_EXPR, LE_EXPR, LT_EXPR, GT_EXPR and GE_EXPR.

	* range-op.cc (range_operator::wi_fold): Apply relation effect.
	(range_operator::fold_range): Adjust and apply relation effect.
	(*::fold_range): Add relation parameters.
	(*::op1_range): Ditto.
	(*::op2_range): Ditto.
	(range_operator::lhs_op1_relation): New.
	(range_operator::lhs_op2_relation): New.
	(range_operator::op1_op2_relation): New.
	(range_operator::op1_op2_relation_effect): New.
	(relop_early_resolve): New.
	(operator_equal::op1_op2_relation): New.
	(operator_equal::fold_range): Call relop_early_resolve.
	(operator_not_equal::op1_op2_relation): New.
	(operator_not_equal::fold_range): Call relop_early_resolve.
	(operator_lt::op1_op2_relation): New.
	(operator_lt::fold_range): Call relop_early_resolve.
	(operator_le::op1_op2_relation): New.
	(operator_le::fold_range): Call relop_early_resolve.
	(operator_gt::op1_op2_relation): New.
	(operator_gt::fold_range): Call relop_early_resolve.
	(operator_ge::op1_op2_relation): New.
	(operator_ge::fold_range): Call relop_early_resolve.
	* range-op.h (class range_operator): Adjust parameters and methods.
---
 gcc/range-op.cc | 584 +++++++++++++++++++++++++++++++++++++-----------
 gcc/range-op.h  |  24 +-
 2 files changed, 469 insertions(+), 139 deletions(-)

diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index e805f26a333..d807693900a 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -44,6 +44,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "gimple-walk.h"
 #include "tree-cfg.h"
 #include "wide-int.h"
+#include "value-relation.h"
 #include "range-op.h"
 
 // Return the upper limit for a type.
@@ -138,7 +139,8 @@  range_operator::wi_fold (irange &r, tree type,
 bool
 range_operator::fold_range (irange &r, tree type,
 			    const irange &lh,
-			    const irange &rh) const
+			    const irange &rh,
+			    relation_kind rel) const
 {
   gcc_checking_assert (irange::supports_type_p (type));
   if (empty_range_varying (r, type, lh, rh))
@@ -152,6 +154,7 @@  range_operator::fold_range (irange &r, tree type,
     {
       wi_fold (r, type, lh.lower_bound (0), lh.upper_bound (0),
 	       rh.lower_bound (0), rh.upper_bound (0));
+      op1_op2_relation_effect (r, type, lh, rh, rel);
       return true;
     }
 
@@ -167,8 +170,12 @@  range_operator::fold_range (irange &r, tree type,
 	wi_fold (tmp, type, lh_lb, lh_ub, rh_lb, rh_ub);
 	r.union_ (tmp);
 	if (r.varying_p ())
-	  return true;
+	  {
+	    op1_op2_relation_effect (r, type, lh, rh, rel);
+	    return true;
+	  }
       }
+  op1_op2_relation_effect (r, type, lh, rh, rel);
   return true;
 }
 
@@ -178,7 +185,8 @@  bool
 range_operator::op1_range (irange &r ATTRIBUTE_UNUSED,
 			   tree type ATTRIBUTE_UNUSED,
 			   const irange &lhs ATTRIBUTE_UNUSED,
-			   const irange &op2 ATTRIBUTE_UNUSED) const
+			   const irange &op2 ATTRIBUTE_UNUSED,
+			   relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return false;
 }
@@ -189,11 +197,47 @@  bool
 range_operator::op2_range (irange &r ATTRIBUTE_UNUSED,
 			   tree type ATTRIBUTE_UNUSED,
 			   const irange &lhs ATTRIBUTE_UNUSED,
-			   const irange &op1 ATTRIBUTE_UNUSED) const
+			   const irange &op1 ATTRIBUTE_UNUSED,
+			   relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return false;
 }
 
+// The default relation routines return VREL_NONE.
+
+enum tree_code
+range_operator::lhs_op1_relation (const irange &lhs ATTRIBUTE_UNUSED,
+				  const irange &op1 ATTRIBUTE_UNUSED,
+				  const irange &op2 ATTRIBUTE_UNUSED) const
+{
+  return VREL_NONE;
+}
+
+enum tree_code
+range_operator::lhs_op2_relation (const irange &lhs ATTRIBUTE_UNUSED,
+				  const irange &op1 ATTRIBUTE_UNUSED,
+				  const irange &op2 ATTRIBUTE_UNUSED) const
+{
+  return VREL_NONE;
+}
+
+enum tree_code
+range_operator::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) const
+{
+  return VREL_NONE;
+}
+
+// Default is no relation affects the LHS.
+
+bool
+range_operator::op1_op2_relation_effect (irange &lhs_range ATTRIBUTE_UNUSED,
+				       tree type ATTRIBUTE_UNUSED,
+				       const irange &op1_range ATTRIBUTE_UNUSED,
+				       const irange &op2_range ATTRIBUTE_UNUSED,
+				       relation_kind rel ATTRIBUTE_UNUSED) const
+{
+  return false;
+}
 
 // Create and return a range from a pair of wide-ints that are known
 // to have overflowed (or underflowed).
@@ -385,27 +429,82 @@  get_bool_state (irange &r, const irange &lhs, tree val_type)
   return BRS_TRUE;
 }
 
+// For relation opcodes, first try to see if the supplied relation
+// forces a true or false result, and return that.
+// Then check for undefined operands.  If none of this applies,
+// return false.
+
+static inline bool
+relop_early_resolve (irange &r, tree type, const irange &op1,
+		     const irange &op2, relation_kind rel,
+		     relation_kind my_rel)
+{
+  // If known relation is a complete subset of this relation, always true.
+  if (relation_union (rel, my_rel) == my_rel)
+    {
+      r = range_true (type);
+      return true;
+    }
+
+  // If known relation has no subset of this relation, always false.
+  if (relation_intersect (rel, my_rel) == VREL_EMPTY)
+    {
+      r = range_false (type);
+      return true;
+    }
+
+  // If either operand is undefined, return VARYING.
+  if (empty_range_varying (r, type, op1, op2))
+    return true;
+
+  return false;
+}
+
 
 class operator_equal : public range_operator
 {
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &val) const;
+			  const irange &val,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &val) const;
+			  const irange &val,
+			  relation_kind rel = VREL_NONE) const;
+  virtual enum tree_code op1_op2_relation (const irange &lhs) const;
 } op_equal;
 
+// Check if the LHS range indicates a relation between OP1 and OP2.
+
+enum tree_code
+operator_equal::op1_op2_relation (const irange &lhs) const
+{
+  if (lhs.undefined_p ())
+    return VREL_EMPTY;
+
+  // FALSE = op1 == op2 indicates NE_EXPR.
+  if (lhs.zero_p ())
+    return NE_EXPR;
+
+  // TRUE = op1 == op2 indicates EQ_EXPR.
+  if (!lhs.contains_p (build_zero_cst (lhs.type ())))
+    return EQ_EXPR;
+  return VREL_NONE;
+}
+
+
 bool
 operator_equal::fold_range (irange &r, tree type,
 			    const irange &op1,
-			    const irange &op2) const
+			    const irange &op2,
+			    relation_kind rel) const
 {
-  if (empty_range_varying (r, type, op1, op2))
+  if (relop_early_resolve (r, type, op1, op2, rel, EQ_EXPR))
     return true;
 
   // We can be sure the values are always equal or not if both ranges
@@ -435,7 +534,8 @@  operator_equal::fold_range (irange &r, tree type,
 bool
 operator_equal::op1_range (irange &r, tree type,
 			   const irange &lhs,
-			   const irange &op2) const
+			   const irange &op2,
+			   relation_kind rel ATTRIBUTE_UNUSED) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -465,32 +565,55 @@  operator_equal::op1_range (irange &r, tree type,
 bool
 operator_equal::op2_range (irange &r, tree type,
 			   const irange &lhs,
-			   const irange &op1) const
+			   const irange &op1,
+			   relation_kind rel) const
 {
-  return operator_equal::op1_range (r, type, lhs, op1);
+  return operator_equal::op1_range (r, type, lhs, op1, rel);
 }
 
-
 class operator_not_equal : public range_operator
 {
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
+  virtual enum tree_code op1_op2_relation (const irange &lhs) const;
 } op_not_equal;
 
+// Check if the LHS range indicates a relation between OP1 and OP2.
+
+enum tree_code
+operator_not_equal::op1_op2_relation (const irange &lhs) const
+{
+  if (lhs.undefined_p ())
+    return VREL_EMPTY;
+
+  // FALSE = op1 != op2  indicates EQ_EXPR.
+  if (lhs.zero_p ())
+    return EQ_EXPR;
+
+  // TRUE = op1 != op2  indicates NE_EXPR.
+  if (!lhs.contains_p (build_zero_cst (lhs.type ())))
+    return NE_EXPR;
+  return VREL_NONE;
+}
+
 bool
 operator_not_equal::fold_range (irange &r, tree type,
 				const irange &op1,
-				const irange &op2) const
+				const irange &op2,
+				relation_kind rel) const
 {
-  if (empty_range_varying (r, type, op1, op2))
+  if (relop_early_resolve (r, type, op1, op2, rel, NE_EXPR))
     return true;
 
   // We can be sure the values are always equal or not if both ranges
@@ -520,7 +643,8 @@  operator_not_equal::fold_range (irange &r, tree type,
 bool
 operator_not_equal::op1_range (irange &r, tree type,
 			       const irange &lhs,
-			       const irange &op2) const
+			       const irange &op2,
+			       relation_kind rel ATTRIBUTE_UNUSED) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -551,9 +675,10 @@  operator_not_equal::op1_range (irange &r, tree type,
 bool
 operator_not_equal::op2_range (irange &r, tree type,
 			       const irange &lhs,
-			       const irange &op1) const
+			       const irange &op1,
+			       relation_kind rel) const
 {
-  return operator_not_equal::op1_range (r, type, lhs, op1);
+  return operator_not_equal::op1_range (r, type, lhs, op1, rel);
 }
 
 // (X < VAL) produces the range of [MIN, VAL - 1].
@@ -607,21 +732,44 @@  class operator_lt :  public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
+  virtual enum tree_code op1_op2_relation (const irange &lhs) const;
 } op_lt;
 
+// Check if the LHS range indicates a relation between OP1 and OP2.
+
+enum tree_code
+operator_lt::op1_op2_relation (const irange &lhs) const
+{
+  if (lhs.undefined_p ())
+    return VREL_EMPTY;
+
+  // FALSE = op1 < op2 indicates GE_EXPR.
+  if (lhs.zero_p ())
+    return GE_EXPR;
+
+  // TRUE = op1 < op2 indicates LT_EXPR.
+  if (!lhs.contains_p (build_zero_cst (lhs.type ())))
+    return LT_EXPR;
+  return VREL_NONE;
+}
+
 bool
 operator_lt::fold_range (irange &r, tree type,
 			 const irange &op1,
-			 const irange &op2) const
+			 const irange &op2,
+			 relation_kind rel) const
 {
-  if (empty_range_varying (r, type, op1, op2))
+  if (relop_early_resolve (r, type, op1, op2, rel, LT_EXPR))
     return true;
 
   signop sign = TYPE_SIGN (op1.type ());
@@ -639,7 +787,8 @@  operator_lt::fold_range (irange &r, tree type,
 bool
 operator_lt::op1_range (irange &r, tree type,
 			const irange &lhs,
-			const irange &op2) const
+			const irange &op2,
+			relation_kind rel ATTRIBUTE_UNUSED) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -660,7 +809,8 @@  operator_lt::op1_range (irange &r, tree type,
 bool
 operator_lt::op2_range (irange &r, tree type,
 			const irange &lhs,
-			const irange &op1) const
+			const irange &op1,
+			relation_kind rel ATTRIBUTE_UNUSED) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -684,21 +834,44 @@  class operator_le :  public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
+  virtual enum tree_code op1_op2_relation (const irange &lhs) const;
 } op_le;
 
+// Check if the LHS range indicates a relation between OP1 and OP2.
+
+enum tree_code
+operator_le::op1_op2_relation (const irange &lhs) const
+{
+  if (lhs.undefined_p ())
+    return VREL_EMPTY;
+
+  // FALSE = op1 <= op2 indicates GT_EXPR.
+  if (lhs.zero_p ())
+    return GT_EXPR;
+
+  // TRUE = op1 <= op2 indicates LE_EXPR.
+  if (!lhs.contains_p (build_zero_cst (lhs.type ())))
+    return LE_EXPR;
+  return VREL_NONE;
+}
+
 bool
 operator_le::fold_range (irange &r, tree type,
 			 const irange &op1,
-			 const irange &op2) const
+			 const irange &op2,
+			 relation_kind rel) const
 {
-  if (empty_range_varying (r, type, op1, op2))
+  if (relop_early_resolve (r, type, op1, op2, rel, LE_EXPR))
     return true;
 
   signop sign = TYPE_SIGN (op1.type ());
@@ -716,7 +889,8 @@  operator_le::fold_range (irange &r, tree type,
 bool
 operator_le::op1_range (irange &r, tree type,
 			const irange &lhs,
-			const irange &op2) const
+			const irange &op2,
+			relation_kind rel ATTRIBUTE_UNUSED) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -737,7 +911,8 @@  operator_le::op1_range (irange &r, tree type,
 bool
 operator_le::op2_range (irange &r, tree type,
 			const irange &lhs,
-			const irange &op1) const
+			const irange &op1,
+			relation_kind rel ATTRIBUTE_UNUSED) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -761,20 +936,44 @@  class operator_gt :  public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
+  virtual enum tree_code op1_op2_relation (const irange &lhs) const;
 } op_gt;
 
+// Check if the LHS range indicates a relation between OP1 and OP2.
+
+enum tree_code
+operator_gt::op1_op2_relation (const irange &lhs) const
+{
+  if (lhs.undefined_p ())
+    return VREL_EMPTY;
+
+  // FALSE = op1 > op2 indicates LE_EXPR.
+  if (lhs.zero_p ())
+    return LE_EXPR;
+
+  // TRUE = op1 > op2 indicates GT_EXPR.
+  if (!lhs.contains_p (build_zero_cst (lhs.type ())))
+    return GT_EXPR;
+  return VREL_NONE;
+}
+
+
 bool
 operator_gt::fold_range (irange &r, tree type,
-			 const irange &op1, const irange &op2) const
+			 const irange &op1, const irange &op2,
+			 relation_kind rel) const
 {
-  if (empty_range_varying (r, type, op1, op2))
+  if (relop_early_resolve (r, type, op1, op2, rel, GT_EXPR))
     return true;
 
   signop sign = TYPE_SIGN (op1.type ());
@@ -791,7 +990,8 @@  operator_gt::fold_range (irange &r, tree type,
 
 bool
 operator_gt::op1_range (irange &r, tree type,
-			const irange &lhs, const irange &op2) const
+			const irange &lhs, const irange &op2,
+			relation_kind rel ATTRIBUTE_UNUSED) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -812,7 +1012,8 @@  operator_gt::op1_range (irange &r, tree type,
 bool
 operator_gt::op2_range (irange &r, tree type,
 			const irange &lhs,
-			const irange &op1) const
+			const irange &op1,
+			relation_kind rel ATTRIBUTE_UNUSED) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -836,21 +1037,44 @@  class operator_ge :  public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
+  virtual enum tree_code op1_op2_relation (const irange &lhs) const;
 } op_ge;
 
+// Check if the LHS range indicates a relation between OP1 and OP2.
+
+enum tree_code
+operator_ge::op1_op2_relation (const irange &lhs) const
+{
+  if (lhs.undefined_p ())
+    return VREL_EMPTY;
+
+  // FALSE = op1 >= op2 indicates LT_EXPR.
+  if (lhs.zero_p ())
+    return LT_EXPR;
+
+  // TRUE = op1 >= op2 indicates GE_EXPR.
+  if (!lhs.contains_p (build_zero_cst (lhs.type ())))
+    return GE_EXPR;
+  return VREL_NONE;
+}
+
 bool
 operator_ge::fold_range (irange &r, tree type,
 			 const irange &op1,
-			 const irange &op2) const
+			 const irange &op2,
+			 relation_kind rel) const
 {
-  if (empty_range_varying (r, type, op1, op2))
+  if (relop_early_resolve (r, type, op1, op2, rel, GE_EXPR))
     return true;
 
   signop sign = TYPE_SIGN (op1.type ());
@@ -868,7 +1092,8 @@  operator_ge::fold_range (irange &r, tree type,
 bool
 operator_ge::op1_range (irange &r, tree type,
 			const irange &lhs,
-			const irange &op2) const
+			const irange &op2,
+			relation_kind rel ATTRIBUTE_UNUSED) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -889,7 +1114,8 @@  operator_ge::op1_range (irange &r, tree type,
 bool
 operator_ge::op2_range (irange &r, tree type,
 			const irange &lhs,
-			const irange &op1) const
+			const irange &op1,
+			relation_kind rel ATTRIBUTE_UNUSED) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -913,10 +1139,12 @@  class operator_plus : public range_operator
 public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel ATTRIBUTE_UNUSED) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel ATTRIBUTE_UNUSED) const;
   virtual void wi_fold (irange &r, tree type,
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
@@ -939,7 +1167,8 @@  operator_plus::wi_fold (irange &r, tree type,
 bool
 operator_plus::op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const
+			  const irange &op2,
+			  relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op2);
 }
@@ -947,7 +1176,8 @@  operator_plus::op1_range (irange &r, tree type,
 bool
 operator_plus::op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const
+			  const irange &op1,
+			  relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op1);
 }
@@ -958,10 +1188,12 @@  class operator_minus : public range_operator
 public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel ATTRIBUTE_UNUSED) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel ATTRIBUTE_UNUSED) const;
   virtual void wi_fold (irange &r, tree type,
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
@@ -984,7 +1216,8 @@  operator_minus::wi_fold (irange &r, tree type,
 bool
 operator_minus::op1_range (irange &r, tree type,
 			   const irange &lhs,
-			   const irange &op2) const
+			   const irange &op2,
+			   relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return range_op_handler (PLUS_EXPR, type)->fold_range (r, type, lhs, op2);
 }
@@ -992,7 +1225,8 @@  operator_minus::op1_range (irange &r, tree type,
 bool
 operator_minus::op2_range (irange &r, tree type,
 			   const irange &lhs,
-			   const irange &op1) const
+			   const irange &op1,
+			   relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return fold_range (r, type, op1, lhs);
 }
@@ -1127,15 +1361,18 @@  public:
 				const wide_int &w0, const wide_int &w1) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel ATTRIBUTE_UNUSED) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel ATTRIBUTE_UNUSED) const;
 } op_mult;
 
 bool
 operator_mult::op1_range (irange &r, tree type,
-			  const irange &lhs, const irange &op2) const
+			  const irange &lhs, const irange &op2,
+			  relation_kind rel ATTRIBUTE_UNUSED) const
 {
   tree offset;
 
@@ -1153,9 +1390,10 @@  operator_mult::op1_range (irange &r, tree type,
 
 bool
 operator_mult::op2_range (irange &r, tree type,
-			  const irange &lhs, const irange &op1) const
+			  const irange &lhs, const irange &op1,
+			  relation_kind rel) const
 {
-  return operator_mult::op1_range (r, type, lhs, op1);
+  return operator_mult::op1_range (r, type, lhs, op1, rel);
 }
 
 bool
@@ -1391,14 +1629,16 @@  public:
   operator_exact_divide () : operator_div (TRUNC_DIV_EXPR) { }
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel ATTRIBUTE_UNUSED) const;
 
 } op_exact_div;
 
 bool
 operator_exact_divide::op1_range (irange &r, tree type,
 				  const irange &lhs,
-				  const irange &op2) const
+				  const irange &op2,
+				  relation_kind rel ATTRIBUTE_UNUSED) const
 {
   tree offset;
   // [2, 4] = op1 / [3,3]   since its exact divide, no need to worry about
@@ -1419,10 +1659,12 @@  class operator_lshift : public cross_product_operator
 public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
 
   virtual void wi_fold (irange &r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
@@ -1438,7 +1680,8 @@  class operator_rshift : public cross_product_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual void wi_fold (irange &r, tree type,
 			const wide_int &lh_lb,
 			const wide_int &lh_ub,
@@ -1450,14 +1693,16 @@  public:
 				const wide_int &w1) const;
   virtual bool op1_range (irange &, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
 } op_rshift;
 
 
 bool
 operator_lshift::fold_range (irange &r, tree type,
 			     const irange &op1,
-			     const irange &op2) const
+			     const irange &op2,
+			     relation_kind rel ATTRIBUTE_UNUSED) const
 {
   int_range_max shift_range;
   if (!get_shift_range (shift_range, type, op2))
@@ -1572,7 +1817,8 @@  bool
 operator_lshift::op1_range (irange &r,
 			    tree type,
 			    const irange &lhs,
-			    const irange &op2) const
+			    const irange &op2,
+			    relation_kind rel ATTRIBUTE_UNUSED) const
 {
   tree shift_amount;
   if (op2.singleton_p (&shift_amount))
@@ -1632,7 +1878,8 @@  bool
 operator_rshift::op1_range (irange &r,
 			    tree type,
 			    const irange &lhs,
-			    const irange &op2) const
+			    const irange &op2,
+			    relation_kind rel ATTRIBUTE_UNUSED) const
 {
   tree shift;
   if (op2.singleton_p (&shift))
@@ -1708,7 +1955,8 @@  operator_rshift::wi_op_overflows (wide_int &res,
 bool
 operator_rshift::fold_range (irange &r, tree type,
 			     const irange &op1,
-			     const irange &op2) const
+			     const irange &op2,
+			     relation_kind rel ATTRIBUTE_UNUSED) const
 {
   int_range_max shift;
   if (!get_shift_range (shift, type, op2))
@@ -1737,10 +1985,12 @@  class operator_cast: public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
 private:
   bool truncating_cast_p (const irange &inner, const irange &outer) const;
   bool inside_domain_p (const wide_int &min, const wide_int &max,
@@ -1818,7 +2068,8 @@  operator_cast::fold_pair (irange &r, unsigned index,
 bool
 operator_cast::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			   const irange &inner,
-			   const irange &outer) const
+			   const irange &outer,
+			   relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, inner, outer))
     return true;
@@ -1844,7 +2095,8 @@  operator_cast::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 bool
 operator_cast::op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const
+			  const irange &op2,
+			  relation_kind rel ATTRIBUTE_UNUSED) const
 {
   tree lhs_type = lhs.type ();
   gcc_checking_assert (types_compatible_p (op2.type(), type));
@@ -1954,20 +2206,24 @@  class operator_logical_and : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
 } op_logical_and;
 
 
 bool
 operator_logical_and::fold_range (irange &r, tree type,
 				  const irange &lh,
-				  const irange &rh) const
+				  const irange &rh,
+				  relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -1989,7 +2245,8 @@  operator_logical_and::fold_range (irange &r, tree type,
 bool
 operator_logical_and::op1_range (irange &r, tree type,
 				 const irange &lhs,
-				 const irange &op2 ATTRIBUTE_UNUSED) const
+				 const irange &op2 ATTRIBUTE_UNUSED,
+				 relation_kind rel ATTRIBUTE_UNUSED) const
 {
    switch (get_bool_state (r, lhs, type))
      {
@@ -2010,7 +2267,8 @@  operator_logical_and::op1_range (irange &r, tree type,
 bool
 operator_logical_and::op2_range (irange &r, tree type,
 				 const irange &lhs,
-				 const irange &op1) const
+				 const irange &op1,
+				 relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return operator_logical_and::op1_range (r, type, lhs, op1);
 }
@@ -2021,13 +2279,16 @@  class operator_bitwise_and : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
   virtual void wi_fold (irange &r, tree type,
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
@@ -2106,7 +2367,8 @@  operator_bitwise_and::remove_impossible_ranges (irange &r,
 bool
 operator_bitwise_and::fold_range (irange &r, tree type,
 				  const irange &lh,
-				  const irange &rh) const
+				  const irange &rh,
+				  relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (range_operator::fold_range (r, type, lh, rh))
     {
@@ -2397,7 +2659,8 @@  operator_bitwise_and::simple_op1_range_solver (irange &r, tree type,
 bool
 operator_bitwise_and::op1_range (irange &r, tree type,
 				 const irange &lhs,
-				 const irange &op2) const
+				 const irange &op2,
+				 relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (types_compatible_p (type, boolean_type_node))
     return op_logical_and.op1_range (r, type, lhs, op2);
@@ -2420,7 +2683,8 @@  operator_bitwise_and::op1_range (irange &r, tree type,
 bool
 operator_bitwise_and::op2_range (irange &r, tree type,
 				 const irange &lhs,
-				 const irange &op1) const
+				 const irange &op1,
+				 relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return operator_bitwise_and::op1_range (r, type, lhs, op1);
 }
@@ -2431,19 +2695,23 @@  class operator_logical_or : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
 } op_logical_or;
 
 bool
 operator_logical_or::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 				 const irange &lh,
-				 const irange &rh) const
+				 const irange &rh,
+				 relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -2456,7 +2724,8 @@  operator_logical_or::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 bool
 operator_logical_or::op1_range (irange &r, tree type,
 				const irange &lhs,
-				const irange &op2 ATTRIBUTE_UNUSED) const
+				const irange &op2 ATTRIBUTE_UNUSED,
+				relation_kind rel ATTRIBUTE_UNUSED) const
 {
    switch (get_bool_state (r, lhs, type))
      {
@@ -2477,7 +2746,8 @@  operator_logical_or::op1_range (irange &r, tree type,
 bool
 operator_logical_or::op2_range (irange &r, tree type,
 				const irange &lhs,
-				const irange &op1) const
+				const irange &op1,
+				relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return operator_logical_or::op1_range (r, type, lhs, op1);
 }
@@ -2488,10 +2758,12 @@  class operator_bitwise_or : public range_operator
 public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel= VREL_NONE) const;
   virtual void wi_fold (irange &r, tree type,
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
@@ -2553,7 +2825,8 @@  operator_bitwise_or::wi_fold (irange &r, tree type,
 bool
 operator_bitwise_or::op1_range (irange &r, tree type,
 				const irange &lhs,
-				const irange &op2) const
+				const irange &op2,
+				relation_kind rel ATTRIBUTE_UNUSED) const
 {
   // If this is really a logical wi_fold, call that.
   if (types_compatible_p (type, boolean_type_node))
@@ -2572,7 +2845,8 @@  operator_bitwise_or::op1_range (irange &r, tree type,
 bool
 operator_bitwise_or::op2_range (irange &r, tree type,
 				const irange &lhs,
-				const irange &op1) const
+				const irange &op1,
+				relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return operator_bitwise_or::op1_range (r, type, lhs, op1);
 }
@@ -2588,10 +2862,12 @@  public:
 		        const wide_int &rh_ub) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
 } op_bitwise_xor;
 
 void
@@ -2628,7 +2904,8 @@  operator_bitwise_xor::wi_fold (irange &r, tree type,
 bool
 operator_bitwise_xor::op1_range (irange &r, tree type,
 				 const irange &lhs,
-				 const irange &op2) const
+				 const irange &op2,
+				 relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (lhs.undefined_p () || lhs.varying_p ())
     {
@@ -2662,7 +2939,8 @@  operator_bitwise_xor::op1_range (irange &r, tree type,
 bool
 operator_bitwise_xor::op2_range (irange &r, tree type,
 				 const irange &lhs,
-				 const irange &op1) const
+				 const irange &op1,
+				 relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return operator_bitwise_xor::op1_range (r, type, lhs, op1);
 }
@@ -2677,10 +2955,12 @@  public:
 		        const wide_int &rh_ub) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel ATTRIBUTE_UNUSED) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel ATTRIBUTE_UNUSED) const;
 } op_trunc_mod;
 
 void
@@ -2730,7 +3010,8 @@  operator_trunc_mod::wi_fold (irange &r, tree type,
 bool
 operator_trunc_mod::op1_range (irange &r, tree type,
 			       const irange &lhs,
-			       const irange &) const
+			       const irange &,
+			       relation_kind rel ATTRIBUTE_UNUSED) const
 {
   // PR 91029.
   signop sign = TYPE_SIGN (type);
@@ -2753,7 +3034,8 @@  operator_trunc_mod::op1_range (irange &r, tree type,
 bool
 operator_trunc_mod::op2_range (irange &r, tree type,
 			       const irange &lhs,
-			       const irange &) const
+			       const irange &,
+			       relation_kind rel ATTRIBUTE_UNUSED) const
 {
   // PR 91029.
   signop sign = TYPE_SIGN (type);
@@ -2792,10 +3074,12 @@  class operator_logical_not : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
 } op_logical_not;
 
 // Folding a logical NOT, oddly enough, involves doing nothing on the
@@ -2815,7 +3099,8 @@  public:
 bool
 operator_logical_not::fold_range (irange &r, tree type,
 				  const irange &lh,
-				  const irange &rh ATTRIBUTE_UNUSED) const
+				  const irange &rh ATTRIBUTE_UNUSED,
+				  relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -2831,7 +3116,8 @@  bool
 operator_logical_not::op1_range (irange &r,
 				 tree type,
 				 const irange &lhs,
-				 const irange &op2) const
+				 const irange &op2,
+				 relation_kind rel ATTRIBUTE_UNUSED) const
 {
   // Logical NOT is involutary...do it again.
   return fold_range (r, type, lhs, op2);
@@ -2843,16 +3129,19 @@  class operator_bitwise_not : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
 } op_bitwise_not;
 
 bool
 operator_bitwise_not::fold_range (irange &r, tree type,
 				  const irange &lh,
-				  const irange &rh) const
+				  const irange &rh,
+				  relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -2870,7 +3159,8 @@  operator_bitwise_not::fold_range (irange &r, tree type,
 bool
 operator_bitwise_not::op1_range (irange &r, tree type,
 				 const irange &lhs,
-				 const irange &op2) const
+				 const irange &op2,
+				 relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (types_compatible_p (type, boolean_type_node))
     return op_logical_not.op1_range (r, type, lhs, op2);
@@ -2885,13 +3175,15 @@  class operator_cst : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
 } op_integer_cst;
 
 bool
 operator_cst::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			  const irange &lh,
-			  const irange &rh ATTRIBUTE_UNUSED) const
+			  const irange &rh ATTRIBUTE_UNUSED,
+			  relation_kind rel ATTRIBUTE_UNUSED) const
 {
   r = lh;
   return true;
@@ -2903,16 +3195,19 @@  class operator_identity : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
 } op_identity;
 
 bool
 operator_identity::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			       const irange &lh,
-			       const irange &rh ATTRIBUTE_UNUSED) const
+			       const irange &rh ATTRIBUTE_UNUSED,
+			       relation_kind rel ATTRIBUTE_UNUSED) const
 {
   r = lh;
   return true;
@@ -2921,7 +3216,8 @@  operator_identity::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 bool
 operator_identity::op1_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			      const irange &lhs,
-			      const irange &op2 ATTRIBUTE_UNUSED) const
+			      const irange &op2 ATTRIBUTE_UNUSED,
+			      relation_kind rel ATTRIBUTE_UNUSED) const
 {
   r = lhs;
   return true;
@@ -2933,13 +3229,15 @@  class operator_unknown : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
 } op_unknown;
 
 bool
 operator_unknown::fold_range (irange &r, tree type,
 			      const irange &lh ATTRIBUTE_UNUSED,
-			      const irange &rh ATTRIBUTE_UNUSED) const
+			      const irange &rh ATTRIBUTE_UNUSED,
+			      relation_kind rel ATTRIBUTE_UNUSED) const
 {
   r.set_varying (type);
   return true;
@@ -2956,7 +3254,8 @@  class operator_abs : public range_operator
 		        const wide_int &rh_ub) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel ATTRIBUTE_UNUSED) const;
 } op_abs;
 
 void
@@ -3036,7 +3335,8 @@  operator_abs::wi_fold (irange &r, tree type,
 bool
 operator_abs::op1_range (irange &r, tree type,
 			 const irange &lhs,
-			 const irange &op2) const
+			 const irange &op2,
+			 relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lhs, op2))
     return true;
@@ -3108,16 +3408,19 @@  class operator_negate : public range_operator
  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
 } op_negate;
 
 bool
 operator_negate::fold_range (irange &r, tree type,
 			     const irange &lh,
-			     const irange &rh) const
+			     const irange &rh,
+			     relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -3130,7 +3433,8 @@  operator_negate::fold_range (irange &r, tree type,
 bool
 operator_negate::op1_range (irange &r, tree type,
 			    const irange &lhs,
-			    const irange &op2) const
+			    const irange &op2,
+			    relation_kind rel ATTRIBUTE_UNUSED) const
 {
   // NEGATE is involutory.
   return fold_range (r, type, lhs, op2);
@@ -3142,16 +3446,19 @@  class operator_addr_expr : public range_operator
 public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
-			   const irange &op2) const;
+			   const irange &op2,
+			   relation_kind rel = VREL_NONE) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
 } op_addr;
 
 bool
 operator_addr_expr::fold_range (irange &r, tree type,
 				const irange &lh,
-				const irange &rh) const
+				const irange &rh,
+				relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -3169,7 +3476,8 @@  operator_addr_expr::fold_range (irange &r, tree type,
 bool
 operator_addr_expr::op1_range (irange &r, tree type,
 			       const irange &lhs,
-			       const irange &op2) const
+			       const irange &op2,
+			       relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return operator_addr_expr::fold_range (r, type, lhs, op2);
 }
@@ -3288,10 +3596,12 @@  class pointer_or_operator : public range_operator
 public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
   virtual void wi_fold (irange &r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
 			const wide_int &rh_lb, const wide_int &rh_ub) const;
@@ -3300,7 +3610,8 @@  public:
 bool
 pointer_or_operator::op1_range (irange &r, tree type,
 				const irange &lhs,
-				const irange &op2 ATTRIBUTE_UNUSED) const
+				const irange &op2 ATTRIBUTE_UNUSED,
+				relation_kind rel ATTRIBUTE_UNUSED) const
 {
   if (lhs.zero_p ())
     {
@@ -3315,7 +3626,8 @@  pointer_or_operator::op1_range (irange &r, tree type,
 bool
 pointer_or_operator::op2_range (irange &r, tree type,
 				const irange &lhs,
-				const irange &op1) const
+				const irange &op1,
+				relation_kind rel ATTRIBUTE_UNUSED) const
 {
   return pointer_or_operator::op1_range (r, type, lhs, op1);
 }
diff --git a/gcc/range-op.h b/gcc/range-op.h
index d3d44083093..2b5db64bb98 100644
--- a/gcc/range-op.h
+++ b/gcc/range-op.h
@@ -52,7 +52,8 @@  public:
   // Perform an operation between 2 ranges and return it.
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
-			   const irange &rh) const;
+			   const irange &rh,
+			   relation_kind rel = VREL_NONE) const;
 
   // Return the range for op[12] in the general case.  LHS is the range for
   // the LHS of the expression, OP[12]is the range for the other
@@ -67,11 +68,23 @@  public:
   // is re-formed as R = [LHS] - OP2.
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op2) const;
+			  const irange &op2,
+			  relation_kind rel = VREL_NONE) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-			  const irange &op1) const;
+			  const irange &op1,
+			  relation_kind rel = VREL_NONE) const;
 
+  // The following routines are used to represent relations between the
+  // various operations.  If the caller knows where the symbolics are,
+  // it can query for relationships between them given known ranges.
+  virtual enum tree_code lhs_op1_relation (const irange &lhs,
+					   const irange &op1,
+					   const irange &op2) const;
+  virtual enum tree_code lhs_op2_relation (const irange &lhs,
+					   const irange &op1,
+					   const irange &op2) const;
+  virtual enum tree_code op1_op2_relation (const irange &lhs) const;
 protected:
   // Perform an integral operation between 2 sub-ranges and return it.
   virtual void wi_fold (irange &r, tree type,
@@ -79,6 +92,11 @@  protected:
 		        const wide_int &lh_ub,
 		        const wide_int &rh_lb,
 		        const wide_int &rh_ub) const;
+  // Side effect of relation for generic fold_range clients.
+  virtual bool op1_op2_relation_effect (irange &lhs_range, tree type,
+					const irange &op1_range,
+					const irange &op2_range,
+					relation_kind rel) const;
 };
 
 extern range_operator *range_op_handler (enum tree_code code, tree type);
-- 
2.17.2