diff options
author | Martin Liska <mliska@suse.cz> | 2022-10-13 15:54:17 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2022-10-13 15:54:17 +0200 |
commit | bd21c04269deded2c7476ceca1100a26f28ea526 (patch) | |
tree | 197bf75eedac69362078a4ccc0afe5615c45c327 /gcc/range-op-float.cc | |
parent | d9e7934d25da4a78ffef1f738206aa1d897911df (diff) | |
parent | 786e4c024f941671a233f5779d73a5d22f4e9588 (diff) | |
download | gcc-bd21c04269deded2c7476ceca1100a26f28ea526.zip gcc-bd21c04269deded2c7476ceca1100a26f28ea526.tar.gz gcc-bd21c04269deded2c7476ceca1100a26f28ea526.tar.bz2 |
Merge branch 'master' into devel/sphinx
Diffstat (limited to 'gcc/range-op-float.cc')
-rw-r--r-- | gcc/range-op-float.cc | 607 |
1 files changed, 556 insertions, 51 deletions
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index 68578aa..229b9d2 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -315,17 +315,17 @@ 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_kind) const final override + relation_kind = VREL_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_kind) const final override + relation_kind = VREL_VARYING) const final override { r = lhs; return true; @@ -338,20 +338,20 @@ 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; - +public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_kind rel) const final override; + relation_kind = VREL_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_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; bool op2_range (frange &r, tree type, const irange &lhs, const frange &op1, - relation_kind rel) const final override + relation_kind rel = VREL_VARYING) const final override { return op1_range (r, type, lhs, op1, rel); } @@ -444,17 +444,17 @@ class foperator_not_equal : public range_operator_float { using range_operator_float::fold_range; using range_operator_float::op1_range; - +public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_kind rel) const final override; + relation_kind rel = VREL_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_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; } fop_not_equal; bool @@ -497,14 +497,17 @@ bool foperator_not_equal::op1_range (frange &r, tree type, const irange &lhs, const frange &op2, - relation_kind) const + relation_kind rel) const { switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: + // The TRUE side of op1 != op1 implies op1 is NAN. + if (rel == VREL_EQ) + r.set_nan (type); // If the result is true, the only time we know anything is if // OP2 is a constant. - if (op2.singleton_p ()) + else 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 @@ -542,20 +545,20 @@ 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; - +public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_kind rel) const final override; + relation_kind = VREL_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_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; bool op2_range (frange &r, tree type, const irange &lhs, const frange &op1, - relation_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; } fop_lt; bool @@ -657,20 +660,20 @@ 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; - +public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_kind rel) const final override; + relation_kind rel = VREL_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_kind rel) const final override; + relation_kind rel = VREL_VARYING) const final override; bool op2_range (frange &r, tree type, const irange &lhs, const frange &op1, - relation_kind rel) const final override; + relation_kind rel = VREL_VARYING) const final override; } fop_le; bool @@ -764,20 +767,20 @@ 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; - +public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_kind rel) const final override; + relation_kind = VREL_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_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; bool op2_range (frange &r, tree type, const irange &lhs, const frange &op1, - relation_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; } fop_gt; bool @@ -879,20 +882,20 @@ 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; - +public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_kind rel) const final override; + relation_kind = VREL_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_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; bool op2_range (frange &r, tree type, const irange &lhs, const frange &op1, - relation_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; } fop_ge; bool @@ -990,17 +993,16 @@ 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_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; bool op1_range (frange &r, tree type, const irange &lhs, const frange &op2, - relation_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; bool op2_range (frange &r, tree type, const irange &lhs, const frange &op1, - relation_kind rel) const final override + relation_kind rel = VREL_VARYING) const final override { return op1_range (r, type, lhs, op1, rel); } @@ -1026,23 +1028,27 @@ bool foperator_unordered::op1_range (frange &r, tree type, const irange &lhs, const frange &op2, - relation_kind) const + relation_kind rel) const { switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: + if (rel == VREL_EQ) + r.set_nan (type); // Since at least one operand must be NAN, if one of them is // not, the other must be. - if (!op2.maybe_isnan ()) + else if (!op2.maybe_isnan ()) r.set_nan (type); else r.set_varying (type); break; case BRS_FALSE: + if (rel == VREL_EQ) + r.clear_nan (); // A false UNORDERED means both operands are !NAN, so it's // impossible for op2 to be a NAN. - if (op2.known_isnan ()) + else if (op2.known_isnan ()) r.set_undefined (); else { @@ -1064,17 +1070,16 @@ 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_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; bool op1_range (frange &r, tree type, const irange &lhs, const frange &op2, - relation_kind rel) const final override; + relation_kind = VREL_VARYING) const final override; bool op2_range (frange &r, tree type, const irange &lhs, const frange &op1, - relation_kind rel) const final override + relation_kind rel = VREL_VARYING) const final override { return op1_range (r, type, lhs, op1, rel); } @@ -1127,25 +1132,481 @@ foperator_ordered::op1_range (frange &r, tree type, return true; } -// Placeholder for unimplemented relational operators. +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_kind = VREL_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_kind rel = VREL_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_kind = VREL_VARYING) const final override; + bool op1_range (frange &r, tree type, + const frange &lhs, const frange &op2, + relation_kind rel = VREL_VARYING) const final override; +} fop_abs; + +bool +foperator_abs::fold_range (frange &r, tree type, + const frange &op1, const frange &op2, + relation_kind) 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_kind) 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; + // Then add the negative of each pair: + // ABS(op1) = [5,20] would yield op1 => [-20,-5][5,20]. + r.union_ (frange (type, + real_value_negate (&positives.upper_bound ()), + real_value_negate (&positives.lower_bound ()))); + return true; +} + +class foperator_unordered_lt : public range_operator_float +{ + using range_operator_float::fold_range; +public: + bool fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel = VREL_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 (finite_operands_p (op1, op2) || r == range_true (type)) + return true; + else + { + r = range_true_and_false (type); + return true; + } + } +} fop_unordered_lt; -class foperator_relop_unknown : public range_operator_float +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_kind rel = VREL_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 (finite_operands_p (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_kind = VREL_VARYING) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind = VREL_VARYING) const final override; +} fop_unordered_le; + +bool +foperator_unordered_le::op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + build_le (r, type, op2); + break; + case BRS_FALSE: + 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_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + build_ge (r, type, op1); + break; + + case BRS_FALSE: + 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_kind) const final override + relation_kind rel = VREL_VARYING) const final override { if (op1.known_isnan () || op2.known_isnan ()) - r = range_true (type); + { + 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 (finite_operands_p (op1, op2) || r == range_true (type)) + return true; else - r.set_varying (type); - return true; + { + r = range_true_and_false (type); + return true; + } + } + bool op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind = VREL_VARYING) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind = VREL_VARYING) const final override; +} fop_unordered_gt; + +bool +foperator_unordered_gt::op1_range (frange &r, + tree type, + const irange &lhs, + const frange &op2, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + build_gt (r, type, op2); + break; + + case BRS_FALSE: + 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_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + build_lt (r, type, op1); + break; + + case BRS_FALSE: + 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_kind rel = VREL_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 (finite_operands_p (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_kind = VREL_VARYING) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind = VREL_VARYING) const final override; +} fop_unordered_ge; + +bool +foperator_unordered_ge::op1_range (frange &r, + tree type, + const irange &lhs, + const frange &op2, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + build_ge (r, type, op2); + break; + + case BRS_FALSE: + 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_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + build_le (r, type, op1); + break; + + case BRS_FALSE: + 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_kind rel = VREL_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 (finite_operands_p (op1, op2) || r == range_true (type)) + return true; + else + { + r = range_true_and_false (type); + return true; + } } -} fop_unordered_relop_unknown; + bool op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind = VREL_VARYING) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind rel = VREL_VARYING) const final override + { + return op1_range (r, type, lhs, op1, rel); + } +} fop_unordered_equal; + +bool +foperator_unordered_equal::op1_range (frange &r, tree type, + const irange &lhs, + const frange &op2, + relation_kind) 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: + // 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; +} // Instantiate a range_op_table for floating point operations. static floating_op_table global_floating_table; @@ -1169,13 +1630,16 @@ floating_op_table::floating_op_table () set (LE_EXPR, fop_le); set (GT_EXPR, fop_gt); set (GE_EXPR, fop_ge); - set (UNLE_EXPR, fop_unordered_relop_unknown); - set (UNLT_EXPR, fop_unordered_relop_unknown); - set (UNGE_EXPR, fop_unordered_relop_unknown); - set (UNGT_EXPR, fop_unordered_relop_unknown); - set (UNEQ_EXPR, fop_unordered_relop_unknown); + 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); } // Return a pointer to the range_operator_float instance, if there is @@ -1195,3 +1659,44 @@ 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); +} + +} // namespace selftest + +#endif // CHECKING_P |