| /* Floating point range operators. |
| Copyright (C) 2022 Free Software Foundation, Inc. |
| Contributed by Aldy Hernandez <aldyh@redhat.com>. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "insn-codes.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "gimple.h" |
| #include "cfghooks.h" |
| #include "tree-pass.h" |
| #include "ssa.h" |
| #include "optabs-tree.h" |
| #include "gimple-pretty-print.h" |
| #include "diagnostic-core.h" |
| #include "flags.h" |
| #include "fold-const.h" |
| #include "stor-layout.h" |
| #include "calls.h" |
| #include "cfganal.h" |
| #include "gimple-iterator.h" |
| #include "gimple-fold.h" |
| #include "tree-eh.h" |
| #include "gimple-walk.h" |
| #include "tree-cfg.h" |
| #include "wide-int.h" |
| #include "value-relation.h" |
| #include "range-op.h" |
| |
| // Default definitions for floating point operators. |
| |
| bool |
| range_operator_float::fold_range (frange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio trio) const |
| { |
| if (empty_range_varying (r, type, op1, op2)) |
| return true; |
| if (op1.known_isnan () || op2.known_isnan ()) |
| { |
| r.set_nan (op1.type ()); |
| return true; |
| } |
| |
| REAL_VALUE_TYPE lb, ub; |
| bool maybe_nan; |
| rv_fold (lb, ub, maybe_nan, type, |
| op1.lower_bound (), op1.upper_bound (), |
| op2.lower_bound (), op2.upper_bound (), trio.op1_op2 ()); |
| |
| // Handle possible NANs by saturating to the appropriate INF if only |
| // one end is a NAN. If both ends are a NAN, just return a NAN. |
| bool lb_nan = real_isnan (&lb); |
| bool ub_nan = real_isnan (&ub); |
| if (lb_nan && ub_nan) |
| { |
| r.set_nan (type); |
| return true; |
| } |
| if (lb_nan) |
| lb = dconstninf; |
| else if (ub_nan) |
| ub = dconstinf; |
| |
| r.set (type, lb, ub); |
| |
| if (lb_nan || ub_nan || maybe_nan |
| || op1.maybe_isnan () |
| || op2.maybe_isnan ()) |
| // Keep the default NAN (with a varying sign) set by the setter. |
| ; |
| else |
| r.clear_nan (); |
| |
| return true; |
| } |
| |
| // For a given operation, fold two sets of ranges into [lb, ub]. |
| // MAYBE_NAN is set to TRUE if, in addition to any result in LB or |
| // UB, the final range has the possiblity of a NAN. |
| void |
| range_operator_float::rv_fold (REAL_VALUE_TYPE &lb, |
| REAL_VALUE_TYPE &ub, |
| bool &maybe_nan, |
| tree type ATTRIBUTE_UNUSED, |
| const REAL_VALUE_TYPE &lh_lb ATTRIBUTE_UNUSED, |
| const REAL_VALUE_TYPE &lh_ub ATTRIBUTE_UNUSED, |
| const REAL_VALUE_TYPE &rh_lb ATTRIBUTE_UNUSED, |
| const REAL_VALUE_TYPE &rh_ub ATTRIBUTE_UNUSED, |
| relation_kind) const |
| { |
| lb = dconstninf; |
| ub = dconstinf; |
| maybe_nan = true; |
| } |
| |
| bool |
| range_operator_float::fold_range (irange &r ATTRIBUTE_UNUSED, |
| tree type ATTRIBUTE_UNUSED, |
| const frange &lh ATTRIBUTE_UNUSED, |
| const irange &rh ATTRIBUTE_UNUSED, |
| relation_trio) const |
| { |
| return false; |
| } |
| |
| bool |
| range_operator_float::fold_range (irange &r ATTRIBUTE_UNUSED, |
| tree type ATTRIBUTE_UNUSED, |
| const frange &lh ATTRIBUTE_UNUSED, |
| const frange &rh ATTRIBUTE_UNUSED, |
| relation_trio) const |
| { |
| return false; |
| } |
| |
| bool |
| range_operator_float::op1_range (frange &r ATTRIBUTE_UNUSED, |
| tree type ATTRIBUTE_UNUSED, |
| const frange &lhs ATTRIBUTE_UNUSED, |
| const frange &op2 ATTRIBUTE_UNUSED, |
| relation_trio) const |
| { |
| return false; |
| } |
| |
| bool |
| range_operator_float::op1_range (frange &r ATTRIBUTE_UNUSED, |
| tree type ATTRIBUTE_UNUSED, |
| const irange &lhs ATTRIBUTE_UNUSED, |
| const frange &op2 ATTRIBUTE_UNUSED, |
| relation_trio) const |
| { |
| return false; |
| } |
| |
| bool |
| range_operator_float::op2_range (frange &r ATTRIBUTE_UNUSED, |
| tree type ATTRIBUTE_UNUSED, |
| const frange &lhs ATTRIBUTE_UNUSED, |
| const frange &op1 ATTRIBUTE_UNUSED, |
| relation_trio) const |
| { |
| return false; |
| } |
| |
| bool |
| range_operator_float::op2_range (frange &r ATTRIBUTE_UNUSED, |
| tree type ATTRIBUTE_UNUSED, |
| const irange &lhs ATTRIBUTE_UNUSED, |
| const frange &op1 ATTRIBUTE_UNUSED, |
| relation_trio) const |
| { |
| return false; |
| } |
| |
| relation_kind |
| range_operator_float::lhs_op1_relation (const frange &lhs ATTRIBUTE_UNUSED, |
| const frange &op1 ATTRIBUTE_UNUSED, |
| const frange &op2 ATTRIBUTE_UNUSED, |
| relation_kind) const |
| { |
| return VREL_VARYING; |
| } |
| |
| relation_kind |
| range_operator_float::lhs_op1_relation (const irange &lhs ATTRIBUTE_UNUSED, |
| const frange &op1 ATTRIBUTE_UNUSED, |
| const frange &op2 ATTRIBUTE_UNUSED, |
| relation_kind) const |
| { |
| return VREL_VARYING; |
| } |
| |
| relation_kind |
| range_operator_float::lhs_op2_relation (const irange &lhs ATTRIBUTE_UNUSED, |
| const frange &op1 ATTRIBUTE_UNUSED, |
| const frange &op2 ATTRIBUTE_UNUSED, |
| relation_kind) const |
| { |
| return VREL_VARYING; |
| } |
| |
| relation_kind |
| range_operator_float::lhs_op2_relation (const frange &lhs ATTRIBUTE_UNUSED, |
| const frange &op1 ATTRIBUTE_UNUSED, |
| const frange &op2 ATTRIBUTE_UNUSED, |
| relation_kind) const |
| { |
| return VREL_VARYING; |
| } |
| |
| relation_kind |
| range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) const |
| { |
| return VREL_VARYING; |
| } |
| |
| relation_kind |
| range_operator_float::op1_op2_relation (const frange &lhs ATTRIBUTE_UNUSED) const |
| { |
| return VREL_VARYING; |
| } |
| |
| // Return TRUE if OP1 and OP2 may be a NAN. |
| |
| static inline bool |
| maybe_isnan (const frange &op1, const frange &op2) |
| { |
| return op1.maybe_isnan () || op2.maybe_isnan (); |
| } |
| |
| // Floating version of relop_early_resolve that takes into account NAN |
| // and -ffinite-math-only. |
| |
| inline bool |
| frelop_early_resolve (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel, relation_kind my_rel) |
| { |
| // If either operand is undefined, return VARYING. |
| if (empty_range_varying (r, type, op1, op2)) |
| return true; |
| |
| // We can fold relations from the oracle when we know both operands |
| // are free of NANs, or when -ffinite-math-only. |
| return (!maybe_isnan (op1, op2) |
| && relop_early_resolve (r, type, op1, op2, rel, my_rel)); |
| } |
| |
| // Set VALUE to its next real value, or INF if the operation overflows. |
| |
| inline void |
| frange_nextafter (enum machine_mode mode, |
| REAL_VALUE_TYPE &value, |
| const REAL_VALUE_TYPE &inf) |
| { |
| const real_format *fmt = REAL_MODE_FORMAT (mode); |
| REAL_VALUE_TYPE tmp; |
| real_nextafter (&tmp, fmt, &value, &inf); |
| value = tmp; |
| } |
| |
| // Like real_arithmetic, but round the result to INF if the operation |
| // produced inexact results. |
| // |
| // ?? There is still one problematic case, i387. With |
| // -fexcess-precision=standard we perform most SF/DFmode arithmetic in |
| // XFmode (long_double_type_node), so that case is OK. But without |
| // -mfpmath=sse, all the SF/DFmode computations are in XFmode |
| // precision (64-bit mantissa) and only occassionally rounded to |
| // SF/DFmode (when storing into memory from the 387 stack). Maybe |
| // this is ok as well though it is just occassionally more precise. ?? |
| |
| static void |
| frange_arithmetic (enum tree_code code, tree type, |
| REAL_VALUE_TYPE &result, |
| const REAL_VALUE_TYPE &op1, |
| const REAL_VALUE_TYPE &op2, |
| const REAL_VALUE_TYPE &inf) |
| { |
| REAL_VALUE_TYPE value; |
| enum machine_mode mode = TYPE_MODE (type); |
| bool mode_composite = MODE_COMPOSITE_P (mode); |
| |
| bool inexact = real_arithmetic (&value, code, &op1, &op2); |
| real_convert (&result, mode, &value); |
| |
| // Be extra careful if there may be discrepancies between the |
| // compile and runtime results. |
| if ((mode_composite || (real_isneg (&inf) ? real_less (&result, &value) |
| : !real_less (&value, &result))) |
| && (inexact || !real_identical (&result, &value))) |
| { |
| if (mode_composite) |
| { |
| if (real_isdenormal (&result, mode) |
| || real_iszero (&result)) |
| { |
| // IBM extended denormals only have DFmode precision. |
| REAL_VALUE_TYPE tmp; |
| real_convert (&tmp, DFmode, &value); |
| frange_nextafter (DFmode, tmp, inf); |
| real_convert (&result, mode, &tmp); |
| return; |
| } |
| } |
| frange_nextafter (mode, result, inf); |
| } |
| } |
| |
| // Crop R to [-INF, MAX] where MAX is the maximum representable number |
| // for TYPE. |
| |
| static inline void |
| frange_drop_inf (frange &r, tree type) |
| { |
| REAL_VALUE_TYPE max = real_max_representable (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) |
| { |
| REAL_VALUE_TYPE min = real_min_representable (type); |
| frange tmp (type, min, r.upper_bound ()); |
| r.intersect (tmp); |
| } |
| |
| // If zero is in R, make sure both -0.0 and +0.0 are in the range. |
| |
| static inline void |
| frange_add_zeros (frange &r, tree type) |
| { |
| if (r.undefined_p () || r.known_isnan ()) |
| return; |
| |
| if (HONOR_SIGNED_ZEROS (type) |
| && (real_iszero (&r.lower_bound ()) || real_iszero (&r.upper_bound ()))) |
| { |
| frange zero; |
| zero.set_zero (type); |
| r.union_ (zero); |
| } |
| } |
| |
| // Build a range that is <= VAL and store it in R. Return TRUE if |
| // further changes may be needed for R, or FALSE if R is in its final |
| // form. |
| |
| static bool |
| build_le (frange &r, tree type, const frange &val) |
| { |
| gcc_checking_assert (!val.known_isnan ()); |
| |
| REAL_VALUE_TYPE ninf = frange_val_min (type); |
| r.set (type, ninf, val.upper_bound ()); |
| |
| // Add both zeros if there's the possibility of zero equality. |
| frange_add_zeros (r, type); |
| |
| return true; |
| } |
| |
| // Build a range that is < VAL and store it in R. Return TRUE if |
| // further changes may be needed for R, or FALSE if R is in its final |
| // form. |
| |
| static bool |
| build_lt (frange &r, tree type, const frange &val) |
| { |
| gcc_checking_assert (!val.known_isnan ()); |
| |
| // < -INF is outside the range. |
| if (real_isinf (&val.upper_bound (), 1)) |
| { |
| if (HONOR_NANS (type)) |
| r.set_nan (type); |
| else |
| r.set_undefined (); |
| return false; |
| } |
| |
| REAL_VALUE_TYPE ninf = frange_val_min (type); |
| REAL_VALUE_TYPE prev = val.upper_bound (); |
| machine_mode mode = TYPE_MODE (type); |
| // Default to the conservatively correct closed ranges for |
| // MODE_COMPOSITE_P, otherwise use nextafter. Note that for |
| // !HONOR_INFINITIES, nextafter will yield -INF, but frange::set() |
| // will crop the range appropriately. |
| if (!MODE_COMPOSITE_P (mode)) |
| frange_nextafter (mode, prev, ninf); |
| r.set (type, ninf, prev); |
| return true; |
| } |
| |
| // Build a range that is >= VAL and store it in R. Return TRUE if |
| // further changes may be needed for R, or FALSE if R is in its final |
| // form. |
| |
| static bool |
| build_ge (frange &r, tree type, const frange &val) |
| { |
| gcc_checking_assert (!val.known_isnan ()); |
| |
| REAL_VALUE_TYPE inf = frange_val_max (type); |
| r.set (type, val.lower_bound (), inf); |
| |
| // Add both zeros if there's the possibility of zero equality. |
| frange_add_zeros (r, type); |
| |
| return true; |
| } |
| |
| // Build a range that is > VAL and store it in R. Return TRUE if |
| // further changes may be needed for R, or FALSE if R is in its final |
| // form. |
| |
| static bool |
| build_gt (frange &r, tree type, const frange &val) |
| { |
| gcc_checking_assert (!val.known_isnan ()); |
| |
| // > +INF is outside the range. |
| if (real_isinf (&val.lower_bound (), 0)) |
| { |
| if (HONOR_NANS (type)) |
| r.set_nan (type); |
| else |
| r.set_undefined (); |
| return false; |
| } |
| |
| REAL_VALUE_TYPE inf = frange_val_max (type); |
| REAL_VALUE_TYPE next = val.lower_bound (); |
| machine_mode mode = TYPE_MODE (type); |
| // Default to the conservatively correct closed ranges for |
| // MODE_COMPOSITE_P, otherwise use nextafter. Note that for |
| // !HONOR_INFINITIES, nextafter will yield +INF, but frange::set() |
| // will crop the range appropriately. |
| if (!MODE_COMPOSITE_P (mode)) |
| frange_nextafter (mode, next, inf); |
| r.set (type, next, inf); |
| return true; |
| } |
| |
| |
| class foperator_identity : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| public: |
| bool fold_range (frange &r, tree type ATTRIBUTE_UNUSED, |
| const frange &op1, const frange &op2 ATTRIBUTE_UNUSED, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| r = op1; |
| return true; |
| } |
| bool op1_range (frange &r, tree type ATTRIBUTE_UNUSED, |
| const frange &lhs, const frange &op2 ATTRIBUTE_UNUSED, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| r = lhs; |
| return true; |
| } |
| public: |
| } fop_identity; |
| |
| class foperator_equal : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| using range_operator_float::op1_op2_relation; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| relation_kind op1_op2_relation (const irange &lhs) const final override |
| { |
| return equal_op1_op2_relation (lhs); |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio rel = TRIO_VARYING) const final override |
| { |
| return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ()); |
| } |
| } fop_equal; |
| |
| bool |
| foperator_equal::fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel) const |
| { |
| if (frelop_early_resolve (r, type, op1, op2, rel, VREL_EQ)) |
| return true; |
| |
| if (op1.known_isnan () || op2.known_isnan ()) |
| r = range_false (type); |
| // We can be sure the values are always equal or not if both ranges |
| // consist of a single value, and then compare them. |
| else if (op1.singleton_p () && op2.singleton_p ()) |
| { |
| if (op1 == op2) |
| r = range_true (type); |
| else |
| r = range_false (type); |
| } |
| else if (!maybe_isnan (op1, op2)) |
| { |
| // If ranges do not intersect, we know the range is not equal, |
| // otherwise we don't know anything for sure. |
| frange tmp = op1; |
| tmp.intersect (op2); |
| if (tmp.undefined_p ()) |
| r = range_false (type); |
| else |
| r = range_true_and_false (type); |
| } |
| else |
| r = range_true_and_false (type); |
| return true; |
| } |
| |
| bool |
| foperator_equal::op1_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio trio) const |
| { |
| relation_kind rel = trio.op1_op2 (); |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // The TRUE side of x == NAN is unreachable. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else |
| { |
| // If it's true, the result is the same as OP2. |
| r = op2; |
| // Add both zeros if there's the possibility of zero equality. |
| frange_add_zeros (r, type); |
| // The TRUE side of op1 == op2 implies op1 is !NAN. |
| r.clear_nan (); |
| } |
| break; |
| |
| case BRS_FALSE: |
| // The FALSE side of op1 == op1 implies op1 is a NAN. |
| if (rel == VREL_EQ) |
| r.set_nan (type); |
| // On the FALSE side of x == NAN, we know nothing about x. |
| else if (op2.known_isnan ()) |
| r.set_varying (type); |
| // If the result is false, the only time we know anything is |
| // if OP2 is a constant. |
| else if (op2.singleton_p () |
| || (!op2.maybe_isnan () && op2.zero_p ())) |
| { |
| REAL_VALUE_TYPE tmp = op2.lower_bound (); |
| r.set (type, tmp, tmp, VR_ANTI_RANGE); |
| } |
| else |
| r.set_varying (type); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| class foperator_not_equal : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op1_op2_relation; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel = TRIO_VARYING) const final override; |
| relation_kind op1_op2_relation (const irange &lhs) const final override |
| { |
| return not_equal_op1_op2_relation (lhs); |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| } fop_not_equal; |
| |
| bool |
| foperator_not_equal::fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel) const |
| { |
| if (frelop_early_resolve (r, type, op1, op2, rel, VREL_NE)) |
| return true; |
| |
| // x != NAN is always TRUE. |
| if (op1.known_isnan () || op2.known_isnan ()) |
| r = range_true (type); |
| // We can be sure the values are always equal or not if both ranges |
| // consist of a single value, and then compare them. |
| else if (op1.singleton_p () && op2.singleton_p ()) |
| { |
| if (op1 != op2) |
| r = range_true (type); |
| else |
| r = range_false (type); |
| } |
| else if (!maybe_isnan (op1, op2)) |
| { |
| // If ranges do not intersect, we know the range is not equal, |
| // otherwise we don't know anything for sure. |
| frange tmp = op1; |
| tmp.intersect (op2); |
| if (tmp.undefined_p ()) |
| r = range_true (type); |
| else |
| r = range_true_and_false (type); |
| } |
| else |
| r = range_true_and_false (type); |
| return true; |
| } |
| |
| bool |
| foperator_not_equal::op1_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio trio) const |
| { |
| relation_kind rel = trio.op1_op2 (); |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // If the result is true, the only time we know anything is if |
| // OP2 is a constant. |
| if (op2.singleton_p ()) |
| { |
| // This is correct even if op1 is NAN, because the following |
| // range would be ~[tmp, tmp] with the NAN property set to |
| // maybe (VARYING). |
| REAL_VALUE_TYPE tmp = op2.lower_bound (); |
| r.set (type, tmp, tmp, VR_ANTI_RANGE); |
| } |
| // The TRUE side of op1 != op1 implies op1 is NAN. |
| else if (rel == VREL_EQ) |
| r.set_nan (type); |
| else |
| r.set_varying (type); |
| break; |
| |
| case BRS_FALSE: |
| // The FALSE side of x != NAN is impossible. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else |
| { |
| // If it's false, the result is the same as OP2. |
| r = op2; |
| // Add both zeros if there's the possibility of zero equality. |
| frange_add_zeros (r, type); |
| // The FALSE side of op1 != op2 implies op1 is !NAN. |
| r.clear_nan (); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| class foperator_lt : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| using range_operator_float::op1_op2_relation; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| relation_kind op1_op2_relation (const irange &lhs) const final override |
| { |
| return lt_op1_op2_relation (lhs); |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio = TRIO_VARYING) const final override; |
| } fop_lt; |
| |
| bool |
| foperator_lt::fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel) const |
| { |
| if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LT)) |
| return true; |
| |
| if (op1.known_isnan () || op2.known_isnan ()) |
| r = range_false (type); |
| else if (!maybe_isnan (op1, op2)) |
| { |
| if (real_less (&op1.upper_bound (), &op2.lower_bound ())) |
| r = range_true (type); |
| else if (!real_less (&op1.lower_bound (), &op2.upper_bound ())) |
| r = range_false (type); |
| else |
| r = range_true_and_false (type); |
| } |
| else |
| r = range_true_and_false (type); |
| return true; |
| } |
| |
| bool |
| foperator_lt::op1_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // The TRUE side of x < NAN is unreachable. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else if (build_lt (r, type, op2)) |
| { |
| r.clear_nan (); |
| // x < y implies x is not +INF. |
| frange_drop_inf (r, type); |
| } |
| break; |
| |
| case BRS_FALSE: |
| // On the FALSE side of x < NAN, we know nothing about x. |
| if (op2.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_ge (r, type, op2); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| bool |
| foperator_lt::op2_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op1, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // The TRUE side of NAN < x is unreachable. |
| if (op1.known_isnan ()) |
| r.set_undefined (); |
| else if (build_gt (r, type, op1)) |
| { |
| r.clear_nan (); |
| // x < y implies y is not -INF. |
| frange_drop_ninf (r, type); |
| } |
| break; |
| |
| case BRS_FALSE: |
| // On the FALSE side of NAN < x, we know nothing about x. |
| if (op1.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_le (r, type, op1); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| class foperator_le : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| using range_operator_float::op1_op2_relation; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel = TRIO_VARYING) const final override; |
| relation_kind op1_op2_relation (const irange &lhs) const final override |
| { |
| return le_op1_op2_relation (lhs); |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio rel = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio rel = TRIO_VARYING) const final override; |
| } fop_le; |
| |
| bool |
| foperator_le::fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel) const |
| { |
| if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LE)) |
| return true; |
| |
| if (op1.known_isnan () || op2.known_isnan ()) |
| r = range_false (type); |
| else if (!maybe_isnan (op1, op2)) |
| { |
| if (real_compare (LE_EXPR, &op1.upper_bound (), &op2.lower_bound ())) |
| r = range_true (type); |
| else if (!real_compare (LE_EXPR, &op1.lower_bound (), &op2.upper_bound ())) |
| r = range_false (type); |
| else |
| r = range_true_and_false (type); |
| } |
| else |
| r = range_true_and_false (type); |
| return true; |
| } |
| |
| bool |
| foperator_le::op1_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // The TRUE side of x <= NAN is unreachable. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else if (build_le (r, type, op2)) |
| r.clear_nan (); |
| break; |
| |
| case BRS_FALSE: |
| // On the FALSE side of x <= NAN, we know nothing about x. |
| if (op2.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_gt (r, type, op2); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| bool |
| foperator_le::op2_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op1, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // The TRUE side of NAN <= x is unreachable. |
| if (op1.known_isnan ()) |
| r.set_undefined (); |
| else if (build_ge (r, type, op1)) |
| r.clear_nan (); |
| break; |
| |
| case BRS_FALSE: |
| // On the FALSE side of NAN <= x, we know nothing about x. |
| if (op1.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_lt (r, type, op1); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| class foperator_gt : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| using range_operator_float::op1_op2_relation; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| relation_kind op1_op2_relation (const irange &lhs) const final override |
| { |
| return gt_op1_op2_relation (lhs); |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio = TRIO_VARYING) const final override; |
| } fop_gt; |
| |
| bool |
| foperator_gt::fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel) const |
| { |
| if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GT)) |
| return true; |
| |
| if (op1.known_isnan () || op2.known_isnan ()) |
| r = range_false (type); |
| else if (!maybe_isnan (op1, op2)) |
| { |
| if (real_compare (GT_EXPR, &op1.lower_bound (), &op2.upper_bound ())) |
| r = range_true (type); |
| else if (!real_compare (GT_EXPR, &op1.upper_bound (), &op2.lower_bound ())) |
| r = range_false (type); |
| else |
| r = range_true_and_false (type); |
| } |
| else |
| r = range_true_and_false (type); |
| return true; |
| } |
| |
| bool |
| foperator_gt::op1_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // The TRUE side of x > NAN is unreachable. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else if (build_gt (r, type, op2)) |
| { |
| r.clear_nan (); |
| // x > y implies x is not -INF. |
| frange_drop_ninf (r, type); |
| } |
| break; |
| |
| case BRS_FALSE: |
| // On the FALSE side of x > NAN, we know nothing about x. |
| if (op2.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_le (r, type, op2); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| bool |
| foperator_gt::op2_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op1, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // The TRUE side of NAN > x is unreachable. |
| if (op1.known_isnan ()) |
| r.set_undefined (); |
| else if (build_lt (r, type, op1)) |
| { |
| r.clear_nan (); |
| // x > y implies y is not +INF. |
| frange_drop_inf (r, type); |
| } |
| break; |
| |
| case BRS_FALSE: |
| // On The FALSE side of NAN > x, we know nothing about x. |
| if (op1.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_ge (r, type, op1); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| class foperator_ge : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| using range_operator_float::op1_op2_relation; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| relation_kind op1_op2_relation (const irange &lhs) const final override |
| { |
| return ge_op1_op2_relation (lhs); |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio = TRIO_VARYING) const final override; |
| } fop_ge; |
| |
| bool |
| foperator_ge::fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel) const |
| { |
| if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GE)) |
| return true; |
| |
| if (op1.known_isnan () || op2.known_isnan ()) |
| r = range_false (type); |
| else if (!maybe_isnan (op1, op2)) |
| { |
| if (real_compare (GE_EXPR, &op1.lower_bound (), &op2.upper_bound ())) |
| r = range_true (type); |
| else if (!real_compare (GE_EXPR, &op1.upper_bound (), &op2.lower_bound ())) |
| r = range_false (type); |
| else |
| r = range_true_and_false (type); |
| } |
| else |
| r = range_true_and_false (type); |
| return true; |
| } |
| |
| bool |
| foperator_ge::op1_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // The TRUE side of x >= NAN is unreachable. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else if (build_ge (r, type, op2)) |
| r.clear_nan (); |
| break; |
| |
| case BRS_FALSE: |
| // On the FALSE side of x >= NAN, we know nothing about x. |
| if (op2.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_lt (r, type, op2); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| bool |
| foperator_ge::op2_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op1, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // The TRUE side of NAN >= x is unreachable. |
| if (op1.known_isnan ()) |
| r.set_undefined (); |
| else if (build_le (r, type, op1)) |
| r.clear_nan (); |
| break; |
| |
| case BRS_FALSE: |
| // On the FALSE side of NAN >= x, we know nothing about x. |
| if (op1.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_gt (r, type, op1); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| // UNORDERED_EXPR comparison. |
| |
| class foperator_unordered : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio rel = TRIO_VARYING) const final override |
| { |
| return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ()); |
| } |
| } fop_unordered; |
| |
| bool |
| foperator_unordered::fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio) const |
| { |
| // UNORDERED is TRUE if either operand is a NAN. |
| if (op1.known_isnan () || op2.known_isnan ()) |
| r = range_true (type); |
| // UNORDERED is FALSE if neither operand is a NAN. |
| else if (!op1.maybe_isnan () && !op2.maybe_isnan ()) |
| r = range_false (type); |
| else |
| r = range_true_and_false (type); |
| return true; |
| } |
| |
| bool |
| foperator_unordered::op1_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio trio) const |
| { |
| relation_kind rel = trio.op1_op2 (); |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // Since at least one operand must be NAN, if one of them is |
| // not, the other must be. |
| if (rel == VREL_EQ || !op2.maybe_isnan ()) |
| r.set_nan (type); |
| else |
| r.set_varying (type); |
| break; |
| |
| case BRS_FALSE: |
| // A false UNORDERED means both operands are !NAN, so it's |
| // impossible for op2 to be a NAN. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else |
| { |
| r.set_varying (type); |
| r.clear_nan (); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| // ORDERED_EXPR comparison. |
| |
| class foperator_ordered : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio rel = TRIO_VARYING) const final override |
| { |
| return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ()); |
| } |
| } fop_ordered; |
| |
| bool |
| foperator_ordered::fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio) const |
| { |
| if (op1.known_isnan () || op2.known_isnan ()) |
| r = range_false (type); |
| else if (!op1.maybe_isnan () && !op2.maybe_isnan ()) |
| r = range_true (type); |
| else |
| r = range_true_and_false (type); |
| return true; |
| } |
| |
| bool |
| foperator_ordered::op1_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio trio) const |
| { |
| relation_kind rel = trio.op1_op2 (); |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // The TRUE side of ORDERED means both operands are !NAN, so |
| // it's impossible for op2 to be a NAN. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else |
| { |
| r.set_varying (type); |
| r.clear_nan (); |
| } |
| break; |
| |
| case BRS_FALSE: |
| // The FALSE side of op1 ORDERED op1 implies op1 is NAN. |
| if (rel == VREL_EQ) |
| r.set_nan (type); |
| else |
| r.set_varying (type); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| class foperator_negate : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| public: |
| bool fold_range (frange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| if (empty_range_varying (r, type, op1, op2)) |
| return true; |
| if (op1.known_isnan ()) |
| { |
| bool sign; |
| if (op1.nan_signbit_p (sign)) |
| r.set_nan (type, !sign); |
| else |
| r.set_nan (type); |
| return true; |
| } |
| |
| REAL_VALUE_TYPE lh_lb = op1.lower_bound (); |
| REAL_VALUE_TYPE lh_ub = op1.upper_bound (); |
| lh_lb = real_value_negate (&lh_lb); |
| lh_ub = real_value_negate (&lh_ub); |
| r.set (type, lh_ub, lh_lb); |
| if (op1.maybe_isnan ()) |
| { |
| bool sign; |
| if (op1.nan_signbit_p (sign)) |
| r.update_nan (!sign); |
| else |
| r.update_nan (); |
| } |
| else |
| r.clear_nan (); |
| return true; |
| } |
| bool op1_range (frange &r, tree type, |
| const frange &lhs, const frange &op2, |
| relation_trio rel = TRIO_VARYING) const final override |
| { |
| return fold_range (r, type, lhs, op2, rel); |
| } |
| } fop_negate; |
| |
| class foperator_abs : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| public: |
| bool fold_range (frange &r, tree type, |
| const frange &op1, const frange &, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op1_range (frange &r, tree type, |
| const frange &lhs, const frange &op2, |
| relation_trio rel = TRIO_VARYING) const final override; |
| } fop_abs; |
| |
| bool |
| foperator_abs::fold_range (frange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio) const |
| { |
| if (empty_range_varying (r, type, op1, op2)) |
| return true; |
| if (op1.known_isnan ()) |
| { |
| r.set_nan (type, /*sign=*/false); |
| return true; |
| } |
| |
| const REAL_VALUE_TYPE lh_lb = op1.lower_bound (); |
| const REAL_VALUE_TYPE lh_ub = op1.upper_bound (); |
| // Handle the easy case where everything is positive. |
| if (real_compare (GE_EXPR, &lh_lb, &dconst0) |
| && !real_iszero (&lh_lb, /*sign=*/true) |
| && !op1.maybe_isnan (/*sign=*/true)) |
| { |
| r = op1; |
| return true; |
| } |
| |
| REAL_VALUE_TYPE min = real_value_abs (&lh_lb); |
| REAL_VALUE_TYPE max = real_value_abs (&lh_ub); |
| // If the range contains zero then we know that the minimum value in the |
| // range will be zero. |
| if (real_compare (LE_EXPR, &lh_lb, &dconst0) |
| && real_compare (GE_EXPR, &lh_ub, &dconst0)) |
| { |
| if (real_compare (GT_EXPR, &min, &max)) |
| max = min; |
| min = dconst0; |
| } |
| else |
| { |
| // If the range was reversed, swap MIN and MAX. |
| if (real_compare (GT_EXPR, &min, &max)) |
| std::swap (min, max); |
| } |
| |
| r.set (type, min, max); |
| if (op1.maybe_isnan ()) |
| r.update_nan (/*sign=*/false); |
| else |
| r.clear_nan (); |
| return true; |
| } |
| |
| bool |
| foperator_abs::op1_range (frange &r, tree type, |
| const frange &lhs, const frange &op2, |
| relation_trio) const |
| { |
| if (empty_range_varying (r, type, lhs, op2)) |
| return true; |
| if (lhs.known_isnan ()) |
| { |
| r.set_nan (type); |
| return true; |
| } |
| |
| // Start with the positives because negatives are an impossible result. |
| frange positives (type, dconst0, frange_val_max (type)); |
| positives.update_nan (/*sign=*/false); |
| positives.intersect (lhs); |
| r = positives; |
| // Add -NAN if relevant. |
| if (r.maybe_isnan ()) |
| { |
| frange neg_nan; |
| neg_nan.set_nan (type, true); |
| r.union_ (neg_nan); |
| } |
| if (r.known_isnan () || r.undefined_p ()) |
| return true; |
| // Then add the negative of each pair: |
| // ABS(op1) = [5,20] would yield op1 => [-20,-5][5,20]. |
| frange negatives (type, real_value_negate (&positives.upper_bound ()), |
| real_value_negate (&positives.lower_bound ())); |
| negatives.clear_nan (); |
| r.union_ (negatives); |
| return true; |
| } |
| |
| class foperator_unordered_lt : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel = TRIO_VARYING) const final override |
| { |
| if (op1.known_isnan () || op2.known_isnan ()) |
| { |
| r = range_true (type); |
| return true; |
| } |
| if (!fop_lt.fold_range (r, type, op1, op2, rel)) |
| return false; |
| // The result is the same as the ordered version when the |
| // comparison is true or when the operands cannot be NANs. |
| if (!maybe_isnan (op1, op2) || r == range_true (type)) |
| return true; |
| else |
| { |
| r = range_true_and_false (type); |
| return true; |
| } |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio trio) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op1, |
| relation_trio trio) const final override; |
| } fop_unordered_lt; |
| |
| bool |
| foperator_unordered_lt::op1_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| if (op2.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_lt (r, type, op2); |
| break; |
| |
| case BRS_FALSE: |
| // A false UNORDERED_LT means both operands are !NAN, so it's |
| // impossible for op2 to be a NAN. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else if (build_ge (r, type, op2)) |
| r.clear_nan (); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| bool |
| foperator_unordered_lt::op2_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op1, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| if (op1.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_gt (r, type, op1); |
| break; |
| |
| case BRS_FALSE: |
| // A false UNORDERED_LT means both operands are !NAN, so it's |
| // impossible for op1 to be a NAN. |
| if (op1.known_isnan ()) |
| r.set_undefined (); |
| else if (build_le (r, type, op1)) |
| r.clear_nan (); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| class foperator_unordered_le : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel = TRIO_VARYING) const final override |
| { |
| if (op1.known_isnan () || op2.known_isnan ()) |
| { |
| r = range_true (type); |
| return true; |
| } |
| if (!fop_le.fold_range (r, type, op1, op2, rel)) |
| return false; |
| // The result is the same as the ordered version when the |
| // comparison is true or when the operands cannot be NANs. |
| if (!maybe_isnan (op1, op2) || r == range_true (type)) |
| return true; |
| else |
| { |
| r = range_true_and_false (type); |
| return true; |
| } |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio = TRIO_VARYING) const final override; |
| } fop_unordered_le; |
| |
| bool |
| foperator_unordered_le::op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| if (op2.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_le (r, type, op2); |
| break; |
| |
| case BRS_FALSE: |
| // A false UNORDERED_LE means both operands are !NAN, so it's |
| // impossible for op2 to be a NAN. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else if (build_gt (r, type, op2)) |
| r.clear_nan (); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| bool |
| foperator_unordered_le::op2_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op1, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| if (op1.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_ge (r, type, op1); |
| break; |
| |
| case BRS_FALSE: |
| // A false UNORDERED_LE means both operands are !NAN, so it's |
| // impossible for op1 to be a NAN. |
| if (op1.known_isnan ()) |
| r.set_undefined (); |
| else if (build_lt (r, type, op1)) |
| r.clear_nan (); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| class foperator_unordered_gt : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel = TRIO_VARYING) const final override |
| { |
| if (op1.known_isnan () || op2.known_isnan ()) |
| { |
| r = range_true (type); |
| return true; |
| } |
| if (!fop_gt.fold_range (r, type, op1, op2, rel)) |
| return false; |
| // The result is the same as the ordered version when the |
| // comparison is true or when the operands cannot be NANs. |
| if (!maybe_isnan (op1, op2) || r == range_true (type)) |
| return true; |
| else |
| { |
| r = range_true_and_false (type); |
| return true; |
| } |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio = TRIO_VARYING) const final override; |
| } fop_unordered_gt; |
| |
| bool |
| foperator_unordered_gt::op1_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| if (op2.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_gt (r, type, op2); |
| break; |
| |
| case BRS_FALSE: |
| // A false UNORDERED_GT means both operands are !NAN, so it's |
| // impossible for op2 to be a NAN. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else if (build_le (r, type, op2)) |
| r.clear_nan (); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| bool |
| foperator_unordered_gt::op2_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op1, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| if (op1.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_lt (r, type, op1); |
| break; |
| |
| case BRS_FALSE: |
| // A false UNORDERED_GT means both operands are !NAN, so it's |
| // impossible for op1 to be a NAN. |
| if (op1.known_isnan ()) |
| r.set_undefined (); |
| else if (build_ge (r, type, op1)) |
| r.clear_nan (); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| class foperator_unordered_ge : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel = TRIO_VARYING) const final override |
| { |
| if (op1.known_isnan () || op2.known_isnan ()) |
| { |
| r = range_true (type); |
| return true; |
| } |
| if (!fop_ge.fold_range (r, type, op1, op2, rel)) |
| return false; |
| // The result is the same as the ordered version when the |
| // comparison is true or when the operands cannot be NANs. |
| if (!maybe_isnan (op1, op2) || r == range_true (type)) |
| return true; |
| else |
| { |
| r = range_true_and_false (type); |
| return true; |
| } |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio = TRIO_VARYING) const final override; |
| } fop_unordered_ge; |
| |
| bool |
| foperator_unordered_ge::op1_range (frange &r, |
| tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| if (op2.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_ge (r, type, op2); |
| break; |
| |
| case BRS_FALSE: |
| // A false UNORDERED_GE means both operands are !NAN, so it's |
| // impossible for op2 to be a NAN. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else if (build_lt (r, type, op2)) |
| r.clear_nan (); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| bool |
| foperator_unordered_ge::op2_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op1, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| if (op1.known_isnan ()) |
| r.set_varying (type); |
| else |
| build_le (r, type, op1); |
| break; |
| |
| case BRS_FALSE: |
| // A false UNORDERED_GE means both operands are !NAN, so it's |
| // impossible for op1 to be a NAN. |
| if (op1.known_isnan ()) |
| r.set_undefined (); |
| else if (build_gt (r, type, op1)) |
| r.clear_nan (); |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| class foperator_unordered_equal : public range_operator_float |
| { |
| using range_operator_float::fold_range; |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| bool fold_range (irange &r, tree type, |
| const frange &op1, const frange &op2, |
| relation_trio rel = TRIO_VARYING) const final override |
| { |
| if (op1.known_isnan () || op2.known_isnan ()) |
| { |
| r = range_true (type); |
| return true; |
| } |
| if (!fop_equal.fold_range (r, type, op1, op2, rel)) |
| return false; |
| // The result is the same as the ordered version when the |
| // comparison is true or when the operands cannot be NANs. |
| if (!maybe_isnan (op1, op2) || r == range_true (type)) |
| return true; |
| else |
| { |
| r = range_true_and_false (type); |
| return true; |
| } |
| } |
| bool op1_range (frange &r, tree type, |
| const irange &lhs, const frange &op2, |
| relation_trio = TRIO_VARYING) const final override; |
| bool op2_range (frange &r, tree type, |
| const irange &lhs, const frange &op1, |
| relation_trio rel = TRIO_VARYING) const final override |
| { |
| return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ()); |
| } |
| } fop_unordered_equal; |
| |
| bool |
| foperator_unordered_equal::op1_range (frange &r, tree type, |
| const irange &lhs, |
| const frange &op2, |
| relation_trio) const |
| { |
| switch (get_bool_state (r, lhs, type)) |
| { |
| case BRS_TRUE: |
| // If it's true, the result is the same as OP2 plus a NAN. |
| r = op2; |
| // Add both zeros if there's the possibility of zero equality. |
| frange_add_zeros (r, type); |
| // Add the posibility of a NAN. |
| r.update_nan (); |
| break; |
| |
| case BRS_FALSE: |
| // A false UNORDERED_EQ means both operands are !NAN, so it's |
| // impossible for op2 to be a NAN. |
| if (op2.known_isnan ()) |
| r.set_undefined (); |
| else |
| { |
| // The false side indictates !NAN and not equal. We can at least |
| // represent !NAN. |
| r.set_varying (type); |
| r.clear_nan (); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| // Final tweaks for float binary op op1_range/op2_range. |
| // Return TRUE if the operation is performed and a valid range is available. |
| |
| static bool |
| float_binary_op_range_finish (bool ret, frange &r, tree type, |
| const frange &lhs) |
| { |
| if (!ret) |
| return false; |
| |
| // If we get a known NAN from reverse op, it means either that |
| // the other operand was known NAN (in that case we know nothing), |
| // or the reverse operation introduced a known NAN. |
| // Say for lhs = op1 * op2 if lhs is [-0, +0] and op2 is too, |
| // 0 / 0 is known NAN. Just punt in that case. |
| // If NANs aren't honored, we get for 0 / 0 UNDEFINED, so punt as well. |
| // Or if lhs is a known NAN, we also don't know anything. |
| if (r.known_isnan () || lhs.known_isnan () || r.undefined_p ()) |
| { |
| r.set_varying (type); |
| return true; |
| } |
| |
| // If lhs isn't NAN, then neither operand could be NAN, |
| // even if the reverse operation does introduce a maybe_nan. |
| if (!lhs.maybe_isnan ()) |
| r.clear_nan (); |
| // If lhs is a maybe or known NAN, the operand could be |
| // NAN. |
| else |
| r.update_nan (); |
| return true; |
| } |
| |
| // True if [lb, ub] is [+-0, +-0]. |
| static bool |
| zero_p (const REAL_VALUE_TYPE &lb, const REAL_VALUE_TYPE &ub) |
| { |
| return real_iszero (&lb) && real_iszero (&ub); |
| } |
| |
| // True if +0 or -0 is in [lb, ub] range. |
| static bool |
| contains_zero_p (const REAL_VALUE_TYPE &lb, const REAL_VALUE_TYPE &ub) |
| { |
| return (real_compare (LE_EXPR, &lb, &dconst0) |
| && real_compare (GE_EXPR, &ub, &dconst0)); |
| } |
| |
| // True if [lb, ub] is [-INF, -INF] or [+INF, +INF]. |
| static bool |
| singleton_inf_p (const REAL_VALUE_TYPE &lb, const REAL_VALUE_TYPE &ub) |
| { |
| return real_isinf (&lb) && real_isinf (&ub, real_isneg (&lb)); |
| } |
| |
| // Return -1 if binary op result must have sign bit set, |
| // 1 if binary op result must have sign bit clear, |
| // 0 otherwise. |
| // Sign bit of binary op result is exclusive or of the |
| // operand's sign bits. |
| static int |
| signbit_known_p (const REAL_VALUE_TYPE &lh_lb, const REAL_VALUE_TYPE &lh_ub, |
| const REAL_VALUE_TYPE &rh_lb, const REAL_VALUE_TYPE &rh_ub) |
| { |
| if (real_isneg (&lh_lb) == real_isneg (&lh_ub) |
| && real_isneg (&rh_lb) == real_isneg (&rh_ub)) |
| { |
| if (real_isneg (&lh_lb) == real_isneg (&rh_ub)) |
| return 1; |
| else |
| return -1; |
| } |
| return 0; |
| } |
| |
| // Set [lb, ub] to [-0, -0], [-0, +0] or [+0, +0] depending on |
| // signbit_known. |
| static void |
| zero_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, int signbit_known) |
| { |
| ub = lb = dconst0; |
| if (signbit_known <= 0) |
| lb = real_value_negate (&dconst0); |
| if (signbit_known < 0) |
| ub = lb; |
| } |
| |
| // Set [lb, ub] to [-INF, -INF], [-INF, +INF] or [+INF, +INF] depending on |
| // signbit_known. |
| static void |
| inf_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, int signbit_known) |
| { |
| if (signbit_known > 0) |
| ub = lb = dconstinf; |
| else if (signbit_known < 0) |
| ub = lb = dconstninf; |
| else |
| { |
| lb = dconstninf; |
| ub = dconstinf; |
| } |
| } |
| |
| // Set [lb, ub] to [-INF, -0], [-INF, +INF] or [+0, +INF] depending on |
| // signbit_known. |
| static void |
| zero_to_inf_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, int signbit_known) |
| { |
| if (signbit_known > 0) |
| { |
| lb = dconst0; |
| ub = dconstinf; |
| } |
| else if (signbit_known < 0) |
| { |
| lb = dconstninf; |
| ub = real_value_negate (&dconst0); |
| } |
| else |
| { |
| lb = dconstninf; |
| ub = dconstinf; |
| } |
| } |
| |
| class foperator_plus : public range_operator_float |
| { |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| virtual bool op1_range (frange &r, tree type, |
| const frange &lhs, |
| const frange &op2, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| if (lhs.undefined_p ()) |
| return false; |
| 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); |
| } |
| virtual bool op2_range (frange &r, tree type, |
| const frange &lhs, |
| const frange &op1, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| return op1_range (r, type, lhs, op1); |
| } |
| private: |
| void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan, |
| tree type, |
| const REAL_VALUE_TYPE &lh_lb, |
| const REAL_VALUE_TYPE &lh_ub, |
| const REAL_VALUE_TYPE &rh_lb, |
| const REAL_VALUE_TYPE &rh_ub, |
| relation_kind) const final override |
| { |
| frange_arithmetic (PLUS_EXPR, type, lb, lh_lb, rh_lb, dconstninf); |
| frange_arithmetic (PLUS_EXPR, type, ub, lh_ub, rh_ub, dconstinf); |
| |
| // [-INF] + [+INF] = NAN |
| if (real_isinf (&lh_lb, true) && real_isinf (&rh_ub, false)) |
| maybe_nan = true; |
| // [+INF] + [-INF] = NAN |
| else if (real_isinf (&lh_ub, false) && real_isinf (&rh_lb, true)) |
| maybe_nan = true; |
| else |
| maybe_nan = false; |
| } |
| } fop_plus; |
| |
| |
| class foperator_minus : public range_operator_float |
| { |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| virtual bool op1_range (frange &r, tree type, |
| const frange &lhs, |
| const frange &op2, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| if (lhs.undefined_p ()) |
| return false; |
| return float_binary_op_range_finish (fop_plus.fold_range (r, type, lhs, |
| op2), |
| r, type, lhs); |
| } |
| virtual bool op2_range (frange &r, tree type, |
| const frange &lhs, |
| const frange &op1, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| if (lhs.undefined_p ()) |
| return false; |
| return float_binary_op_range_finish (fold_range (r, type, op1, lhs), |
| r, type, lhs); |
| } |
| private: |
| void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan, |
| tree type, |
| const REAL_VALUE_TYPE &lh_lb, |
| const REAL_VALUE_TYPE &lh_ub, |
| const REAL_VALUE_TYPE &rh_lb, |
| const REAL_VALUE_TYPE &rh_ub, |
| relation_kind) const final override |
| { |
| frange_arithmetic (MINUS_EXPR, type, lb, lh_lb, rh_ub, dconstninf); |
| frange_arithmetic (MINUS_EXPR, type, ub, lh_ub, rh_lb, dconstinf); |
| |
| // [+INF] - [+INF] = NAN |
| if (real_isinf (&lh_ub, false) && real_isinf (&rh_ub, false)) |
| maybe_nan = true; |
| // [-INF] - [-INF] = NAN |
| else if (real_isinf (&lh_lb, true) && real_isinf (&rh_lb, true)) |
| maybe_nan = true; |
| else |
| maybe_nan = false; |
| } |
| } fop_minus; |
| |
| |
| class foperator_mult_div_base : public range_operator_float |
| { |
| protected: |
| // Given CP[0] to CP[3] floating point values rounded to -INF, |
| // set LB to the smallest of them (treating -0 as smaller to +0). |
| // Given CP[4] to CP[7] floating point values rounded to +INF, |
| // set UB to the largest of them (treating -0 as smaller to +0). |
| static void find_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, |
| const REAL_VALUE_TYPE (&cp)[8]) |
| { |
| lb = cp[0]; |
| ub = cp[4]; |
| for (int i = 1; i < 4; ++i) |
| { |
| if (real_less (&cp[i], &lb) |
| || (real_iszero (&lb) && real_isnegzero (&cp[i]))) |
| lb = cp[i]; |
| if (real_less (&ub, &cp[i + 4]) |
| || (real_isnegzero (&ub) && real_iszero (&cp[i + 4]))) |
| ub = cp[i + 4]; |
| } |
| } |
| }; |
| |
| |
| class foperator_mult : public foperator_mult_div_base |
| { |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| virtual bool op1_range (frange &r, tree type, |
| const frange &lhs, |
| const frange &op2, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| if (lhs.undefined_p ()) |
| return false; |
| range_op_handler rdiv (RDIV_EXPR, type); |
| if (!rdiv) |
| return false; |
| return float_binary_op_range_finish (rdiv.fold_range (r, type, lhs, op2), |
| r, type, lhs); |
| } |
| virtual bool op2_range (frange &r, tree type, |
| const frange &lhs, |
| const frange &op1, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| return op1_range (r, type, lhs, op1); |
| } |
| private: |
| void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan, |
| tree type, |
| const REAL_VALUE_TYPE &lh_lb, |
| const REAL_VALUE_TYPE &lh_ub, |
| const REAL_VALUE_TYPE &rh_lb, |
| const REAL_VALUE_TYPE &rh_ub, |
| relation_kind kind) const final override |
| { |
| bool is_square |
| = (kind == VREL_EQ |
| && real_equal (&lh_lb, &rh_lb) |
| && real_equal (&lh_ub, &rh_ub) |
| && real_isneg (&lh_lb) == real_isneg (&rh_lb) |
| && real_isneg (&lh_ub) == real_isneg (&rh_ub)); |
| |
| maybe_nan = false; |
| // x * x never produces a new NAN and we only multiply the same |
| // values, so the 0 * INF problematic cases never appear there. |
| if (!is_square) |
| { |
| // [+-0, +-0] * [+INF,+INF] (or [-INF,-INF] or swapped is a known NAN. |
| if ((zero_p (lh_lb, lh_ub) && singleton_inf_p (rh_lb, rh_ub)) |
| || (zero_p (rh_lb, rh_ub) && singleton_inf_p (lh_lb, lh_ub))) |
| { |
| real_nan (&lb, "", 0, TYPE_MODE (type)); |
| ub = lb; |
| maybe_nan = true; |
| return; |
| } |
| |
| // Otherwise, if one range includes zero and the other ends with +-INF, |
| // it is a maybe NAN. |
| if ((contains_zero_p (lh_lb, lh_ub) |
| && (real_isinf (&rh_lb) || real_isinf (&rh_ub))) |
| || (contains_zero_p (rh_lb, rh_ub) |
| && (real_isinf (&lh_lb) || real_isinf (&lh_ub)))) |
| { |
| maybe_nan = true; |
| |
| int signbit_known = signbit_known_p (lh_lb, lh_ub, rh_lb, rh_ub); |
| |
| // If one of the ranges that includes INF is singleton |
| // and the other range includes zero, the resulting |
| // range is INF and NAN, because the 0 * INF boundary |
| // case will be NAN, but already nextafter (0, 1) * INF |
| // is INF. |
| if (singleton_inf_p (lh_lb, lh_ub) |
| || singleton_inf_p (rh_lb, rh_ub)) |
| return inf_range (lb, ub, signbit_known); |
| |
| // If one of the multiplicands must be zero, the resulting |
| // range is +-0 and NAN. |
| if (zero_p (lh_lb, lh_ub) || zero_p (rh_lb, rh_ub)) |
| return zero_range (lb, ub, signbit_known); |
| |
| // Otherwise one of the multiplicands could be |
| // [0.0, nextafter (0.0, 1.0)] and the [DBL_MAX, INF] |
| // or similarly with different signs. 0.0 * DBL_MAX |
| // is still 0.0, nextafter (0.0, 1.0) * INF is still INF, |
| // so if the signs are always the same or always different, |
| // result is [+0.0, +INF] or [-INF, -0.0], otherwise VARYING. |
| return zero_to_inf_range (lb, ub, signbit_known); |
| } |
| } |
| |
| REAL_VALUE_TYPE cp[8]; |
| // Do a cross-product. At this point none of the multiplications |
| // should produce a NAN. |
| frange_arithmetic (MULT_EXPR, type, cp[0], lh_lb, rh_lb, dconstninf); |
| frange_arithmetic (MULT_EXPR, type, cp[4], lh_lb, rh_lb, dconstinf); |
| if (is_square) |
| { |
| // For x * x we can just do max (lh_lb * lh_lb, lh_ub * lh_ub) |
| // as maximum and -0.0 as minimum if 0.0 is in the range, |
| // otherwise min (lh_lb * lh_lb, lh_ub * lh_ub). |
| // -0.0 rather than 0.0 because VREL_EQ doesn't prove that |
| // x and y are bitwise equal, just that they compare equal. |
| if (contains_zero_p (lh_lb, lh_ub)) |
| { |
| if (real_isneg (&lh_lb) == real_isneg (&lh_ub)) |
| cp[1] = dconst0; |
| else |
| cp[1] = real_value_negate (&dconst0); |
| } |
| else |
| cp[1] = cp[0]; |
| cp[2] = cp[0]; |
| cp[5] = cp[4]; |
| cp[6] = cp[4]; |
| } |
| else |
| { |
| frange_arithmetic (MULT_EXPR, type, cp[1], lh_lb, rh_ub, dconstninf); |
| frange_arithmetic (MULT_EXPR, type, cp[5], lh_lb, rh_ub, dconstinf); |
| frange_arithmetic (MULT_EXPR, type, cp[2], lh_ub, rh_lb, dconstninf); |
| frange_arithmetic (MULT_EXPR, type, cp[6], lh_ub, rh_lb, dconstinf); |
| } |
| frange_arithmetic (MULT_EXPR, type, cp[3], lh_ub, rh_ub, dconstninf); |
| frange_arithmetic (MULT_EXPR, type, cp[7], lh_ub, rh_ub, dconstinf); |
| |
| find_range (lb, ub, cp); |
| } |
| } fop_mult; |
| |
| |
| class foperator_div : public foperator_mult_div_base |
| { |
| using range_operator_float::op1_range; |
| using range_operator_float::op2_range; |
| public: |
| virtual bool op1_range (frange &r, tree type, |
| const frange &lhs, |
| const frange &op2, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| if (lhs.undefined_p ()) |
| return false; |
| return float_binary_op_range_finish (fop_mult.fold_range (r, type, lhs, |
| op2), |
| r, type, lhs); |
| } |
| virtual bool op2_range (frange &r, tree type, |
| const frange &lhs, |
| const frange &op1, |
| relation_trio = TRIO_VARYING) const final override |
| { |
| if (lhs.undefined_p ()) |
| return false; |
| return float_binary_op_range_finish (fold_range (r, type, op1, lhs), |
| r, type, lhs); |
| } |
| private: |
| void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan, |
| tree type, |
| const REAL_VALUE_TYPE &lh_lb, |
| const REAL_VALUE_TYPE &lh_ub, |
| const REAL_VALUE_TYPE &rh_lb, |
| const REAL_VALUE_TYPE &rh_ub, |
| relation_kind) const final override |
| { |
| // +-0.0 / +-0.0 or +-INF / +-INF is a known NAN. |
| if ((zero_p (lh_lb, lh_ub) && zero_p (rh_lb, rh_ub)) |
| || (singleton_inf_p (lh_lb, lh_ub) || singleton_inf_p (rh_lb, rh_ub))) |
| { |
| real_nan (&lb, "", 0, TYPE_MODE (type)); |
| ub = lb; |
| maybe_nan = true; |
| return; |
| } |
| |
| // If +-0.0 is in both ranges, it is a maybe NAN. |
| if (contains_zero_p (lh_lb, lh_ub) && contains_zero_p (rh_lb, rh_ub)) |
| maybe_nan = true; |
| // If +-INF is in both ranges, it is a maybe NAN. |
| else if ((real_isinf (&lh_lb) || real_isinf (&lh_ub)) |
| && (real_isinf (&rh_lb) || real_isinf (&rh_ub))) |
| maybe_nan = true; |
| else |
| maybe_nan = false; |
| |
| int signbit_known = signbit_known_p (lh_lb, lh_ub, rh_lb, rh_ub); |
| |
| // If dividend must be zero, the range is just +-0 |
| // (including if the divisor is +-INF). |
| // If divisor must be +-INF, the range is just +-0 |
| // (including if the dividend is zero). |
| if (zero_p (lh_lb, lh_ub) || singleton_inf_p (rh_lb, rh_ub)) |
| return zero_range (lb, ub, signbit_known); |
| |
| // If divisor must be zero, the range is just +-INF |
| // (including if the dividend is +-INF). |
| // If dividend must be +-INF, the range is just +-INF |
| // (including if the dividend is zero). |
| if (zero_p (rh_lb, rh_ub) || singleton_inf_p (lh_lb, lh_ub)) |
| return inf_range (lb, ub, signbit_known); |
| |
| // Otherwise if both operands may be zero, divisor could be |
| // nextafter(0.0, +-1.0) and dividend +-0.0 |
| // in which case result is going to INF or vice versa and |
| // result +0.0. So, all we can say for that case is if the |
| // signs of divisor and dividend are always the same we have |
| // [+0.0, +INF], if they are always different we have |
| // [-INF, -0.0]. If they vary, VARING. |
| // If both may be +-INF, divisor could be INF and dividend FLT_MAX, |
| // in which case result is going to INF or vice versa and |
| // result +0.0. So, all we can say for that case is if the |
| // signs of divisor and dividend are always the same we have |
| // [+0.0, +INF], if they are always different we have |
| // [-INF, -0.0]. If they vary, VARYING. |
| if (maybe_nan) |
| return zero_to_inf_range (lb, ub, signbit_known); |
| |
| REAL_VALUE_TYPE cp[8]; |
| // Do a cross-division. At this point none of the divisions should |
| // produce a NAN. |
| frange_arithmetic (RDIV_EXPR, type, cp[0], lh_lb, rh_lb, dconstninf); |
| frange_arithmetic (RDIV_EXPR, type, cp[1], lh_lb, rh_ub, dconstninf); |
| frange_arithmetic (RDIV_EXPR, type, cp[2], lh_ub, rh_lb, dconstninf); |
| frange_arithmetic (RDIV_EXPR, type, cp[3], lh_ub, rh_ub, dconstninf); |
| frange_arithmetic (RDIV_EXPR, type, cp[4], lh_lb, rh_lb, dconstinf); |
| frange_arithmetic (RDIV_EXPR, type, cp[5], lh_lb, rh_ub, dconstinf); |
| frange_arithmetic (RDIV_EXPR, type, cp[6], lh_ub, rh_lb, dconstinf); |
| frange_arithmetic (RDIV_EXPR, type, cp[7], lh_ub, rh_ub, dconstinf); |
| |
| find_range (lb, ub, cp); |
| |
| // If divisor may be zero (but is not known to be only zero), |
| // and dividend can't be zero, the range can go up to -INF or +INF |
| // depending on the signs. |
| if (contains_zero_p (rh_lb, rh_ub)) |
| { |
| if (signbit_known <= 0) |
| real_inf (&lb, true); |
| if (signbit_known >= 0) |
| real_inf (&ub, false); |
| } |
| } |
| } fop_div; |
| |
| // Instantiate a range_op_table for floating point operations. |
| static floating_op_table global_floating_table; |
| |
| // Pointer to the float table so the dispatch code can access it. |
| floating_op_table *floating_tree_table = &global_floating_table; |
| |
| floating_op_table::floating_op_table () |
| { |
| set (SSA_NAME, fop_identity); |
| set (PAREN_EXPR, fop_identity); |
| set (OBJ_TYPE_REF, fop_identity); |
| set (REAL_CST, fop_identity); |
| |
| // All the relational operators are expected to work, because the |
| // calculation of ranges on outgoing edges expect the handlers to be |
| // present. |
| set (EQ_EXPR, fop_equal); |
| set (NE_EXPR, fop_not_equal); |
| set (LT_EXPR, fop_lt); |
| set (LE_EXPR, fop_le); |
| set (GT_EXPR, fop_gt); |
| set (GE_EXPR, fop_ge); |
| set (UNLE_EXPR, fop_unordered_le); |
| set (UNLT_EXPR, fop_unordered_lt); |
| set (UNGE_EXPR, fop_unordered_ge); |
| set (UNGT_EXPR, fop_unordered_gt); |
| set (UNEQ_EXPR, fop_unordered_equal); |
| set (ORDERED_EXPR, fop_ordered); |
| set (UNORDERED_EXPR, fop_unordered); |
| |
| set (ABS_EXPR, fop_abs); |
| set (NEGATE_EXPR, fop_negate); |
| set (PLUS_EXPR, fop_plus); |
| set (MINUS_EXPR, fop_minus); |
| set (MULT_EXPR, fop_mult); |
| set (RDIV_EXPR, fop_div); |
| } |
| |
| // Return a pointer to the range_operator_float instance, if there is |
| // one associated with tree_code CODE. |
| |
| range_operator_float * |
| floating_op_table::operator[] (enum tree_code code) |
| { |
| return m_range_tree[code]; |
| } |
| |
| // Add OP to the handler table for CODE. |
| |
| void |
| floating_op_table::set (enum tree_code code, range_operator_float &op) |
| { |
| gcc_checking_assert (m_range_tree[code] == NULL); |
| m_range_tree[code] = &op; |
| } |
| |
| #if CHECKING_P |
| #include "selftest.h" |
| |
| namespace selftest |
| { |
| |
| // Build an frange from string endpoints. |
| |
| inline frange |
| frange_float (const char *lb, const char *ub, tree type = float_type_node) |
| { |
| REAL_VALUE_TYPE min, max; |
| gcc_assert (real_from_string (&min, lb) == 0); |
| gcc_assert (real_from_string (&max, ub) == 0); |
| return frange (type, min, max); |
| } |
| |
| void |
| range_op_float_tests () |
| { |
| frange r, r0, r1; |
| frange trange (float_type_node); |
| |
| // negate([-5, +10]) => [-10, 5] |
| r0 = frange_float ("-5", "10"); |
| fop_negate.fold_range (r, float_type_node, r0, trange); |
| ASSERT_EQ (r, frange_float ("-10", "5")); |
| |
| // negate([0, 1] -NAN) => [-1, -0] +NAN |
| r0 = frange_float ("0", "1"); |
| r0.update_nan (true); |
| fop_negate.fold_range (r, float_type_node, r0, trange); |
| r1 = frange_float ("-1", "-0"); |
| r1.update_nan (false); |
| ASSERT_EQ (r, r1); |
| |
| // [-INF,+INF] + [-INF,+INF] could be a NAN. |
| range_op_handler plus (PLUS_EXPR, float_type_node); |
| r0.set_varying (float_type_node); |
| r1.set_varying (float_type_node); |
| r0.clear_nan (); |
| r1.clear_nan (); |
| plus.fold_range (r, float_type_node, r0, r1); |
| if (HONOR_NANS (float_type_node)) |
| ASSERT_TRUE (r.maybe_isnan ()); |
| } |
| |
| } // namespace selftest |
| |
| #endif // CHECKING_P |