diff mbox series

[COMMITTED] Add relation_trio class for range-ops.

Message ID 0f993407-dc43-c120-8bad-4b6c5b7a1aad@redhat.com
State New
Headers show
Series [COMMITTED] Add relation_trio class for range-ops. | expand

Commit Message

Andrew MacLeod Oct. 17, 2022, 1:25 p.m. UTC
When I first added relations to range_ops, I struggled with obfuscating 
the API too much by adding all of the 3 possible relations.  For 
simplicity, it seemed like only one was ever relevant, so elected to add 
one relation, and make it always the relation between the 2 known operands.

fold_range()    - relation passed  op1 REL op2
op1_range()    - relation passed  lhs REL op2
op2_range()    - relation passed  lhs REL op1

With some of the floating point enhancements, we've tripped over cases 
where its useful to know one or more of the relations.

This patch provides a new class in value_relation.h called 
"relation_trio" which packages up 3 relations into a single unsigned 
value, and allows them to be extracted by request.  I have changed all 3 
of the primary range-ops interface routines mentioned above to take a 
relation_trio object by value rather than a relation kind, and then each 
range_op routines explicitly asks for the relation it is looking for.

I have also audited the range-op and range-op-float routines to make 
sure that when op2_range invokes op1_range that we do the appropriate 
relation swapping.

There is virtually no performance impact by this, and it is now clear 
when looking at one of the range-ops routines exactly what relation it 
is using.   This seems much less confusing.

I have also adjusted the compute_operand[12]_range routines in GORI to 
also provide a second relation when appropriate.

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

Andrew
diff mbox series

Patch

From b565ac19264a5827162d28537bccc8531c25e817 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacleod@redhat.com>
Date: Thu, 13 Oct 2022 18:03:58 -0400
Subject: [PATCH 3/4] Add relation_trio class for range-ops.

There are 3 possible relations range-ops might care about, but only the one
most likely to be needed is supplied.   This patch provides a new class
relation_trio which allows 3 relations to be passed in a single word.

fold_range (), op1_range (), and op2_range () are adjusted to take a
relation_trio class instead of a relation_kind, then the routine can
extract which relation it wants to work with.

	* gimple-range-fold.cc (fold_using_range::range_of_range_op):
	Provide relation_trio class.
	* gimple-range-gori.cc (gori_compute::refine_using_relation):
	Provide relation_trio class.
	(gori_compute::refine_using_relation): Ditto.
	(gori_compute::compute_operand1_range): Provide lhs_op2 and
	op1_op2 relations via relation_trio class.
	(gori_compute::compute_operand2_range): Ditto.
	* gimple-range-op.cc (gimple_range_op_handler::calc_op1): Use
	relation_trio instead of relation_kind.
	(gimple_range_op_handler::calc_op2): Ditto.
	(*::fold_range): Ditto.
	* gimple-range-op.h (gimple_range_op::calc_op1): Adjust prototypes.
	(gimple_range_op::calc_op2): Adjust prototypes.
	* range-op-float.cc (*::fold_range): Use relation_trio instead of
	relation_kind.
	(*::op1_range): Ditto.
	(*::op2_range): Ditto.
	* range-op.cc (*::fold_range): Use relation_trio instead of
	relation_kind.
	(*::op1_range): Ditto.
	(*::op2_range): Ditto.
	* range-op.h (class range_operator): Adjust prototypes.
	(class range_operator_float): Ditto.
	(class range_op_handler): Adjust prototypes.
	(relop_early_resolve): Pickup op1_op2 relation from relation_trio.
	* value-relation.cc (VREL_LAST): Adjust use to be one past the end of
	the enum.
	(relation_oracle::validate_relation): Use relation_trio in call
	to fold_range.
	* value-relation.h (enum relation_kind_t): Add VREL_LAST as
	final element.
	(class relation_trio): New.
	(TRIO_VARYING, TRIO_SHIFT, TRIO_MASK): New.
---
 gcc/gimple-range-fold.cc |   5 +-
 gcc/gimple-range-gori.cc |  43 ++++---
 gcc/gimple-range-op.cc   |  40 +++---
 gcc/gimple-range-op.h    |   4 +-
 gcc/range-op-float.cc    | 170 +++++++++++++------------
 gcc/range-op.cc          | 267 ++++++++++++++++++++-------------------
 gcc/range-op.h           |  29 +++--
 gcc/value-relation.cc    |  19 ++-
 gcc/value-relation.h     | 119 +++++++++++++++--
 9 files changed, 405 insertions(+), 291 deletions(-)

diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index c381ef94087..f91923782dc 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -578,7 +578,8 @@  fold_using_range::range_of_range_op (vrange &r,
 	      fputc ('\n', dump_file);
 	    }
 	  // Fold range, and register any dependency if available.
-	  if (!handler.fold_range (r, type, range1, range2, rel))
+	  if (!handler.fold_range (r, type, range1, range2,
+				   relation_trio::op1_op2 (rel)))
 	    r.set_varying (type);
 	  if (irange::supports_p (type))
 	    relation_fold_and_or (as_a <irange> (r), s, src);
@@ -597,7 +598,7 @@  fold_using_range::range_of_range_op (vrange &r,
 		}
 	      if (gimple_range_ssa_p (op2))
 		{
-		  rel= handler.lhs_op2_relation (r, range1, range2, rel);
+		  rel = handler.lhs_op2_relation (r, range1, range2, rel);
 		  if (rel != VREL_VARYING)
 		    src.register_relation (s, rel, lhs, op2);
 		}
