aboutsummaryrefslogtreecommitdiff
path: root/gcc/range-op-float.cc
diff options
context:
space:
mode:
authorAldy Hernandez <aldyh@redhat.com>2022-08-30 08:23:33 +0200
committerAldy Hernandez <aldyh@redhat.com>2022-08-30 11:26:24 +0200
commit8bb1df032cc080b751e00c0d7d86c31a630105f9 (patch)
tree109c86b64c96335dbdc87a78fa15028ba97eccd6 /gcc/range-op-float.cc
parentdf8fe4adb0721ab0e4486bc58482b501fe06287d (diff)
downloadgcc-8bb1df032cc080b751e00c0d7d86c31a630105f9.zip
gcc-8bb1df032cc080b751e00c0d7d86c31a630105f9.tar.gz
gcc-8bb1df032cc080b751e00c0d7d86c31a630105f9.tar.bz2
Add support for floating point endpoints to frange.
The current implementation of frange is just a type with some bits to represent NAN and INF. We can do better and represent endpoints to ultimately solve longstanding PRs such as PR24021. This patch adds these endpoints. In follow-up patches I will add support for a bare bones PLUS_EXPR range-op-float entry to solve the PR. I have chosen to use REAL_VALUE_TYPEs for the endpoints, since that's what we use underneath the trees. This will be somewhat analogous to our eventual use of wide-ints in the irange. No sense going through added levels of indirection if we can avoid it. That, plus real.* already has a nice API for dealing with floats. With this patch, ranges will be closed float point intervals, which make the implementation simpler, since we don't have to keep track of open/closed intervals. This is conservative enough for use in the ranger world, as we'd rather err on the side of more elements in a range, than less. For example, even though we cannot precisely represent the open interval (3.0, 5.0) with this approach, it is perfectably reasonable to represent it as [3.0, 5.0] since the closed interval is a super set of the open one. In the VRP/ranger world, it is always better to err on the side of more information in a range, than not. After all, when we don't know anything about a range, we just use VARYING which is a fancy term for a range spanning the entire domain. Since REAL_VALUE_TYPEs have properly defined infinity and NAN semantics, all the math can be made to work: [-INF, 3.0] !NAN => Numbers <= 3.0 (NAN cannot happen) [3.0, 3.0] => 3.0 or NAN. [3.0, +INF] => Numbers >= 3.0 (NAN is possible) [-INF, +INF] => VARYING (NAN is possible) [-INF, +INF] !NAN => Entire domain. NAN cannot happen. Also, since REAL_VALUE_TYPEs can represent the minimum and maximum representable values of a TYPE_MODE, we can disambiguate between them and negative and positive infinity (see get_max_float in real.cc). This also makes the math all work. For example, suppose we know nothing about x and y (VARYING). On the TRUE side of x > y, we can deduce that: (a) x cannot be NAN (b) y cannot be NAN (c) y cannot be +INF. (c) means that we can drop the upper bound of "y" from +INF to the maximum representable value for its type. Having endpoints with different representation for infinity and the maximum representable values, means we can drop the +-INF properties we currently have in the frange. gcc/ChangeLog: * range-op-float.cc (frange_set_nan): New. (frange_drop_inf): New. (frange_drop_ninf): New. (foperator_equal::op1_range): Adjust for endpoints. (foperator_lt::op1_range): Same. (foperator_lt::op2_range): Same. (foperator_gt::op1_range): Same. (foperator_gt::op2_range): Same. (foperator_unordered::op1_range): Same. * value-query.cc (range_query::get_tree_range): Same. * value-range-pretty-print.cc (vrange_printer::visit): Same. * value-range-storage.cc (frange_storage_slot::get_frange): Same. * value-range.cc (frange::set): Same. (frange::normalize_kind): Same. (frange::union_): Same. (frange::intersect): Same. (frange::operator=): Same. (early_nan_resolve): New. (frange::contains_p): New. (frange::singleton_p): New. (frange::set_nonzero): New. (frange::nonzero_p): New. (frange::set_zero): New. (frange::zero_p): New. (frange::set_nonnegative): New. (frange_float): New. (frange_nan): New. (range_tests_nan): New. (range_tests_signed_zeros): New. (range_tests_floats): New. (range_tests): New. * value-range.h (frange::lower_bound): New. (frange::upper_bound): New. (vrp_val_min): Use real_inf with a sign instead of negating inf. (frange::frange): New. (frange::set_varying): Adjust for endpoints. (real_max_representable): New. (real_min_representable): New.
Diffstat (limited to 'gcc/range-op-float.cc')
-rw-r--r--gcc/range-op-float.cc79
1 files changed, 58 insertions, 21 deletions
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index ff9fe31..ca41136 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -150,6 +150,18 @@ range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) cons
return VREL_VARYING;
}
+// Set R to [NAN, NAN].
+
+static inline void
+frange_set_nan (frange &r, tree type)
+{
+ REAL_VALUE_TYPE rv;
+ bool res = real_nan (&rv, "", 1, TYPE_MODE (type));
+ if (flag_checking)
+ gcc_assert (res);
+ r.set (type, rv, rv);
+}
+
// Return TRUE if OP1 and OP2 are known to be free of NANs.
static inline bool
@@ -178,6 +190,40 @@ frelop_early_resolve (irange &r, tree type,
&& relop_early_resolve (r, type, op1, op2, rel, my_rel));
}
+// Crop R to [-INF, MAX] where MAX is the maximum representable number
+// for TYPE.
+
+static inline void
+frange_drop_inf (frange &r, tree type)
+{
+ // FIXME: build_real() bails on decimal float modes when called with
+ // a max representable endpoint.
+ if (DECIMAL_FLOAT_MODE_P (TYPE_MODE (type)))
+ return;
+
+ REAL_VALUE_TYPE max;
+ real_max_representable (&max, type);
+ frange tmp (type, r.lower_bound (), max);
+ r.intersect (tmp);
+}
+
+// Crop R to [MIN, +INF] where MIN is the minimum representable number
+// for TYPE.
+
+static inline void
+frange_drop_ninf (frange &r, tree type)
+{
+ // FIXME: build_real() bails on decimal float modes when called with
+ // a max representable endpoint.
+ if (DECIMAL_FLOAT_MODE_P (TYPE_MODE (type)))
+ return;
+
+ REAL_VALUE_TYPE min;
+ real_min_representable (&min, type);
+ frange tmp (type, min, r.upper_bound ());
+ r.intersect (tmp);
+}
+
// Default implementation of fold_range for relational operators.
// This amounts to passing on any known relations from the oracle, iff
// we know the operands are not NAN or -ffinite-math-only holds.
@@ -252,21 +298,8 @@ foperator_equal::op1_range (frange &r, tree type,
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- if (HONOR_SIGNED_ZEROS (type)
- && op2.contains_p (build_zero_cst (type)))
- {
- // With signed zeros, x == -0.0 does not mean we can replace
- // x with -0.0, because x may be either +0.0 or -0.0.
- r.set_varying (type);
- }
- else
- {
- // If it's true, the result is the same as OP2.
- //
- // If the range does not actually contain zeros, this should
- // always be OK.
- r = op2;
- }
+ // If it's true, the result is the same as OP2.
+ r = op2;
// The TRUE side of op1 == op2 implies op1 is !NAN.
r.set_nan (fp_prop::NO);
break;
@@ -275,7 +308,7 @@ foperator_equal::op1_range (frange &r, tree type,
r.set_varying (type);
// The FALSE side of op1 == op1 implies op1 is a NAN.
if (rel == VREL_EQ)
- r.set_nan (fp_prop::YES);
+ frange_set_nan (r, type);
break;
default:
@@ -365,7 +398,8 @@ foperator_lt::op1_range (frange &r,
r.set_varying (type);
// The TRUE side of op1 < op2 implies op1 is !NAN and !INF.
r.set_nan (fp_prop::NO);
- r.set_inf (fp_prop::NO);
+ // x < y implies x is not +INF.
+ frange_drop_inf (r, type);
break;
case BRS_FALSE:
@@ -391,7 +425,8 @@ foperator_lt::op2_range (frange &r,
r.set_varying (type);
// The TRUE side of op1 < op2 implies op2 is !NAN and !NINF.
r.set_nan (fp_prop::NO);
- r.set_ninf (fp_prop::NO);
+ // x < y implies y is not -INF.
+ frange_drop_ninf (r, type);
break;
case BRS_FALSE:
@@ -493,7 +528,8 @@ foperator_gt::op1_range (frange &r,
r.set_varying (type);
// The TRUE side of op1 > op2 implies op1 is !NAN and !NINF.
r.set_nan (fp_prop::NO);
- r.set_ninf (fp_prop::NO);
+ // x > y implies x is not -INF.
+ frange_drop_ninf (r, type);
break;
case BRS_FALSE:
@@ -519,7 +555,8 @@ foperator_gt::op2_range (frange &r,
r.set_varying (type);
// The TRUE side of op1 > op2 implies op2 is !NAN and !INF.
r.set_nan (fp_prop::NO);
- r.set_inf (fp_prop::NO);
+ // x > y implies y is not +INF.
+ frange_drop_inf (r, type);
break;
case BRS_FALSE:
@@ -636,7 +673,7 @@ foperator_unordered::op1_range (frange &r, tree type,
// Since at least one operand must be NAN, if one of them is
// not, the other must be.
if (op2.get_nan ().no_p ())
- r.set_nan (fp_prop::YES);
+ frange_set_nan (r, type);
break;
case BRS_FALSE: