On Sat, Oct 3, 2026 at 11:01 PM Aldy Hernandez <[email protected]> wrote:
>
> 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.

I think this fixes https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126750
.  I would have to look to double check.

>
> 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
>

Reply via email to