https://gcc.gnu.org/g:15211513ea09d13c1c13bdf1e057c6f52836ab48
commit r17-3043-g15211513ea09d13c1c13bdf1e057c6f52836ab48 Author: Aldy Hernandez <[email protected]> Date: Tue Aug 4 13:21:03 2026 +0000 [frange] Convert operator_abs::fold_range to sub-ranges. 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. 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. Diff: --- 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 3e0258c4cc01..c3a287fbf501 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
