diff options
Diffstat (limited to 'gcc/range-op-float.cc')
-rw-r--r-- | gcc/range-op-float.cc | 362 |
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 |