diff --git a/gcc/gimple-range-gori.cc b/gcc/gimple-range-gori.cc
index 5ff067cadb5..610a5295e62 100644
--- a/gcc/gimple-range-gori.cc
+++ b/gcc/gimple-range-gori.cc
@@ -991,7 +991,7 @@  gori_compute::refine_using_relation (tree op1, vrange &op1_range,
       Value_Range new_result (type);
       if (!op_handler.op1_range (new_result, type,
 				 op1_def_p ? op1_range : op2_range,
-				 other_op, k))
+				 other_op, relation_trio::lhs_op2 (k)))
 	return false;
       if (op1_def_p)
 	{
@@ -1023,7 +1023,7 @@  gori_compute::refine_using_relation (tree op1, vrange &op1_range,
       Value_Range new_result (type);
       if (!op_handler.op2_range (new_result, type,
 				 op1_def_p ? op1_range : op2_range,
-				 other_op, k))
+				 other_op, relation_trio::lhs_op1 (k)))
 	return false;
       if (op1_def_p)
 	{
@@ -1074,6 +1074,7 @@  gori_compute::compute_operand1_range (vrange &r,
     {
       src.get_operand (op2_range, op2);
       relation_kind k = VREL_VARYING;
+      relation_kind op_op = (op1 == op2) ? VREL_EQ : VREL_VARYING;
       if (rel)
 	{
 	 if (lhs_name == rel->op1 () && op1 == rel->op2 ())
@@ -1081,13 +1082,18 @@  gori_compute::compute_operand1_range (vrange &r,
 	 else if (lhs_name == rel->op2 () && op1 == rel->op1 ())
 	   k = relation_swap (rel->kind ());
 	 else if (op1 == rel->op1 () && op2 == rel->op2 ())
-	   refine_using_relation (op1, op1_range, op2, op2_range, src,
-				  rel->kind ());
+	   {
+	     op_op = rel->kind ();
+	     refine_using_relation (op1, op1_range, op2, op2_range, src, op_op);
+	   }
 	 else if (op1 == rel->op2 () && op2 == rel->op1 ())
-	   refine_using_relation (op1, op1_range, op2, op2_range, src,
-				  relation_swap (rel->kind ()));
+	   {
+	     op_op = relation_swap (rel->kind ());
+	     refine_using_relation (op1, op1_range, op2, op2_range, src, op_op);
+	   }
        }
-      if (!handler.calc_op1 (tmp, lhs, op2_range, k))
+      if (!handler.calc_op1 (tmp, lhs, op2_range, relation_trio (VREL_VARYING,
+								 k, op_op)))
 	return false;
     }
   else
@@ -1095,7 +1101,7 @@  gori_compute::compute_operand1_range (vrange &r,
       // We pass op1_range to the unary operation.  Nomally it's a
       // hidden range_for_type parameter, but sometimes having the
       // actual range can result in better information.
-      if (!handler.calc_op1 (tmp, lhs, op1_range, VREL_VARYING))
+      if (!handler.calc_op1 (tmp, lhs, op1_range, TRIO_VARYING))
 	return false;
     }
 
@@ -1167,23 +1173,28 @@  gori_compute::compute_operand2_range (vrange &r,
   src.get_operand (op1_range, op1);
   src.get_operand (op2_range, op2);
   relation_kind k = VREL_VARYING;
+  relation_kind op_op = (op1 == op2) ? VREL_EQ : VREL_VARYING;
   if (rel)
     {
       if (lhs_name == rel->op1 () && op2 == rel->op2 ())
-       k = rel->kind ();
+	k = rel->kind ();
       else if (lhs_name == rel->op2 () && op2 == rel->op1 ())
-       k = relation_swap (rel->kind ());
+	k = relation_swap (rel->kind ());
       else if (op1 == rel->op1 () && op2 == rel->op2 ())
-       refine_using_relation (op1, op1_range, op2, op2_range, src,
-			      rel->kind ());
+	{
+	  op_op = rel->kind ();
+	  refine_using_relation (op1, op1_range, op2, op2_range, src, op_op);
+	}
       else if (op1 == rel->op2 () && op2 == rel->op1 ())
-       refine_using_relation (op1, op1_range, op2, op2_range, src,
-			      relation_swap (rel->kind ()));
+	{
+	  op_op = relation_swap (rel->kind ());
+	  refine_using_relation (op1, op1_range, op2, op2_range, src, op_op);
+	}
     }
 
-
   // Intersect with range for op2 based on lhs and op1.
-  if (!handler.calc_op2 (tmp, lhs, op1_range, k))
+  if (!handler.calc_op2 (tmp, lhs, op1_range, relation_trio (k, VREL_VARYING,
+							     op_op)))
     return false;
 
   unsigned idx;
diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc
index 2662319aadc..7764166d5fb 100644
--- a/gcc/gimple-range-op.cc
+++ b/gcc/gimple-range-op.cc
@@ -202,7 +202,7 @@  gimple_range_op_handler::calc_op1 (vrange &r, const vrange &lhs_range)
 
 bool
 gimple_range_op_handler::calc_op1 (vrange &r, const vrange &lhs_range,
-				   const vrange &op2_range, relation_kind k)
+				   const vrange &op2_range, relation_trio k)
 {
   // Give up on empty ranges.
   if (lhs_range.undefined_p ())
@@ -237,7 +237,7 @@  gimple_range_op_handler::calc_op1 (vrange &r, const vrange &lhs_range,
 
 bool
 gimple_range_op_handler::calc_op2 (vrange &r, const vrange &lhs_range,
-				   const vrange &op1_range, relation_kind k)
+				   const vrange &op1_range, relation_trio k)
 {
   // Give up on empty ranges.
   if (lhs_range.undefined_p ())
@@ -263,7 +263,7 @@  class cfn_constant_float_p : public range_operator_float
 public:
   using range_operator_float::fold_range;
   virtual bool fold_range (irange &r, tree type, const frange &lh,
-			   const irange &, relation_kind) const
+			   const irange &, relation_trio) const
   {
     if (lh.singleton_p ())
       {
@@ -285,7 +285,7 @@  class cfn_constant_p : public range_operator
 public:
   using range_operator::fold_range;
   virtual bool fold_range (irange &r, tree type, const irange &lh,
-			   const irange &, relation_kind) const
+			   const irange &, relation_trio) const
   {
     if (lh.singleton_p ())
       {
@@ -308,7 +308,7 @@  public:
   using range_operator_float::fold_range;
   using range_operator_float::op1_range;
   virtual bool fold_range (irange &r, tree type, const frange &lh,
-			   const irange &, relation_kind) const override
+			   const irange &, relation_trio) const override
   {
     bool signbit;
     if (lh.signbit_p (signbit))
@@ -322,7 +322,7 @@  public:
    return false;
   }
   virtual bool op1_range (frange &r, tree type, const irange &lhs,
-			  const frange &, relation_kind) const override
+			  const frange &, relation_trio) const override
   {
     if (lhs.zero_p ())
       {
@@ -348,7 +348,7 @@  class cfn_copysign : public range_operator_float
 public:
   using range_operator_float::fold_range;
   virtual bool fold_range (frange &r, tree type, const frange &lh,
-			   const frange &rh, relation_kind) const override
+			   const frange &rh, relation_trio) const override
   {
     frange neg;
     range_op_handler abs_op (ABS_EXPR, type);
@@ -381,7 +381,7 @@  public:
   using range_operator::fold_range;
   cfn_toupper_tolower (bool toupper)  { m_toupper = toupper; }
   virtual bool fold_range (irange &r, tree type, const irange &lh,
-			   const irange &, relation_kind) const;
+			   const irange &, relation_trio) const;
 private:
   bool get_letter_range (tree type, irange &lowers, irange &uppers) const;
   bool m_toupper;
@@ -412,7 +412,7 @@  cfn_toupper_tolower::get_letter_range (tree type, irange &lowers,
 
 bool
 cfn_toupper_tolower::fold_range (irange &r, tree type, const irange &lh,
-				 const irange &, relation_kind) const
+				 const irange &, relation_trio) const
 {
   int_range<3> lowers;
   int_range<3> uppers;
@@ -445,7 +445,7 @@  class cfn_ffs : public range_operator
 public:
   using range_operator::fold_range;
   virtual bool fold_range (irange &r, tree type, const irange &lh,
-			   const irange &, relation_kind) const
+			   const irange &, relation_trio) const
   {
     if (lh.undefined_p ())
       return false;
@@ -472,7 +472,7 @@  class cfn_popcount : public cfn_ffs
 public:
   using range_operator::fold_range;
   virtual bool fold_range (irange &r, tree type, const irange &lh,
-			   const irange &rh, relation_kind rel) const
+			   const irange &rh, relation_trio rel) const
   {
     if (lh.undefined_p ())
       return false;
@@ -502,14 +502,14 @@  public:
   cfn_clz (bool internal) { m_gimple_call_internal_p = internal; }
   using range_operator::fold_range;
   virtual bool fold_range (irange &r, tree type, const irange &lh,
-			   const irange &, relation_kind) const;
+			   const irange &, relation_trio) const;
 private:
   bool m_gimple_call_internal_p;
 } op_cfn_clz (false), op_cfn_clz_internal (true);
 
 bool
 cfn_clz::fold_range (irange &r, tree type, const irange &lh,
-		     const irange &, relation_kind) const
+		     const irange &, relation_trio) const
 {
   // __builtin_c[lt]z* return [0, prec-1], except when the
   // argument is 0, but that is undefined behavior.
@@ -577,14 +577,14 @@  public:
   cfn_ctz (bool internal) { m_gimple_call_internal_p = internal; }
   using range_operator::fold_range;
   virtual bool fold_range (irange &r, tree type, const irange &lh,
-			   const irange &, relation_kind) const;
+			   const irange &, relation_trio) const;
 private:
   bool m_gimple_call_internal_p;
 } op_cfn_ctz (false), op_cfn_ctz_internal (true);
 
 bool
 cfn_ctz::fold_range (irange &r, tree type, const irange &lh,
-		     const irange &, relation_kind) const
+		     const irange &, relation_trio) const
 {
   if (lh.undefined_p ())
     return false;
@@ -647,7 +647,7 @@  class cfn_clrsb : public range_operator
 public:
   using range_operator::fold_range;
   virtual bool fold_range (irange &r, tree type, const irange &lh,
-			   const irange &, relation_kind) const
+			   const irange &, relation_trio) const
   {
     if (lh.undefined_p ())
       return false;
@@ -665,7 +665,7 @@  public:
   cfn_ubsan (enum tree_code code) { m_code = code; }
   using range_operator::fold_range;
   virtual bool fold_range (irange &r, tree type, const irange &lh,
-			   const irange &rh, relation_kind rel) const
+			   const irange &rh, relation_trio rel) const
   {
     range_op_handler handler (m_code, type);
     gcc_checking_assert (handler);
@@ -699,7 +699,7 @@  class cfn_strlen : public range_operator
 public:
   using range_operator::fold_range;
   virtual bool fold_range (irange &r, tree type, const irange &,
-			   const irange &, relation_kind) const
+			   const irange &, relation_trio) const
   {
     tree max = vrp_val_max (ptrdiff_type_node);
     wide_int wmax
@@ -724,7 +724,7 @@  public:
   cfn_goacc_dim (bool is_pos) { m_is_pos = is_pos; }
   using range_operator::fold_range;
   virtual bool fold_range (irange &r, tree type, const irange &lh,
-			   const irange &, relation_kind) const
+			   const irange &, relation_trio) const
   {
     tree axis_tree;
     if (!lh.singleton_p (&axis_tree))
@@ -751,7 +751,7 @@  class cfn_parity : public range_operator
 public:
   using range_operator::fold_range;
   virtual bool fold_range (irange &r, tree type, const irange &,
-			   const irange &, relation_kind) const
+			   const irange &, relation_trio) const
   {
     r.set (build_zero_cst (type), build_one_cst (type));
     return true;
diff --git a/gcc/gimple-range-op.h b/gcc/gimple-range-op.h
index 3a555f29a65..be1305c58aa 100644
--- a/gcc/gimple-range-op.h
+++ b/gcc/gimple-range-op.h
@@ -36,9 +36,9 @@  public:
   tree operand2 () const { gcc_checking_assert (m_valid); return m_op2; }
   bool calc_op1 (vrange &r, const vrange &lhs_range);
   bool calc_op1 (vrange &r, const vrange &lhs_range, const vrange &op2_range,
-		 relation_kind k = VREL_VARYING);
+		 relation_trio = TRIO_VARYING);
   bool calc_op2 (vrange &r, const vrange &lhs_range, const vrange &op1_range,
-		 relation_kind k = VREL_VARYING);
+		 relation_trio = TRIO_VARYING);
 private:
   void maybe_builtin_call ();
   gimple *m_stmt;
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index 6cf2180ce59..e7334794b0d 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -53,7 +53,7 @@  range_operator_float::fold_range (frange &r ATTRIBUTE_UNUSED,
 				  tree type ATTRIBUTE_UNUSED,
 				  const frange &lh ATTRIBUTE_UNUSED,
 				  const frange &rh ATTRIBUTE_UNUSED,
-				  relation_kind rel ATTRIBUTE_UNUSED) const
+				  relation_trio) const
 {
   return false;
 }
