When x == y, x & y is x, similarly for x | y.

When x <= y, MIN(x,y) is x.

When x >= y, MAX(x,y) is x.

When we implement this, we can fold the following:

  if (x == y)
    {
      int t = x & y;
      if (t != x)      // folds to false since t == x
        link_error ();
    }

Found while auditing DOM.  It's doing some simplifications that are
best done in range-ops.  In a follow-up patch I will rip out the
relevant code from DOM.

Tested on ppc64le Linux.

Pushed.

gcc/ChangeLog:

        * range-op-mixed.h (operator_bitwise_or::lhs_op1_relation): New.
        (operator_min::lhs_op1_relation): New.
        (operator_min::lhs_op2_relation): New.
        (operator_max::lhs_op1_relation): New.
        (operator_max::lhs_op2_relation): New.
        * range-op.cc (operator_bitwise_and::lhs_op1_relation): Return
        VREL_EQ when the operands are equal.
        (operator_bitwise_or::lhs_op1_relation): New.
        (operator_min::lhs_op1_relation): New.
        (operator_min::lhs_op2_relation): New.
        (operator_max::lhs_op1_relation): New.
        (operator_max::lhs_op2_relation): New.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/vrp-and-or-relation-1.c: New test.
---
 gcc/range-op-mixed.h                          | 20 +++++
 gcc/range-op.cc                               | 80 ++++++++++++++++++-
 .../gcc.dg/tree-ssa/vrp-and-or-relation-1.c   | 61 ++++++++++++++
 3 files changed, 160 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-and-or-relation-1.c

diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h
index b084c3edeab..a294958ce8f 100644
--- a/gcc/range-op-mixed.h
+++ b/gcc/range-op-mixed.h
@@ -852,8 +852,12 @@ public:
   using range_operator::fold_range;
   using range_operator::op1_range;
   using range_operator::op2_range;
+  using range_operator::lhs_op1_relation;
   using range_operator::op1_op2_relation_effect;
   using range_operator::update_bitmask;
