static bool
build_le (frange &r, tree type, const frange &val)
{
- if (val.known_isnan ())
- {
- r.set_undefined ();
- return false;
- }
+ gcc_checking_assert (!val.known_isnan ());
+
r.set (type, dconstninf, val.upper_bound ());
// Add both zeros if there's the possibility of zero equality.
static bool
build_lt (frange &r, tree type, const frange &val)
{
- if (val.known_isnan ())
- {
- r.set_undefined ();
- return false;
- }
+ gcc_checking_assert (!val.known_isnan ());
+
// < -INF is outside the range.
if (real_isinf (&val.upper_bound (), 1))
{
static bool
build_ge (frange &r, tree type, const frange &val)
{
- if (val.known_isnan ())
- {
- r.set_undefined ();
- return false;
- }
+ gcc_checking_assert (!val.known_isnan ());
+
r.set (type, val.lower_bound (), dconstinf);
// Add both zeros if there's the possibility of zero equality.
static bool
build_gt (frange &r, tree type, const frange &val)
{
- if (val.known_isnan ())
- {
- r.set_undefined ();
- return false;
- }
+ gcc_checking_assert (!val.known_isnan ());
+
// > +INF is outside the range.
if (real_isinf (&val.lower_bound (), 0))
{
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.
- if (op1.singleton_p () && op2.singleton_p ())
+ else if (op1.singleton_p () && op2.singleton_p ())
{
if (op1 == op2)
r = range_true (type);
bool
foperator_equal::op1_range (frange &r, tree type,
const irange &lhs,
- const frange &op2 ATTRIBUTE_UNUSED,
+ const frange &op2,
relation_kind rel) const
{
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- // 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 ();
+ // 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:
- r.set_varying (type);
// 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 ()
REAL_VALUE_TYPE tmp = op2.lower_bound ();
r.set (type, tmp, tmp, VR_ANTI_RANGE);
}
+ else
+ r.set_varying (type);
break;
default:
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.
- if (op1.singleton_p () && op2.singleton_p ())
+ else if (op1.singleton_p () && op2.singleton_p ())
{
if (op1 != op2)
r = range_true (type);
bool
foperator_not_equal::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))
break;
case BRS_FALSE:
- // 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 ();
+ // 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:
if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LT))
return true;
- if (finite_operands_p (op1, op2))
+ if (op1.known_isnan () || op2.known_isnan ())
+ r = range_false (type);
+ else if (finite_operands_p (op1, op2))
{
if (real_less (&op1.upper_bound (), &op2.lower_bound ()))
r = range_true (type);
else
r = range_true_and_false (type);
}
- else if (op1.known_isnan () || op2.known_isnan ())
- r = range_false (type);
else
r = range_true_and_false (type);
return true;
foperator_lt::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:
- if (build_lt (r, type, op2))
+ // 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.
break;
case BRS_FALSE:
- build_ge (r, type, op2);
+ // 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:
foperator_lt::op2_range (frange &r,
tree type,
const irange &lhs,
- const frange &op1 ATTRIBUTE_UNUSED,
+ const frange &op1,
relation_kind) const
{
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- if (build_gt (r, type, op1))
+ // 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.
break;
case BRS_FALSE:
- build_le (r, type, op1);
+ // 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:
if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LE))
return true;
- if (finite_operands_p (op1, op2))
+ if (op1.known_isnan () || op2.known_isnan ())
+ r = range_false (type);
+ else if (finite_operands_p (op1, op2))
{
if (real_compare (LE_EXPR, &op1.upper_bound (), &op2.lower_bound ()))
r = range_true (type);
else
r = range_true_and_false (type);
}
- else if (op1.known_isnan () || op2.known_isnan ())
- r = range_false (type);
else
r = range_true_and_false (type);
return true;
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- if (build_le (r, type, op2))
+ // 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:
- build_gt (r, type, op2);
+ // 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:
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- if (build_ge (r, type, op1))
+ // 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:
- build_lt (r, type, op1);
+ // 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:
if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GT))
return true;
- if (finite_operands_p (op1, op2))
+ if (op1.known_isnan () || op2.known_isnan ())
+ r = range_false (type);
+ else if (finite_operands_p (op1, op2))
{
if (real_compare (GT_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
r = range_true (type);
else
r = range_true_and_false (type);
}
- else if (op1.known_isnan () || op2.known_isnan ())
- r = range_false (type);
else
r = range_true_and_false (type);
return true;
foperator_gt::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:
- if (build_gt (r, type, op2))
+ // 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.
break;
case BRS_FALSE:
- build_le (r, type, op2);
+ // 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:
foperator_gt::op2_range (frange &r,
tree type,
const irange &lhs,
- const frange &op1 ATTRIBUTE_UNUSED,
+ const frange &op1,
relation_kind) const
{
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- if (build_lt (r, type, op1))
+ // 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.
break;
case BRS_FALSE:
- build_ge (r, type, op1);
+ // 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:
if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GE))
return true;
- if (finite_operands_p (op1, op2))
+ if (op1.known_isnan () || op2.known_isnan ())
+ r = range_false (type);
+ else if (finite_operands_p (op1, op2))
{
if (real_compare (GE_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
r = range_true (type);
else
r = range_true_and_false (type);
}
- else if (op1.known_isnan () || op2.known_isnan ())
- r = range_false (type);
else
r = range_true_and_false (type);
return true;
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- build_ge (r, type, op2);
- r.clear_nan ();
+ // 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:
- build_lt (r, type, op2);
+ // 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:
{
switch (get_bool_state (r, lhs, type))
{
- case BRS_FALSE:
- build_gt (r, type, op1);
+ case BRS_TRUE:
+ // The TRUE side of NAN >= x is unreachable.
+ if (op1.known_isnan ())
+ r.set_undefined ();
+ else
+ {
+ build_le (r, type, op1);
+ r.clear_nan ();
+ }
break;
- case BRS_TRUE:
- build_le (r, type, op1);
- r.clear_nan ();
+ 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:
bool
foperator_unordered::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);
// Since at least one operand must be NAN, if one of them is
// not, the other must be.
if (!op2.maybe_isnan ())
r.set_nan (type);
+ else
+ r.set_varying (type);
break;
case BRS_FALSE:
- r.set_varying (type);
- // A false UNORDERED means both operands are !NAN.
- 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 ())
+ r.set_undefined ();
+ else
+ {
+ r.set_varying (type);
+ r.clear_nan ();
+ }
break;
default:
const frange &op1, const frange &op2,
relation_kind) const
{
- if (!op1.maybe_isnan () && !op2.maybe_isnan ())
- r = range_true (type);
- else if (op1.known_isnan () || op2.known_isnan ())
+ 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 ATTRIBUTE_UNUSED,
+ const frange &op2,
relation_kind rel) const
{
switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- r.set_varying (type);
- // The TRUE side of op1 ORDERED op2 implies op1 is !NAN.
- r.clear_nan ();
+ // 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:
public:
bool fold_range (irange &r, tree type,
- const frange &, const frange &,
+ const frange &op1, const frange &op2,
relation_kind) const final override
{
- r.set_varying (type);
+ if (op1.known_isnan () || op2.known_isnan ())
+ r = range_true (type);
+ else
+ r.set_varying (type);
return true;
}
-} fop_relop_unknown;
+} fop_unordered_relop_unknown;
// Instantiate a range_op_table for floating point operations.
set (LE_EXPR, fop_le);
set (GT_EXPR, fop_gt);
set (GE_EXPR, fop_ge);
- set (UNLE_EXPR, fop_relop_unknown);
- set (UNLT_EXPR, fop_relop_unknown);
- set (UNGE_EXPR, fop_relop_unknown);
- set (UNGT_EXPR, fop_relop_unknown);
- set (UNEQ_EXPR, fop_relop_unknown);
+ 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 (ORDERED_EXPR, fop_ordered);
set (UNORDERED_EXPR, fop_unordered);
}