@@ -63,7 +63,7 @@  range_operator_float::fold_range (irange &r ATTRIBUTE_UNUSED,
 				  tree type ATTRIBUTE_UNUSED,
 				  const frange &lh ATTRIBUTE_UNUSED,
 				  const irange &rh ATTRIBUTE_UNUSED,
-				  relation_kind rel ATTRIBUTE_UNUSED) const
+				  relation_trio) const
 {
   return false;
 }
@@ -73,7 +73,7 @@  range_operator_float::fold_range (irange &r ATTRIBUTE_UNUSED,
 				  tree type ATTRIBUTE_UNUSED,
 				  const frange &lh ATTRIBUTE_UNUSED,
 				  const frange &rh ATTRIBUTE_UNUSED,
-				  relation_kind rel ATTRIBUTE_UNUSED) const
+				  relation_trio) const
 {
   return false;
 }
@@ -83,7 +83,7 @@  range_operator_float::op1_range (frange &r ATTRIBUTE_UNUSED,
 				 tree type ATTRIBUTE_UNUSED,
 				 const frange &lhs ATTRIBUTE_UNUSED,
 				 const frange &op2 ATTRIBUTE_UNUSED,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   return false;
 }
@@ -93,7 +93,7 @@  range_operator_float::op1_range (frange &r ATTRIBUTE_UNUSED,
 				 tree type ATTRIBUTE_UNUSED,
 				 const irange &lhs ATTRIBUTE_UNUSED,
 				 const frange &op2 ATTRIBUTE_UNUSED,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   return false;
 }
@@ -103,7 +103,7 @@  range_operator_float::op2_range (frange &r ATTRIBUTE_UNUSED,
 				 tree type ATTRIBUTE_UNUSED,
 				 const frange &lhs ATTRIBUTE_UNUSED,
 				 const frange &op1 ATTRIBUTE_UNUSED,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   return false;
 }
@@ -113,7 +113,7 @@  range_operator_float::op2_range (frange &r ATTRIBUTE_UNUSED,
 				 tree type ATTRIBUTE_UNUSED,
 				 const irange &lhs ATTRIBUTE_UNUSED,
 				 const frange &op1 ATTRIBUTE_UNUSED,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   return false;
 }
@@ -188,7 +188,7 @@  finite_operands_p (const frange &op1, const frange &op2)
 inline bool
 frelop_early_resolve (irange &r, tree type,
 		      const frange &op1, const frange &op2,
-		      relation_kind rel, relation_kind my_rel)
+		      relation_trio rel, relation_kind my_rel)
 {
   // If either operand is undefined, return VARYING.
   if (empty_range_varying (r, type, op1, op2))
@@ -324,14 +324,14 @@  class foperator_identity : public range_operator_float
 public:
   bool fold_range (frange &r, tree type ATTRIBUTE_UNUSED,
 		   const frange &op1, const frange &op2 ATTRIBUTE_UNUSED,
-		   relation_kind = VREL_VARYING) const final override
+		   relation_trio = TRIO_VARYING) const final override
   {
     r = op1;
     return true;
   }
   bool op1_range (frange &r, tree type ATTRIBUTE_UNUSED,
 		  const frange &lhs, const frange &op2 ATTRIBUTE_UNUSED,
-		  relation_kind = VREL_VARYING) const final override
+		  relation_trio = TRIO_VARYING) const final override
   {
     r = lhs;
     return true;
@@ -348,26 +348,26 @@  class foperator_equal : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind = VREL_VARYING) const final override;
+		   relation_trio = TRIO_VARYING) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return equal_op1_op2_relation (lhs);
   }
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind rel = VREL_VARYING) const final override
+		  relation_trio rel = TRIO_VARYING) const final override
   {
-    return op1_range (r, type, lhs, op1, rel);
+    return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
   }
 } fop_equal;
 
 bool
 foperator_equal::fold_range (irange &r, tree type,
 			     const frange &op1, const frange &op2,
-			     relation_kind rel) const
+			     relation_trio rel) const
 {
   if (frelop_early_resolve (r, type, op1, op2, rel, VREL_EQ))
     return true;
@@ -403,8 +403,9 @@  bool
 foperator_equal::op1_range (frange &r, tree type,
 			    const irange &lhs,
 			    const frange &op2,
-			    relation_kind rel) const
+			    relation_trio trio) const
 {
+  relation_kind rel = trio.op1_op2 ();
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
@@ -455,20 +456,20 @@  class foperator_not_equal : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel = VREL_VARYING) const final override;
+		   relation_trio rel = TRIO_VARYING) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return not_equal_op1_op2_relation (lhs);
   }
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
 } fop_not_equal;
 
 bool
 foperator_not_equal::fold_range (irange &r, tree type,
 				 const frange &op1, const frange &op2,
-				 relation_kind rel) const
+				 relation_trio rel) const
 {
   if (frelop_early_resolve (r, type, op1, op2, rel, VREL_NE))
     return true;
@@ -505,8 +506,9 @@  bool
 foperator_not_equal::op1_range (frange &r, tree type,
 				const irange &lhs,
 				const frange &op2,
-				relation_kind rel) const
+				relation_trio trio) const
 {
+  relation_kind rel = trio.op1_op2 ();
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
@@ -557,23 +559,23 @@  class foperator_lt : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind = VREL_VARYING) const final override;
+		   relation_trio = TRIO_VARYING) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return lt_op1_op2_relation (lhs);
   }
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
 } fop_lt;
 
 bool
 foperator_lt::fold_range (irange &r, tree type,
 			  const frange &op1, const frange &op2,
-			  relation_kind rel) const
+			  relation_trio rel) const
 {
   if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LT))
     return true;
@@ -599,7 +601,7 @@  foperator_lt::op1_range (frange &r,
 			 tree type,
 			 const irange &lhs,
 			 const frange &op2,
-			 relation_kind) const
+			 relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -634,7 +636,7 @@  foperator_lt::op2_range (frange &r,
 			 tree type,
 			 const irange &lhs,
 			 const frange &op1,
-			 relation_kind) const
+			 relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -673,23 +675,23 @@  class foperator_le : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel = VREL_VARYING) const final override;
+		   relation_trio rel = TRIO_VARYING) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return le_op1_op2_relation (lhs);
   }
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind rel = VREL_VARYING) const final override;
+		  relation_trio rel = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind rel = VREL_VARYING) const final override;
+		  relation_trio rel = TRIO_VARYING) const final override;
 } fop_le;
 
 bool
 foperator_le::fold_range (irange &r, tree type,
 			  const frange &op1, const frange &op2,
-			  relation_kind rel) const
+			  relation_trio rel) const
 {
   if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LE))
     return true;
@@ -715,7 +717,7 @@  foperator_le::op1_range (frange &r,
 			 tree type,
 			 const irange &lhs,
 			 const frange &op2,
-			 relation_kind) const
+			 relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -746,7 +748,7 @@  foperator_le::op2_range (frange &r,
 			 tree type,
 			 const irange &lhs,
 			 const frange &op1,
-			 relation_kind) const
+			 relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -781,23 +783,23 @@  class foperator_gt : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind = VREL_VARYING) const final override;
+		   relation_trio = TRIO_VARYING) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return gt_op1_op2_relation (lhs);
   }
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
 } fop_gt;
 
 bool
 foperator_gt::fold_range (irange &r, tree type,
 			  const frange &op1, const frange &op2,
-			  relation_kind rel) const
+			  relation_trio rel) const
 {
   if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GT))
     return true;
@@ -823,7 +825,7 @@  foperator_gt::op1_range (frange &r,
 			 tree type,
 			 const irange &lhs,
 			 const frange &op2,
-			 relation_kind) const
+			 relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -858,7 +860,7 @@  foperator_gt::op2_range (frange &r,
 			 tree type,
 			 const irange &lhs,
 			 const frange &op1,
-			 relation_kind) const
+			 relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -897,23 +899,23 @@  class foperator_ge : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind = VREL_VARYING) const final override;
+		   relation_trio = TRIO_VARYING) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return ge_op1_op2_relation (lhs);
   }
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
 } fop_ge;
 
 bool
 foperator_ge::fold_range (irange &r, tree type,
 			  const frange &op1, const frange &op2,
-			  relation_kind rel) const
+			  relation_trio rel) const
 {
   if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GE))
     return true;
