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