On Thu, Nov 12, 2026 at 12:57 PM Andrew MacLeod <[email protected]> wrote:
>
> If there is a relation between op1 and op2, an unsigned minus operation
> can sometimes do better than simply non-zero.
>
> For unsigned, we can calculate more precise ranges for <, <=, > and >=, just 
> like we do for signed values.
>
> I adjusted the testcase to also test the other unsigned relations variations.
>
> Bootstrapped on x86_64-pc-linux-gnu with no regressions.  Pushed.

See 
https://inbox.sourceware.org/gcc-patches/[email protected]/
and 
https://inbox.sourceware.org/gcc-patches/[email protected]/
for 2 related patches that need to be approved.


> Andrew
>
> Assisted-by: Claude Opus 5
>
>         PR tree-optimization/127915
>         gcc/
>         * range-op.cc (minus_op1_op2_relation_effect): Handle more
>         unsigned range restrictions with known relations.
>
>         gcc/testsuite/
>         * gcc.dg/pr127915.c: New.
> ---
>  gcc/range-op.cc                 | 45 +++++++++++++++++++++++++--
>  gcc/testsuite/gcc.dg/pr127915.c | 54 +++++++++++++++++++++++++++++++++
>  2 files changed, 96 insertions(+), 3 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/pr127915.c
>
> diff --git a/gcc/range-op.cc b/gcc/range-op.cc
> index d08193ce41d..c80d611f391 100644
> --- a/gcc/range-op.cc
> +++ b/gcc/range-op.cc
> @@ -2090,7 +2090,7 @@ operator_minus::lhs_op1_relation (const irange &, const 
> irange &op1,
>
>  bool
>  minus_op1_op2_relation_effect (irange &lhs_range, tree type,
> -                              const irange &op1_range ATTRIBUTE_UNUSED,
> +                              const irange &op1_range,
>                                const irange &op2_range ATTRIBUTE_UNUSED,
>                                relation_kind rel)
>  {
> @@ -2107,12 +2107,51 @@ minus_op1_op2_relation_effect (irange &lhs_range, 
> tree type,
>    else if (rel == VREL_NE)
>      rel_range = int_range<2> (type, wi::zero (prec), wi::zero (prec),
>                               VR_ANTI_RANGE);
> +  else if (TYPE_UNSIGNED (type))
> +    {
> +      wide_int ub1 = op1_range.upper_bound ();
> +      wide_int ub2 = op2_range.upper_bound ();
> +      switch (rel)
> +       {
> +         // op1 > op2, so the subtraction cannot wrap : [1, UB (op1)].
> +         case VREL_GT:
> +           // [0, 0] > op2 is undefined, so just return.
> +           if (ub1 == 0)
> +             return false;
> +           rel_range = int_range<2> (type, wi::one (prec), ub1);
> +           break;
> +         // op1 >= op2 >= 0, so the subtraction cannot wrap : [0, UB (op1)].
> +         case VREL_GE:
> +           rel_range = int_range<2> (type, wi::zero (prec), ub1);
> +           break;
> +         // op1 < op2, so the subtraction always wraps : [-UB (op2), -1].
> +         case VREL_LT:
> +           // op1 < [0, 0] is undefined, so just return.
> +           if (ub2 == 0)
> +             return false;
> +           rel_range = int_range<2> (type, wi::neg (ub2),
> +                                     wi::minus_one (prec));
> +           break;
> +         // As VREL_LT, except that op1 == op2 also produces zero.
> +         case VREL_LE:
> +           // If UB (op2) is MAX_INT the result won't change, plus
> +           // we dont want to construct an illegal  [1, 0] range next.
> +           if (ub2 == wi::max_value (prec, UNSIGNED))
> +             return false;
> +           rel_range = int_range<2> (type, wi::one (prec),
> +                                     wi::sub (wi::minus_one (prec), ub2),
> +                                     VR_ANTI_RANGE);
> +           break;
> +         default:
> +           return false;
> +       }
> +    }
>    else if (TYPE_OVERFLOW_WRAPS (type))
>      {
>        switch (rel)
>         {
> -         // For wrapping signed values and unsigned, if op1 > op2 or
> -         // op1 < op2, then op1 - op2 can be restricted to ~[0, 0].
> +         // For wrapping signed values , if op1 > op2 or op1 < op2,
> +         // then op1 - op2 can be restricted to ~[0, 0].
>           case VREL_GT:
>           case VREL_LT:
>               rel_range = int_range<2> (type, wi::zero (prec), wi::zero 
> (prec),
> diff --git a/gcc/testsuite/gcc.dg/pr127915.c b/gcc/testsuite/gcc.dg/pr127915.c
> new file mode 100644
> index 00000000000..cd8650b6efc
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr127915.c
> @@ -0,0 +1,54 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-evrp" } */
> +
> +/* last >= first and last <= 100, so last - first is [0, 100].  */
> +void
> +f1 (unsigned first, unsigned last)
> +{
> +  if (first > last || last > 100)
> +    __builtin_unreachable ();
> +
> +  unsigned t = last - first;
> +  if (t > 100)
> +    __builtin_trap ();
> +}
> +
> +/* last > first and last <= 100, so last - first is [1, 100].  */
> +void
> +f2 (unsigned first, unsigned last)
> +{
> +  if (first >= last || last > 100)
> +    __builtin_unreachable ();
> +
> +  unsigned t = last - first;
> +  if (t == 0 || t > 100)
> +    __builtin_trap ();
> +}
> +
> +/* first < last and last <= 100.  The subtraction always wraps, so
> +   first - last is [-100, -1].  */
> +void
> +f3 (unsigned first, unsigned last)
> +{
> +  if (first >= last || last > 100)
> +    __builtin_unreachable ();
> +
> +  unsigned t = first - last;
> +  if (t < (unsigned) -100)
> +    __builtin_trap ();
> +}
> +
> +/* first <= last and last <= 100.  As f3, except that first == last also
> +   produces zero, giving ~[1, -101].  */
> +void
> +f4 (unsigned first, unsigned last)
> +{
> +  if (first > last || last > 100)
> +    __builtin_unreachable ();
> +
> +  unsigned t = first - last;
> +  if (t != 0 && t < (unsigned) -100)
> +    __builtin_trap ();
> +}
> +
> +/* { dg-final { scan-tree-dump-not "__builtin_trap" "evrp" } } */
> --
> 2.56.0
>

Reply via email to