@@ -939,7 +941,7 @@  foperator_ge::op1_range (frange &r,
 			 tree type,
 			 const irange &lhs,
 			 const frange &op2,
-			 relation_kind) const
+			 relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -969,7 +971,7 @@  bool
 foperator_ge::op2_range (frange &r, tree type,
 			 const irange &lhs,
 			 const frange &op1,
-			 relation_kind) const
+			 relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1008,22 +1010,22 @@  class foperator_unordered : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind = VREL_VARYING) const final override;
+		   relation_trio = TRIO_VARYING) const final override;
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind rel = VREL_VARYING) const final override
+		  relation_trio rel = TRIO_VARYING) const final override
   {
-    return op1_range (r, type, lhs, op1, rel);
+    return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
   }
 } fop_unordered;
 
 bool
 foperator_unordered::fold_range (irange &r, tree type,
 				 const frange &op1, const frange &op2,
-				 relation_kind) const
+				 relation_trio) const
 {
   // UNORDERED is TRUE if either operand is a NAN.
   if (op1.known_isnan () || op2.known_isnan ())
@@ -1040,8 +1042,9 @@  bool
 foperator_unordered::op1_range (frange &r, tree type,
 				const irange &lhs,
 				const frange &op2,
-				relation_kind rel) const
+				relation_trio trio) const
 {
+  relation_kind rel = trio.op1_op2 ();
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
@@ -1081,22 +1084,22 @@  class foperator_ordered : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind = VREL_VARYING) const final override;
+		   relation_trio = TRIO_VARYING) const final override;
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind rel = VREL_VARYING) const final override
+		  relation_trio rel = TRIO_VARYING) const final override
   {
-    return op1_range (r, type, lhs, op1, rel);
+    return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
   }
 } fop_ordered;
 
 bool
 foperator_ordered::fold_range (irange &r, tree type,
 			       const frange &op1, const frange &op2,
-			       relation_kind) const
+			       relation_trio) const
 {
   if (op1.known_isnan () || op2.known_isnan ())
     r = range_false (type);
@@ -1111,8 +1114,9 @@  bool
 foperator_ordered::op1_range (frange &r, tree type,
 			      const irange &lhs,
 			      const frange &op2,
-			      relation_kind rel) const
+			      relation_trio trio) const
 {
+  relation_kind rel = trio.op1_op2 ();
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
@@ -1148,7 +1152,7 @@  class foperator_negate : public range_operator_float
 public:
   bool fold_range (frange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind = VREL_VARYING) const final override
+		   relation_trio = TRIO_VARYING) const final override
   {
     if (empty_range_varying (r, type, op1, op2))
       return true;
@@ -1181,7 +1185,7 @@  public:
   }
   bool op1_range (frange &r, tree type,
 		  const frange &lhs, const frange &op2,
-		  relation_kind rel = VREL_VARYING) const final override
+		  relation_trio rel = TRIO_VARYING) const final override
   {
     return fold_range (r, type, lhs, op2, rel);
   }
@@ -1194,16 +1198,16 @@  class foperator_abs : public range_operator_float
 public:
   bool fold_range (frange &r, tree type,
 		   const frange &op1, const frange &,
-		   relation_kind = VREL_VARYING) const final override;
+		   relation_trio = TRIO_VARYING) const final override;
   bool op1_range (frange &r, tree type,
 		  const frange &lhs, const frange &op2,
-		  relation_kind rel = VREL_VARYING) const final override;
+		  relation_trio rel = TRIO_VARYING) const final override;
 } fop_abs;
 
 bool
 foperator_abs::fold_range (frange &r, tree type,
 			   const frange &op1, const frange &op2,
-			   relation_kind) const
+			   relation_trio) const
 {
   if (empty_range_varying (r, type, op1, op2))
     return true;
@@ -1253,7 +1257,7 @@  foperator_abs::fold_range (frange &r, tree type,
 bool
 foperator_abs::op1_range (frange &r, tree type,
 			  const frange &lhs, const frange &op2,
-			  relation_kind) const
+			  relation_trio) const
 {
   if (empty_range_varying (r, type, lhs, op2))
     return true;
@@ -1282,7 +1286,7 @@  class foperator_unordered_lt : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel = VREL_VARYING) const final override
+		   relation_trio rel = TRIO_VARYING) const final override
   {
     if (op1.known_isnan () || op2.known_isnan ())
       {
@@ -1311,7 +1315,7 @@  class foperator_unordered_le : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel = VREL_VARYING) const final override
+		   relation_trio rel = TRIO_VARYING) const final override
   {
     if (op1.known_isnan () || op2.known_isnan ())
       {
@@ -1332,16 +1336,16 @@  public:
   }
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
 } fop_unordered_le;
 
 bool
 foperator_unordered_le::op1_range (frange &r, tree type,
 				   const irange &lhs, const frange &op2,
-				   relation_kind) const
+				   relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1365,7 +1369,7 @@  foperator_unordered_le::op2_range (frange &r,
 				   tree type,
 				   const irange &lhs,
 				   const frange &op1,
-				   relation_kind) const
+				   relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1392,7 +1396,7 @@  class foperator_unordered_gt : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel = VREL_VARYING) const final override
+		   relation_trio rel = TRIO_VARYING) const final override
   {
     if (op1.known_isnan () || op2.known_isnan ())
       {
@@ -1413,10 +1417,10 @@  public:
   }
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
 } fop_unordered_gt;
 
 bool
@@ -1424,7 +1428,7 @@  foperator_unordered_gt::op1_range (frange &r,
 			 tree type,
 			 const irange &lhs,
 			 const frange &op2,
-			 relation_kind) const
+			 relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1448,7 +1452,7 @@  foperator_unordered_gt::op2_range (frange &r,
 				   tree type,
 				   const irange &lhs,
 				   const frange &op1,
-				   relation_kind) const
+				   relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1475,7 +1479,7 @@  class foperator_unordered_ge : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel = VREL_VARYING) const final override
+		   relation_trio rel = TRIO_VARYING) const final override
   {
     if (op1.known_isnan () || op2.known_isnan ())
       {
@@ -1496,10 +1500,10 @@  public:
   }
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
 } fop_unordered_ge;
 
 bool
@@ -1507,7 +1511,7 @@  foperator_unordered_ge::op1_range (frange &r,
 				   tree type,
 				   const irange &lhs,
 				   const frange &op2,
-				   relation_kind) const
+				   relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1530,7 +1534,7 @@  bool
 foperator_unordered_ge::op2_range (frange &r, tree type,
 				   const irange &lhs,
 				   const frange &op1,
-				   relation_kind) const
+				   relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1557,7 +1561,7 @@  class foperator_unordered_equal : public range_operator_float
 public:
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel = VREL_VARYING) const final override
+		   relation_trio rel = TRIO_VARYING) const final override
   {
     if (op1.known_isnan () || op2.known_isnan ())
       {
@@ -1578,12 +1582,12 @@  public:
   }
   bool op1_range (frange &r, tree type,
 		  const irange &lhs, const frange &op2,
-		  relation_kind = VREL_VARYING) const final override;
+		  relation_trio = TRIO_VARYING) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind rel = VREL_VARYING) const final override
+		  relation_trio rel = TRIO_VARYING) const final override
   {
-    return op1_range (r, type, lhs, op1, rel);
+    return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
   }
 } fop_unordered_equal;
 
@@ -1591,7 +1595,7 @@  bool
 foperator_unordered_equal::op1_range (frange &r, tree type,
 				      const irange &lhs,
 				      const frange &op2,
-				      relation_kind) const
+				      relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index cf7f0dcd670..49ee7be3d3b 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -179,12 +179,13 @@  bool
 range_operator::fold_range (irange &r, tree type,
 			    const irange &lh,
 			    const irange &rh,
-			    relation_kind rel) const
+			    relation_trio trio) const
 {
   gcc_checking_assert (r.supports_type_p (type));
   if (empty_range_varying (r, type, lh, rh))
     return true;
 
+  relation_kind rel = trio.op1_op2 ();
   unsigned num_lh = lh.num_pairs ();
   unsigned num_rh = rh.num_pairs ();
 
@@ -227,7 +228,7 @@  range_operator::op1_range (irange &r ATTRIBUTE_UNUSED,
 			   tree type ATTRIBUTE_UNUSED,
 			   const irange &lhs ATTRIBUTE_UNUSED,
 			   const irange &op2 ATTRIBUTE_UNUSED,
-			   relation_kind rel ATTRIBUTE_UNUSED) const
+			   relation_trio) const
 {
   return false;
 }
@@ -239,7 +240,7 @@  range_operator::op2_range (irange &r ATTRIBUTE_UNUSED,
 			   tree type ATTRIBUTE_UNUSED,
 			   const irange &lhs ATTRIBUTE_UNUSED,
 			   const irange &op1 ATTRIBUTE_UNUSED,
-			   relation_kind rel ATTRIBUTE_UNUSED) const
+			   relation_trio) const
 {
   return false;
 }
@@ -453,15 +454,15 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &val,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &val,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual relation_kind op1_op2_relation (const irange &lhs) const;
 } op_equal;
 
