If there is a relation between op1 and op2, an unsigned minus operation
can sometimes do better than simply non-zero.

For unsigned, we can calculate more precise ranges for <, <=, > and >=, just 
like we do for signed values.

I adjusted the testcase to also test the other unsigned relations variations.

Bootstrapped on x86_64-pc-linux-gnu with no regressions.  Pushed.
Andrew

Assisted-by: Claude Opus 5

        PR tree-optimization/127915
        gcc/
        * range-op.cc (minus_op1_op2_relation_effect): Handle more
        unsigned range restrictions with known relations.

        gcc/testsuite/
        * gcc.dg/pr127915.c: New.
---
 gcc/range-op.cc                 | 45 +++++++++++++++++++++++++--
 gcc/testsuite/gcc.dg/pr127915.c | 54 +++++++++++++++++++++++++++++++++
 2 files changed, 96 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr127915.c

diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index d08193ce41d..c80d611f391 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -2090,7 +2090,7 @@ operator_minus::lhs_op1_relation (const irange &, const 
irange &op1,
 
 bool
 minus_op1_op2_relation_effect (irange &lhs_range, tree type,
-                              const irange &op1_range ATTRIBUTE_UNUSED,
+                              const irange &op1_range,
                               const irange &op2_range ATTRIBUTE_UNUSED,
                               relation_kind rel)
 {
@@ -2107,12 +2107,51 @@ minus_op1_op2_relation_effect (irange &lhs_range, tree 
type,
   else if (rel == VREL_NE)
     rel_range = int_range<2> (type, wi::zero (prec), wi::zero (prec),
                              VR_ANTI_RANGE);
+  else if (TYPE_UNSIGNED (type))
+    {
+      wide_int ub1 = op1_range.upper_bound ();
+      wide_int ub2 = op2_range.upper_bound ();
+      switch (rel)
+       {
+         // op1 > op2, so the subtraction cannot wrap : [1, UB (op1)].
+         case VREL_GT:
+           // [0, 0] > op2 is undefined, so just return.
+           if (ub1 == 0)
+             return false;
+           rel_range = int_range<2> (type, wi::one (prec), ub1);
+           break;
+         // op1 >= op2 >= 0, so the subtraction cannot wrap : [0, UB (op1)].
+         case VREL_GE:
+           rel_range = int_range<2> (type, wi::zero (prec), ub1);
+           break;
+         // op1 < op2, so the subtraction always wraps : [-UB (op2), -1].
+         case VREL_LT:
+           // op1 < [0, 0] is undefined, so just return.
+           if (ub2 == 0)
+             return false;
+           rel_range = int_range<2> (type, wi::neg (ub2),
+                                     wi::minus_one (prec));
+           break;
+         // As VREL_LT, except that op1 == op2 also produces zero.
+         case VREL_LE:
+           // If UB (op2) is MAX_INT the result won't change, plus
+           // we dont want to construct an illegal  [1, 0] range next.
+           if (ub2 == wi::max_value (prec, UNSIGNED))
+             return false;
+           rel_range = int_range<2> (type, wi::one (prec),
+                                     wi::sub (wi::minus_one (prec), ub2),
+                                     VR_ANTI_RANGE);
+           break;
+         default:
+           return false;
+       }
+    }
   else if (TYPE_OVERFLOW_WRAPS (type))
     {
       switch (rel)
        {
-         // For wrapping signed values and unsigned, if op1 > op2 or
-         // op1 < op2, then op1 - op2 can be restricted to ~[0, 0].
+         // For wrapping signed values , if op1 > op2 or op1 < op2,
+         // then op1 - op2 can be restricted to ~[0, 0].
          case VREL_GT:
          case VREL_LT:
              rel_range = int_range<2> (type, wi::zero (prec), wi::zero (prec),
diff --git a/gcc/testsuite/gcc.dg/pr127915.c b/gcc/testsuite/gcc.dg/pr127915.c
new file mode 100644
index 00000000000..cd8650b6efc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr127915.c
@@ -0,0 +1,54 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-evrp" } */
+
+/* last >= first and last <= 100, so last - first is [0, 100].  */
+void
+f1 (unsigned first, unsigned last)
+{
+  if (first > last || last > 100)
+    __builtin_unreachable ();
+
+  unsigned t = last - first;
+  if (t > 100)
+    __builtin_trap ();
+}
+
+/* last > first and last <= 100, so last - first is [1, 100].  */
+void
+f2 (unsigned first, unsigned last)
+{
+  if (first >= last || last > 100)
+    __builtin_unreachable ();
+
+  unsigned t = last - first;
+  if (t == 0 || t > 100)
+    __builtin_trap ();
+}
+
+/* first < last and last <= 100.  The subtraction always wraps, so
+   first - last is [-100, -1].  */
+void
+f3 (unsigned first, unsigned last)
+{
+  if (first >= last || last > 100)
+    __builtin_unreachable ();
+
+  unsigned t = first - last;
+  if (t < (unsigned) -100)
+    __builtin_trap ();
+}
+
+/* first <= last and last <= 100.  As f3, except that first == last also
+   produces zero, giving ~[1, -101].  */
+void
+f4 (unsigned first, unsigned last)
+{
+  if (first > last || last > 100)
+    __builtin_unreachable ();
+
+  unsigned t = first - last;
+  if (t != 0 && t < (unsigned) -100)
+    __builtin_trap ();
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_trap" "evrp" } } */
-- 
2.56.0

Reply via email to