[COMMITTED,4/4] Provide a new dispatch mechanism for range-ops.

Message ID b191c018-04be-dbd8-0e68-b96d9fcc3889@redhat.com
State Committed
Headers
Series [COMMITTED,1/4] Fix floating point bug in fold_range. |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 fail Patch failed to apply

Commit Message

Andrew MacLeod June 8, 2023, 6:58 p.m. UTC
  This patch introduces a new dispatch mechanism for range_op_handler.

Instead of ad-hoc if then elses based on is_a<irange*> and is_a,frange 
*>, the discriminators in class vrange are used for each operand to 
create a triplet, ie (III for "LHS = Irange, op1 = Irange, op2 = 
Irange", and IFI for "Irange Frange Irange")

These triplets are then used ina  switch to dispatch the call to the 
approriate one in range_operator for those types.  And added bonus is 
that if there is a pattern that is not recognized, we no longer trap.. 
Tthe dispatch routine simply returns the same thing as a default routine 
does in range_operator.. either false or VREL_VARYING depending on the 
signature.

This will make adding additional range types much simplier going 
forward, and aleviates the need to check for supported types before 
invoking routines like fold_range.

As part fo the rework, this patch also simplifies range_op_handler to 
now only contain a single pointer to a range_operator instead of 2 
pointers and a flag.   This is enabled by the previous patch which 
unifies all range operators to one class.

And added bonus is a bit of a compile time improvement for VRP and 
threading, as well as other clients due to less conditional checks at 
dispatch time. It only amounts to about a 0.75% improvement in those 
passes for the moment... but every little bit helps.

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

Andrew
  

Patch

From f36f25792b3cb0b9067f318dd4d5c968f75a5c3d Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacleod@redhat.com>
Date: Wed, 31 May 2023 13:10:31 -0400
Subject: [PATCH 4/4] Provide a new dispatch mechanism for range-ops.

Simplify range_op_handler to have a single range_operator pointer and
provide a more flexible dispatch mechanism for calls via generic vrange
classes.   This is more extensible for adding new classes of range support.
Any unsupported dispatch patterns will simply return FALSE now rather
than generating compile time exceptions, aleviating the need to
constantly check for supoprted types.

	* gimple-range-op.cc
	(gimple_range_op_handler::gimple_range_op_handler): Adjust.
	(gimple_range_op_handler::maybe_builtin_call): Adjust.
	* gimple-range-op.h (operand1, operand2): Use m_operator.
	* range-op.cc (integral_table, pointer_table): Relocate.
	(get_op_handler): Rename from get_handler and handle all types.
	(range_op_handler::range_op_handler): Relocate.
	(range_op_handler::set_op_handler): Relocate and adjust.
	(range_op_handler::range_op_handler): Relocate.
	(dispatch_trio): New.
	(RO_III, RO_IFI, RO_IFF, RO_FFF, RO_FIF, RO_FII): New consts.
	(range_op_handler::dispatch_kind): New.
	(range_op_handler::fold_range): Relocate and Use new dispatch value.
	(range_op_handler::op1_range): Ditto.
	(range_op_handler::op2_range): Ditto.
	(range_op_handler::lhs_op1_relation): Ditto.
	(range_op_handler::lhs_op2_relation): Ditto.
	(range_op_handler::op1_op2_relation): Ditto.
	(range_op_handler::set_op_handler): Use m_operator member.
	* range-op.h (range_op_handler::operator bool): Use m_operator.
	(range_op_handler::dispatch_kind): New.
	(range_op_handler::m_valid): Delete.
	(range_op_handler::m_int): Delete
	(range_op_handler::m_float): Delete
	(range_op_handler::m_operator): New.
	(range_op_table::operator[]): Relocate from .cc file.
	(range_op_table::set): Ditto.
	* value-range.h (class vrange): Make range_op_handler a friend.
---
 gcc/gimple-range-op.cc |  84 +++-----
 gcc/gimple-range-op.h  |   4 +-
 gcc/range-op.cc        | 470 ++++++++++++++++++++++-------------------
 gcc/range-op.h         |  27 ++-
 gcc/value-range.h      |   1 +
 5 files changed, 306 insertions(+), 280 deletions(-)

diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc
index 293d76402e1..b6b10e47b78 100644
--- a/gcc/gimple-range-op.cc
+++ b/gcc/gimple-range-op.cc
@@ -144,7 +144,7 @@  gimple_range_op_handler::gimple_range_op_handler (gimple *s)
   if (type)
     set_op_handler (code, type);
 
-  if (m_valid)
+  if (m_operator)
     switch (gimple_code (m_stmt))
       {
 	case GIMPLE_COND:
@@ -152,7 +152,7 @@  gimple_range_op_handler::gimple_range_op_handler (gimple *s)
 	  m_op2 = gimple_cond_rhs (m_stmt);
 	  // Check that operands are supported types.  One check is enough.
 	  if (!Value_Range::supports_type_p (TREE_TYPE (m_op1)))
-	    m_valid = false;
+	    m_operator = NULL;
 	  return;
 	case GIMPLE_ASSIGN:
 	  m_op1 = gimple_range_base_of_assignment (m_stmt);
@@ -171,7 +171,7 @@  gimple_range_op_handler::gimple_range_op_handler (gimple *s)
 	    m_op2 = gimple_assign_rhs2 (m_stmt);
 	  // Check that operands are supported types.  One check is enough.
 	  if ((m_op1 && !Value_Range::supports_type_p (TREE_TYPE (m_op1))))
-	    m_valid = false;
+	    m_operator = NULL;
 	  return;
 	default:
 	  gcc_unreachable ();
@@ -1193,7 +1193,6 @@  gimple_range_op_handler::maybe_non_standard ()
       {
 	case WIDEN_MULT_EXPR:
 	{
-	  m_valid = false;
 	  m_op1 = gimple_assign_rhs1 (m_stmt);
 	  m_op2 = gimple_assign_rhs2 (m_stmt);
 	  tree ret = gimple_assign_lhs (m_stmt);
@@ -1210,14 +1209,13 @@  gimple_range_op_handler::maybe_non_standard ()
 	  if ((signed1 ^ signed2) && signed_ret)
 	    return;
 
-	  m_valid = true;
 	  if (signed2 && !signed1)
 	    std::swap (m_op1, m_op2);
 
 	  if (signed1 || signed2)
-	    m_int = signed_op;
+	    m_operator = signed_op;
 	  else
-	    m_int = unsigned_op;
+	    m_operator = unsigned_op;
 	  break;
 	}
 	default:
@@ -1246,47 +1244,41 @@  gimple_range_op_handler::maybe_builtin_call ()
     {
     case CFN_BUILT_IN_CONSTANT_P:
       m_op1 = gimple_call_arg (call, 0);
-      m_valid = true;
       if (irange::supports_p (TREE_TYPE (m_op1)))
-	m_int = &op_cfn_constant_p;
+	m_operator = &op_cfn_constant_p;
       else if (frange::supports_p (TREE_TYPE (m_op1)))
-	m_float = &op_cfn_constant_float_p;
+	m_operator = &op_cfn_constant_float_p;
       else
-	m_valid = false;
+	m_operator = NULL;
       break;
 
     CASE_FLT_FN (CFN_BUILT_IN_SIGNBIT):
       m_op1 = gimple_call_arg (call, 0);
-      m_float = &op_cfn_signbit;
-      m_valid = true;
+      m_operator = &op_cfn_signbit;
       break;
 
     CASE_CFN_COPYSIGN_ALL:
       m_op1 = gimple_call_arg (call, 0);
       m_op2 = gimple_call_arg (call, 1);
-      m_float = &op_cfn_copysign;
-      m_valid = true;
+      m_operator = &op_cfn_copysign;
       break;
 
     CASE_CFN_SQRT:
     CASE_CFN_SQRT_FN:
       m_op1 = gimple_call_arg (call, 0);
-      m_float = &op_cfn_sqrt;
-      m_valid = true;
+      m_operator = &op_cfn_sqrt;
       break;
 
     CASE_CFN_SIN:
     CASE_CFN_SIN_FN:
       m_op1 = gimple_call_arg (call, 0);
-      m_float = &op_cfn_sin;
-      m_valid = true;
+      m_operator = &op_cfn_sin;
       break;
 
     CASE_CFN_COS:
     CASE_CFN_COS_FN:
       m_op1 = gimple_call_arg (call, 0);
-      m_float = &op_cfn_cos;
-      m_valid = true;
+      m_operator = &op_cfn_cos;
       break;
 
     case CFN_BUILT_IN_TOUPPER:
@@ -1294,68 +1286,57 @@  gimple_range_op_handler::maybe_builtin_call ()
       // Only proceed If the argument is compatible with the LHS.
       m_op1 = gimple_call_arg (call, 0);
       if (range_compatible_p (type, TREE_TYPE (m_op1)))
-	{
-	  m_valid = true;
-	  m_int = (func == CFN_BUILT_IN_TOLOWER) ? &op_cfn_tolower
-						 : &op_cfn_toupper;
-	}
+	m_operator = (func == CFN_BUILT_IN_TOLOWER) ? &op_cfn_tolower
+						    : &op_cfn_toupper;
       break;
 
     CASE_CFN_FFS:
       m_op1 = gimple_call_arg (call, 0);
-      m_int = &op_cfn_ffs;
-      m_valid = true;
+      m_operator = &op_cfn_ffs;
       break;
 
     CASE_CFN_POPCOUNT:
       m_op1 = gimple_call_arg (call, 0);
-      m_int = &op_cfn_popcount;
-      m_valid = true;
+      m_operator = &op_cfn_popcount;
       break;
 
     CASE_CFN_CLZ:
       m_op1 = gimple_call_arg (call, 0);
-      m_valid = true;
       if (gimple_call_internal_p (call))
-	m_int = &op_cfn_clz_internal;
+	m_operator = &op_cfn_clz_internal;
       else
-	m_int = &op_cfn_clz;
+	m_operator = &op_cfn_clz;
       break;
 
     CASE_CFN_CTZ:
       m_op1 = gimple_call_arg (call, 0);
-      m_valid = true;
       if (gimple_call_internal_p (call))
-	m_int = &op_cfn_ctz_internal;
+	m_operator = &op_cfn_ctz_internal;
       else
-	m_int = &op_cfn_ctz;
+	m_operator = &op_cfn_ctz;
       break;
 
     CASE_CFN_CLRSB:
       m_op1 = gimple_call_arg (call, 0);
-      m_valid = true;
-      m_int = &op_cfn_clrsb;
+      m_operator = &op_cfn_clrsb;
       break;
 
     case CFN_UBSAN_CHECK_ADD:
       m_op1 = gimple_call_arg (call, 0);
       m_op2 = gimple_call_arg (call, 1);
-      m_valid = true;
-      m_int = &op_cfn_ubsan_add;
+      m_operator = &op_cfn_ubsan_add;
       break;
 
     case CFN_UBSAN_CHECK_SUB:
       m_op1 = gimple_call_arg (call, 0);
       m_op2 = gimple_call_arg (call, 1);
-      m_valid = true;
-      m_int = &op_cfn_ubsan_sub;
+      m_operator = &op_cfn_ubsan_sub;
       break;
 
     case CFN_UBSAN_CHECK_MUL:
       m_op1 = gimple_call_arg (call, 0);
       m_op2 = gimple_call_arg (call, 1);
-      m_valid = true;
-      m_int = &op_cfn_ubsan_mul;
+      m_operator = &op_cfn_ubsan_mul;
       break;
 
     case CFN_BUILT_IN_STRLEN:
@@ -1365,8 +1346,7 @@  gimple_range_op_handler::maybe_builtin_call ()
 					 == TYPE_PRECISION (TREE_TYPE (lhs))))
 	  {
 	    m_op1 = gimple_call_arg (call, 0);
-	    m_valid = true;
-	    m_int = &op_cfn_strlen;
+	    m_operator = &op_cfn_strlen;
 	  }
 	break;
       }
@@ -1378,21 +1358,18 @@  gimple_range_op_handler::maybe_builtin_call ()
       // This call will ensure all the asserts are triggered.
       oacc_get_ifn_dim_arg (call);
       m_op1 = gimple_call_arg (call, 0);
-      m_valid = true;
-      m_int = &op_cfn_goacc_dim_size;
+      m_operator = &op_cfn_goacc_dim_size;
       break;
 
     case CFN_GOACC_DIM_POS:
       // This call will ensure all the asserts are triggered.
       oacc_get_ifn_dim_arg (call);
       m_op1 = gimple_call_arg (call, 0);
-      m_valid = true;
-      m_int = &op_cfn_goacc_dim_pos;
+      m_operator = &op_cfn_goacc_dim_pos;
       break;
 
     CASE_CFN_PARITY:
-      m_valid = true;
-      m_int = &op_cfn_parity;
+      m_operator = &op_cfn_parity;
       break;
 
     default:
@@ -1400,9 +1377,8 @@  gimple_range_op_handler::maybe_builtin_call ()
 	unsigned arg;
 	if (gimple_call_fnspec (call).returns_arg (&arg) && arg == 0)
 	  {
-	    m_valid = true;
 	    m_op1 = gimple_call_arg (call, 0);
-	    m_int = &op_cfn_pass_through_arg1;
+	    m_operator = &op_cfn_pass_through_arg1;
 	  }
 	break;
       }