@@ -494,7 +495,7 @@  bool
 operator_equal::fold_range (irange &r, tree type,
 			    const irange &op1,
 			    const irange &op2,
-			    relation_kind rel) const
+			    relation_trio rel) const
 {
   if (relop_early_resolve (r, type, op1, op2, rel, VREL_EQ))
     return true;
@@ -527,7 +528,7 @@  bool
 operator_equal::op1_range (irange &r, tree type,
 			   const irange &lhs,
 			   const irange &op2,
-			   relation_kind rel ATTRIBUTE_UNUSED) const
+			   relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -558,9 +559,9 @@  bool
 operator_equal::op2_range (irange &r, tree type,
 			   const irange &lhs,
 			   const irange &op1,
-			   relation_kind rel) const
+			   relation_trio rel) const
 {
-  return operator_equal::op1_range (r, type, lhs, op1, rel);
+  return operator_equal::op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
 }
 
 class operator_not_equal : public range_operator
@@ -572,15 +573,15 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual relation_kind op1_op2_relation (const irange &lhs) const;
 } op_not_equal;
 
@@ -612,7 +613,7 @@  bool
 operator_not_equal::fold_range (irange &r, tree type,
 				const irange &op1,
 				const irange &op2,
-				relation_kind rel) const
+				relation_trio rel) const
 {
   if (relop_early_resolve (r, type, op1, op2, rel, VREL_NE))
     return true;
@@ -645,7 +646,7 @@  bool
 operator_not_equal::op1_range (irange &r, tree type,
 			       const irange &lhs,
 			       const irange &op2,
-			       relation_kind rel ATTRIBUTE_UNUSED) const
+			       relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -677,9 +678,9 @@  bool
 operator_not_equal::op2_range (irange &r, tree type,
 			       const irange &lhs,
 			       const irange &op1,
-			       relation_kind rel) const
+			       relation_trio rel) const
 {
-  return operator_not_equal::op1_range (r, type, lhs, op1, rel);
+  return operator_not_equal::op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
 }
 
 // (X < VAL) produces the range of [MIN, VAL - 1].
@@ -751,15 +752,15 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual relation_kind op1_op2_relation (const irange &lhs) const;
 } op_lt;
 
@@ -791,7 +792,7 @@  bool
 operator_lt::fold_range (irange &r, tree type,
 			 const irange &op1,
 			 const irange &op2,
-			 relation_kind rel) const
+			 relation_trio rel) const
 {
   if (relop_early_resolve (r, type, op1, op2, rel, VREL_LT))
     return true;
@@ -815,7 +816,7 @@  bool
 operator_lt::op1_range (irange &r, tree type,
 			const irange &lhs,
 			const irange &op2,
-			relation_kind rel ATTRIBUTE_UNUSED) const
+			relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -837,7 +838,7 @@  bool
 operator_lt::op2_range (irange &r, tree type,
 			const irange &lhs,
 			const irange &op1,
-			relation_kind rel ATTRIBUTE_UNUSED) const
+			relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -865,15 +866,15 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual relation_kind op1_op2_relation (const irange &lhs) const;
 } op_le;
 
@@ -905,7 +906,7 @@  bool
 operator_le::fold_range (irange &r, tree type,
 			 const irange &op1,
 			 const irange &op2,
-			 relation_kind rel) const
+			 relation_trio rel) const
 {
   if (relop_early_resolve (r, type, op1, op2, rel, VREL_LE))
     return true;
@@ -926,7 +927,7 @@  bool
 operator_le::op1_range (irange &r, tree type,
 			const irange &lhs,
 			const irange &op2,
-			relation_kind rel ATTRIBUTE_UNUSED) const
+			relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -948,7 +949,7 @@  bool
 operator_le::op2_range (irange &r, tree type,
 			const irange &lhs,
 			const irange &op1,
-			relation_kind rel ATTRIBUTE_UNUSED) const
+			relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -976,15 +977,15 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual relation_kind op1_op2_relation (const irange &lhs) const;
 } op_gt;
 
@@ -1016,7 +1017,7 @@  operator_gt::op1_op2_relation (const irange &lhs) const
 bool
 operator_gt::fold_range (irange &r, tree type,
 			 const irange &op1, const irange &op2,
-			 relation_kind rel) const
+			 relation_trio rel) const
 {
   if (relop_early_resolve (r, type, op1, op2, rel, VREL_GT))
     return true;
@@ -1036,7 +1037,7 @@  operator_gt::fold_range (irange &r, tree type,
 bool
 operator_gt::op1_range (irange &r, tree type,
 			const irange &lhs, const irange &op2,
-			relation_kind rel ATTRIBUTE_UNUSED) const
+			relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1058,7 +1059,7 @@  bool
 operator_gt::op2_range (irange &r, tree type,
 			const irange &lhs,
 			const irange &op1,
-			relation_kind rel ATTRIBUTE_UNUSED) const
+			relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1086,15 +1087,15 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual relation_kind op1_op2_relation (const irange &lhs) const;
 } op_ge;
 
@@ -1126,7 +1127,7 @@  bool
 operator_ge::fold_range (irange &r, tree type,
 			 const irange &op1,
 			 const irange &op2,
-			 relation_kind rel) const
+			 relation_trio rel) const
 {
   if (relop_early_resolve (r, type, op1, op2, rel, VREL_GE))
     return true;
@@ -1147,7 +1148,7 @@  bool
 operator_ge::op1_range (irange &r, tree type,
 			const irange &lhs,
 			const irange &op2,
-			relation_kind rel ATTRIBUTE_UNUSED) const
+			relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1169,7 +1170,7 @@  bool
 operator_ge::op2_range (irange &r, tree type,
 			const irange &lhs,
 			const irange &op1,
-			relation_kind rel ATTRIBUTE_UNUSED) const
+			relation_trio) const
 {
   switch (get_bool_state (r, lhs, type))
     {
@@ -1198,11 +1199,11 @@  public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel ATTRIBUTE_UNUSED) const;
+			  relation_trio) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel ATTRIBUTE_UNUSED) const;
+			  relation_trio) const;
   virtual void wi_fold (irange &r, tree type,
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
@@ -1402,7 +1403,7 @@  bool
 operator_plus::op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel) const
+			  relation_trio trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -1411,6 +1412,7 @@  operator_plus::op1_range (irange &r, tree type,
   if (!minus)
     return false;
   bool res = minus.fold_range (r, type, lhs, op2);
+  relation_kind rel = trio.lhs_op2 ();
   // Check for a relation refinement.
   if (res)
     adjust_op1_for_overflow (r, op2, rel, true /* PLUS_EXPR */);
@@ -1421,9 +1423,9 @@  bool
 operator_plus::op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel) const
+			  relation_trio rel) const
 {
-  return op1_range (r, type, lhs, op1, rel);
+  return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
 }
 
 
@@ -1436,11 +1438,11 @@  public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel ATTRIBUTE_UNUSED) const;
+			  relation_trio) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel ATTRIBUTE_UNUSED) const;
+			  relation_trio) const;
   virtual void wi_fold (irange &r, tree type,
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
@@ -1573,7 +1575,7 @@  bool
 operator_minus::op1_range (irange &r, tree type,
 			   const irange &lhs,
 			   const irange &op2,
-			   relation_kind rel ATTRIBUTE_UNUSED) const
+			   relation_trio trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -1582,6 +1584,7 @@  operator_minus::op1_range (irange &r, tree type,
   if (!minus)
     return false;
   bool res = minus.fold_range (r, type, lhs, op2);
+  relation_kind rel = trio.lhs_op2 ();
   if (res)
     adjust_op1_for_overflow (r, op2, rel, false /* PLUS_EXPR */);
   return res;
@@ -1592,7 +1595,7 @@  bool
 operator_minus::op2_range (irange &r, tree type,
 			   const irange &lhs,
 			   const irange &op1,
-			   relation_kind rel ATTRIBUTE_UNUSED) const
+			   relation_trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -1752,17 +1755,17 @@  public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel ATTRIBUTE_UNUSED) const;
+			  relation_trio) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel ATTRIBUTE_UNUSED) const;
+			  relation_trio) const;
 } op_mult;
 
 bool
 operator_mult::op1_range (irange &r, tree type,
 			  const irange &lhs, const irange &op2,
-			  relation_kind rel ATTRIBUTE_UNUSED) const
+			  relation_trio) const
 {
   tree offset;
   if (lhs.undefined_p ())
@@ -1783,9 +1786,9 @@  operator_mult::op1_range (irange &r, tree type,
 bool
 operator_mult::op2_range (irange &r, tree type,
 			  const irange &lhs, const irange &op1,
-			  relation_kind rel) const
+			  relation_trio rel) const
 {
-  return operator_mult::op1_range (r, type, lhs, op1, rel);
+  return operator_mult::op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
 }
 
 bool
@@ -2009,7 +2012,7 @@  public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel ATTRIBUTE_UNUSED) const;
+			  relation_trio) const;
 
 } op_exact_div;
 
