https://gcc.gnu.org/g:7b3563c38b7b1e7b611ae90152cd35a1a2e3ca47
commit r17-4863-g7b3563c38b7b1e7b611ae90152cd35a1a2e3ca47 Author: Aldy Hernandez <[email protected]> Date: Sat Oct 3 15:18:47 2026 +0000 range-op: implement lhs_op[12]_relation for AND, OR, MIN, and MAX 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. 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. Diff: --- 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(-) diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h index b084c3edeab5..a294958ce8ff 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 07318bca71ee..3f302bdc1d19 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 000000000000..cb17e56be4f2 --- /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" } } */