diff --git a/gcc/gimple-range-op.h b/gcc/gimple-range-op.h
index 1bf63c5ce6f..e7bb0095440 100644
--- a/gcc/gimple-range-op.h
+++ b/gcc/gimple-range-op.h
@@ -32,8 +32,8 @@  public:
   gimple_range_op_handler (gimple *s);
   inline gimple *stmt () const { return m_stmt; }
   inline tree lhs () const { return gimple_get_lhs (m_stmt); }
-  tree operand1 () const { gcc_checking_assert (m_valid); return m_op1; }
-  tree operand2 () const { gcc_checking_assert (m_valid); return m_op2; }
+  tree operand1 () const { gcc_checking_assert (m_operator); return m_op1; }
+  tree operand2 () const { gcc_checking_assert (m_operator); 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_trio = TRIO_VARYING);
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index 203c30f6e94..4d122de3026 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -48,6 +48,256 @@  along with GCC; see the file COPYING3.  If not see
 #include "range-op.h"
 #include "tree-ssa-ccp.h"
 
+// Instantiate a range op table for integral operations.
+
+class integral_table : public range_op_table
+{
+public:
+  integral_table ();
+} integral_tree_table;
+
+// Instantiate a range op table for pointer operations.
+
+class pointer_table : public range_op_table
+{
+public:
+  pointer_table ();
+} pointer_tree_table;
+
+
+// The tables are hidden and accessed via a simple extern function.
+
+range_operator *
+get_op_handler (enum tree_code code, tree type)
+{
+  // First check if there is a pointer specialization.
+  if (POINTER_TYPE_P (type))
+    return pointer_tree_table[code];
+  if (INTEGRAL_TYPE_P (type))
+    return integral_tree_table[code];
+  if (frange::supports_p (type))
+    return (*floating_tree_table)[code];
+  return NULL;
+}
+
+range_op_handler::range_op_handler ()
+{
+  m_operator = NULL;
+}
+
+void
+range_op_handler::set_op_handler (tree_code code, tree type)
+{
+  m_operator = get_op_handler (code, type);
+}
+
+range_op_handler::range_op_handler (tree_code code, tree type)
+{
+  set_op_handler (code, type);
+}
+
+// Create a dispatch pattern for value range discriminators LHS, OP1, and OP2.
+// This is used to produce a unique value for each dispatch pattern.  Shift
+// values are based on the size of the m_discriminator field in value_range.h.
+
+constexpr unsigned
+dispatch_trio (unsigned lhs, unsigned op1, unsigned op2)
+{
+  return ((lhs << 8) + (op1 << 4) + (op2));
+}
+
+// These are the supported dispatch patterns. These map to the parameter list
+// of the routines in range_operator.  Note the last 3 characters are
+// shorthand for the LHS, OP1, and OP2 range discriminator class.
+
+const unsigned RO_III =	dispatch_trio (VR_IRANGE, VR_IRANGE, VR_IRANGE);
+const unsigned RO_IFI = dispatch_trio (VR_IRANGE, VR_FRANGE, VR_IRANGE);
+const unsigned RO_IFF = dispatch_trio (VR_IRANGE, VR_FRANGE, VR_FRANGE);
+const unsigned RO_FFF = dispatch_trio (VR_FRANGE, VR_FRANGE, VR_FRANGE);
+const unsigned RO_FIF = dispatch_trio (VR_FRANGE, VR_IRANGE, VR_FRANGE);
+const unsigned RO_FII = dispatch_trio (VR_FRANGE, VR_IRANGE, VR_IRANGE);
+
+// Return a dispatch value for parameter types LHS, OP1 and OP2.
+
+unsigned
+range_op_handler::dispatch_kind (const vrange &lhs, const vrange &op1,
+				 const vrange& op2) const
+{
+  return dispatch_trio (lhs.m_discriminator, op1.m_discriminator,
+			op2.m_discriminator);
+}
+
+// Dispatch a call to fold_range based on the types of R, LH and RH.
+
+bool
+range_op_handler::fold_range (vrange &r, tree type,
+			      const vrange &lh,
+			      const vrange &rh,
+			      relation_trio rel) const
+{
+  gcc_checking_assert (m_operator);
+  switch (dispatch_kind (r, lh, rh))
+    {
+      case RO_III:
+	return m_operator->fold_range (as_a <irange> (r), type,
+				       as_a <irange> (lh),
+				       as_a <irange> (rh), rel);
+      case RO_IFI:
+	return m_operator->fold_range (as_a <irange> (r), type,
+				       as_a <frange> (lh),
+				       as_a <irange> (rh), rel);
+      case RO_IFF:
+	return m_operator->fold_range (as_a <irange> (r), type,
+				       as_a <frange> (lh),
+				       as_a <frange> (rh), rel);
+      case RO_FFF:
+	return m_operator->fold_range (as_a <frange> (r), type,
+				       as_a <frange> (lh),
+				       as_a <frange> (rh), rel);
+      default:
+	return false;
+    }
+}
+
+// Dispatch a call to op1_range based on the types of R, LHS and OP2.
+
+bool
+range_op_handler::op1_range (vrange &r, tree type,
+			     const vrange &lhs,
+			     const vrange &op2,
+			     relation_trio rel) const
+{
+  gcc_checking_assert (m_operator);
+
+  if (lhs.undefined_p ())
+    return false;
+  switch (dispatch_kind (r, lhs, op2))
+    {
+      case RO_III:
+	return m_operator->op1_range (as_a <irange> (r), type,
+				      as_a <irange> (lhs),
+				      as_a <irange> (op2), rel);
+      case RO_FIF:
+	return m_operator->op1_range (as_a <frange> (r), type,
+				      as_a <irange> (lhs),
+				      as_a <frange> (op2), rel);
+      case RO_FFF:
+	return m_operator->op1_range (as_a <frange> (r), type,
+				      as_a <frange> (lhs),
+				      as_a <frange> (op2), rel);
+      default:
+	return false;
+    }
+}
+
+// Dispatch a call to op2_range based on the types of R, LHS and OP1.
+
+bool
+range_op_handler::op2_range (vrange &r, tree type,
+			     const vrange &lhs,
+			     const vrange &op1,
+			     relation_trio rel) const
+{
+  gcc_checking_assert (m_operator);
+  if (lhs.undefined_p ())
+    return false;
+
+  switch (dispatch_kind (r, lhs, op1))
+    {
+      case RO_III:
+	return m_operator->op2_range (as_a <irange> (r), type,
+				      as_a <irange> (lhs),
+				      as_a <irange> (op1), rel);
+      case RO_FIF:
+	return m_operator->op2_range (as_a <frange> (r), type,
+				      as_a <irange> (lhs),
+				      as_a <frange> (op1), rel);
+      case RO_FFF:
+	return m_operator->op2_range (as_a <frange> (r), type,
+				      as_a <frange> (lhs),
+				      as_a <frange> (op1), rel);
+      default:
+	return false;
+    }
+}
+
+// Dispatch a call to lhs_op1_relation based on the types of LHS, OP1 and OP2.
+
+relation_kind
+range_op_handler::lhs_op1_relation (const vrange &lhs,
+				    const vrange &op1,
+				    const vrange &op2,
+				    relation_kind rel) const
+{
+  gcc_checking_assert (m_operator);
+
+  switch (dispatch_kind (lhs, op1, op2))
+    {
+      case RO_III:
+	return m_operator->lhs_op1_relation (as_a <irange> (lhs),
+					     as_a <irange> (op1),
+					     as_a <irange> (op2), rel);
+      case RO_IFF:
+	return m_operator->lhs_op1_relation (as_a <irange> (lhs),
+					     as_a <frange> (op1),
+					     as_a <frange> (op2), rel);
+      case RO_FFF:
+	return m_operator->lhs_op1_relation (as_a <frange> (lhs),
+					     as_a <frange> (op1),
+					     as_a <frange> (op2), rel);
+      default:
+	return VREL_VARYING;
+    }
+}
+
+// Dispatch a call to lhs_op2_relation based on the types of LHS, OP1 and OP2.
+
+relation_kind
+range_op_handler::lhs_op2_relation (const vrange &lhs,
+				    const vrange &op1,
+				    const vrange &op2,
+				    relation_kind rel) const
+{
+  gcc_checking_assert (m_operator);
+  switch (dispatch_kind (lhs, op1, op2))
+    {
+      case RO_III:
+	return m_operator->lhs_op2_relation (as_a <irange> (lhs),
+					     as_a <irange> (op1),
+					     as_a <irange> (op2), rel);
+      case RO_IFF:
+	return m_operator->lhs_op2_relation (as_a <irange> (lhs),
+					     as_a <frange> (op1),
+					     as_a <frange> (op2), rel);
+      case RO_FFF:
+	return m_operator->lhs_op2_relation (as_a <frange> (lhs),
+					     as_a <frange> (op1),
+					     as_a <frange> (op2), rel);
+      default:
+	return VREL_VARYING;
+    }
+}
+
+// Dispatch a call to op1_op2_relation based on the type of LHS.
+
+relation_kind
+range_op_handler::op1_op2_relation (const vrange &lhs) const
+{
+  gcc_checking_assert (m_operator);
+  switch (dispatch_kind (lhs, lhs, lhs))
+    {
+      case RO_III:
+	return m_operator->op1_op2_relation (as_a <irange> (lhs));
+
+      case RO_FFF:
+	return m_operator->op1_op2_relation (as_a <frange> (lhs));
+
+      default:
+	return VREL_VARYING;
+    }
+}
+
+
 // Convert irange bitmasks into a VALUE MASK pair suitable for calling CCP.
 
 static void
