On Tue, Aug 4, 2026 at 3:12 PM 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 = 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" set
> > 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 nonzero
> > +// range by comparing intervals against set_nonzero's with the NAN state
> > +// ignored. A strict *this == 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 = *this;
> > + tmp.clear_nan ();
> > + return tmp == 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
> > nonzero.
> > + r0.clear_nan ();
> > + ASSERT_TRUE (r0.nonzero_p ());
> > +
> > + // A range that merely avoids zero is not the nonzero range.
> > + r0 = 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 = frange_float ("-0.0", "5.0");
> > r0.clear_nan ();
> > r1 = 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")));
>