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