aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorAldy Hernandez <aldyh@redhat.com>2022-08-30 08:23:51 +0200
committerAldy Hernandez <aldyh@redhat.com>2022-08-30 11:26:24 +0200
commit4fbe3e6aa74dae5c75a73c46ae6683fdecd1a75d (patch)
tree220ecc3307a028f7c960b977f5feaf66e9989a3a /gcc
parent8bb1df032cc080b751e00c0d7d86c31a630105f9 (diff)
downloadgcc-4fbe3e6aa74dae5c75a73c46ae6683fdecd1a75d.zip
gcc-4fbe3e6aa74dae5c75a73c46ae6683fdecd1a75d.tar.gz
gcc-4fbe3e6aa74dae5c75a73c46ae6683fdecd1a75d.tar.bz2
Implement relational operators for frange with endpoints.
This is the implementation of the relational range operators for frange. These are the core operations that require specific FP domain knowledge. gcc/ChangeLog: * range-op-float.cc (finite_operand_p): New. (build_le): New. (build_lt): New. (build_ge): New. (build_gt): New. (foperator_equal::fold_range): New implementation with endpoints. (foperator_equal::op1_range): Same. (foperator_not_equal::fold_range): Same. (foperator_not_equal::op1_range): Same. (foperator_lt::fold_range): Same. (foperator_lt::op1_range): Same. (foperator_lt::op2_range): Same. (foperator_le::fold_range): Same. (foperator_le::op1_range): Same. (foperator_le::op2_range): Same. (foperator_gt::fold_range): Same. (foperator_gt::op1_range): Same. (foperator_gt::op2_range): Same. (foperator_ge::fold_range): Same. (foperator_ge::op1_range): Same. (foperator_ge::op2_range): Same. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/recip-3.c: Avoid premature optimization so test has a chance to succeed.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/range-op-float.cc358
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/recip-3.c5
2 files changed, 309 insertions, 54 deletions
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index ca41136..d859309 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -162,6 +162,14 @@ frange_set_nan (frange &r, tree type)
r.set (type, rv, rv);
}
+// Return TRUE if OP1 is known to be free of NANs.
+
+static inline bool
+finite_operand_p (const frange &op1)
+{
+ return flag_finite_math_only || op1.get_nan ().no_p ();
+}
+
// Return TRUE if OP1 and OP2 are known to be free of NANs.
static inline bool
@@ -240,6 +248,45 @@ default_frelop_fold_range (irange &r, tree type,
return true;
}
+// (X <= VAL) produces the range of [MIN, VAL].
+
+static void
+build_le (frange &r, tree type, const REAL_VALUE_TYPE &val)
+{
+ REAL_VALUE_TYPE min;
+ real_inf (&min, 1);
+ r.set (type, min, val);
+}
+
+// (X < VAL) produces the range of [MIN, VAL).
+
+static void
+build_lt (frange &r, tree type, const REAL_VALUE_TYPE &val)
+{
+ // Hijack LE because we only support closed intervals.
+ build_le (r, type, val);
+}
+
+// (X >= VAL) produces the range of [VAL, MAX].
+
+static void
+build_ge (frange &r, tree type, const REAL_VALUE_TYPE &val)
+{
+ REAL_VALUE_TYPE max;
+ real_inf (&max, 0);
+ r.set (type, val, max);
+}
+
+// (X > VAL) produces the range of (VAL, MAX].
+
+static void
+build_gt (frange &r, tree type, const REAL_VALUE_TYPE &val)
+{
+ // Hijack GE because we only support closed intervals.
+ build_ge (r, type, val);
+}
+
+
class foperator_identity : public range_operator_float
{
using range_operator_float::fold_range;
@@ -270,10 +317,7 @@ class foperator_equal : public range_operator_float
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override
- {
- return default_frelop_fold_range (r, type, op1, op2, rel, VREL_EQ);
- }
+ relation_kind rel) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return equal_op1_op2_relation (lhs);
@@ -290,6 +334,39 @@ class foperator_equal : public range_operator_float
} fop_equal;
bool
+foperator_equal::fold_range (irange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind rel) const
+{
+ if (frelop_early_resolve (r, type, op1, op2, rel, VREL_EQ))
+ return true;
+
+ // We can be sure the values are always equal or not if both ranges
+ // consist of a single value, and then compare them.
+ if (op1.singleton_p () && op2.singleton_p ())
+ {
+ if (op1 == op2)
+ r = range_true (type);
+ else
+ r = range_false (type);
+ }
+ else if (finite_operands_p (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 ATTRIBUTE_UNUSED,
@@ -309,6 +386,14 @@ foperator_equal::op1_range (frange &r, tree type,
// The FALSE side of op1 == op1 implies op1 is a NAN.
if (rel == VREL_EQ)
frange_set_nan (r, type);
+ // If the result is false, the only time we know anything is
+ // if OP2 is a constant.
+ else if (op2.singleton_p ()
+ || (finite_operand_p (op2) && op2.zero_p ()))
+ {
+ REAL_VALUE_TYPE tmp = op2.lower_bound ();
+ r.set (type, tmp, tmp, VR_ANTI_RANGE);
+ }
break;
default:
@@ -324,10 +409,7 @@ class foperator_not_equal : public range_operator_float
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override
- {
- return default_frelop_fold_range (r, type, op1, op2, rel, VREL_NE);
- }
+ relation_kind rel) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return not_equal_op1_op2_relation (lhs);
@@ -338,6 +420,39 @@ class foperator_not_equal : public range_operator_float
} fop_not_equal;
bool
+foperator_not_equal::fold_range (irange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind rel) const
+{
+ if (frelop_early_resolve (r, type, op1, op2, rel, VREL_NE))
+ return true;
+
+ // We can be sure the values are always equal or not if both ranges
+ // consist of a single value, and then compare them.
+ if (op1.singleton_p () && op2.singleton_p ())
+ {
+ if (op1 != op2)
+ r = range_true (type);
+ else
+ r = range_false (type);
+ }
+ else if (finite_operands_p (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 ATTRIBUTE_UNUSED,
@@ -346,11 +461,23 @@ foperator_not_equal::op1_range (frange &r, tree type,
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- r.set_varying (type);
+ // 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);
+ }
+ else
+ r.set_varying (type);
break;
case BRS_FALSE:
- r.set_varying (type);
+ // If it's false, the result is the same as OP2.
+ r = op2;
// The FALSE side of op1 != op2 implies op1 is !NAN.
r.set_nan (fp_prop::NO);
break;
@@ -369,10 +496,7 @@ class foperator_lt : public range_operator_float
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override
- {
- return default_frelop_fold_range (r, type, op1, op2, rel, VREL_LT);
- }
+ relation_kind rel) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return lt_op1_op2_relation (lhs);
@@ -386,6 +510,31 @@ class foperator_lt : public range_operator_float
} fop_lt;
bool
+foperator_lt::fold_range (irange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind rel) const
+{
+ if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LT))
+ return true;
+
+ if (finite_operands_p (op1, op2))
+ {
+ if (real_less (&op1.upper_bound (), &op2.lower_bound ()))
+ r = range_true (type);
+ else if (finite_operands_p (op1, op2)
+ && !real_less (&op1.lower_bound (), &op2.upper_bound ()))
+ r = range_false (type);
+ else
+ r = range_true_and_false (type);
+ }
+ else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+ r = range_false (type);
+ else
+ r = range_true_and_false (type);
+ return true;
+}
+
+bool
foperator_lt::op1_range (frange &r,
tree type,
const irange &lhs,
@@ -395,15 +544,14 @@ foperator_lt::op1_range (frange &r,
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- r.set_varying (type);
- // The TRUE side of op1 < op2 implies op1 is !NAN and !INF.
+ build_lt (r, type, op2.upper_bound ());
r.set_nan (fp_prop::NO);
// x < y implies x is not +INF.
frange_drop_inf (r, type);
break;
case BRS_FALSE:
- r.set_varying (type);
+ build_ge (r, type, op2.lower_bound ());
break;
default:
@@ -422,15 +570,14 @@ foperator_lt::op2_range (frange &r,
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- r.set_varying (type);
- // The TRUE side of op1 < op2 implies op2 is !NAN and !NINF.
+ build_gt (r, type, op1.lower_bound ());
r.set_nan (fp_prop::NO);
// x < y implies y is not -INF.
frange_drop_ninf (r, type);
break;
case BRS_FALSE:
- r.set_varying (type);
+ build_le (r, type, op1.upper_bound ());
break;
default:
@@ -447,10 +594,7 @@ class foperator_le : public range_operator_float
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override
- {
- return default_frelop_fold_range (r, type, op1, op2, rel, VREL_LE);
- }
+ relation_kind rel) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return le_op1_op2_relation (lhs);
@@ -460,29 +604,74 @@ class foperator_le : public range_operator_float
relation_kind rel) const final override;
bool op2_range (frange &r, tree type,
const irange &lhs, const frange &op1,
- relation_kind rel) const final override
- {
- return op1_range (r, type, lhs, op1, rel);
- }
+ relation_kind rel) const final override;
} fop_le;
bool
+foperator_le::fold_range (irange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind rel) const
+{
+ if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LE))
+ return true;
+
+ if (finite_operands_p (op1, op2))
+ {
+ if (real_compare (LE_EXPR, &op1.upper_bound (), &op2.lower_bound ()))
+ r = range_true (type);
+ else if (finite_operands_p (op1, op2)
+ && !real_compare (LE_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
+ r = range_false (type);
+ else
+ r = range_true_and_false (type);
+ }
+ else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+ r = range_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 ATTRIBUTE_UNUSED,
+ const frange &op2,
relation_kind) const
{
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- r.set_varying (type);
- // The TRUE side of op1 <= op2 implies op1 is !NAN.
+ build_le (r, type, op2.upper_bound ());
r.set_nan (fp_prop::NO);
break;
case BRS_FALSE:
- r.set_varying (type);
+ build_gt (r, type, op2.lower_bound ());
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+bool
+foperator_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.lower_bound ());
+ r.set_nan (fp_prop::NO);
+ break;
+
+ case BRS_FALSE:
+ build_lt (r, type, op1.upper_bound ());
break;
default:
@@ -499,10 +688,7 @@ class foperator_gt : public range_operator_float
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override
- {
- return default_frelop_fold_range (r, type, op1, op2, rel, VREL_GT);
- }
+ relation_kind rel) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return gt_op1_op2_relation (lhs);
@@ -516,6 +702,31 @@ class foperator_gt : public range_operator_float
} fop_gt;
bool
+foperator_gt::fold_range (irange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind rel) const
+{
+ if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GT))
+ return true;
+
+ if (finite_operands_p (op1, op2))
+ {
+ if (real_compare (GT_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
+ r = range_true (type);
+ else if (finite_operands_p (op1, op2)
+ && !real_compare (GT_EXPR, &op1.upper_bound (), &op2.lower_bound ()))
+ r = range_false (type);
+ else
+ r = range_true_and_false (type);
+ }
+ else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+ r = range_false (type);
+ else
+ r = range_true_and_false (type);
+ return true;
+}
+
+bool
foperator_gt::op1_range (frange &r,
tree type,
const irange &lhs,
@@ -525,15 +736,14 @@ foperator_gt::op1_range (frange &r,
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- r.set_varying (type);
- // The TRUE side of op1 > op2 implies op1 is !NAN and !NINF.
+ build_gt (r, type, op2.lower_bound ());
r.set_nan (fp_prop::NO);
// x > y implies x is not -INF.
frange_drop_ninf (r, type);
break;
case BRS_FALSE:
- r.set_varying (type);
+ build_le (r, type, op2.upper_bound ());
break;
default:
@@ -552,15 +762,14 @@ foperator_gt::op2_range (frange &r,
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- r.set_varying (type);
- // The TRUE side of op1 > op2 implies op2 is !NAN and !INF.
+ build_lt (r, type, op1.upper_bound ());
r.set_nan (fp_prop::NO);
// x > y implies y is not +INF.
frange_drop_inf (r, type);
break;
case BRS_FALSE:
- r.set_varying (type);
+ build_ge (r, type, op1.lower_bound ());
break;
default:
@@ -577,10 +786,7 @@ class foperator_ge : public range_operator_float
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
- relation_kind rel) const final override
- {
- return default_frelop_fold_range (r, type, op1, op2, rel, VREL_GE);
- }
+ relation_kind rel) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override
{
return ge_op1_op2_relation (lhs);
@@ -590,29 +796,73 @@ class foperator_ge : public range_operator_float
relation_kind rel) const final override;
bool op2_range (frange &r, tree type,
const irange &lhs, const frange &op1,
- relation_kind rel) const final override
- {
- return op1_range (r, type, lhs, op1, rel);
- }
+ relation_kind rel) const final override;
} fop_ge;
bool
+foperator_ge::fold_range (irange &r, tree type,
+ const frange &op1, const frange &op2,
+ relation_kind rel) const
+{
+ if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GE))
+ return true;
+
+ if (finite_operands_p (op1, op2))
+ {
+ if (real_compare (GE_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
+ r = range_true (type);
+ else if (finite_operands_p (op1, op2)
+ && !real_compare (GE_EXPR, &op1.upper_bound (), &op2.lower_bound ()))
+ r = range_false (type);
+ else
+ r = range_true_and_false (type);
+ }
+ else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+ r = range_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 ATTRIBUTE_UNUSED,
+ const frange &op2,
relation_kind) const
{
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- r.set_varying (type);
- // The TRUE side of op1 >= op2 implies op1 is !NAN.
+ build_ge (r, type, op2.lower_bound ());
r.set_nan (fp_prop::NO);
break;
case BRS_FALSE:
- r.set_varying (type);
+ build_lt (r, type, op2.upper_bound ());
+ break;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+bool
+foperator_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_FALSE:
+ build_gt (r, type, op1.lower_bound ());
+ break;
+
+ case BRS_TRUE:
+ build_le (r, type, op1.upper_bound ());
+ r.set_nan (fp_prop::NO);
break;
default:
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c b/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c
index 410b280..036f32a 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c
@@ -1,6 +1,11 @@
/* { dg-do compile } */
/* { dg-options "-O1 -fno-trapping-math -funsafe-math-optimizations -fdump-tree-recip" } */
+/* The recip pass has a threshold of 3 reciprocal operations before it attempts
+ to optimize a sequence. With a FP enabled ranger, we eliminate one of them
+ earlier, causing the pass to skip this optimization. */
+/* { dg-additional-options "-fno-thread-jumps -fno-tree-dominator-opts" } */
+
double F[5] = { 0.0, 0.0 }, e;
/* In this case the optimization is interesting. */