[PATCH] [frange] Convert operator_abs::fold_range to sub-ranges.

Aldy Hernandez <[email protected]> Thu, 6 Aug 2026 23:07:36 +0200
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
Take the absolute value of each sub-range and union the results, so a
range that mixes signs keeps its gaps instead of collapsing through
zero to the [lower, upper] hull.  The all-positive fast path already
preserved the sub-ranges.

Tested on ppc64le: regstrap, LAPACK.

Pushed.

gcc/ChangeLog:

	* range-op-float.cc (operator_abs::fold_range): Take the absolute
	value of each sub-range and union the results.
	(range_op_float_tests): New test.
---
 gcc/range-op-float.cc | 51 ++++++++++++++++++++++++++++---------------
 1 file changed, 33 insertions(+), 18 deletions(-)

diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index 3e0258c4cc0..c3a287fbf50 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -1536,7 +1536,6 @@ operator_abs::fold_range (frange &r, tree type,
     }
 
   const REAL_VALUE_TYPE lh_lb = op1.lower_bound ();
-  const REAL_VALUE_TYPE lh_ub = op1.upper_bound ();
   // Handle the easy case where everything is positive.
   if (real_compare (GE_EXPR, &lh_lb, &dconst0)
       && !real_iszero (&lh_lb, /*sign=*/true)
@@ -1546,25 +1545,32 @@ operator_abs::fold_range (frange &r, tree type,
       return true;
     }
 
-  REAL_VALUE_TYPE min = real_value_abs (&lh_lb);
-  REAL_VALUE_TYPE max = real_value_abs (&lh_ub);
-  // If the range contains zero then we know that the minimum value in the
-  // range will be zero.
-  if (real_compare (LE_EXPR, &lh_lb, &dconst0)
-      && real_compare (GE_EXPR, &lh_ub, &dconst0))
-    {
-      if (real_compare (GT_EXPR, &min, &max))
-	max = min;
-      min = dconst0;
-    }
-  else
+  // Take the absolute value of each sub-range and union the results.
+  r.set_undefined ();
+  for (unsigned i = 0; i < op1.num_pairs (); ++i)
     {
-      // If the range was reversed, swap MIN and MAX.
-      if (real_compare (GT_EXPR, &min, &max))
-	std::swap (min, max);
+      const REAL_VALUE_TYPE lb = op1.lower_bound (i);
+      const REAL_VALUE_TYPE ub = op1.upper_bound (i);
+      REAL_VALUE_TYPE min = real_value_abs (&lb);
+      REAL_VALUE_TYPE max = real_value_abs (&ub);
+      // If the range contains zero then we know that the minimum value in the
+      // range will be zero.
+      if (real_compare (LE_EXPR, &lb, &dconst0)
+	  && real_compare (GE_EXPR, &ub, &dconst0))
+	{
+	  if (real_compare (GT_EXPR, &min, &max))
+	    max = min;
+	  min = dconst0;
+	}
+      else
+	{
+	  // If the range was reversed, swap MIN and MAX.
+	  if (real_compare (GT_EXPR, &min, &max))
+	    std::swap (min, max);
+	}
+      frange tmp (type, min, max);
+      r.union_ (tmp);
     }
-
-  r.set (type, min, max);
   if (op1.maybe_isnan ())
     r.update_nan (/*sign=*/false);
   else
@@ -3293,6 +3299,15 @@ range_op_float_tests ()
   r0.clear_nan ();
   range_op_handler (NEGATE_EXPR).fold_range (r, float_type_node, r0, trange);
   ASSERT_EQ (r.num_pairs (), 2);
+
+  // abs([-6, -5] U [1, 2]) => [1, 2] U [5, 6], keeping the gap rather than
+  // collapsing to [0, 6].
+  r0 = frange_float ("-6.0", "-5.0");
+  r1 = frange_float ("1.0", "2.0");
+  r0.union_ (r1);
+  r0.clear_nan ();
+  range_op_handler (ABS_EXPR).fold_range (r, float_type_node, r0, trange);
+  ASSERT_EQ (r.num_pairs (), 2);
 }
 
 } // namespace selftest
-- 
2.47.3