Re: [PATCH] [frange] Implement set_nonzero and nonzero_p.

Richard Biener <[email protected]> Wed, 5 Aug 2026 09:09:01 +0200
Newsgroups gmane.comp.gcc.patches
Message-ID <CAFiYyc0dWF_oYKiOicQ=ezQ0N5PD5faSmwzUbMR8YJbMZeQLiQ@mail.gmail.com>
On Tue, Aug 4, 2026 at 3:12=E2=80=AFPM Andrew MacLeod <[email protected]>=
 wrote:
>
>
> On 8/3/26 09:09, Aldy Hernandez wrote:
> > [Jakub, Richi, Andrew, etc: Does anyone remember the reason for this
> > craziness?  Should we revisit what vrange::nonzero_p() does, or will
> > this mess up the ranger's cache or something I'm not aware of?]
> >
> > Excluding an interval is now expressible, and excluding zero is just
> > the [-0.0, +0.0] case of it, so say so.
> >
> > For some stupid historical reason which I can't remember, the irange
> > and prange nonzero_p() predicates returns true only for ~[0,0], so
> > even [5,5] returns false.  When we want to test whether a range
> > contains a zero, we usually use the contains_p() idiom.  I think this
> > is idotic, but perhaps there is a reason for it.
>
>
> It was historical.. probably related to pointers.
>
> I have no issues with changing it, it certainly can be confusing... It
> would require changing existing uses, and then finding all the places
> where we use the !contains_p(zero) version.
>
> Perhaps part of the problem is that it is ambiguous anyway.. perhaps it
> would be much clearer to simply introduce a contains_zero_p ()  method.
> thats seems better to me.
>
> We could also audit the existing nonzero_p () uses and see if there
> might be  a better name, or even a real need for it..

There is no sense in a query for exactly ~[0, 0] give ranges conservatively
error on the wider side.  So asking for ~[0, 0] but not accepting ~[0, 0]
intersected with ~[5, 5] doesn't make sense.

So IMO no ambiguity here.

Richard.

>
> Andrew
>
> >
> > I've implemented the frange version the same way, with the wrinkle
> > that the constructor for ~[-0.0, +0.0] includes the possibility of
> > +-NAN, which means that nonzero_p() must ignore the NAN bits,
> > otherwise anything but a strict ~[-0.0, +0.0] +-NAN would return
> > false.  For example, this:
> >
> >       x =3D frange(0.0, VR_ANTI_RANGE);
> >       x.clear_nan();
> >       x.nonzero_p();  <-- would return false
> >
> > Tested on ppc64le Linux: regstrap and LAPACK.  Surprisingly there are
> > no changes to generated output in my Fortran files, presumably because
> > intersect/union are enough to fold inequalities away, and also because
> > there are no callers to nonzero_p() for frange.  Every nonzero_p()
> > call is guarded by prange or irange checks, but it's nice to
> > implement these since they are pure virtuals from the base vrange
> > class.
> >
> > Pushed.
> >
> > p.s. This concludes the multi-range frange work, sans the
> > range-op-float.cc tidbit Jakub pointed out.
> >
> > gcc/ChangeLog:
> >
> >       * value-range.cc (frange::set_nonzero): Implement.
> >       (frange::nonzero_p): Implement.
> >       (range_tests_excluding): Test set_nonzero and nonzero_p.
> > ---
> >   gcc/value-range.cc | 41 +++++++++++++++++++++++++++++++++++++----
> >   1 file changed, 37 insertions(+), 4 deletions(-)
> >
> > diff --git a/gcc/value-range.cc b/gcc/value-range.cc
> > index b4c097343d9..69d89f34227 100644
> > --- a/gcc/value-range.cc
> > +++ b/gcc/value-range.cc
> > @@ -1685,18 +1685,35 @@ frange::verify_range () const
> >                        || !frange_val_is_max (m_pairs[0].max, m_type));
> >   }
> >
> > -// We can't do much with nonzeros yet.
> >   void
> >   frange::set_nonzero (tree type)
> >   {
> > -  set_varying (type);
> > +  set (type, dconstm0, dconst0, VR_ANTI_RANGE);
> >   }
> >
> > -// We can't do much with nonzeros yet.
> > +// Return TRUE when this range is exactly the "everything but zero" se=
t that
> > +// set_nonzero builds, mirroring irange::nonzero_p.  Callers wanting "=
does not
> > +// contain zero" should use the !contains_p (0) idiom.
> > +//
> > +// A NAN is not a zero, so nonzero-ness depends only on the intervals,=
 not on
> > +// whether the range may also be a NAN.  We therefore recognize the no=
nzero
> > +// range by comparing intervals against set_nonzero's with the NAN sta=
te
> > +// ignored.  A strict *this =3D=3D set_nonzero () would be wrong: set_=
nonzero
> > +// leaves the NAN able to be either sign, so a range that is otherwise=
 exactly
> > +// nonzero but whose NAN has been cleared would compare unequal.
> > +
> >   bool
> >   frange::nonzero_p () const
> >   {
> > -  return false;
> > +  if (undefined_p () || known_isnan ())
> > +    return false;
> > +
> > +  frange nz;
> > +  nz.set_nonzero (type ());
> > +  nz.clear_nan ();
> > +  frange tmp =3D *this;
> > +  tmp.clear_nan ();
> > +  return tmp =3D=3D nz;
> >   }
> >
> >   // Set range to [+0.0, +0.0] if honoring signed zeros, or [0.0, 0.0]
> > @@ -3826,11 +3843,27 @@ range_tests_sub_ranges_zero ()
> >     ASSERT_TRUE (r0.contains_p (real_from_str ("-1.0")));
> >
> >     // Excluding zero from [-0.0, 5.0] eats the lower end entirely.
> > +  r0.set_nonzero (float_type_node);
> > +  ASSERT_TRUE (r0.nonzero_p ());
> > +  ASSERT_FALSE (r0.contains_p (dconst0));
> > +  ASSERT_FALSE (r0.contains_p (dconstm0));
> > +
> > +  // A NAN is not a zero, so clearing the NAN leaves a nonzero range n=
onzero.
> > +  r0.clear_nan ();
> > +  ASSERT_TRUE (r0.nonzero_p ());
> > +
> > +  // A range that merely avoids zero is not the nonzero range.
> > +  r0 =3D frange_float ("1.0", "10.0");
> > +  ASSERT_FALSE (r0.nonzero_p ());
> > +
> > +  // Excluding zero from [-0.0, 5.0] leaves (0, 5]: it avoids zero but=
 is not
> > +  // the whole nonzero range.
> >     r0 =3D frange_float ("-0.0", "5.0");
> >     r0.clear_nan ();
> >     r1 =3D frange_float_excluding ("0.0");
> >     r0.intersect (r1);
> >     ASSERT_EQ (r0.num_pairs (), 1);
> > +  ASSERT_FALSE (r0.nonzero_p ());
> >     ASSERT_FALSE (r0.contains_p (dconst0));
> >     ASSERT_FALSE (r0.contains_p (dconstm0));
> >     ASSERT_TRUE (r0.contains_p (real_from_str ("5.0")));
>