@@ -4612,33 +4862,6 @@  pointer_or_operator::wi_fold (irange &r, tree type,
     r.set_varying (type);
 }
 
-// Return a pointer to the range_operator instance, if there is one
-// associated with tree_code CODE.
-
-range_operator *
-range_op_table::operator[] (enum tree_code code)
-{
-  gcc_checking_assert (code > 0 && code < MAX_TREE_CODES);
-  return m_range_tree[code];
-}
-
-// Add OP to the handler table for CODE.
-
-void
-range_op_table::set (enum tree_code code, range_operator &op)
-{
-  gcc_checking_assert (m_range_tree[code] == NULL);
-  m_range_tree[code] = &op;
-}
-
-// Instantiate a range op table for integral operations.
-
-class integral_table : public range_op_table
-{
-public:
-  integral_table ();
-} integral_tree_table;
-
 integral_table::integral_table ()
 {
   set (EQ_EXPR, op_equal);
@@ -4682,14 +4905,6 @@  integral_table::integral_table ()
   set (ADDR_EXPR, op_addr);
 }
 
-// Instantiate a range op table for pointer operations.
-
-class pointer_table : public range_op_table
-{
-public:
-  pointer_table ();
-} pointer_tree_table;
-
 pointer_table::pointer_table ()
 {
   set (BIT_AND_EXPR, op_pointer_and);
@@ -4714,191 +4929,6 @@  pointer_table::pointer_table ()
   set (BIT_XOR_EXPR, op_bitwise_xor);
 }
 
