aboutsummaryrefslogtreecommitdiff
path: root/gcc/range-op-float.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/range-op-float.cc')
-rw-r--r--gcc/range-op-float.cc362
1 files changed, 311 insertions, 51 deletions
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index dafd9c0..32a6cd7 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -51,8 +51,8 @@ along with GCC; see the file COPYING3. If not see
bool
range_operator::fold_range (frange &r, tree type,
- const frange &op1, const frange &op2,
- relation_trio trio) const
+ const frange &op1, const frange &op2,
+ relation_trio trio) const
{
if (empty_range_varying (r, type, op1, op2))
return true;
@@ -112,20 +112,20 @@ range_operator::rv_fold (frange &r, tree type,
bool
range_operator::fold_range (irange &r ATTRIBUTE_UNUSED,
- tree type ATTRIBUTE_UNUSED,
- const frange &lh ATTRIBUTE_UNUSED,
- const irange &rh ATTRIBUTE_UNUSED,
- relation_trio) const
+ tree type ATTRIBUTE_UNUSED,
+ const frange &lh ATTRIBUTE_UNUSED,
+ const irange &rh ATTRIBUTE_UNUSED,
+ relation_trio) const
{
return false;
}
bool
range_operator::fold_range (irange &r ATTRIBUTE_UNUSED,
- tree type ATTRIBUTE_UNUSED,
- const frange &lh ATTRIBUTE_UNUSED,
- const frange &rh ATTRIBUTE_UNUSED,
- relation_trio) const
+ tree type ATTRIBUTE_UNUSED,
+ const frange &lh ATTRIBUTE_UNUSED,
+ const frange &rh ATTRIBUTE_UNUSED,
+ relation_trio) const
{
return false;
}
@@ -142,10 +142,10 @@ range_operator::fold_range (frange &r ATTRIBUTE_UNUSED,
bool
range_operator::op1_range (frange &r ATTRIBUTE_UNUSED,
- tree type ATTRIBUTE_UNUSED,
- const frange &lhs ATTRIBUTE_UNUSED,
- const frange &op2 ATTRIBUTE_UNUSED,
- relation_trio) const
+ tree type ATTRIBUTE_UNUSED,
+ const frange &lhs ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_trio) const
{
return false;
}
@@ -162,56 +162,56 @@ range_operator::op1_range (frange &r ATTRIBUTE_UNUSED,
bool
range_operator::op2_range (frange &r ATTRIBUTE_UNUSED,
- tree type ATTRIBUTE_UNUSED,
- const frange &lhs ATTRIBUTE_UNUSED,
- const frange &op1 ATTRIBUTE_UNUSED,
- relation_trio) const
+ tree type ATTRIBUTE_UNUSED,
+ const frange &lhs ATTRIBUTE_UNUSED,
+ const frange &op1 ATTRIBUTE_UNUSED,
+ relation_trio) const
{
return false;
}
bool
range_operator::op2_range (frange &r ATTRIBUTE_UNUSED,
- tree type ATTRIBUTE_UNUSED,
- const irange &lhs ATTRIBUTE_UNUSED,
- const frange &op1 ATTRIBUTE_UNUSED,
- relation_trio) const
+ tree type ATTRIBUTE_UNUSED,
+ const irange &lhs ATTRIBUTE_UNUSED,
+ const frange &op1 ATTRIBUTE_UNUSED,
+ relation_trio) const
{
return false;
}
relation_kind
range_operator::lhs_op1_relation (const frange &lhs ATTRIBUTE_UNUSED,
- const frange &op1 ATTRIBUTE_UNUSED,
- const frange &op2 ATTRIBUTE_UNUSED,
- relation_kind) const
+ const frange &op1 ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind) const
{
return VREL_VARYING;
}
relation_kind
range_operator::lhs_op1_relation (const irange &lhs ATTRIBUTE_UNUSED,
- const frange &op1 ATTRIBUTE_UNUSED,
- const frange &op2 ATTRIBUTE_UNUSED,
- relation_kind) const
+ const frange &op1 ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind) const
{
return VREL_VARYING;
}
relation_kind
range_operator::lhs_op2_relation (const irange &lhs ATTRIBUTE_UNUSED,
- const frange &op1 ATTRIBUTE_UNUSED,
- const frange &op2 ATTRIBUTE_UNUSED,
- relation_kind) const
+ const frange &op1 ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind) const
{
return VREL_VARYING;
}
relation_kind
range_operator::lhs_op2_relation (const frange &lhs ATTRIBUTE_UNUSED,
- const frange &op1 ATTRIBUTE_UNUSED,
- const frange &op2 ATTRIBUTE_UNUSED,
- relation_kind) const
+ const frange &op1 ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind) const
{
return VREL_VARYING;
}
@@ -675,9 +675,9 @@ operator_equal::fold_range (irange &r, tree type,
bool
operator_equal::op1_range (frange &r, tree type,
- const irange &lhs,
- const frange &op2,
- relation_trio trio) const
+ const irange &lhs,
+ const frange &op2,
+ relation_trio trio) const
{
relation_kind rel = trio.op1_op2 ();
switch (get_bool_state (r, lhs, type))
@@ -1871,10 +1871,10 @@ public:
bool
foperator_unordered_gt::op1_range (frange &r,
- tree type,
- const irange &lhs,
- const frange &op2,
- relation_trio) const
+ tree type,
+ const irange &lhs,
+ const frange &op2,
+ relation_trio) const
{
switch (get_bool_state (r, lhs, type))
{
@@ -2899,36 +2899,296 @@ private:
}
} fop_div;
+bool
+operator_cast::fold_range (frange &r, tree type, const frange &op1,
+ const frange &, relation_trio) const
+{
+ REAL_VALUE_TYPE lb, ub;
+ enum machine_mode mode = TYPE_MODE (type);
+ bool mode_composite = MODE_COMPOSITE_P (mode);
+
+ if (empty_range_varying (r, type, op1, op1))
+ return true;
+ if (!MODE_HAS_NANS (mode) && op1.maybe_isnan ())
+ {
+ r.set_varying (type);
+ return true;
+ }
+ if (op1.known_isnan ())
+ {
+ r.set_nan (type);
+ return true;
+ }
+
+ const REAL_VALUE_TYPE &lh_lb = op1.lower_bound ();
+ const REAL_VALUE_TYPE &lh_ub = op1.upper_bound ();
+ real_convert (&lb, mode, &lh_lb);
+ real_convert (&ub, mode, &lh_ub);
+
+ if (flag_rounding_math)
+ {
+ if (real_less (&lh_lb, &lb))
+ {
+ if (mode_composite
+ && (real_isdenormal (&lb, mode) || real_iszero (&lb)))
+ {
+ // IBM extended denormals only have DFmode precision.
+ REAL_VALUE_TYPE tmp, tmp2;
+ real_convert (&tmp2, DFmode, &lh_lb);
+ real_nextafter (&tmp, REAL_MODE_FORMAT (DFmode), &tmp2,
+ &dconstninf);
+ real_convert (&lb, mode, &tmp);
+ }
+ else
+ frange_nextafter (mode, lb, dconstninf);
+ }
+ if (real_less (&ub, &lh_ub))
+ {
+ if (mode_composite
+ && (real_isdenormal (&ub, mode) || real_iszero (&ub)))
+ {
+ // IBM extended denormals only have DFmode precision.
+ REAL_VALUE_TYPE tmp, tmp2;
+ real_convert (&tmp2, DFmode, &lh_ub);
+ real_nextafter (&tmp, REAL_MODE_FORMAT (DFmode), &tmp2,
+ &dconstinf);
+ real_convert (&ub, mode, &tmp);
+ }
+ else
+ frange_nextafter (mode, ub, dconstinf);
+ }
+ }
+
+ r.set (type, lb, ub, op1.get_nan_state ());
+
+ if (flag_trapping_math
+ && MODE_HAS_INFINITIES (TYPE_MODE (type))
+ && r.known_isinf ()
+ && !op1.known_isinf ())
+ {
+ REAL_VALUE_TYPE inf = r.lower_bound ();
+ if (real_isneg (&inf))
+ {
+ REAL_VALUE_TYPE min = real_min_representable (type);
+ r.set (type, inf, min);
+ }
+ else
+ {
+ REAL_VALUE_TYPE max = real_max_representable (type);
+ r.set (type, max, inf);
+ }
+ }
+
+ r.flush_denormals_to_zero ();
+ return true;
+}
+
+// Implement fold for a cast from float to another float.
+bool
+operator_cast::op1_range (frange &r, tree type, const frange &lhs,
+ const frange &op2, relation_trio) const
+{
+ if (lhs.undefined_p ())
+ return false;
+ tree lhs_type = lhs.type ();
+ enum machine_mode mode = TYPE_MODE (type);
+ enum machine_mode lhs_mode = TYPE_MODE (lhs_type);
+ frange wlhs;
+ bool rm;
+ if (REAL_MODE_FORMAT (mode)->ieee_bits
+ && REAL_MODE_FORMAT (lhs_mode)->ieee_bits
+ && (REAL_MODE_FORMAT (lhs_mode)->ieee_bits
+ >= REAL_MODE_FORMAT (mode)->ieee_bits)
+ && pow2p_hwi (REAL_MODE_FORMAT (mode)->ieee_bits))
+ {
+ /* If the cast is widening from IEEE exchange mode to
+ wider exchange mode or extended mode, no need to extend
+ the range on reverse operation. */
+ rm = false;
+ wlhs = lhs;
+ }
+ else
+ {
+ rm = true;
+ wlhs = float_widen_lhs_range (lhs_type, lhs);
+ }
+ auto save_flag_rounding_math = flag_rounding_math;
+ flag_rounding_math = rm;
+ bool ret = float_binary_op_range_finish (fold_range (r, type, wlhs, op2),
+ r, type, lhs);
+ flag_rounding_math = save_flag_rounding_math;
+ return ret;
+}
+
// Implement fold for a cast from float to an int.
bool
-operator_cast::fold_range (irange &, tree, const frange &,
+operator_cast::fold_range (irange &r, tree type, const frange &op1,
const irange &, relation_trio) const
{
- return false;
+ if (empty_range_varying (r, type, op1, op1))
+ return true;
+ if (op1.maybe_isnan () || op1.maybe_isinf ())
+ {
+ r.set_varying (type);
+ return true;
+ }
+ REAL_VALUE_TYPE lb, ub;
+ real_trunc (&lb, VOIDmode, &op1.lower_bound ());
+ real_trunc (&ub, VOIDmode, &op1.upper_bound ());
+ REAL_VALUE_TYPE l, u;
+ l = real_value_from_int_cst (NULL_TREE, TYPE_MIN_VALUE (type));
+ if (real_less (&lb, &l))
+ {
+ r.set_varying (type);
+ return true;
+ }
+ u = real_value_from_int_cst (NULL_TREE, TYPE_MAX_VALUE (type));
+ if (real_less (&u, &ub))
+ {
+ r.set_varying (type);
+ return true;
+ }
+ bool fail = false;
+ wide_int wlb = real_to_integer (&lb, &fail, TYPE_PRECISION (type));
+ wide_int wub = real_to_integer (&ub, &fail, TYPE_PRECISION (type));
+ if (fail)
+ {
+ r.set_varying (type);
+ return true;
+ }
+ r.set (type, wlb, wub);
+ return true;
}
// Implement op1_range for a cast from float to an int.
bool
-operator_cast::op1_range (frange &, tree, const irange &,
- const irange &, relation_trio) const
+operator_cast::op1_range (frange &r, tree type, const irange &lhs,
+ const frange &, relation_trio) const
{
- return false;
+ if (lhs.undefined_p ())
+ return false;
+ REAL_VALUE_TYPE lb, lbo, ub, ubo;
+ wide_int lhs_lb = lhs.lower_bound ();
+ wide_int lhs_ub = lhs.upper_bound ();
+ tree lhs_type = lhs.type ();
+ enum machine_mode mode = TYPE_MODE (type);
+ real_from_integer (&lbo, VOIDmode, lhs_lb, TYPE_SIGN (lhs_type));
+ real_from_integer (&ubo, VOIDmode, lhs_ub, TYPE_SIGN (lhs_type));
+ real_convert (&lb, mode, &lbo);
+ real_convert (&ub, mode, &ubo);
+ if (real_identical (&lb, &lbo))
+ {
+ /* If low bound is exactly representable in type,
+ use nextafter (lb - 1., +inf). */
+ real_arithmetic (&lb, PLUS_EXPR, &lbo, &dconstm1);
+ real_convert (&lb, mode, &lb);
+ if (!real_identical (&lb, &lbo))
+ frange_nextafter (mode, lb, dconstinf);
+ if (real_identical (&lb, &lbo))
+ frange_nextafter (mode, lb, dconstninf);
+ }
+ else if (real_less (&lbo, &lb))
+ frange_nextafter (mode, lb, dconstninf);
+ if (real_identical (&ub, &ubo))
+ {
+ /* If upper bound is exactly representable in type,
+ use nextafter (ub + 1., -inf). */
+ real_arithmetic (&ub, PLUS_EXPR, &ubo, &dconst1);
+ real_convert (&ub, mode, &ub);
+ if (!real_identical (&ub, &ubo))
+ frange_nextafter (mode, ub, dconstninf);
+ if (real_identical (&ub, &ubo))
+ frange_nextafter (mode, ub, dconstinf);
+ }
+ else if (real_less (&ub, &ubo))
+ frange_nextafter (mode, ub, dconstinf);
+ r.set (type, lb, ub, nan_state (false));
+ return true;
}
// Implement fold for a cast from int to a float.
bool
-operator_cast::fold_range (frange &, tree, const irange &,
+operator_cast::fold_range (frange &r, tree type, const irange &op1,
const frange &, relation_trio) const
{
- return false;
+ if (empty_range_varying (r, type, op1, op1))
+ return true;
+ REAL_VALUE_TYPE lb, ub;
+ wide_int op1_lb = op1.lower_bound ();
+ wide_int op1_ub = op1.upper_bound ();
+ tree op1_type = op1.type ();
+ enum machine_mode mode = flag_rounding_math ? VOIDmode : TYPE_MODE (type);
+ real_from_integer (&lb, mode, op1_lb, TYPE_SIGN (op1_type));
+ real_from_integer (&ub, mode, op1_ub, TYPE_SIGN (op1_type));
+ if (flag_rounding_math)
+ {
+ REAL_VALUE_TYPE lbo = lb, ubo = ub;
+ mode = TYPE_MODE (type);
+ real_convert (&lb, mode, &lb);
+ real_convert (&ub, mode, &ub);
+ if (real_less (&lbo, &lb))
+ frange_nextafter (mode, lb, dconstninf);
+ if (real_less (&ub, &ubo))
+ frange_nextafter (mode, ub, dconstinf);
+ }
+ r.set (type, lb, ub, nan_state (false));
+ frange_drop_infs (r, type);
+ if (r.undefined_p ())
+ r.set_varying (type);
+ return true;
}
// Implement op1_range for a cast from int to a float.
bool
-operator_cast::op1_range (irange &, tree, const frange &,
- const frange &, relation_trio) const
+operator_cast::op1_range (irange &r, tree type, const frange &lhs,
+ const irange &, relation_trio) const
{
- return false;
+ if (lhs.undefined_p ())
+ return false;
+ if (lhs.known_isnan ())
+ {
+ r.set_varying (type);
+ return true;
+ }
+ REAL_VALUE_TYPE lb = lhs.lower_bound ();
+ REAL_VALUE_TYPE ub = lhs.upper_bound ();
+ enum machine_mode mode = TYPE_MODE (lhs.type ());
+ frange_nextafter (mode, lb, dconstninf);
+ frange_nextafter (mode, ub, dconstinf);
+ if (flag_rounding_math)
+ {
+ real_floor (&lb, mode, &lb);
+ real_ceil (&ub, mode, &ub);
+ }
+ else
+ {
+ real_trunc (&lb, mode, &lb);
+ real_trunc (&ub, mode, &ub);
+ }
+ REAL_VALUE_TYPE l, u;
+ wide_int wlb, wub;
+ l = real_value_from_int_cst (NULL_TREE, TYPE_MIN_VALUE (type));
+ if (real_less (&lb, &l))
+ wlb = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type));
+ else
+ {
+ bool fail = false;
+ wlb = real_to_integer (&lb, &fail, TYPE_PRECISION (type));
+ if (fail)
+ wlb = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type));
+ }
+ u = real_value_from_int_cst (NULL_TREE, TYPE_MAX_VALUE (type));
+ if (real_less (&u, &ub))
+ wub = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type));
+ else
+ {
+ bool fail = false;
+ wub = real_to_integer (&ub, &fail, TYPE_PRECISION (type));
+ if (fail)
+ wub = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type));
+ }
+ r.set (type, wlb, wub);
+ return true;
}
// Initialize any float operators to the primary table