@@ -2017,7 +2020,7 @@  bool
 operator_exact_divide::op1_range (irange &r, tree type,
 				  const irange &lhs,
 				  const irange &op2,
-				  relation_kind rel ATTRIBUTE_UNUSED) const
+				  relation_trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -2043,11 +2046,11 @@  public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
 
   virtual void wi_fold (irange &r, tree type,
 			const wide_int &lh_lb, const wide_int &lh_ub,
@@ -2067,7 +2070,7 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
   virtual void wi_fold (irange &r, tree type,
 			const wide_int &lh_lb,
 			const wide_int &lh_ub,
@@ -2080,7 +2083,7 @@  public:
   virtual bool op1_range (irange &, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual relation_kind lhs_op1_relation (const irange &lhs,
 					   const irange &op1,
 					   const irange &op2,
@@ -2106,7 +2109,7 @@  bool
 operator_lshift::fold_range (irange &r, tree type,
 			     const irange &op1,
 			     const irange &op2,
-			     relation_kind rel) const
+			     relation_trio rel) const
 {
   int_range_max shift_range;
   if (!get_shift_range (shift_range, type, op2))
@@ -2228,7 +2231,7 @@  operator_lshift::op1_range (irange &r,
 			    tree type,
 			    const irange &lhs,
 			    const irange &op2,
-			    relation_kind rel ATTRIBUTE_UNUSED) const
+			    relation_trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -2301,7 +2304,7 @@  operator_rshift::op1_range (irange &r,
 			    tree type,
 			    const irange &lhs,
 			    const irange &op2,
-			    relation_kind rel ATTRIBUTE_UNUSED) const
+			    relation_trio) const
 {
   tree shift;
   if (lhs.undefined_p ())
@@ -2380,7 +2383,7 @@  bool
 operator_rshift::fold_range (irange &r, tree type,
 			     const irange &op1,
 			     const irange &op2,
-			     relation_kind rel) const
+			     relation_trio rel) const
 {
   int_range_max shift;
   if (!get_shift_range (shift, type, op2))
@@ -2412,11 +2415,11 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual relation_kind lhs_op1_relation (const irange &lhs,
 					  const irange &op1,
 					  const irange &op2,
@@ -2528,7 +2531,7 @@  bool
 operator_cast::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			   const irange &inner,
 			   const irange &outer,
-			   relation_kind rel ATTRIBUTE_UNUSED) const
+			   relation_trio) const
 {
   if (empty_range_varying (r, type, inner, outer))
     return true;
@@ -2567,7 +2570,7 @@  bool
 operator_cast::op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel ATTRIBUTE_UNUSED) const
+			  relation_trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -2682,15 +2685,15 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
 			   const irange &rh,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
 } op_logical_and;
 
 
@@ -2698,7 +2701,7 @@  bool
 operator_logical_and::fold_range (irange &r, tree type,
 				  const irange &lh,
 				  const irange &rh,
-				  relation_kind rel ATTRIBUTE_UNUSED) const
+				  relation_trio) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -2721,7 +2724,7 @@  bool
 operator_logical_and::op1_range (irange &r, tree type,
 				 const irange &lhs,
 				 const irange &op2 ATTRIBUTE_UNUSED,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
    switch (get_bool_state (r, lhs, type))
      {
@@ -2743,7 +2746,7 @@  bool
 operator_logical_and::op2_range (irange &r, tree type,
 				 const irange &lhs,
 				 const irange &op1,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   return operator_logical_and::op1_range (r, type, lhs, op1);
 }
@@ -2758,15 +2761,15 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
 			   const irange &rh,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual void wi_fold (irange &r, tree type,
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
@@ -2786,7 +2789,7 @@  bool
 operator_bitwise_and::fold_range (irange &r, tree type,
 				  const irange &lh,
 				  const irange &rh,
-				  relation_kind rel ATTRIBUTE_UNUSED) const
+				  relation_trio) const
 {
   if (range_operator::fold_range (r, type, lh, rh))
     {
@@ -3136,7 +3139,7 @@  bool
 operator_bitwise_and::op1_range (irange &r, tree type,
 				 const irange &lhs,
 				 const irange &op2,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -3171,7 +3174,7 @@  bool
 operator_bitwise_and::op2_range (irange &r, tree type,
 				 const irange &lhs,
 				 const irange &op1,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   return operator_bitwise_and::op1_range (r, type, lhs, op1);
 }
@@ -3186,22 +3189,22 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
 			   const irange &rh,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
 } op_logical_or;
 
 bool
 operator_logical_or::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 				 const irange &lh,
 				 const irange &rh,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -3215,7 +3218,7 @@  bool
 operator_logical_or::op1_range (irange &r, tree type,
 				const irange &lhs,
 				const irange &op2 ATTRIBUTE_UNUSED,
-				relation_kind rel ATTRIBUTE_UNUSED) const
+				relation_trio) const
 {
    switch (get_bool_state (r, lhs, type))
      {
@@ -3237,7 +3240,7 @@  bool
 operator_logical_or::op2_range (irange &r, tree type,
 				const irange &lhs,
 				const irange &op1,
-				relation_kind rel ATTRIBUTE_UNUSED) const
+				relation_trio) const
 {
   return operator_logical_or::op1_range (r, type, lhs, op1);
 }
@@ -3251,11 +3254,11 @@  public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel= VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual void wi_fold (irange &r, tree type,
 		        const wide_int &lh_lb,
 		        const wide_int &lh_ub,
@@ -3323,7 +3326,7 @@  bool
 operator_bitwise_or::op1_range (irange &r, tree type,
 				const irange &lhs,
 				const irange &op2,
-				relation_kind rel ATTRIBUTE_UNUSED) const
+				relation_trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -3345,7 +3348,7 @@  bool
 operator_bitwise_or::op2_range (irange &r, tree type,
 				const irange &lhs,
 				const irange &op1,
-				relation_kind rel ATTRIBUTE_UNUSED) const
+				relation_trio) const
 {
   return operator_bitwise_or::op1_range (r, type, lhs, op1);
 }
@@ -3364,11 +3367,11 @@  public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual bool op1_op2_relation_effect (irange &lhs_range,
 					tree type,
 					const irange &op1_range,
@@ -3454,7 +3457,7 @@  bool
 operator_bitwise_xor::op1_range (irange &r, tree type,
 				 const irange &lhs,
 				 const irange &op2,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   if (lhs.undefined_p () || lhs.varying_p ())
     {
@@ -3489,7 +3492,7 @@  bool
 operator_bitwise_xor::op2_range (irange &r, tree type,
 				 const irange &lhs,
 				 const irange &op1,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   return operator_bitwise_xor::op1_range (r, type, lhs, op1);
 }
@@ -3507,11 +3510,11 @@  public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel ATTRIBUTE_UNUSED) const;
+			  relation_trio) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel ATTRIBUTE_UNUSED) const;
+			  relation_trio) const;
 } op_trunc_mod;
 
 void
@@ -3574,7 +3577,7 @@  bool
 operator_trunc_mod::op1_range (irange &r, tree type,
 			       const irange &lhs,
 			       const irange &,
-			       relation_kind rel ATTRIBUTE_UNUSED) const
+			       relation_trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -3600,7 +3603,7 @@  bool
 operator_trunc_mod::op2_range (irange &r, tree type,
 			       const irange &lhs,
 			       const irange &,
-			       relation_kind rel ATTRIBUTE_UNUSED) const
+			       relation_trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -3644,11 +3647,11 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
 			   const irange &rh,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
 } op_logical_not;
 
 // Folding a logical NOT, oddly enough, involves doing nothing on the
@@ -3669,7 +3672,7 @@  bool
 operator_logical_not::fold_range (irange &r, tree type,
 				  const irange &lh,
 				  const irange &rh ATTRIBUTE_UNUSED,
-				  relation_kind rel ATTRIBUTE_UNUSED) const
+				  relation_trio) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -3686,7 +3689,7 @@  operator_logical_not::op1_range (irange &r,
 				 tree type,
 				 const irange &lhs,
 				 const irange &op2,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   // Logical NOT is involutary...do it again.
   return fold_range (r, type, lhs, op2);
@@ -3701,18 +3704,18 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
 			   const irange &rh,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
 } op_bitwise_not;
 
 bool
 operator_bitwise_not::fold_range (irange &r, tree type,
 				  const irange &lh,
 				  const irange &rh,
-				  relation_kind rel ATTRIBUTE_UNUSED) const
+				  relation_trio) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -3730,7 +3733,7 @@  bool
 operator_bitwise_not::op1_range (irange &r, tree type,
 				 const irange &lhs,
 				 const irange &op2,
-				 relation_kind rel ATTRIBUTE_UNUSED) const
+				 relation_trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -3749,14 +3752,14 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
 } op_integer_cst;
 
 bool
 operator_cst::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			  const irange &lh,
 			  const irange &rh ATTRIBUTE_UNUSED,
