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

Reply via email to