PR 120231 highlights that we’ve never implemented range operators for casting between integers and floating-point types — in either direction. This patch fills in the missing dispatch infrastructure so that casts between int and float types can eventually be handled cleanly via range_operator.

These cast operations previously lacked support because the necessary dispatch patterns were unused by any existing range_operator. This patch adds those missing dispatch cases to the system and provides default stub implementations. It also serves as a reference example for anyone needing to implement similar handlers in the future.

Background on Dispatch System
===========================

If you need a primer on range-operators themselves: https://gcc.gnu.org/wiki/AndrewMacLeod/RangeOperator

Specifically what this patch involves is the dispatch system, documented by range_op_handlers at : https://gcc.gnu.org/wiki/AndrewMacLeod/RangeOpHandler

The range_operator framework uses a 3-letter code system to route operations to the correct fold_range or op1_range handler, based on the operand types:
* Each operand is categorized as I (integer), F (float), or P (pointer).
* For example, a call with (int, float, int) is labeled RO_IFI.
* The dispatch enums start with RO_ and are matched in the dispatcher routine for a specific method (e.g., range_op_handler::fold_range() will dispatch a call to the correct version of fold_range ()).

For unary operations like casts, the LHS type is duplicated as op2 to fill the 3-operand signature required by the dispatch logic. (While op2 is often ignored in this case, it's sometimes used in corner cases.)

If a particular dispatch pattern isn’t implemented, the dispatcher returns false to avoid unnecessary overhead and implementation of default routines which are goign to return false anyway.

This Patch Adds
=============

Two routines for float-to-int casts:
   bool operator_cast::fold_range (irange &lhs, tree type, const frange &op1, const irange &, relation_trio) const;    bool operator_cast::op1_range (frange &op1, tree type, const irange &lhs, const irange &, relation_trio) const;

This requires 2 dispatch patterns in range_op_handler methods:

* fold_range: pattern RO_IFI (already handled)
* op1_range: pattern RO_FII (new case added)

and as well, range_operator needed a prototype and default definition for the new pattern case for op1_range:    bool range_operator::op1_range (frange &op1, tree type, const irange &lhs, const irange &, relation_trio) const;

For the 2 routines for int-to-float cast:
   bool operator_cast::fold_range (frange &lhs, tree type, const irange &op1, const frange &, relation_trio) const;    bool operator_cast::op1_range (irange &op1, tree type, const frange &lhs, const frange &, relation_trio) const;

The dispatch patterns required were:

* fold_range: pattern RO_FIF (new case added)
* op1_range: pattern RO_IFF (new case added)

And likewise range_operator needed prototypes and default definitions for both these new methods that were being added.

In summary
==========

  -  The fold_range dispatcher (range_op_handler::fold_range()) was already handling RO_IFI, but missing RO_FIF, which is now added.   -  The op1_range dispatcher lacked both RO_FII and RO_IFF. This patch adds both cases.   -  All four RO_XXX enum values already existed, so no new enumeration constants were needed.   -  The base class 'range_operator' is updated to declare all the 3 new virtual method prototypes that are required.
  -  Each is given a default stub implementation returning false.
  -  This ensures that any derived operator inherits default behavior, and the dispatch system has valid targets even when specific functionality isn’t implemented.
  -  class 'operator_cast' is given all 4 virtual function prototypes.
  -  class 'operator_cast' is given definitions for all 4 routines to override the default behaviour and which normally would implement the required functionality.

This can serve as a reference implementation for how to extend range_operator for new operand combinations.

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


Andrew
From 6f375445ef09d5c97d5bcc0fcb6069612217963e Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacl...@redhat.com>
Date: Mon, 12 May 2025 11:41:37 -0400
Subject: [PATCH] Add dispatch for casts between integer and float.

GCC currently does not implement range operators for casting between
integers and float.  This patch adds the missing dispatch patterns and
routines to facilitate implmenting these casts.

	PR tree-optimization/120231
	* range-op-float.cc (operator_cast::fold_range): New variants.
	(operator_cast::op1_range): Likewise.
	* range-op-mixed.h (operator_cast::fold_range): Likewise.
	(operator_cast::op1_range): Likewise
	* range-op.cc (range_op_handler::fold_range): Add RO_FIF dispatch.
	(range_op_handler::op1_range): Add RO_IFF and RO_FII patterns.
	(range_operator::fold_range): Provide new variant default.
	(range_operator::op1_range): Likewise.
	* range-op.h (range_operator): Add new variant methods.
---
 gcc/range-op-float.cc | 31 +++++++++++++++++++++++++++++++
 gcc/range-op-mixed.h  | 18 ++++++++++++++++++
 gcc/range-op.cc       | 38 ++++++++++++++++++++++++++++++++++++++
 gcc/range-op.h        | 13 ++++++++++++-
 4 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index 4719829974d..dafd9c0688c 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -2899,6 +2899,37 @@ private:
   }
 } fop_div;
 
+// Implement fold for a cast from float to an int.
+bool
+operator_cast::fold_range (irange &, tree, const frange &,
+			   const irange &, relation_trio) const
+{
+  return false;
+}
+
+// Implement op1_range for a cast from float to an int.
+bool
+operator_cast::op1_range (frange &, tree, const irange &,
+			  const irange &, relation_trio) const
+{
+  return false;
+}
+
+// Implement fold for a cast from int to a float.
+bool
+operator_cast::fold_range (frange &, tree, const irange &,
+			   const frange &, relation_trio) const
+{
+  return false;
+}
+
+// Implement op1_range for a cast from int to a float.
+bool
+operator_cast::op1_range (irange &, tree, const frange &,
+			  const frange &, relation_trio) const
+{
+  return false;
+}
 
 // Initialize any float operators to the primary table
 
diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h
index bb8e90aeddd..3fb7bff90bd 100644
--- a/gcc/range-op-mixed.h
+++ b/gcc/range-op-mixed.h
@@ -473,6 +473,15 @@ public:
   bool fold_range (prange &r, tree type,
 		   const irange &op1, const prange &op2,
 		   relation_trio rel = TRIO_VARYING) const final override;
+  bool fold_range (irange &r, tree type,
+		   const frange &lh,
+		   const irange &rh,
+		   relation_trio = TRIO_VARYING) const;
+  bool fold_range (frange &r, tree type,
+		   const irange &lh,
+		   const frange &rh,
+		   relation_trio = TRIO_VARYING) const;
+
   bool op1_range (irange &r, tree type,
 		  const irange &lhs, const irange &op2,
 		  relation_trio rel = TRIO_VARYING) const final override;
@@ -485,6 +494,15 @@ public:
   bool op1_range (prange &r, tree type,
 		  const irange &lhs, const prange &op2,
 		  relation_trio rel = TRIO_VARYING) const final override;
+  bool op1_range (frange &r, tree type,
+		  const irange &lhs,
+		  const irange &op2,
+		  relation_trio = TRIO_VARYING) const;
+  bool op1_range (irange &r, tree type,
+		  const frange &lhs,
+		  const frange &op2,
+		  relation_trio = TRIO_VARYING) const;
+
   relation_kind lhs_op1_relation (const irange &lhs,
 				  const irange &op1, const irange &op2,
 				  relation_kind) const final override;
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index 35b3e18ebed..06d357f5199 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -164,6 +164,8 @@ dispatch_trio (unsigned lhs, unsigned op1, unsigned 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.
+// Reminder, single operand instructions use the LHS type for op2, even if
+// unused. so FLOAT = INT would be RO_FIF.
 
 const unsigned RO_III =	dispatch_trio (VR_IRANGE, VR_IRANGE, VR_IRANGE);
 const unsigned RO_IFI = dispatch_trio (VR_IRANGE, VR_FRANGE, VR_IRANGE);
@@ -246,6 +248,10 @@ range_op_handler::fold_range (vrange &r, tree type,
 	return m_operator->fold_range (as_a <frange> (r), type,
 				       as_a <irange> (lh),
 				       as_a <irange> (rh), rel);
+      case RO_FIF:
+	return m_operator->fold_range (as_a <frange> (r), type,
+				       as_a <irange> (lh),
+				       as_a <frange> (rh), rel);
       case RO_PPP:
 	return m_operator->fold_range (as_a <prange> (r), type,
 				       as_a <prange> (lh),
@@ -292,6 +298,10 @@ range_op_handler::op1_range (vrange &r, tree type,
 	return m_operator->op1_range (as_a <irange> (r), type,
 				      as_a <irange> (lhs),
 				      as_a <irange> (op2), rel);
+      case RO_IFF:
+	return m_operator->op1_range (as_a <irange> (r), type,
+				      as_a <frange> (lhs),
+				      as_a <frange> (op2), rel);
       case RO_PPP:
 	return m_operator->op1_range (as_a <prange> (r), type,
 				      as_a <prange> (lhs),
@@ -312,6 +322,10 @@ range_op_handler::op1_range (vrange &r, tree type,
 	return m_operator->op1_range (as_a <frange> (r), type,
 				      as_a <irange> (lhs),
 				      as_a <frange> (op2), rel);
+      case RO_FII:
+	return m_operator->op1_range (as_a <frange> (r), type,
+				      as_a <irange> (lhs),
+				      as_a <irange> (op2), rel);
       case RO_FFF:
 	return m_operator->op1_range (as_a <frange> (r), type,
 				      as_a <frange> (lhs),
@@ -761,6 +775,30 @@ range_operator::fold_range (irange &r, tree type,
   return true;
 }
 
+
+bool
+range_operator::fold_range (frange &, tree, const irange &,
+			   const frange &, relation_trio) const
+{
+  return false;
+}
+
+bool
+range_operator::op1_range (irange &, tree, const frange &,
+			  const frange &, relation_trio) const
+{
+  return false;
+}
+
+bool
+range_operator::op1_range (frange &, tree, const irange &,
+			  const irange &, relation_trio) const
+{
+  return false;
+}
+
+
+
 // The default for op1_range is to return false.
 
 bool
diff --git a/gcc/range-op.h b/gcc/range-op.h
index 594e6782dc3..10757861378 100644
--- a/gcc/range-op.h
+++ b/gcc/range-op.h
@@ -86,6 +86,10 @@ public:
 			   const irange &lh,
 			   const irange &rh,
 			   relation_trio = TRIO_VARYING) const;
+  virtual bool fold_range (frange &r, tree type,
+			   const irange &lh,
+			   const frange &rh,
+			   relation_trio = TRIO_VARYING) const;
   virtual bool fold_range (prange &r, tree type,
 			   const prange &lh,
 			   const prange &rh,
@@ -146,7 +150,14 @@ public:
 			  const irange &lhs,
 			  const frange &op2,
 			  relation_trio = TRIO_VARYING) const;
-
+  virtual bool op1_range (irange &r, tree type,
+			  const frange &lhs,
+			  const frange &op2,
+			  relation_trio = TRIO_VARYING) const;
+  virtual bool op1_range (frange &r, tree type,
+			  const irange &lhs,
+			  const irange &op2,
+			  relation_trio = TRIO_VARYING) const;
 
   virtual bool op2_range (irange &r, tree type,
 			  const irange &lhs,
-- 
2.45.0

Reply via email to