-			  relation_kind rel ATTRIBUTE_UNUSED) const
+			  relation_trio) const
 {
   r = lh;
   return true;
@@ -3772,11 +3775,11 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual relation_kind lhs_op1_relation (const irange &lhs,
 					   const irange &op1,
 					   const irange &op2,
@@ -3801,7 +3804,7 @@  bool
 operator_identity::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			       const irange &lh,
 			       const irange &rh ATTRIBUTE_UNUSED,
-			       relation_kind rel ATTRIBUTE_UNUSED) const
+			       relation_trio) const
 {
   r = lh;
   return true;
@@ -3811,7 +3814,7 @@  bool
 operator_identity::op1_range (irange &r, tree type ATTRIBUTE_UNUSED,
 			      const irange &lhs,
 			      const irange &op2 ATTRIBUTE_UNUSED,
-			      relation_kind rel ATTRIBUTE_UNUSED) const
+			      relation_trio) const
 {
   r = lhs;
   return true;
@@ -3825,14 +3828,14 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
 } op_unknown;
 
 bool
 operator_unknown::fold_range (irange &r, tree type,
 			      const irange &lh ATTRIBUTE_UNUSED,
 			      const irange &rh ATTRIBUTE_UNUSED,
-			      relation_kind rel ATTRIBUTE_UNUSED) const
+			      relation_trio) const
 {
   r.set_varying (type);
   return true;
@@ -3851,7 +3854,7 @@  class operator_abs : public range_operator
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel ATTRIBUTE_UNUSED) const;
+			  relation_trio) const;
 } op_abs;
 
 void
@@ -3932,7 +3935,7 @@  bool
 operator_abs::op1_range (irange &r, tree type,
 			 const irange &lhs,
 			 const irange &op2,
-			 relation_kind rel ATTRIBUTE_UNUSED) const
+			 relation_trio) const
 {
   if (empty_range_varying (r, type, lhs, op2))
     return true;
@@ -4013,18 +4016,18 @@  class operator_negate : public range_operator
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
 } op_negate;
 
 bool
 operator_negate::fold_range (irange &r, tree type,
 			     const irange &lh,
 			     const irange &rh,
-			     relation_kind rel ATTRIBUTE_UNUSED) const
+			     relation_trio) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -4037,7 +4040,7 @@  bool
 operator_negate::op1_range (irange &r, tree type,
 			    const irange &lhs,
 			    const irange &op2,
-			    relation_kind rel ATTRIBUTE_UNUSED) const
+			    relation_trio) const
 {
   // NEGATE is involutory.
   return fold_range (r, type, lhs, op2);
@@ -4052,18 +4055,18 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &op1,
 			   const irange &op2,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio rel = TRIO_VARYING) const;
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
 } op_addr;
 
 bool
 operator_addr_expr::fold_range (irange &r, tree type,
 				const irange &lh,
 				const irange &rh,
-				relation_kind rel ATTRIBUTE_UNUSED) const
+				relation_trio) const
 {
   if (empty_range_varying (r, type, lh, rh))
     return true;
@@ -4082,7 +4085,7 @@  bool
 operator_addr_expr::op1_range (irange &r, tree type,
 			       const irange &lhs,
 			       const irange &op2,
-			       relation_kind rel ATTRIBUTE_UNUSED) const
+			       relation_trio) const
 {
   return operator_addr_expr::fold_range (r, type, lhs, op2);
 }
@@ -4204,11 +4207,11 @@  public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio rel = TRIO_VARYING) 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;
@@ -4218,7 +4221,7 @@  bool
 pointer_or_operator::op1_range (irange &r, tree type,
 				const irange &lhs,
 				const irange &op2 ATTRIBUTE_UNUSED,
-				relation_kind rel ATTRIBUTE_UNUSED) const
+				relation_trio) const
 {
   if (lhs.undefined_p ())
     return false;
@@ -4236,7 +4239,7 @@  bool
 pointer_or_operator::op2_range (irange &r, tree type,
 				const irange &lhs,
 				const irange &op1,
-				relation_kind rel ATTRIBUTE_UNUSED) const
+				relation_trio) const
 {
   return pointer_or_operator::op1_range (r, type, lhs, op1);
 }
