On Thu, Sep 10, 2026 at 2:50 AM Aldy Hernandez <[email protected]> wrote:
>
> [Andrew, are you OK with this?  It seems simple enough, but I may be
> missing something subtle, or perhaps there is another place in the
> compiler we should be fixing this?  Either way, it seems something
> good to implement for completeness sake.  OK for trunk?]
>
> A conditional such as:
>
>   d_5 = a_3 - b_4;
>   if (d_5 == 0)
>
> implies a_3 == b_4 on the true edge regardless of overflow behavior,
> but no relation was recorded because operator_minus lacks
> op1_op2_relation and register_outgoing_edges only derives relations
> from boolean exports.  Add the former and relax the latter.
>
> match.pd already folds this:
>
>   /* Transform comparisons of the form X - Y CMP 0 to X CMP Y.  */
>   (cmp (sub@2 @0 @1) integer_zerop)
>    (if (single_use (@2)) (cmp @0 @1))
>
> but only for a single-use difference.
>
> Tested on ppc64le Linux.
>
> gcc/ChangeLog:
>
>         * range-op-mixed.h (class operator_minus): Add op1_op2_relation.
>         * range-op.cc (operator_minus::op1_op2_relation): New.
>         * gimple-range-fold.cc (fur_source::register_outgoing_edges):
>         Derive relations from any integral export, not just booleans.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/tree-ssa/vrp-minus-relation-1.c: New test.
> ---
>  gcc/gimple-range-fold.cc                      |  5 ++++-
>  gcc/range-op-mixed.h                          |  3 +++
>  gcc/range-op.cc                               | 19 +++++++++++++++++++
>  .../gcc.dg/tree-ssa/vrp-minus-relation-1.c    | 17 +++++++++++++++++
>  4 files changed, 43 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-minus-relation-1.c
>
> diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
> index abe3ade2960..a143c19647a 100644
> --- a/gcc/gimple-range-fold.cc
> +++ b/gcc/gimple-range-fold.cc
> @@ -1751,9 +1751,12 @@ fur_source::register_outgoing_edges (gcond *s, irange 
> &lhs_range,
>    // leading to the condition such as:
>    // c_2 = a_4 < b_7
>    // if (c_2)
> +  // as well as relations implied by a known result, such as:
> +  // c_2 = a_4 - b_7
> +  // if (c_2 == 0)
>    FOR_EACH_GORI_EXPORT_NAME (gori_ssa (), bb, name)
>      {
> -      if (TREE_CODE (TREE_TYPE (name)) != BOOLEAN_TYPE)
> +      if (!INTEGRAL_TYPE_P (TREE_TYPE (name)))
>         continue;
>        gimple *stmt = SSA_NAME_DEF_STMT (name);
>        gimple_range_op_handler handler (stmt);
> diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h
> index 0e97fb11688..b084c3edeab 100644
> --- a/gcc/range-op-mixed.h
> +++ b/gcc/range-op-mixed.h
> @@ -648,6 +648,7 @@ public:
>    using range_operator::op1_range;
>    using range_operator::op2_range;
>    using range_operator::lhs_op1_relation;
> +  using range_operator::op1_op2_relation;
>    using range_operator::op1_op2_relation_effect;
>    using range_operator::update_bitmask;
>    bool op1_range (irange &r, tree type,
> @@ -668,6 +669,8 @@ public:
>    relation_kind lhs_op1_relation (const irange &lhs,
>                                   const irange &op1, const irange &op2,
>                                   relation_kind rel) const final override;
> +  relation_kind op1_op2_relation (const irange &lhs, const irange &,
> +                                 const irange &) const final override;
>    bool op1_op2_relation_effect (irange &lhs_range, tree type,
>                                 const irange &op1_range,
>                                 const irange &op2_range,
> diff --git a/gcc/range-op.cc b/gcc/range-op.cc
> index 350548951f4..07318bca71e 100644
> --- a/gcc/range-op.cc
> +++ b/gcc/range-op.cc
> @@ -2102,6 +2102,25 @@ minus_op1_op2_relation_effect (irange &lhs_range, tree 
> type,
>    return true;
>  }
>
> +// Check if the LHS range indicates a relation between OP1 and OP2.
> +// OP1 - OP2 == 0 implies OP1 == OP2 even with wrapping arithmetic.
> +
> +relation_kind
> +operator_minus::op1_op2_relation (const irange &lhs, const irange &,
> +                                 const irange &) const
> +{
> +  if (lhs.undefined_p ())
> +    return VREL_UNDEFINED;
> +
> +  if (lhs.zero_p ())
> +    return VREL_EQ;
> +
> +  if (!lhs.contains_zero_p ())
> +    return VREL_NE;

There is another relationship which we could add.
If both rhs1 are non-negative and the type is signed, then if the lhs
is non-negative, then we have VREL_GE. If it is non-positive then we
have VREL_LE.

That is (for non-negative a and b):
t = a - b;
if (t < 0)
here a < b is true on the true branch and >= on the false branch.

A simple testcase for that:
```
int f(int a, int b)
{
  a&=0xff;
  b&=0xff;
  int t = a - b;
  if (t < 0)
  {
      return a < b;
  }
  return 0;
}
```

Should I file a bug for this case?

> +
> +  return VREL_VARYING;
> +}
> +
>  bool
>  operator_minus::op1_op2_relation_effect (irange &lhs_range, tree type,
>                                          const irange &op1_range,
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-minus-relation-1.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/vrp-minus-relation-1.c
> new file mode 100644
> index 00000000000..f071ffa5796
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-minus-relation-1.c
> @@ -0,0 +1,17 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-vrp1" } */
> +
> +void dead (void);
> +
> +void f (int a, int b)
> +{
> +  int d = a - b;
> +  if (d < 0)
> +    return;
> +  if (d > 0)
> +    return;
> +  if (a != b)
> +    dead ();
> +}
> +
> +/* { dg-final { scan-tree-dump-not "dead" "vrp1" } } */
> --
> 2.53.0
>

Reply via email to