On Tue, Aug 25, 2026 at 6:20 AM Jian Li <[email protected]> wrote:
>
> This patch allows the operator_plus::op1_op2_relation_effect to record
> the known least significant bit (which is zero) for both full and partial
> equivalences of PLUS_EXPR operands.
>
> The normal update_bitmask hook, which computes a bitmask from operand ranges,
> previously replaced the refined bitmask from the relation effect if run 
> afterward,
> losing the known-zero bit. This patch adjusts the order to run normal bitmask
> propagation first and apply the relation effect last, ensuring the constraint
> from the relation intersects with the complete result.
>
> Existing range-op tests have been expanded to include selftests for VREL_EQ
> and partial equivalences. Additionally, an EVRP regression test with DOM 
> disabled
> and a -ftrapv test (to verify the retention of potentially trapping additions)
> have been added.
>
> Bootstrapped and regression tested on x86_64-pc-linux-gnu.
>
> PR tree-optimization/126877
>
> gcc/ChangeLog:
>
>         * range-op-mixed.h (class operator_plus): Expose and override
>         op1_op2_relation_effect.
>         * range-op.cc (range_operator::fold_range): Apply relation effects
>         after normal bitmask propagation.
>         (operator_plus::op1_op2_relation_effect): New.
>         (range_op_plus_relation_tests): New.
>         (range_op_tests): Call it.
>
> gcc/testsuite/ChangeLog:
>
>         * 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                            | 51 ++++++++++++++++++++--
>  gcc/testsuite/gcc.dg/tree-ssa/pr126877-2.c | 15 +++++++
>  gcc/testsuite/gcc.dg/tree-ssa/pr126877.c   | 18 ++++++++
>  4 files changed, 86 insertions(+), 4 deletions(-)
>  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..906bad196b1 100644
> --- a/gcc/range-op.cc
> +++ b/gcc/range-op.cc
> @@ -735,8 +735,8 @@ range_operator::fold_range (irange &r, tree type,
>           if (r.varying_p ())
>             break;
>         }
> -      op1_op2_relation_effect (r, type, lh, rh, rel);
>        update_bitmask (r, lh, rh);
> +      op1_op2_relation_effect (r, type, lh, rh, rel);
>        return true;

I am kind of cautious of this change.  Maybe we should do the
update_bitmask only inside operator_plus::op1_op2_relation_effect for
the relation_equiv_p case since that is the only case I think needs it
beforehand.
I can think of times where the update_bitmask afterwards would be
needed after op1_op2_relation_effect (since many intersects with
either zero or non-zero or non-negative in another case).

>      }
>
> @@ -747,8 +747,8 @@ range_operator::fold_range (irange &r, tree type,
>      {
>        wi_fold_in_parts (r, type, lh.lower_bound (), lh.upper_bound (),
>                         rh.lower_bound (), rh.upper_bound ());
> -      op1_op2_relation_effect (r, type, lh, rh, rel);
>        update_bitmask (r, lh, rh);
> +      op1_op2_relation_effect (r, type, lh, rh, rel);
>        return true;
>      }
>
> @@ -765,13 +765,13 @@ range_operator::fold_range (irange &r, tree type,
>         r.union_ (tmp);
>         if (r.varying_p ())
>           {
> -           op1_op2_relation_effect (r, type, lh, rh, rel);
>             update_bitmask (r, lh, rh);
> +           op1_op2_relation_effect (r, type, lh, rh, rel);
>             return true;
>           }
>        }
> -  op1_op2_relation_effect (r, type, lh, rh, rel);
>    update_bitmask (r, lh, rh);
> +  op1_op2_relation_effect (r, type, lh, rh, rel);
>    return true;
>  }
>
> @@ -1716,6 +1716,28 @@ operator_plus::update_bitmask (irange &r, const irange 
> &lh,
>    update_known_bitmask (r, PLUS_EXPR, lh, rh);
>  }
>
> +// 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
>  // between LHS and OP1.
>
> @@ -5436,6 +5458,26 @@ 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);
> +
> +  relation_kind relations[] = {
> +    VREL_EQ, VREL_PE8, VREL_PE16, VREL_PE32, VREL_PE64
> +  };
> +  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 +5486,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