@@ -4422,7 +4425,7 @@  bool
 range_op_handler::fold_range (vrange &r, tree type,
 			      const vrange &lh,
 			      const vrange &rh,
-			      relation_kind rel) const
+			      relation_trio rel) const
 {
   gcc_checking_assert (m_valid);
   if (m_int)
@@ -4450,7 +4453,7 @@  bool
 range_op_handler::op1_range (vrange &r, tree type,
 			     const vrange &lhs,
 			     const vrange &op2,
-			     relation_kind rel) const
+			     relation_trio rel) const
 {
   gcc_checking_assert (m_valid);
 
@@ -4474,7 +4477,7 @@  bool
 range_op_handler::op2_range (vrange &r, tree type,
 			     const vrange &lhs,
 			     const vrange &op1,
-			     relation_kind rel) const
+			     relation_trio rel) const
 {
   gcc_checking_assert (m_valid);
   if (lhs.undefined_p ())
diff --git a/gcc/range-op.h b/gcc/range-op.h
index 48adcecc7c6..c7249890142 100644
--- a/gcc/range-op.h
+++ b/gcc/range-op.h
@@ -53,7 +53,7 @@  public:
   virtual bool fold_range (irange &r, tree type,
 			   const irange &lh,
 			   const irange &rh,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio = TRIO_VARYING) 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
@@ -69,11 +69,11 @@  public:
   virtual bool op1_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
 			  const irange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
 
   // The following routines are used to represent relations between the
   // various operations.  If the caller knows where the symbolics are,
@@ -116,32 +116,32 @@  public:
   virtual bool fold_range (frange &r, tree type,
 			   const frange &lh,
 			   const frange &rh,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio = TRIO_VARYING) const;
   // Unary operations have the range of the LHS as op2.
   virtual bool fold_range (irange &r, tree type,
 			   const frange &lh,
 			   const irange &rh,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio = TRIO_VARYING) const;
   virtual bool fold_range (irange &r, tree type,
 			   const frange &lh,
 			   const frange &rh,
-			   relation_kind rel = VREL_VARYING) const;
+			   relation_trio = TRIO_VARYING) const;
   virtual bool op1_range (frange &r, tree type,
 			  const frange &lhs,
 			  const frange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual bool op1_range (frange &r, tree type,
 			  const irange &lhs,
 			  const frange &op2,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual bool op2_range (frange &r, tree type,
 			  const frange &lhs,
 			  const frange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
   virtual bool op2_range (frange &r, tree type,
 			  const irange &lhs,
 			  const frange &op1,
-			  relation_kind rel = VREL_VARYING) const;
+			  relation_trio = TRIO_VARYING) const;
 
   virtual relation_kind lhs_op1_relation (const frange &lhs,
 					  const frange &op1,
@@ -173,15 +173,15 @@  public:
   bool fold_range (vrange &r, tree type,
 		   const vrange &lh,
 		   const vrange &rh,
-		   relation_kind rel = VREL_VARYING) const;
+		   relation_trio = TRIO_VARYING) const;
   bool op1_range (vrange &r, tree type,
 		  const vrange &lhs,
 		  const vrange &op2,
-		  relation_kind rel = VREL_VARYING) const;
+		  relation_trio = TRIO_VARYING) const;
   bool op2_range (vrange &r, tree type,
 		  const vrange &lhs,
 		  const vrange &op1,
-		  relation_kind rel = VREL_VARYING) const;
+		  relation_trio = TRIO_VARYING) const;
   relation_kind lhs_op1_relation (const vrange &lhs,
 				  const vrange &op1,
 				  const vrange &op2,
@@ -240,9 +240,10 @@  empty_range_varying (vrange &r, tree type,
 
 inline bool
 relop_early_resolve (irange &r, tree type, const vrange &op1,
-		     const vrange &op2, relation_kind rel,
+		     const vrange &op2, relation_trio trio,
 		     relation_kind my_rel)
 {
+  relation_kind rel = trio.op1_op2 ();
   // If known relation is a complete subset of this relation, always true.
   if (relation_union (rel, my_rel) == my_rel)
     {
diff --git a/gcc/value-relation.cc b/gcc/value-relation.cc
index 3fb7b96c9e0..fed8a78723c 100644
--- a/gcc/value-relation.cc
+++ b/gcc/value-relation.cc
@@ -32,9 +32,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "alloc-pool.h"
 #include "dominance.h"
 
-#define VREL_LAST               VREL_PE64
-
-static const char *kind_string[VREL_LAST + 1] =
+static const char *kind_string[VREL_LAST] =
 { "varying", "undefined", "<", "<=", ">", ">=", "==", "!=", "pe8", "pe16",
   "pe32", "pe64" };
 
@@ -47,7 +45,7 @@  print_relation (FILE *f, relation_kind rel)
 }
 
 // This table is used to negate the operands.  op1 REL op2 -> !(op1 REL op2).
-relation_kind rr_negate_table[VREL_LAST + 1] = {
+relation_kind rr_negate_table[VREL_LAST] = {
   VREL_VARYING, VREL_UNDEFINED, VREL_GE, VREL_GT, VREL_LE, VREL_LT, VREL_NE,
   VREL_EQ };
 
@@ -60,7 +58,7 @@  relation_negate (relation_kind r)
 }
 
 // This table is used to swap the operands.  op1 REL op2 -> op2 REL op1.
-relation_kind rr_swap_table[VREL_LAST + 1] = {
+relation_kind rr_swap_table[VREL_LAST] = {
   VREL_VARYING, VREL_UNDEFINED, VREL_GT, VREL_GE, VREL_LT, VREL_LE, VREL_EQ,
   VREL_NE };
 
@@ -74,7 +72,7 @@  relation_swap (relation_kind r)
 
 // This table is used to perform an intersection between 2 relations.
 
-relation_kind rr_intersect_table[VREL_LAST + 1][VREL_LAST + 1] = {
+relation_kind rr_intersect_table[VREL_LAST][VREL_LAST] = {
 // VREL_VARYING
   { VREL_VARYING, VREL_UNDEFINED, VREL_LT, VREL_LE, VREL_GT, VREL_GE, VREL_EQ,
     VREL_NE },
@@ -112,7 +110,7 @@  relation_intersect (relation_kind r1, relation_kind r2)
 
 // This table is used to perform a union between 2 relations.
 
-relation_kind rr_union_table[VREL_LAST + 1][VREL_LAST + 1] = {
+relation_kind rr_union_table[VREL_LAST][VREL_LAST] = {
 // VREL_VARYING
   { VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING,
     VREL_VARYING, VREL_VARYING, VREL_VARYING },
@@ -150,7 +148,7 @@  relation_union (relation_kind r1, relation_kind r2)
 // This table is used to determine transitivity between 2 relations.
 // (A relation0 B) and (B relation1 C) implies  (A result C)
 
-relation_kind rr_transitive_table[VREL_LAST + 1][VREL_LAST + 1] = {
+relation_kind rr_transitive_table[VREL_LAST][VREL_LAST] = {
 // VREL_VARYING
   { VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING, VREL_VARYING,
     VREL_VARYING, VREL_VARYING, VREL_VARYING },
@@ -187,7 +185,7 @@  relation_transitive (relation_kind r1, relation_kind r2)
 
 // This vector maps a relation to the equivalent tree code.
 
-tree_code relation_to_code [VREL_LAST + 1] = {
+tree_code relation_to_code [VREL_LAST] = {
   ERROR_MARK, ERROR_MARK, LT_EXPR, LE_EXPR, GT_EXPR, GE_EXPR, EQ_EXPR,
   NE_EXPR };
 
@@ -226,7 +224,8 @@  relation_oracle::validate_relation (relation_kind rel, vrange &op1, vrange &op2)
 
   // If the relation cannot be folded for any reason, leave as is.
   Value_Range result (boolean_type_node);
-  if (!handler.fold_range (result, boolean_type_node, op1, op2, rel))
+  if (!handler.fold_range (result, boolean_type_node, op1, op2,
+			   relation_trio::op1_op2 (rel)))
     return rel;
 
   // The expression op1 REL op2 using REL should fold to [1,1].
diff --git a/gcc/value-relation.h b/gcc/value-relation.h
index fa9097a8069..027d7416163 100644
--- a/gcc/value-relation.h
+++ b/gcc/value-relation.h
@@ -35,20 +35,21 @@  along with GCC; see the file COPYING3.  If not see
 // utilizes the relation information to enhance it's range calculations, this
 // is totally transparent to the client, and they are free to make queries.
 //
-//
-// relation_kind is a typedef of enum tree_code, but has restricted range
-// and a couple of extra values.
+// relation_kind is a new enum which represents the different relations,
+// often with a direct mapping to treee codes. ie VREL_EQ is equivalent to
+// EQ_EXPR.
 //
 // A query is made requesting the relation between SSA1 and SSA@ in a basic
 // block, or on an edge, the possible return values are:
 //
-//  EQ_EXPR, NE_EXPR, LT_EXPR, LE_EXPR, GT_EXPR, and GE_EXPR mean the same.
+//  VREL_EQ, VREL_NE, VREL_LT, VREL_LE, VREL_GT, and VREL_GE mean the same.
 //  VREL_VARYING : No relation between the 2 names.
 //  VREL_UNDEFINED : Impossible relation (ie, A < B && A > B)
 //
-// The oracle maintains EQ_EXPR relations with equivalency sets, so if a
-// relation comes back EQ_EXPR, it is also possible to query the set of
-// equivlaencies.  These are basically bitmaps over ssa_names.
+// The oracle maintains VREL_EQ relations with equivalency sets, so if a
+// relation comes back VREL_EQ, it is also possible to query the set of
+// equivlaencies.  These are basically bitmaps over ssa_names.  An iterator is
+// provided later for this activity.
 //
 // Relations are maintained via the dominace trees and are optimized assuming
 // they are registered in dominance order.   When a new relation is added, it
@@ -56,10 +57,8 @@  along with GCC; see the file COPYING3.  If not see
 // and registered at the specified block.
 
 
-// Rather than introduce a new enumerated type for relations, we can use the
-// existing tree_codes for relations, plus add a couple of #defines for
-// the other cases.  These codes are arranged such that VREL_VARYING is the
-// first code, and all the rest are contiguous.
+// These codes are arranged such that VREL_VARYING is the first code, and all
+// the rest are contiguous.
 
 typedef enum relation_kind_t
 {
@@ -74,7 +73,8 @@  typedef enum relation_kind_t
   VREL_PE8,		// 8 bit partial equivalency
   VREL_PE16,		// 16 bit partial equivalency
   VREL_PE32,		// 32 bit partial equivalency
-  VREL_PE64		// 64 bit partial equivalency
+  VREL_PE64,		// 64 bit partial equivalency
+  VREL_LAST		// terminate, not a real relation.
 } relation_kind;
 
 // General relation kind transformations.
@@ -315,6 +315,101 @@  protected:
        ((equiv_name) = iter.get_name (&equiv_rel));			\
        iter.next ())
 
+// -----------------------------------------------------------------------
+
+// Range-ops deals with a LHS and 2 operands. A relation trio is a set of
+// 3 potential relations packed into a single unsigned value.
+//  1 - LHS relation OP1
+//  2 - LHS relation OP2
+//  3 - OP1 relation OP2
+//  VREL_VARYING is a value of 0, and is the default for each position.
+class relation_trio
+{
+public:
+  relation_trio ();
+  relation_trio (relation_kind lhs_op1, relation_kind lhs_op2,
+		 relation_kind op1_op2);
+  relation_kind lhs_op1 ();
+  relation_kind lhs_op2 ();
+  relation_kind op1_op2 ();
+  relation_trio swap_op1_op2 ();
+
+  static relation_trio lhs_op1 (relation_kind k);
+  static relation_trio lhs_op2 (relation_kind k);
+  static relation_trio op1_op2 (relation_kind k);
+
+protected:
+  unsigned m_val;
+};
+
+//  Default VREL_VARYING for all 3 relations.
+#define TRIO_VARYING	relation_trio ()
+
+#define TRIO_SHIFT	4
+#define TRIO_MASK	0x000F
+
+// These 3 classes are shortcuts for when a caller has a single relation to
+// pass as a trio, it can simply construct the appropriate one.  The other
+// unspecified realtions will be VREL_VARYING.
+
+inline relation_trio::relation_trio ()
+{
+  STATIC_ASSERT (VREL_LAST <= (1 << TRIO_SHIFT));
+  m_val = 0;
+}
+
+inline relation_trio::relation_trio (relation_kind lhs_op1,
+				     relation_kind lhs_op2,
+				     relation_kind op1_op2)
+{
+  STATIC_ASSERT (VREL_LAST <= (1 << TRIO_SHIFT));
+  unsigned i1 = (unsigned) lhs_op1;
+  unsigned i2 = ((unsigned) lhs_op2) << TRIO_SHIFT;
+  unsigned i3 = ((unsigned) op1_op2) << (TRIO_SHIFT * 2);
+  m_val = i1 | i2 | i3;
+}
+
+inline relation_trio
+relation_trio::lhs_op1 (relation_kind k)
+{
+  return relation_trio (k, VREL_VARYING, VREL_VARYING);
+}
+inline relation_trio
+relation_trio::lhs_op2 (relation_kind k)
+{
+  return relation_trio (VREL_VARYING, k, VREL_VARYING);
+}
+inline relation_trio
+relation_trio::op1_op2 (relation_kind k)
+{
+  return relation_trio (VREL_VARYING, VREL_VARYING, k);
+}
+
+inline relation_kind
+relation_trio::lhs_op1 ()
+{
+  return (relation_kind) (m_val & TRIO_MASK);
+}
+
+inline relation_kind
+relation_trio::lhs_op2 ()
+{
+  return (relation_kind) ((m_val >> TRIO_SHIFT) & TRIO_MASK);
+}
+
+inline relation_kind
+relation_trio::op1_op2 ()
+{
+  return (relation_kind) ((m_val >> (TRIO_SHIFT * 2)) & TRIO_MASK);
+}
+
+inline relation_trio
+relation_trio::swap_op1_op2 ()
+{
+  return relation_trio (lhs_op2 (), lhs_op1 (), relation_swap (op1_op2 ()));
+}
+
+// -----------------------------------------------------------------------
 
 // The value-relation class is used to encapsulate the represention of an
 // individual relation between 2 ssa-names, and to facilitate operating on
-- 
2.37.3