@@ -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.
new file mode 100644
@@ -0,0 +1,23 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdisable-tree-fre1 -fdump-tree-evrp" }
+
+void link_error ();
+void func ();
+
+void foo1 (float x, float y)
+{
+ if (x != y)
+ if (x == y)
+ link_error();
+}
+
+void foo2 (float a, float b)
+{
+ if (a != b)
+ // This conditional should be folded away.
+ if (a != b)
+ func ();
+}
+
+// { dg-final { scan-tree-dump-not "link_error" "evrp" } }
+// { dg-final { scan-tree-dump-times "if \\(a_2\\(D\\) != b_3\\(D\\)" 1 "evrp" } }