For `(a - b)`, if a and b are signed types are non negative and there
was no overflow, you can infer a relationship if you know the result
of the minus is non-negative (`a >= b`) or is non-positive (`a <= b`).
You can also remove the `==` part if you know the range does not contain
0.

This allows us to optimize stuff like this:
```
int f(int a, int b)
{
  a&=0xff;
  b&=0xff;
  int t = a - b;
  if (t < 0)
  {
      return a < b;
  }
  return 0;
}
```
without needing to optimize `(a - b) < 0` into `a < b`.

The main thing that needed to be changed to support is passing
the ranges to op1_op2_relation inside register_outgoing_edges.

Bootstrapped and tested on x86_64-linux-gnu.

gcc/ChangeLog:

        * gimple-range-fold.cc (fur_source::register_outgoing_edges): Use
        the range of the rhs1/rsh2 on that edge rather than varry.
        * range-op.cc (operator_minus::op1_op2_relation): Add
        case for signed types with non-negative rhs sides.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/vrp-cmp-sub-4.c: New test.

Signed-off-by: Andrea Pinski <[email protected]>
---
 gcc/gimple-range-fold.cc                      | 12 +++++++
 gcc/range-op.cc                               | 21 +++++++++--
 gcc/testsuite/gcc.dg/tree-ssa/vrp-cmp-sub-4.c | 36 +++++++++++++++++++
 3 files changed, 66 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-cmp-sub-4.c

diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index a66993a0ab0..2f2a92edb36 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -1869,6 +1869,12 @@ fur_source::register_outgoing_edges (gcond *s, irange 
&lhs_range,
              && !r.undefined_p ()
              && !r.varying_p ())
            {
+             if (!gori ()->edge_range_p (r1, e0, ssa1, *m_query)
+                 || r1.undefined_p ())
+               r1.set_varying (TREE_TYPE (ssa1));
+             if (!gori ()->edge_range_p (r2, e0, ssa2, *m_query)
+                 || r2.undefined_p ())
+               r2.set_varying (TREE_TYPE (ssa2));
              relation_kind relation = handler.op1_op2_relation (r, r1, r2);
              if (relation != VREL_VARYING)
                register_relation (e0, relation, ssa1, ssa2);
@@ -1877,6 +1883,12 @@ fur_source::register_outgoing_edges (gcond *s, irange 
&lhs_range,
              && !r.undefined_p ()
              && !r.varying_p ())
            {
+             if (!gori ()->edge_range_p (r1, e1, ssa1, *m_query)
+                 || r1.undefined_p ())
+               r1.set_varying (TREE_TYPE (ssa1));
+             if (!gori ()->edge_range_p (r2, e1, ssa2, *m_query)
+                 || r2.undefined_p ())
+               r2.set_varying (TREE_TYPE (ssa2));
              relation_kind relation = handler.op1_op2_relation (r, r1, r2);
              if (relation != VREL_VARYING)
                register_relation (e1, relation, ssa1, ssa2);
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index da788182899..289488b9502 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -2104,10 +2104,12 @@ minus_op1_op2_relation_effect (irange &lhs_range, tree 
type,
 
 // Check if the LHS range indicates a relation between OP1 and OP2.
 // OP1 - OP2 == 0 implies OP1 == OP2 even with wrapping arithmetic.
+// For signed types, when both OP1 and OP2 are both positive, infer
+// a relationship if there were it is overflow free.
 
 relation_kind
-operator_minus::op1_op2_relation (const irange &lhs, const irange &,
-                                 const irange &) const
+operator_minus::op1_op2_relation (const irange &lhs, const irange &rhs1,
+                                 const irange &rhs2) const
 {
   if (lhs.undefined_p ())
     return VREL_UNDEFINED;
@@ -2115,7 +2117,20 @@ operator_minus::op1_op2_relation (const irange &lhs, 
const irange &,
   if (lhs.zero_p ())
     return VREL_EQ;
 
-  if (!lhs.contains_zero_p ())
+  bool contains_z = lhs.contains_zero_p ();
+  // When a and b are both positive (or zero), then infer the
+  // relationship between a and b.
+  if (!TYPE_UNSIGNED (lhs.type ())
+      && rhs1.nonnegative_p () && rhs2.nonnegative_p ()
+      && overflow_free_p (rhs1, rhs2))
+    {
+      if (lhs.nonnegative_p ())
+       return contains_z ? VREL_GE : VREL_GT;
+      if (lhs.nonpositive_p ())
+       return contains_z ? VREL_LE : VREL_LT;
+    }
+
+  if (!contains_z)
     return VREL_NE;
 
   return VREL_VARYING;
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-cmp-sub-4.c 
b/gcc/testsuite/gcc.dg/tree-ssa/vrp-cmp-sub-4.c
new file mode 100644
index 00000000000..775a7f79de3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-cmp-sub-4.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int f(int a, int b)
+{
+  a&=0xff;
+  b&=0xff;
+  int t = a - b;
+  if (t < 0)
+    return a < b;
+  return 0;
+}
+
+int g();
+
+int f1(int a, int b)
+{
+  a&=0xff;
+  b&=0xff;
+  int t = a - b;
+  if (t < 0)
+   {
+     if (a < b)
+       return g();
+   }
+  return 0;
+}
+
+
+/* { dg-final { scan-tree-dump-times " < " 2 "optimized" } } */
+/* Only 1 if left for f1 */
+/* { dg-final { scan-tree-dump-times "if " 1 "optimized" } } */
+/* 4 &, 2 `& 255` for each functions. */
+/* { dg-final { scan-tree-dump-times " & " 4 "optimized" } } */
+/* { dg-final { scan-tree-dump-not " != " "optimized" } } */
+/* { dg-final { scan-tree-dump-not " == " "optimized" } } */
-- 
2.43.0

Reply via email to