Re: [RFC] [frange] Convert range_operator::fold_range to sub-ranges.
Aldy Hernandez <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
On Fri, Aug 07, 2026 at 07:17:45PM +0200, Aldy Hernandez wrote: > Fold each pair of operand sub-ranges through rv_fold and union the > results, which keeps the gaps between sub-ranges instead of folding > the [lower, upper] hull. When the operands are known equal (x op x) > fold the diagonal instead, folding each sub-range against itself. > This follows irange's fold_range. > > I'm not sure about this, and made a lot of comments to wrap my head > around this, that I'll strip before the final commit. > > I'm trying to follow irange's convention, without doing all the fancy > things we do there (fold in parts, etc), but keeping relations sane. > > I've tested this, but I'd appreciate another set of eyes before > cleaning this up and contributing. In a private message, Andrew didn't have anything to add, so I'm going to push this, as I'd like to move onto something else. This concludes my foray into frange again. I believe I've converted everything, save a few operands that are way to hard for me to even think about. The work in range-op*.cc is never ending anyhow. An FP expert can handle anything else :). Attached is what I've pushed. Aldy
0001-frange-Convert-range_operator-fold_range-to-sub-rang.patch
(text/x-diff, 4.9 KB)
From 7f4f4b9fe867c07b597412629c7b33636db4a066 Mon Sep 17 00:00:00 2001 From: Aldy Hernandez <[email protected]> Date: Tue, 4 Aug 2026 10:03:39 +0000 Subject: [PATCH] [frange] Convert range_operator::fold_range to sub-ranges. Fold each pair of operand sub-ranges through rv_fold and union the results, which keeps the gaps between sub-ranges instead of folding the [lower, upper] hull. When the operands are known equal (x op x) fold the diagonal instead, folding each sub-range against itself. This follows irange's fold_range. Tested on ppc64le Linux: regstrap, LAPACK. Shrinks 6 of 3631 LAPACK asm TUs, none larger. gcc/ChangeLog: * range-op-float.cc (range_operator::fold_range): Fold each pair of sub-ranges, or the diagonal when the operands are equal. (range_op_float_tests): New test. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/vrp-float-subrange-1.c: New test. --- gcc/range-op-float.cc | 75 ++++++++++++++++++- .../gcc.dg/tree-ssa/vrp-float-subrange-1.c | 17 +++++ 2 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-subrange-1.c diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index 60c522ee2a0..49dd705db32 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -62,9 +62,49 @@ range_operator::fold_range (frange &r, tree type, return true; } - rv_fold (r, type, - op1.lower_bound (), op1.upper_bound (), - op2.lower_bound (), op2.upper_bound (), trio.op1_op2 ()); + relation_kind rel = trio.op1_op2 (); + r.set_undefined (); + frange tmp; + if (relation_equiv_p (rel) && op1 == op2) + { + // The operands are known equal (x op x). Fold each sub-range against + // itself, the diagonal of the cross product, so a relation-aware rv_fold + // like operator_mult's is_square stays valid. + // + // For x * x with x in [-3, -2] U [2, 3] the diagonal is + // + // [-3, -2] * [-3, -2] = [4, 9] + // [ 2, 3] * [ 2, 3] = [4, 9] + // + // giving the exact [4, 9]. The full cross product would form: + // + // [-3, -2] * [-3, -2] = [4, 9] + // [-3, -2] * [ 2, 3] = [-9, -4] + // [ 2, 3] * [-3, -2] = [-9, -4] + // [ 2, 3] * [ 2, 3] = [4, 9] + // + // whose union [-9, -4] U [4, 9] contains negative products that x * x + // can never produce: the off-diagonal terms pair a value from one + // sub-range with a value from the other, which x (a single value) can + // never do. + for (unsigned i = 0; i < op1.num_pairs (); ++i) + { + rv_fold (tmp, type, + op1.lower_bound (i), op1.upper_bound (i), + op1.lower_bound (i), op1.upper_bound (i), rel); + r.union_ (tmp); + } + } + else + // Otherwise do the straight cross product. + for (unsigned i = 0; i < op1.num_pairs (); ++i) + for (unsigned j = 0; j < op2.num_pairs (); ++j) + { + rv_fold (tmp, type, + op1.lower_bound (i), op1.upper_bound (i), + op2.lower_bound (j), op2.upper_bound (j), rel); + r.union_ (tmp); + } if (r.known_isnan ()) return true; @@ -3296,6 +3336,35 @@ range_op_float_tests () range_op_handler (FIX_TRUNC_EXPR).fold_range (ir, integer_type_node, r0, ir_op2); ASSERT_EQ (ir.num_pairs (), 2); + + // ([1, 2] U [10, 11]) + 0 stays two pieces. + r0 = frange_float ("1.0", "2.0"); + r1 = frange_float ("10.0", "11.0"); + r0.union_ (r1); + r0.clear_nan (); + r1 = frange_float ("0.0", "0.0"); + r1.clear_nan (); + range_op_handler (PLUS_EXPR).fold_range (r, float_type_node, r0, r1); + ASSERT_EQ (r.num_pairs (), 2); + + // x * x (VREL_EQ) with a two-piece x: the diagonal fold gives the exact + // [4, 9], not the full cross product's [-9, -4] U [4, 9], nor the hull's + // looser [0, 9]. + r0 = frange_float ("-3.0", "-2.0"); + r1 = frange_float ("2.0", "3.0"); + r0.union_ (r1); + r0.clear_nan (); + range_op_handler (MULT_EXPR).fold_range (r, float_type_node, r0, r0, + relation_trio (VREL_VARYING, + VREL_VARYING, + VREL_EQ)); + REAL_VALUE_TYPE val; + real_from_string (&val, "5.0"); + ASSERT_TRUE (r.contains_p (val)); // the result is [4, 9] + real_from_string (&val, "-5.0"); + ASSERT_FALSE (r.contains_p (val)); // not the cross product's negatives + real_from_string (&val, "3.0"); + ASSERT_FALSE (r.contains_p (val)); // tighter than the hull [0, 9] } } // namespace selftest diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-subrange-1.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-subrange-1.c new file mode 100644 index 00000000000..d7740e2868a --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-subrange-1.c @@ -0,0 +1,17 @@ +// { dg-do compile } +// { dg-options "-O2 -fdump-tree-optimized" } + +extern void link_error (); + +void +test (double x) +{ + if (x != 3.0) + { + double z = x + 0.0; + if (z == 3.0) + link_error (); + } +} + +// { dg-final { scan-tree-dump-not "link_error" "optimized" } } -- 2.47.3