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

