[gcc r13-10447] range-op-float: Fix up inf handling in reverse narrowing float to float cast [PR126464]

Jakub Jelinek via Gcc-cvs <[email protected]> Sat, 1 Aug 2026 10:58:37 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:de161ea6d7522cc945cb2fe30f90304bbc6affbf

commit r13-10447-gde161ea6d7522cc945cb2fe30f90304bbc6affbf
Author: Jakub Jelinek <[email protected]>
Date:   Thu Jul 30 09:56:41 2026 +0200

    range-op-float: Fix up inf handling in reverse narrowing float to float cast [PR126464]
    
    The following testcase is miscompiled since my r16-1108 change.
    The problem is if we handle a reverse of a narrowing float to float cast
    (in the example there are double -> float and long double -> double
    cast) and the lhs range is [-inf, -inf] or [+inf, +inf] (note, regardless
    of whether some NaNs are allowed or not, so not necessarily
    lhs.known_isinf ()), then handling that range in the wider type also
    as [-inf, -inf] or [+inf, +inf] is wrong, e.g. for the double -> float
    conversion, [-inf, -0x0.ffffff8p+128] double range could map to just
    that [-inf, -inf].  We have already float_widen_lhs_range function
    but that just extends the range by +/-1ulp or 0.5ulp if the bounds
    are finite.  If the range isn't singleton (except for optional NaN),
    then the minimum (or maximum) finite is already in the range, so this just
    extends the case where they are singleton.
    I don't know how to portably figure out that 0x0.ffffff8p+128 for
    double -> float (especially when in float_widen_lhs_range we don't know
    yet the wider type), so the patch just uses the +/-1ulp extension (i.e.
    [-inf, min_finite] or [+inf, max_finite] case.
    
    2026-07-30  Jakub Jelinek  <[email protected]>
    
            PR tree-optimization/126464
            * range-op-float.cc (float_widen_lhs_range): Add also_inf argument
            defaulted to false, if true, extend even lb of +inf and ub of -inf.
    
            * gcc.dg/pr126464.c: New test.
    
    Reviewed-by: Richard Biener <[email protected]>
    (cherry picked from commit 3754582630fa1eeff44a39f656ba0c7db6e58d0d)

Diff:
---
 gcc/range-op-float.cc           | 14 ++++++----
 gcc/testsuite/gcc.dg/pr126464.c | 61 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index 606b201fd7a7..3535b04071f2 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -2334,14 +2334,14 @@ zero_to_inf_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, int signbit_known)
    in each direction.  See PR109008 for more details.  */
 
 static frange
-float_widen_lhs_range (tree type, const frange &lhs)
+float_widen_lhs_range (tree type, const frange &lhs, bool also_inf = false)
 {
   frange ret = lhs;
   if (lhs.known_isnan ())
     return ret;
   REAL_VALUE_TYPE lb = lhs.lower_bound ();
   REAL_VALUE_TYPE ub = lhs.upper_bound ();
-  if (real_isfinite (&lb))
+  if (real_isfinite (&lb) || (also_inf && !real_isneg (&lb)))
     {
       frange_nextafter (TYPE_MODE (type), lb, dconstninf);
       if (real_isinf (&lb))
@@ -2355,7 +2355,9 @@ float_widen_lhs_range (tree type, const frange &lhs)
 	  lb = dconstm1;
 	  SET_REAL_EXP (&lb, FLOAT_MODE_FORMAT (TYPE_MODE (type))->emax + 1);
 	}
-      if (!flag_rounding_math && !MODE_COMPOSITE_P (TYPE_MODE (type)))
+      if (!flag_rounding_math
+	  && !MODE_COMPOSITE_P (TYPE_MODE (type))
+	  && (!also_inf || real_isfinite (&lhs.lower_bound ())))
 	{
 	  /* If not -frounding-math nor IBM double double, actually widen
 	     just by 0.5ulp rather than 1ulp.  */
@@ -2364,7 +2366,7 @@ float_widen_lhs_range (tree type, const frange &lhs)
 	  real_arithmetic (&lb, RDIV_EXPR, &tem, &dconst2);
 	}
     }
-  if (real_isfinite (&ub))
+  if (real_isfinite (&ub) || (also_inf && real_isneg (&ub)))
     {
       frange_nextafter (TYPE_MODE (type), ub, dconstinf);
       if (real_isinf (&ub))
@@ -2373,7 +2375,9 @@ float_widen_lhs_range (tree type, const frange &lhs)
 	  ub = dconst1;
 	  SET_REAL_EXP (&ub, FLOAT_MODE_FORMAT (TYPE_MODE (type))->emax + 1);
 	}
-      if (!flag_rounding_math && !MODE_COMPOSITE_P (TYPE_MODE (type)))
+      if (!flag_rounding_math
+	  && !MODE_COMPOSITE_P (TYPE_MODE (type))
+	  && (!also_inf || real_isfinite (&lhs.upper_bound ())))
 	{
 	  /* If not -frounding-math nor IBM double double, actually widen
 	     just by 0.5ulp rather than 1ulp.  */
diff --git a/gcc/testsuite/gcc.dg/pr126464.c b/gcc/testsuite/gcc.dg/pr126464.c
new file mode 100644
index 000000000000..b6eb1320dec5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr126464.c
@@ -0,0 +1,61 @@
+/* PR tree-optimization/126464 */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-add-options ieee } */
+/* { dg-skip-if "not IEEE float" { "pdp11-*-*" } } */
+
+[[gnu::noipa]] double
+foo (double x)
+{
+  float y = (float) x;
+
+  if (y == -__builtin_inff ())
+    return x * 0.5;
+  return y;
+}
+
+[[gnu::noipa]] long double
+bar (long double x)
+{
+  double y = (double) x;
+
+  if (y == __builtin_inf ())
+    return x * 0.5L;
+  return y;
+}
+
+[[gnu::noipa]] double
+baz (double x)
+{
+  float y = (float) x;
+
+  if (y == __builtin_inff ())
+    return x * 0.5;
+  return y;
+}
+
+[[gnu::noipa]] long double
+qux (long double x)
+{
+  double y = (double) x;
+
+  if (y == -__builtin_inf ())
+    return x * 0.5L;
+  return y;
+}
+
+int
+main ()
+{
+  if (!__builtin_isinf ((double) 1e300)
+      && __builtin_isinf ((float) 1e300)
+      && (foo (-1e300) != -5e299
+	  || baz (1e300) != 5e299))
+    __builtin_abort ();
+
+  if (!__builtin_isinf ((long double) 1e4000L)
+      && __builtin_isinf ((double) 1e4000L)
+      && (bar (1e4000L) != 5e3999L
+	  || qux (-1e4000L) != -5e3999L))
+    __builtin_abort ();
+}