diff options
Diffstat (limited to 'gcc/range-op-float.cc')
-rw-r--r-- | gcc/range-op-float.cc | 148 |
1 files changed, 120 insertions, 28 deletions
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index e30b489..1419964 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -268,22 +268,75 @@ 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. +// Floating point version of relop_early_resolve that takes NANs into +// account. +// +// For relation opcodes, first try to see if the supplied relation +// forces a true or false result, and return that. +// Then check for undefined operands. If none of this applies, +// return false. +// +// TRIO are the relations between operands as they appear in the IL. +// MY_REL is the relation that corresponds to the operator being +// folded. For example, when attempting to fold x_3 == y_5, MY_REL is +// VREL_EQ, and if the statement is dominated by x_3 > y_5, then +// TRIO.op1_op2() is VREL_GT. +// +// Relations in a floating point world are a bit tricky, as TRIO +// behaves as the corresponding unordered variant if either operand +// could be a NAN. For example, when resolving "if (x_5 == x_5)", the +// relation is VREL_EQ, but it behaves like VREL_UNEQ if NANs are a +// possibility. Similarly, the false edge of "if (x >= y)" has a +// relation of VREL_LT, but behaves as VREL_UNLT unless we can prove +// neither operand can be NAN. +// +// ?? It is unclear whether providing unordered VREL relations would +// simplify things, as we'd have to add more entries to various +// tables, tweak all the op1_op2_relation() entries, etc. static inline bool frelop_early_resolve (irange &r, tree type, const frange &op1, const frange &op2, - relation_trio rel, relation_kind my_rel) + relation_trio trio, relation_kind my_rel) { + relation_kind rel = trio.op1_op2 (); + + if (maybe_isnan (op1, op2)) + { + // There's not much we can do for VREL_EQ if NAN is a + // possibility. It is up to the caller to deal with these + // special cases. + if (rel == VREL_EQ) + return empty_range_varying (r, type, op1, op2); + } + // If known relation is a complete subset of this relation, always + // return true. However, avoid doing this when NAN is a possibility + // as we'll incorrectly fold conditions: + // + // if (x_3 >= y_5) + // ; + // else + // ;; With NANs the relation here is basically VREL_UNLT, so we + // ;; can't fold the following: + // if (x_3 < y_5) + else if (relation_union (rel, my_rel) == my_rel) + { + r = range_true (type); + return true; + } + + // If known relation has no subset of this relation, always false. + if (relation_intersect (rel, my_rel) == VREL_UNDEFINED) + { + r = range_false (type); + return true; + } + // 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)); + return false; } // Set VALUE to its next real value, or INF if the operation overflows. @@ -738,9 +791,17 @@ operator_equal::op1_op2_relation (const irange &lhs, const frange &, bool operator_not_equal::fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_trio rel) const + relation_trio trio) const { - if (frelop_early_resolve (r, type, op1, op2, rel, VREL_NE)) + relation_kind rel = trio.op1_op2 (); + + // VREL_NE & NE_EXPR is always true, even with NANs. + if (rel == VREL_NE) + { + r = range_true (type); + return true; + } + if (frelop_early_resolve (r, type, op1, op2, trio, VREL_NE)) return true; // x != NAN is always TRUE. @@ -862,9 +923,17 @@ operator_not_equal::op1_op2_relation (const irange &lhs, const frange &, bool operator_lt::fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_trio rel) const + relation_trio trio) const { - if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LT)) + relation_kind rel = trio.op1_op2 (); + + // VREL_EQ & LT_EXPR is impossible, even with NANs. + if (rel == VREL_EQ) + { + r = range_false (type); + return true; + } + if (frelop_early_resolve (r, type, op1, op2, trio, VREL_LT)) return true; if (op1.known_isnan () @@ -1083,9 +1152,17 @@ operator_le::op1_op2_relation (const irange &lhs, const frange &, bool operator_gt::fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_trio rel) const + relation_trio trio) const { - if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GT)) + relation_kind rel = trio.op1_op2 (); + + // VREL_EQ & GT_EXPR is impossible, even with NANs. + if (rel == VREL_EQ) + { + r = range_false (type); + return true; + } + if (frelop_early_resolve (r, type, op1, op2, trio, VREL_GT)) return true; if (op1.known_isnan () @@ -1587,9 +1664,12 @@ class foperator_unordered_lt : public range_operator public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_trio rel = TRIO_VARYING) const final override + relation_trio trio = TRIO_VARYING) const final override { - if (op1.known_isnan () || op2.known_isnan ()) + relation_kind rel = trio.op1_op2 (); + + if (op1.known_isnan () || op2.known_isnan () + || rel == VREL_LT) { r = range_true (type); return true; @@ -1601,7 +1681,7 @@ public: if (op2.maybe_isnan ()) op2_no_nan.clear_nan (); if (!range_op_handler (LT_EXPR).fold_range (r, type, op1_no_nan, - op2_no_nan, rel)) + op2_no_nan, trio)) return false; // The result is the same as the ordered version when the // comparison is true or when the operands cannot be NANs. @@ -1699,9 +1779,12 @@ class foperator_unordered_le : public range_operator public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_trio rel = TRIO_VARYING) const final override + relation_trio trio = TRIO_VARYING) const final override { - if (op1.known_isnan () || op2.known_isnan ()) + relation_kind rel = trio.op1_op2 (); + + if (op1.known_isnan () || op2.known_isnan () + || rel == VREL_LE) { r = range_true (type); return true; @@ -1713,7 +1796,7 @@ public: if (op2.maybe_isnan ()) op2_no_nan.clear_nan (); if (!range_op_handler (LE_EXPR).fold_range (r, type, op1_no_nan, - op2_no_nan, rel)) + op2_no_nan, trio)) return false; // The result is the same as the ordered version when the // comparison is true or when the operands cannot be NANs. @@ -1807,9 +1890,12 @@ class foperator_unordered_gt : public range_operator public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_trio rel = TRIO_VARYING) const final override + relation_trio trio = TRIO_VARYING) const final override { - if (op1.known_isnan () || op2.known_isnan ()) + relation_kind rel = trio.op1_op2 (); + + if (op1.known_isnan () || op2.known_isnan () + || rel == VREL_GT) { r = range_true (type); return true; @@ -1821,7 +1907,7 @@ public: if (op2.maybe_isnan ()) op2_no_nan.clear_nan (); if (!range_op_handler (GT_EXPR).fold_range (r, type, op1_no_nan, - op2_no_nan, rel)) + op2_no_nan, trio)) return false; // The result is the same as the ordered version when the // comparison is true or when the operands cannot be NANs. @@ -1919,9 +2005,12 @@ class foperator_unordered_ge : public range_operator public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_trio rel = TRIO_VARYING) const final override + relation_trio trio = TRIO_VARYING) const final override { - if (op1.known_isnan () || op2.known_isnan ()) + relation_kind rel = trio.op1_op2 (); + + if (op1.known_isnan () || op2.known_isnan () + || rel == VREL_GE) { r = range_true (type); return true; @@ -1933,7 +2022,7 @@ public: if (op2.maybe_isnan ()) op2_no_nan.clear_nan (); if (!range_op_handler (GE_EXPR).fold_range (r, type, op1_no_nan, - op2_no_nan, rel)) + op2_no_nan, trio)) return false; // The result is the same as the ordered version when the // comparison is true or when the operands cannot be NANs. @@ -2030,9 +2119,12 @@ class foperator_unordered_equal : public range_operator public: bool fold_range (irange &r, tree type, const frange &op1, const frange &op2, - relation_trio rel = TRIO_VARYING) const final override + relation_trio trio = TRIO_VARYING) const final override { - if (op1.known_isnan () || op2.known_isnan ()) + relation_kind rel = trio.op1_op2 (); + + if (op1.known_isnan () || op2.known_isnan () + || rel == VREL_EQ) { r = range_true (type); return true; @@ -2044,7 +2136,7 @@ public: if (op2.maybe_isnan ()) op2_no_nan.clear_nan (); if (!range_op_handler (EQ_EXPR).fold_range (r, type, op1_no_nan, - op2_no_nan, rel)) + op2_no_nan, trio)) return false; // The result is the same as the ordered version when the // comparison is true or when the operands cannot be NANs. |