diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index ff42b95de4f2153641013d90262a484527153360..63b7ad1a008d21bab6eedb0cbe3aa9716a4302ae 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -2199,6 +2199,33 @@ zero_to_inf_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, int signbit_known) } } +/* Extend the LHS range by 1ulp in each direction. For op1_range + or op2_range of binary operations just computing the inverse + operation on ranges isn't sufficient. Consider e.g. + [1., 1.] = op1 + [1., 1.]. op1's range is not [0., 0.], but + [-0x1.0p-54, 0x1.0p-53] (when not -frounding-math), any value for + which adding 1. to it results in 1. after rounding to nearest. + So, for op1_range/op2_range extend the lhs range by 1ulp in each + direction. See PR109008 for more details. */ + +static frange +float_widen_lhs_range (tree type, const frange &lhs) +{ + frange ret = lhs; + if (lhs.known_isnan ()) + return ret; + REAL_VALUE_TYPE lb = lhs.lower_bound (); + REAL_VALUE_TYPE ub = lhs.upper_bound (); + if (real_isfinite (&lb)) + frange_nextafter (TYPE_MODE (type), lb, dconstninf); + if (real_isfinite (&ub)) + frange_nextafter (TYPE_MODE (type), ub, dconstinf); + ret.set (type, lb, ub); + ret.clear_nan (); + ret.union_ (lhs); + return ret; +} + class foperator_plus : public range_operator_float { using range_operator_float::op1_range; @@ -2214,8 +2241,9 @@ public: range_op_handler minus (MINUS_EXPR, type); if (!minus) return false; - return float_binary_op_range_finish (minus.fold_range (r, type, lhs, op2), - r, type, lhs); + frange wlhs = float_widen_lhs_range (type, lhs); + return float_binary_op_range_finish (minus.fold_range (r, type, wlhs, op2), + r, type, wlhs); } virtual bool op2_range (frange &r, tree type, const frange &lhs, @@ -2260,9 +2288,10 @@ public: { if (lhs.undefined_p ()) return false; - return float_binary_op_range_finish (fop_plus.fold_range (r, type, lhs, + frange wlhs = float_widen_lhs_range (type, lhs); + return float_binary_op_range_finish (fop_plus.fold_range (r, type, wlhs, op2), - r, type, lhs); + r, type, wlhs); } virtual bool op2_range (frange &r, tree type, const frange &lhs, @@ -2271,8 +2300,9 @@ public: { if (lhs.undefined_p ()) return false; - return float_binary_op_range_finish (fold_range (r, type, op1, lhs), - r, type, lhs); + frange wlhs = float_widen_lhs_range (type, lhs); + return float_binary_op_range_finish (fold_range (r, type, op1, wlhs), + r, type, wlhs); } private: void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan, @@ -2338,13 +2368,14 @@ public: range_op_handler rdiv (RDIV_EXPR, type); if (!rdiv) return false; - bool ret = rdiv.fold_range (r, type, lhs, op2); + frange wlhs = float_widen_lhs_range (type, lhs); + bool ret = rdiv.fold_range (r, type, wlhs, op2); if (ret == false) return false; - if (lhs.known_isnan () || op2.known_isnan () || op2.undefined_p ()) - return float_binary_op_range_finish (ret, r, type, lhs); - const REAL_VALUE_TYPE &lhs_lb = lhs.lower_bound (); - const REAL_VALUE_TYPE &lhs_ub = lhs.upper_bound (); + if (wlhs.known_isnan () || op2.known_isnan () || op2.undefined_p ()) + return float_binary_op_range_finish (ret, r, type, wlhs); + const REAL_VALUE_TYPE &lhs_lb = wlhs.lower_bound (); + const REAL_VALUE_TYPE &lhs_ub = wlhs.upper_bound (); const REAL_VALUE_TYPE &op2_lb = op2.lower_bound (); const REAL_VALUE_TYPE &op2_ub = op2.upper_bound (); if ((contains_zero_p (lhs_lb, lhs_ub) && contains_zero_p (op2_lb, op2_ub)) @@ -2363,7 +2394,7 @@ public: // or if lhs must be zero and op2 doesn't include zero, it would be // UNDEFINED, while rdiv.fold_range computes a zero or singleton INF // range. Those are supersets of UNDEFINED, so let's keep that way. - return float_binary_op_range_finish (ret, r, type, lhs); + return float_binary_op_range_finish (ret, r, type, wlhs); } virtual bool op2_range (frange &r, tree type, const frange &lhs, @@ -2490,13 +2521,14 @@ public: { if (lhs.undefined_p ()) return false; - bool ret = fop_mult.fold_range (r, type, lhs, op2); + frange wlhs = float_widen_lhs_range (type, lhs); + bool ret = fop_mult.fold_range (r, type, wlhs, op2); if (!ret) return ret; - if (lhs.known_isnan () || op2.known_isnan () || op2.undefined_p ()) - return float_binary_op_range_finish (ret, r, type, lhs); - const REAL_VALUE_TYPE &lhs_lb = lhs.lower_bound (); - const REAL_VALUE_TYPE &lhs_ub = lhs.upper_bound (); + if (wlhs.known_isnan () || op2.known_isnan () || op2.undefined_p ()) + return float_binary_op_range_finish (ret, r, type, wlhs); + const REAL_VALUE_TYPE &lhs_lb = wlhs.lower_bound (); + const REAL_VALUE_TYPE &lhs_ub = wlhs.upper_bound (); const REAL_VALUE_TYPE &op2_lb = op2.lower_bound (); const REAL_VALUE_TYPE &op2_ub = op2.upper_bound (); if ((contains_zero_p (lhs_lb, lhs_ub) @@ -2512,7 +2544,7 @@ public: zero_to_inf_range (lb, ub, signbit_known); r.set (type, lb, ub); } - return float_binary_op_range_finish (ret, r, type, lhs); + return float_binary_op_range_finish (ret, r, type, wlhs); } virtual bool op2_range (frange &r, tree type, const frange &lhs, @@ -2521,13 +2553,14 @@ public: { if (lhs.undefined_p ()) return false; - bool ret = fold_range (r, type, op1, lhs); + frange wlhs = float_widen_lhs_range (type, lhs); + bool ret = fold_range (r, type, op1, wlhs); if (!ret) return ret; - if (lhs.known_isnan () || op1.known_isnan () || op1.undefined_p ()) - return float_binary_op_range_finish (ret, r, type, lhs, true); - const REAL_VALUE_TYPE &lhs_lb = lhs.lower_bound (); - const REAL_VALUE_TYPE &lhs_ub = lhs.upper_bound (); + if (wlhs.known_isnan () || op1.known_isnan () || op1.undefined_p ()) + return float_binary_op_range_finish (ret, r, type, wlhs, true); + const REAL_VALUE_TYPE &lhs_lb = wlhs.lower_bound (); + const REAL_VALUE_TYPE &lhs_ub = wlhs.upper_bound (); const REAL_VALUE_TYPE &op1_lb = op1.lower_bound (); const REAL_VALUE_TYPE &op1_ub = op1.upper_bound (); if ((contains_zero_p (lhs_lb, lhs_ub) && contains_zero_p (op1_lb, op1_ub)) @@ -2542,7 +2575,7 @@ public: zero_to_inf_range (lb, ub, signbit_known); r.set (type, lb, ub); } - return float_binary_op_range_finish (ret, r, type, lhs, true); + return float_binary_op_range_finish (ret, r, type, wlhs, true); } private: void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan, diff --git a/gcc/testsuite/gcc.c-torture/execute/ieee/pr109008.c b/gcc/testsuite/gcc.c-torture/execute/ieee/pr109008.c new file mode 100644 index 0000000000000000000000000000000000000000..610aed4cac01f71b7f2a74eaa6470c0ccb5c1498 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/ieee/pr109008.c @@ -0,0 +1,18 @@ +/* PR tree-optimization/109008 */ + +__attribute__((noipa)) double +foo (double eps) +{ + double d = 1. + eps; + if (d == 1.) + return eps; + return 0.0; +} + +int +main () +{ + if (foo (__DBL_EPSILON__ / 8.0) == 0.0) + __builtin_abort (); + return 0; +}