On Mon, Jun 1, 2026 at 3:26 PM Andrew MacLeod <[email protected]> wrote:
>
>
> On 6/1/26 16:08, Andrew Pinski wrote:
> > When I was looking into fixing tree_expr_nonnegative_p not to be recusive,
> > we should have tree_expr_nonnegative_p use the ranger.
> > I also didn't realize I wrote this patch before so this is
> > the updated version of the already approved:
> > https://gcc.gnu.org/pipermail/gcc-patches/2023-October/634205.html
> > Updated for the review comments.
> >
> > Note testsuite/g++.dg/ipa/pure-const-3.C testcase will always fail as we can
> > use the fact the argument is always non-negative in many different places 
> > now.
> > Since there is no way to test it, let's remove the testcase.
> >
> > Bootstrapped and tested on x86_64-linux-gnu.
> >
> >       PR tree-optimization/111959
> >
> > gcc/ChangeLog:
> >
> >       * fold-const.cc (tree_single_nonnegative_p): Use the range to see
> >       if the SSA_NAME was nonnegative.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * gcc.dg/pr80776-1.c: xfail and update comment.
> >       * gcc.dg/tree-ssa/forwprop-44.c: New test.
> >       * testsuite/g++.dg/ipa/pure-const-3.C: Remove.
> >
> > Signed-off-by: Andrew Pinski <[email protected]>
> > ---
> >   gcc/fold-const.cc                           | 13 +++++++++++++
> >   gcc/testsuite/g++.dg/ipa/pure-const-3.C     |  6 ------
> >   gcc/testsuite/gcc.dg/pr80776-1.c            | 10 +++++-----
> >   gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c | 14 ++++++++++++++
> >   4 files changed, 32 insertions(+), 11 deletions(-)
> >   delete mode 100644 gcc/testsuite/g++.dg/ipa/pure-const-3.C
> >   create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/forwprop-44.c
> >
> > diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> > index 179c7e167a3..bc690b24663 100644
> > --- a/gcc/fold-const.cc
> > +++ b/gcc/fold-const.cc
> > @@ -14576,6 +14576,19 @@ tree_single_nonnegative_p (tree t, int depth)
> >         return RECURSE (TREE_OPERAND (t, 1)) && RECURSE (TREE_OPERAND (t, 
> > 2));
> >
> >       case SSA_NAME:
> > +      /* For integral types, query the range if possible. */
> > +      if (INTEGRAL_TYPE_P (TREE_TYPE (t)))
> > +     {
> > +       int_range_max r;
> > +       get_global_range_query ()->range_of_expr (r, t);
>
>
> IS there a reason not to use get_range_query() ? get_global_range_query
> will only uses ranges from SSA_NAME_RANGE_INFO(ssaname), and is the
> fallback if there is no active ranger...   You will not get any
> contextual info this way, just global values.

I was not 100% sure it would always be valid to do the folding that
way. I had tested it both ways though. The same testcases needed to be
changed.

>
>
> > +       if (!r.undefined_p () && !r.varying_p())
> > +         {
> > +           if (r.nonnegative_p ())
> > +             return true;
> > +           if (r.nonpositive_p () && !range_includes_zero_p (r))
> > +             return false;
> > +         }
> > +     }
> >         /* Limit the depth of recursion to avoid quadratic behavior.
> >        This is expected to catch almost all occurrences in practice.
> >        If this code misses important cases that unbounded recursion
> > diff --git a/gcc/testsuite/g++.dg/ipa/pure-const-3.C 
> > b/gcc/testsuite/g++.dg/ipa/pure-const-3.C
> > deleted file mode 100644
> > index 62d355b4ce7..00000000000
> > --- a/gcc/testsuite/g++.dg/ipa/pure-const-3.C
> > +++ /dev/null
> > @@ -1,6 +0,0 @@
> > -/* { dg-do compile } */
> > -/* { dg-options "-O2 -fno-ipa-vrp -fdump-tree-optimized -fno-tree-ccp 
> > -fdisable-tree-evrp -fdisable-tree-vrp1 -fdisable-tree-vrp2 
> > -fno-thread-jumps -fno-tree-dominator-opts"  } */
> > -
> > -#include "pure-const-3.h"
> > -
> > -/* { dg-final { scan-tree-dump "barvar"  "optimized"  } } */
> > diff --git a/gcc/testsuite/gcc.dg/pr80776-1.c 
> > b/gcc/testsuite/gcc.dg/pr80776-1.c
> > index b9bce62d982..bf0fcd86a65 100644
> > --- a/gcc/testsuite/gcc.dg/pr80776-1.c
> > +++ b/gcc/testsuite/gcc.dg/pr80776-1.c
> > @@ -18,14 +18,14 @@ Foo (void)
> >     if (! (0 <= i && i <= 999999))
> >       __builtin_unreachable ();
> >
> > -  /* Legacy evrp sets the range of i to [0, MAX] *before* the first 
> > conditional,
> > -     and to [0,999999] *before* the second conditional.  This is because 
> > both
> > -     evrp and VRP use trickery to set global ranges when this particular 
> > use of
> > +  /* vrp1 sets the range of i to [0, MAX] *before* the first conditional,
> > +     and to [0,999999] *before* the second conditional.  This is because
> > +     vrp use trickery to set global ranges when this particular use of
> >        a __builtin_unreachable is in play (see uses of
> >        assert_unreachable_fallthru_edge_p).
> >
> > -     Setting these ranges at the definition site, causes VRP to remove the
> > +     Setting these ranges at the definition site, causes other to remove 
> > the
> >        unreachable code altogether, leaving the following sprintf 
> > unguarded.  This
>
> And as a side note, These comments should not be true any more. no
> version of VRP does any trickery with regards to builtin_unreachable.
>
> i_4 is VARYING until we get to BB4, at which point it is [0, +INF].
> AFTER the conditional :)
>
> =========== BB 2 ============
> Imports: i_4
> Exports: i_4
>      <bb 2> :
>      i_4 = somerandom ();
>      if (i_4 < 0)
>        goto <bb 3>; [INV]
>      else
>        goto <bb 4>; [INV]
>
> 2->3  (T) i_4 :         [irange] int [-INF, -1]
> 2->4  (F) i_4 :         [irange] int [0, +INF]
>
> =========== BB 3 ============
>      <bb 3> :
>      __builtin_unreachable ();
>
>
> =========== BB 4 ============
> Imports: i_4
> Exports: i.0_1  i_4
>           i.0_1 : i_4(I)
> i_4     [irange] int [0, +INF]
> Partial equiv (i.0_1 pe32 i_4)
>      <bb 4> :
>      i.0_1 = (unsigned int) i_4;
>      if (i.0_1 > 999999)
>        goto <bb 5>; [INV]
>      else
>        goto <bb 6>; [INV]
>
> i.0_1 : [irange] unsigned int [0, 2147483647] MASK 0x7fffffff VALUE 0x0
> 4->5  (T) i.0_1 :       [irange] unsigned int [1000000, 2147483647] MASK
> 0x7fffffff VALUE 0x0
> 4->5  (T) i_4 :         [irange] int [1000000, +INF] MASK 0x7fffffff
> VALUE 0x0
> 4->6  (F) i.0_1 :       [irange] unsigned int [0, 999999] MASK 0xfffff
> VALUE 0x0
> 4->6  (F) i_4 :         [irange] int [0, 999999] MASK 0xfffff VALUE 0x0
>
> =========== BB 5 ============
>      <bb 5> :
>      __builtin_unreachable ();
>
> =========== BB 6 ============
> i_4     [irange] int [0, 999999] MASK 0xfffff VALUE 0x0
>      <bb 6> :
>      _7 = __builtin___sprintf_chk (&number, 1, 7, "%d", i_4);
>      return;
>
> Non-varying global ranges:
> =========================:
> i.0_1  : [irange] unsigned int [0, 2147483647] MASK 0x7fffffff VALUE 0x0
>
>
> Likewise with i.0_1, its only in bb6 that it becomes  [0, 999999].
>
> i_4 has a global range of VARYING, and as you can see, i.0_1 is set to
> [0, 0x7fffffff].  no trickery!
>
>
> After VRP2 and the unreachable calls  are removed, well then things
> change and there is nothing but i_4 with  a global range of [0, 999999]

So looking into this closer the problem is dom rather than a VRP issue.
Dom does not export the ranges across 2 conditionals, only one. So in dom2:
```
  # RANGE [irange] int [0, +INF]
  # USE = nonlocal escaped
  # CLB = nonlocal escaped
  i_4 = somerandomD.2969 ();
```
Notice [0,INF] only.

And then during dom3 things go down hill because a range associated
with the cast to unsigned
Before the range is not associated with the cast
```
_6 = (unsigned int) i_4;
  _7 = _6 > 999999;
  if (_7 != 0)
```

but after we get there is one:
```
  # RANGE [irange] unsigned int [0, 999999] MASK 0xfffff VALUE 0x0
  i.0_1 = (unsigned int) i_4;
  if (i.0_1 > 999999)
```

And dom decides to not to prop the range for i.0_1 back to  i_4
because it folds away that condition before unreachable code handling
happens.

I will update the comment about this mess.

Thanks,
Andrea


>
> Andrew
>

Reply via email to