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
>