On Wed, Jun 17, 2026 at 3:54 AM Eikansh Gupta
<[email protected]> wrote:
>
> When integer operands are converted to a floating-point type before a
> comparison, the existing fold only checks whether the whole integer type
> is exactly representable. Use SSA range information as a fallback so
> range-limited values, such as those masked to [0, 255], can be compared
> directly as integers. Apply the same range-aware exactness check to
> integer-to-float comparisons against real constants.
>
> PR tree-optimization/125385
>
> gcc/ChangeLog:
>
> * match.pd ((FTYPE) N CMP (FTYPE) M): Use range information
> to decide whether integer operands are exactly representable.
> ((FTYPE) N CMP CST): Likewise.
> * real.cc: Include backend.h and vr-values.h.
> (format_helper::can_represent_integral_value_p): New function.
> * real.h (format_helper::can_represent_integral_value_p): Declare.
> * tree-core.h (class irange): Forward declare.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/tree-ssa/pr125385.c: New test.
>
> Signed-off-by: Eikansh Gupta <[email protected]>
> ---
> gcc/match.pd | 41 ++++++++++--
> gcc/real.cc | 27 ++++++++
> gcc/real.h | 1 +
> gcc/testsuite/gcc.dg/tree-ssa/pr125385.c | 82 ++++++++++++++++++++++++
> gcc/tree-core.h | 1 +
> 5 files changed, 145 insertions(+), 7 deletions(-)
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr125385.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index e0d7ef80e14..20d852d5fb0 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -7530,7 +7530,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> (for cmp (lt le eq ne ge gt unordered ordered unlt unle ungt unge uneq ltgt)
> icmp (lt le eq ne ge gt unordered ordered lt le gt ge eq ne)
> (simplify
> - (cmp (float@0 @1) (float @2))
> + (cmp (float@0 @1) (float@3 @2))
> (if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (@0))
> && ! DECIMAL_FLOAT_TYPE_P (TREE_TYPE (@0)))
> (with
> @@ -7538,11 +7538,21 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> format_helper fmt (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (@0))));
> tree type1 = TREE_TYPE (@1);
> bool type1_signed_p = TYPE_SIGN (type1) == SIGNED;
> + bool type1_ok = fmt.can_represent_integral_type_p (type1);
> tree type2 = TREE_TYPE (@2);
> bool type2_signed_p = TYPE_SIGN (type2) == SIGNED;
> + bool type2_ok = fmt.can_represent_integral_type_p (type2);
> +#if GIMPLE
> + int_range_max vr1, vr2;
> + if (!type1_ok
> + && gimple_match_range_of_expr (vr1, @1, @0))
> + type1_ok = fmt.can_represent_integral_value_p (&vr1);
> + if (!type2_ok
> + && gimple_match_range_of_expr (vr2, @2, @3))
> + type2_ok = fmt.can_represent_integral_value_p (&vr2);
> +#endif
> }
> - (if (fmt.can_represent_integral_type_p (type1)
> - && fmt.can_represent_integral_type_p (type2))
> + (if (type1_ok && type2_ok)
> (if (cmp == ORDERED_EXPR || cmp == UNORDERED_EXPR)
> { constant_boolean_node (cmp == ORDERED_EXPR, type); }
> (if (TYPE_PRECISION (type1) > TYPE_PRECISION (type2)
> @@ -7559,13 +7569,15 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> (for cmp (lt le eq ne ge gt)
> icmp (le le eq ne ge ge)
> (simplify
> - (cmp (float @0) REAL_CST@1)
> + (cmp (float@2 @0) REAL_CST@1)
> (if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (@1))
> && ! DECIMAL_FLOAT_TYPE_P (TREE_TYPE (@1)))
> (with
> {
> tree itype = TREE_TYPE (@0);
> format_helper fmt (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (@1))));
> + bool itype_ok = fmt.can_represent_integral_type_p (itype);
> + bool value_ok = itype_ok;
> const REAL_VALUE_TYPE *cst = TREE_REAL_CST_PTR (@1);
> /* Be careful to preserve any potential exceptions due to NaNs.
> qNaNs are ok in == or != context. */
> @@ -7574,16 +7586,31 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> && ((cmp != EQ_EXPR && cmp != NE_EXPR)
> || (cst->signalling
> && HONOR_SNANS (TREE_TYPE (@1))));
> +#if GIMPLE
> + int_range_max vr;
> + if (!value_ok
> + && gimple_match_range_of_expr (vr, @0, @2))
> + value_ok = fmt.can_represent_integral_value_p (&vr);
> +#endif
> }
> /* TODO: allow non-fitting itype and SNaNs when
> -fno-trapping-math. */
> - (if (fmt.can_represent_integral_type_p (itype) && ! exception_p)
> + (if (value_ok && ! exception_p)
> (with
> {
> signop isign = TYPE_SIGN (itype);
> + wide_int imin_val = wi::min_value (itype);
> + wide_int imax_val = wi::max_value (itype);
> +#if GIMPLE
> + if (!itype_ok)
> + {
> + imin_val = vr.lower_bound ();
> + imax_val = vr.upper_bound ();
> + }
> +#endif
> REAL_VALUE_TYPE imin, imax;
> - real_from_integer (&imin, fmt, wi::min_value (itype), isign);
> - real_from_integer (&imax, fmt, wi::max_value (itype), isign);
> + real_from_integer (&imin, fmt, imin_val, isign);
> + real_from_integer (&imax, fmt, imax_val, isign);
>
> REAL_VALUE_TYPE icst;
> if (cmp == GT_EXPR || cmp == GE_EXPR)
> diff --git a/gcc/real.cc b/gcc/real.cc
> index 4c017cb878d..27ba9cd5429 100644
> --- a/gcc/real.cc
> +++ b/gcc/real.cc
> @@ -22,9 +22,13 @@
> #include "config.h"
> #include "system.h"
> #include "coretypes.h"
> +#include "bitmap.h"
> +#include "function.h"
> #include "tm.h"
> #include "rtl.h"
> #include "tree.h"
> +#include "value-range.h"
> +#include "vr-values.h"
> #include "realmpfr.h"
> #include "dfp.h"
>
> @@ -5512,6 +5516,29 @@ bool format_helper::can_represent_integral_type_p
> (tree type) const
> return TYPE_PRECISION (type) - signed_p <= significand_size (*this);
> }
>
> +/* True if all values in integer range *VR can be represented by this
> + floating-point type exactly. */
> +
> +bool
> +format_helper::can_represent_integral_value_p (const irange *vr) const
A better name for this is can_represent_range_value_p.
> +{
> + gcc_assert (!decimal_p ());
> +
> + if (!vr || vr->undefined_p () || vr->varying_p ())
You can remove the !vr here.
> + return false;
> +
> + tree type = vr->type ();
> + if (!INTEGRAL_TYPE_P (type))
> + return false;
a type of the irange will always be an INTEGRAL_TYPE_P so this check
can be removed.
Looks good otherwise.
Thanks,
Andrea
> +
> + unsigned precision = significand_size (*this);
> +
> + if (TYPE_SIGN (type) == SIGNED)
> + precision++;
> +
> + return range_fits_type_p (vr, precision, TYPE_SIGN (type));
> +}
> +
> /* True if mode M has a NaN representation and
> the treatment of NaN operands is important. */
>
> diff --git a/gcc/real.h b/gcc/real.h
> index dc39c32d85f..e941628b4ca 100644
> --- a/gcc/real.h
> +++ b/gcc/real.h
> @@ -223,6 +223,7 @@ public:
>
> bool decimal_p () const { return m_format && m_format->b == 10; }
> bool can_represent_integral_type_p (tree type) const;
> + bool can_represent_integral_value_p (const class irange *) const;
>
> private:
> const real_format *m_format;
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr125385.c
> b/gcc/testsuite/gcc.dg/tree-ssa/pr125385.c
> new file mode 100644
> index 00000000000..630fa58d23e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr125385.c
> @@ -0,0 +1,82 @@
> +/* PR tree-optimization/125385 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +int
> +f_lt (int a, int b)
> +{
> + a &= 0xff;
> + b &= 0xff;
> + return (float) a < (float) b;
> +}
> +
> +int
> +f_le (int a, int b)
> +{
> + a &= 0xff;
> + b &= 0xff;
> + return (float) a <= (float) b;
> +}
> +
> +int
> +f_eq (int a, int b)
> +{
> + a &= 0xff;
> + b &= 0xff;
> + return (float) a == (float) b;
> +}
> +
> +int
> +f_ne (int a, int b)
> +{
> + a &= 0xff;
> + b &= 0xff;
> + return (float) a != (float) b;
> +}
> +
> +int
> +f_ge (int a, int b)
> +{
> + a &= 0xff;
> + b &= 0xff;
> + return (float) a >= (float) b;
> +}
> +
> +int
> +f_gt (int a, int b)
> +{
> + a &= 0xff;
> + b &= 0xff;
> + return (float) a > (float) b;
> +}
> +
> +int
> +f_signed_range (int a, int b)
> +{
> + a = (a & 0xff) - 128;
> + b = (b & 0xff) - 128;
> + return (float) a < (float) b;
> +}
> +
> +int
> +f_cst (int a)
> +{
> + a &= 0xff;
> + return (float) a < 42.0f;
> +}
> +
> +int
> +f_cst_fractional (int a)
> +{
> + a &= 0xff;
> + return (float) a < 42.5f;
> +}
> +
> +int
> +f_cst_outside_type (int a)
> +{
> + a &= 0xff;
> + return (float) a < 2147483648.0f;
> +}
> +
> +/* { dg-final { scan-tree-dump-not "\\(float\\)" "optimized" } } */
> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> index 09c07c5c129..c9232c16fec 100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -34,6 +34,7 @@ struct real_value;
> struct fixed_value;
> struct ptr_info_def;
> struct die_struct;
> +class irange;
>
>
> /*---------------------------------------------------------------------------
> --
> 2.34.1
>