-// The tables are hidden and accessed via a simple extern function.
-
-static inline range_operator *
-get_handler (enum tree_code code, tree type)
-{
-  // First check if there is a pointer specialization.
-  if (POINTER_TYPE_P (type))
-    return pointer_tree_table[code];
-  if (INTEGRAL_TYPE_P (type))
-    return integral_tree_table[code];
-  return NULL;
-}
-
-// Return the floating point operator for CODE or NULL if none available.
-
-static inline range_operator *
-get_float_handler (enum tree_code code, tree)
-{
-  return (*floating_tree_table)[code];
-}
-
-void
-range_op_handler::set_op_handler (tree_code code, tree type)
-{
-  if (irange::supports_p (type))
-    {
-      m_float = NULL;
-      m_int = get_handler (code, type);
-      m_valid = m_int != NULL;
-    }
-  else if (frange::supports_p (type))
-    {
-      m_int = NULL;
-      m_float = get_float_handler (code, type);
-      m_valid = m_float != NULL;
-    }
-  else
-    {
-      m_int = NULL;
-      m_float = NULL;
-      m_valid = false;
-    }
-}
-
-range_op_handler::range_op_handler ()
-{
-  m_int = NULL;
-  m_float = NULL;
-  m_valid = false;
-}
-
-range_op_handler::range_op_handler (tree_code code, tree type)
-{
-  set_op_handler (code, type);
-}
-
-
-bool
-range_op_handler::fold_range (vrange &r, tree type,
-			      const vrange &lh,
-			      const vrange &rh,
-			      relation_trio rel) const
-{
-  gcc_checking_assert (m_valid);
-  if (m_int)
-    return m_int->fold_range (as_a <irange> (r), type,
-			   as_a <irange> (lh),
-			   as_a <irange> (rh), rel);
-
-  if (is_a <irange> (r))
-    {
-      if (is_a <irange> (rh))
-	return m_float->fold_range (as_a <irange> (r), type,
-				    as_a <frange> (lh),
-				    as_a <irange> (rh), rel);
-      else
-	return m_float->fold_range (as_a <irange> (r), type,
-				    as_a <frange> (lh),
-				    as_a <frange> (rh), rel);
-    }
-  return m_float->fold_range (as_a <frange> (r), type,
-			      as_a <frange> (lh),
-			      as_a <frange> (rh), rel);
-}
-
-bool
-range_op_handler::op1_range (vrange &r, tree type,
-			     const vrange &lhs,
-			     const vrange &op2,
-			     relation_trio rel) const
-{
-  gcc_checking_assert (m_valid);
-
-  if (lhs.undefined_p ())
-    return false;
-  if (m_int)
-    return m_int->op1_range (as_a <irange> (r), type,
-			     as_a <irange> (lhs),
-			     as_a <irange> (op2), rel);
-
-  if (is_a <irange> (lhs))
-    return m_float->op1_range (as_a <frange> (r), type,
-			       as_a <irange> (lhs),
-			       as_a <frange> (op2), rel);
-  return m_float->op1_range (as_a <frange> (r), type,
-			     as_a <frange> (lhs),
-			     as_a <frange> (op2), rel);
-}
-
-bool
-range_op_handler::op2_range (vrange &r, tree type,
-			     const vrange &lhs,
-			     const vrange &op1,
-			     relation_trio rel) const
-{
-  gcc_checking_assert (m_valid);
-  if (lhs.undefined_p ())
-    return false;
-  if (m_int)
-    return m_int->op2_range (as_a <irange> (r), type,
-			     as_a <irange> (lhs),
-			     as_a <irange> (op1), rel);
-
-  if (is_a <irange> (lhs))
-    return m_float->op2_range (as_a <frange> (r), type,
-			       as_a <irange> (lhs),
-			       as_a <frange> (op1), rel);
-  return m_float->op2_range (as_a <frange> (r), type,
-			     as_a <frange> (lhs),
-			     as_a <frange> (op1), rel);
-}
-
-relation_kind
-range_op_handler::lhs_op1_relation (const vrange &lhs,
-				    const vrange &op1,
-				    const vrange &op2,
-				    relation_kind rel) const
-{
-  gcc_checking_assert (m_valid);
-  if (m_int)
-    return m_int->lhs_op1_relation (as_a <irange> (lhs),
-				    as_a <irange> (op1),
-				    as_a <irange> (op2), rel);
-
-  if (is_a <irange> (lhs))
-    return m_float->lhs_op1_relation (as_a <irange> (lhs),
-				 as_a <frange> (op1),
-				 as_a <frange> (op2), rel);
-  return m_float->lhs_op1_relation (as_a <frange> (lhs),
-			       as_a <frange> (op1),
-			       as_a <frange> (op2), rel);
-}
-
-relation_kind
-range_op_handler::lhs_op2_relation (const vrange &lhs,
-				    const vrange &op1,
-				    const vrange &op2,
-				    relation_kind rel) const
-{
-  gcc_checking_assert (m_valid);
-  if (m_int)
-    return m_int->lhs_op2_relation (as_a <irange> (lhs),
-				    as_a <irange> (op1),
-				    as_a <irange> (op2), rel);
-
-  if (is_a <irange> (lhs))
-    return m_float->lhs_op2_relation (as_a <irange> (lhs),
-				      as_a <frange> (op1),
-				      as_a <frange> (op2), rel);
-  return m_float->lhs_op2_relation (as_a <frange> (lhs),
-				      as_a <frange> (op1),
-				      as_a <frange> (op2), rel);
-}
-
-relation_kind
-range_op_handler::op1_op2_relation (const vrange &lhs) const
-{
-  gcc_checking_assert (m_valid);
-  if (m_int)
-    return m_int->op1_op2_relation (as_a <irange> (lhs));
-  if (is_a <irange> (lhs))
-    return m_float->op1_op2_relation (as_a <irange> (lhs));
-  return m_float->op1_op2_relation (as_a <frange> (lhs));
-}
-
 // Cast the range in R to TYPE.
 
 bool
