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