[gcc r17-3239] [frange] Convert range_operator::fold_range to sub-ranges.

Aldy Hernandez via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
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" } }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.