diff --git a/gcc/range-op.h b/gcc/range-op.h
index cad16f4cd20..7af58736c3f 100644
--- a/gcc/range-op.h
+++ b/gcc/range-op.h
@@ -186,7 +186,7 @@  class range_op_handler
 public:
   range_op_handler ();
   range_op_handler (enum tree_code code, tree type);
-  inline operator bool () const { return m_valid; }
+  inline operator bool () const { return m_operator != NULL; }
 
   bool fold_range (vrange &r, tree type,
 		   const vrange &lh,
@@ -210,10 +210,10 @@  public:
 				  relation_kind = VREL_VARYING) const;
   relation_kind op1_op2_relation (const vrange &lhs) const;
 protected:
+  unsigned dispatch_kind (const vrange &lhs, const vrange &op1,
+			  const vrange& op2) const;
   void set_op_handler (enum tree_code code, tree type);
-  bool m_valid;
-  range_operator *m_int;
-  range_operator *m_float;
+  range_operator *m_operator;
 };
 
 extern bool range_cast (vrange &, tree type);
@@ -296,6 +296,25 @@  private:
 };
 
 
+// Return a pointer to the range_operator instance, if there is one
+// associated with tree_code CODE.
+
+inline range_operator *
+range_op_table::operator[] (enum tree_code code)
+{
+  gcc_checking_assert (code > 0 && code < MAX_TREE_CODES);
+  return m_range_tree[code];
+}
+
+// Add OP to the handler table for CODE.
+
+inline void
+range_op_table::set (enum tree_code code, range_operator &op)
+{
+  gcc_checking_assert (m_range_tree[code] == NULL);
+  m_range_tree[code] = &op;
+}
+
 // This holds the range op table for floating point operations.
 extern range_op_table *floating_tree_table;
 
diff --git a/gcc/value-range.h b/gcc/value-range.h
index 2b4ebabe7c8..9103e9c41c7 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -77,6 +77,7 @@  class GTY((user)) vrange
   template <typename T> friend bool is_a (vrange &);
   friend class Value_Range;
   friend void streamer_write_vrange (struct output_block *, const vrange &);
+  friend class range_op_handler;
 public:
   virtual void accept (const class vrange_visitor &v) const = 0;
   virtual void set (tree, tree, value_range_kind = VR_RANGE);
-- 
2.40.1