+  relation_kind lhs_op1_relation (const irange &lhs,
+                                 const irange &op1, const irange &op2,
+                                 relation_kind) const final override;
 
   bool fold_range (prange &r, tree type,
                   const prange &op1,
@@ -922,7 +926,15 @@ class operator_min : public range_operator
 {
 public:
   using range_operator::fold_range;
+  using range_operator::lhs_op1_relation;
+  using range_operator::lhs_op2_relation;
   using range_operator::update_bitmask;
+  relation_kind lhs_op1_relation (const irange &lhs,
+                                 const irange &op1, const irange &op2,
+                                 relation_kind) const final override;
+  relation_kind lhs_op2_relation (const irange &lhs,
+                                 const irange &op1, const irange &op2,
+                                 relation_kind) const final override;
   bool fold_range (prange &r, tree type,
                   const prange &op1,
                   const prange &op2,
@@ -942,7 +954,15 @@ class operator_max : public range_operator
 {
 public:
   using range_operator::fold_range;
+  using range_operator::lhs_op1_relation;
+  using range_operator::lhs_op2_relation;
   using range_operator::update_bitmask;
+  relation_kind lhs_op1_relation (const irange &lhs,
+                                 const irange &op1, const irange &op2,
+                                 relation_kind) const final override;
+  relation_kind lhs_op2_relation (const irange &lhs,
+                                 const irange &op1, const irange &op2,
+                                 relation_kind) const final override;
   bool fold_range (prange &r, tree type,
                   const prange &op1,
                   const prange &op2,
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index 07318bca71e..3f302bdc1d1 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -2180,6 +2180,36 @@ operator_min::wi_fold (irange &r, tree type,
   value_range_with_overflow (r, type, new_lb, new_ub);
 }
 
+// When op1 <= op2, MIN (op1, op2) is op1.
+
+relation_kind
+operator_min::lhs_op1_relation (const irange &lhs, const irange &op1,
+                               const irange &op2, relation_kind rel) const
+{
+  if (lhs.undefined_p () || op1.undefined_p () || op2.undefined_p ())
+    return VREL_VARYING;
+  if (rel == VREL_LE || rel == VREL_LT || rel == VREL_EQ
+      || wi::le_p (op1.upper_bound (), op2.lower_bound (),
+                  TYPE_SIGN (op1.type ())))
+    return VREL_EQ;
+  return VREL_VARYING;
+}
+
+// Same as above, but for op2.
+
+relation_kind
+operator_min::lhs_op2_relation (const irange &lhs, const irange &op1,
+                               const irange &op2, relation_kind rel) const
+{
+  if (lhs.undefined_p () || op1.undefined_p () || op2.undefined_p ())
+    return VREL_VARYING;
+  if (rel == VREL_GE || rel == VREL_GT || rel == VREL_EQ
+      || wi::le_p (op2.upper_bound (), op1.lower_bound (),
+                  TYPE_SIGN (op1.type ())))
+    return VREL_EQ;
+  return VREL_VARYING;
+}
+
 
 void
 operator_max::update_bitmask (irange &r, const irange &lh,
@@ -2199,6 +2229,36 @@ operator_max::wi_fold (irange &r, tree type,
   value_range_with_overflow (r, type, new_lb, new_ub);
 }
 
+// When op1 >= op2, MAX (op1, op2) is op1.
+
+relation_kind
+operator_max::lhs_op1_relation (const irange &lhs, const irange &op1,
+                               const irange &op2, relation_kind rel) const
+{
+  if (lhs.undefined_p () || op1.undefined_p () || op2.undefined_p ())
+    return VREL_VARYING;
+  if (rel == VREL_GE || rel == VREL_GT || rel == VREL_EQ
+      || wi::ge_p (op1.lower_bound (), op2.upper_bound (),
+                  TYPE_SIGN (op1.type ())))
+    return VREL_EQ;
+  return VREL_VARYING;
+}
+
+// Same as above, but for op2.
+
+relation_kind
+operator_max::lhs_op2_relation (const irange &lhs, const irange &op1,
+                               const irange &op2, relation_kind rel) const
+{
+  if (lhs.undefined_p () || op1.undefined_p () || op2.undefined_p ())
+    return VREL_VARYING;
+  if (rel == VREL_LE || rel == VREL_LT || rel == VREL_EQ
+      || wi::ge_p (op2.lower_bound (), op1.upper_bound (),
+                  TYPE_SIGN (op1.type ())))
+    return VREL_EQ;
+  return VREL_VARYING;
+}
+
 
 // Calculate the cross product of two sets of ranges and return it.
 //
@@ -3568,10 +3628,13 @@ relation_kind
 operator_bitwise_and::lhs_op1_relation (const irange &lhs,
                                        const irange &op1,
                                        const irange &op2,
-                                       relation_kind) const
+                                       relation_kind rel) const
 {
   if (lhs.undefined_p () || op1.undefined_p () || op2.undefined_p ())
     return VREL_VARYING;
+  // When x == y, x & y is x.
+  if (rel == VREL_EQ)
+    return VREL_EQ;
   if (!op2.singleton_p ())
     return VREL_VARYING;
   // if val == 0xff or 0xFFFF OR 0Xffffffff OR 0Xffffffffffffffff, return TRUE
@@ -4134,6 +4197,21 @@ operator_bitwise_or::wi_fold (irange &r, tree type,
   value_range_with_overflow (r, type, new_lb, new_ub);
 }
 
+// When x == y, x | y is x.
+
+relation_kind
+operator_bitwise_or::lhs_op1_relation (const irange &lhs,
+                                      const irange &op1,
+                                      const irange &op2,
+                                      relation_kind rel) const
+{
+  if (lhs.undefined_p () || op1.undefined_p () || op2.undefined_p ())
+    return VREL_VARYING;
+  if (rel == VREL_EQ)
+    return VREL_EQ;
+  return VREL_VARYING;
+}
+
 bool
 operator_bitwise_or::op1_op2_relation_effect (irange &lhs_range,
                                              tree type,
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-and-or-relation-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/vrp-and-or-relation-1.c
new file mode 100644
index 00000000000..cb17e56be4f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-and-or-relation-1.c
@@ -0,0 +1,61 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fgimple -fdump-tree-evrp" } */
+
+/* x & y and x | y are x when x == y: range-op records that relation
+   when the statement is folded, and the test against x folds.  */
+
+void link_error (void);
+
+void __GIMPLE (ssa,startwith("evrp"))
+and_it (int x, int y)
+{
+  int t;
+
+  __BB(2):
+  if (x_1(D) == y_2(D))
+    goto __BB3;
+  else
+    goto __BB5;
+
+  __BB(3):
+  t_3 = x_1(D) & y_2(D);
+  if (t_3 != x_1(D))
+    goto __BB4;
+  else
+    goto __BB5;
+
+  __BB(4):
+  link_error ();
+  goto __BB5;
+
+  __BB(5):
+  return;
+}
+
+void __GIMPLE (ssa,startwith("evrp"))
+or_it (int x, int y)
+{
+  int t;
+
+  __BB(2):
+  if (x_1(D) == y_2(D))
+    goto __BB3;
+  else
+    goto __BB5;
+
+  __BB(3):
+  t_3 = x_1(D) | y_2(D);
+  if (t_3 != x_1(D))
+    goto __BB4;
+  else
+    goto __BB5;
+
+  __BB(4):
+  link_error ();
+  goto __BB5;
+
+  __BB(5):
+  return;
+}
+
+/* { dg-final { scan-tree-dump-not "link_error" "evrp" } } */
-- 
2.53.0

Reply via email to