Re: [PATCH] [frange] Implement set_nonzero and nonzero_p.
Andrew MacLeod <[email protected]> Tue, 4 Aug 2026 09:11:24 -0400
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
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..
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")));