When the operands of PLUS_EXPR are equivalent, their least significant
bits are equal and the least significant bit of their sum is therefore
zero. Teach operator_plus::op1_op2_relation_effect to record that
known bit for both full and partial equivalences. Do not apply the
transformation to types whose overflow traps.
The normal PLUS_EXPR bitmask update replaces the explicit mask in the
result range. Compute that mask in a temporary range and intersect it
with the result instead, preserving constraints already added by the
relation effect.
Add range-op selftests for ordinary PLUS propagation with no effective
relation, preserving an existing result mask, VREL_EQ, and partial
equivalences. Add an EVRP regression test with DOM disabled and a
-ftrapv test that verifies the potentially trapping addition is retained.
Bootstrapped and regression tested on x86_64-pc-linux-gnu.
gcc/ChangeLog:
PR tree-optimization/126877
* range-op-mixed.h (class operator_plus): Expose and override
op1_op2_relation_effect.
* range-op.cc (operator_plus::update_bitmask): Preserve existing
result constraints.
(operator_plus::op1_op2_relation_effect): New.
(range_op_plus_relation_tests): New.
(range_op_tests): Call it.
gcc/testsuite/ChangeLog:
PR tree-optimization/126877
* gcc.dg/tree-ssa/pr126877.c: New test.
* gcc.dg/tree-ssa/pr126877-2.c: New test.
Signed-off-by: Jian Li <[email protected]>
---
gcc/range-op-mixed.h | 6 ++
gcc/range-op.cc | 80 +++++++++++++++++++++-
gcc/testsuite/gcc.dg/tree-ssa/pr126877-2.c | 15 ++++
gcc/testsuite/gcc.dg/tree-ssa/pr126877.c | 18 +++++
4 files changed, 118 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr126877-2.c
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr126877.c
diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h
index 0e97fb11688..bb0034ebf19 100644
--- a/gcc/range-op-mixed.h
+++ b/gcc/range-op-mixed.h
@@ -576,6 +576,7 @@ public:
using range_operator::lhs_op1_relation;
using range_operator::lhs_op2_relation;
using range_operator::update_bitmask;
+ using range_operator::op1_op2_relation_effect;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &op2,
relation_trio) const final override;
@@ -599,6 +600,11 @@ public:
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
+ bool op1_op2_relation_effect (irange &lhs_range, tree type,
+ const irange &op1_range,
+ const irange &op2_range,
+ relation_kind rel) const final override;
+
bool overflow_free_p (const irange &lh, const irange &rh,
relation_trio = TRIO_VARYING) const final override;
// Check compatibility of all operands.
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index 4199300f4c8..068d31db564 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -1713,7 +1713,31 @@ void
operator_plus::update_bitmask (irange &r, const irange &lh,
const irange &rh) const
{
- update_known_bitmask (r, PLUS_EXPR, lh, rh);
+ int_range_max tmp (r);
+ update_known_bitmask (tmp, PLUS_EXPR, lh, rh);
+ r.intersect (tmp);
+}
+
+// If both operands are equivalent, their low bit is the same, so the low
+// bit of their sum is known to be zero.
+
+bool
+operator_plus::op1_op2_relation_effect (irange &lhs_range, tree type,
+ const irange &,
+ const irange &,
+ relation_kind rel) const
+{
+ if (!relation_equiv_p (rel)
+ || TYPE_OVERFLOW_TRAPS (type))
+ return false;
+
+ unsigned prec = TYPE_PRECISION (type);
+ int_range<2> rel_range;
+ rel_range.set_varying (type);
+ rel_range.update_bitmask
+ (irange_bitmask (wi::zero (prec), wi::minus_one (prec) << 1));
+ lhs_range.intersect (rel_range);
+ return true;
}
// Check to see if the range of OP2 indicates anything about the relation
@@ -5436,6 +5460,59 @@ range_relational_tests ()
ASSERT_TRUE (code == VREL_LT);
}
+static void
+range_op_plus_relation_tests ()
+{
+ int_range_max result;
+ int_range<1> op1 (integer_type_node);
+ int_range<1> op2 (integer_type_node);
+ int_range_max normal_result;
+ int_range_max unrelated_result;
+
+ relation_kind relations[] = {
+ VREL_EQ, VREL_PE8, VREL_PE16, VREL_PE32, VREL_PE64
+ };
+
+ // A normal PLUS bitmask update must preserve constraints already present
+ // in the result range.
+ result.set_varying (integer_type_node);
+ unsigned prec = TYPE_PRECISION (integer_type_node);
+ result.update_bitmask
+ (irange_bitmask (wi::zero (prec), wi::minus_one (prec) << 1));
+ op_plus.update_bitmask (result, op1, op2);
+ ASSERT_FALSE (result.get_bitmask ().member_p (INT (1)));
+
+ // Without an operand relation, normal PLUS bitmask propagation must still
+ // produce the expected result.
+ op1.set_varying (integer_type_node);
+ op2.set_varying (integer_type_node);
+ op1.update_bitmask
+ (irange_bitmask (wi::zero (prec), wi::minus_one (prec) << 1));
+ op2.update_bitmask
+ (irange_bitmask (wi::zero (prec), wi::minus_one (prec) << 1));
+ op_plus.fold_range (normal_result, integer_type_node, op1, op2,
+ TRIO_VARYING);
+ ASSERT_FALSE (normal_result.get_bitmask ().member_p (INT (1)));
+ ASSERT_TRUE (normal_result.get_bitmask ().member_p (INT (2)));
+
+ // Calling the relation hook with an unrelated relation must not suppress
+ // or otherwise change normal PLUS bitmask propagation.
+ op_plus.fold_range (unrelated_result, integer_type_node, op1, op2,
+ relation_trio::op1_op2 (VREL_NE));
+ ASSERT_TRUE (unrelated_result == normal_result);
+
+ op1.set_varying (integer_type_node);
+ op2.set_varying (integer_type_node);
+ for (relation_kind rel : relations)
+ {
+ op_plus.fold_range (result, integer_type_node, op1, op2,
+ relation_trio::op1_op2 (rel));
+ irange_bitmask bm = result.get_bitmask ();
+ ASSERT_FALSE (bm.member_p (INT (1)));
+ ASSERT_TRUE (bm.member_p (INT (2)));
+ }
+}
+
void
range_op_tests ()
{
@@ -5444,6 +5521,7 @@ range_op_tests ()
range_op_bitwise_and_tests ();
range_op_cast_tests ();
range_relational_tests ();
+ range_op_plus_relation_tests ();
extern void range_op_float_tests ();
range_op_float_tests ();
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr126877-2.c
b/gcc/testsuite/gcc.dg/tree-ssa/pr126877-2.c
new file mode 100644
index 00000000000..62b31bab82a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126877-2.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -ftrapv -fdump-tree-evrp" } */
+
+int
+f (int a, int b)
+{
+ if (a == b)
+ {
+ int c = a + b;
+ return c & 1;
+ }
+ return 1;
+}
+
+/* { dg-final { scan-tree-dump-times " \\+ " 1 "evrp" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr126877.c
b/gcc/testsuite/gcc.dg/tree-ssa/pr126877.c
new file mode 100644
index 00000000000..5ae06d75d6e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126877.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdisable-tree-dom1 -fdisable-tree-dom2
-fdisable-tree-dom3 -fdump-tree-evrp" } */
+
+int
+f (int a, int b)
+{
+ if (a == b)
+ {
+ int c = a + b;
+ return c & 1;
+ }
+ return 1;
+}
+
+/* The relation between distinct SSA names must be sufficient to make the
+ addition's low bit known zero. */
+/* { dg-final { scan-tree-dump-not " \\+ " "evrp" } } */
+/* { dg-final { scan-tree-dump-not " & 1" "evrp" } } */
--
2.43.0