https://gcc.gnu.org/g:7f4f4b9fe867c07b597412629c7b33636db4a066

commit r17-3239-g7f4f4b9fe867c07b597412629c7b33636db4a066
Author: Aldy Hernandez <[email protected]>
Date:   Tue Aug 4 10:03:39 2026 +0000

    [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.

Diff:
---
 gcc/range-op-float.cc                              | 75 +++++++++++++++++++++-
 .../gcc.dg/tree-ssa/vrp-float-subrange-1.c         | 17 +++++
 2 files changed, 89 insertions(+), 3 deletions(-)

diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index 60c522ee2a0e..49dd705db323 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 000000000000..d7740e2868a3
--- /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" } }

Reply via email to