From: Aldy Hernandez Date: Tue, 14 Jul 2026 17:27:25 +0000 (+0000) Subject: [frange] Implement set_nonzero and nonzero_p. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=HEAD;p=thirdparty%2Fgcc.git [frange] Implement set_nonzero and nonzero_p. 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. 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. gcc/ChangeLog: * value-range.cc (frange::set_nonzero): Implement. (frange::nonzero_p): Implement. (range_tests_excluding): Test set_nonzero and nonzero_p. --- 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")));