PING Andrew, anyone, would you mind giving this a once over? I realize reviewing ranger's range-op code is not on anyone's list of priorities, but I could use a sanity check. The patch is sufficiently self-contained to easily catch anything caused by it, and I'd like to commit earlier in the week to have enough time to field any possible fallout before I take a few days off next week. Updated patch attached. Thanks. Aldy On Mon, Jul 25, 2022 at 8:50 PM Aldy Hernandez wrote: > > Without further ado, here is the implementation for floating point > range operators, plus the switch to enable all ranger clients to > handle floats. > > These are bare bone implementations good enough for relation operators > to work, while keeping the NAN bits up to date in the frange. There > is also minimal support for keeping track of +-INF when it is obvious. > > I have included some basic tests to help get a feel of what is > ultimately handled. > > Since range-ops is the domain specific core of ranger, I think its > best if a global maintainer or an FP expert could review this. > > OK for trunk? > > Tested on x86-64 Linux. > > p.s. I haven't done extensive testing in DOM, but with this we're mighty > close for the forward threader there to be replaceable with the backward > threader, thus removing the last use of the forward threader. > > gcc/ChangeLog: > > * range-op-float.cc (finite_operands_p): New. > (frelop_early_resolve): New. > (default_frelop_fold_range): New. > (class foperator_equal): New. > (class foperator_not_equal): New. > (class foperator_lt): New. > (class foperator_le): New. > (class foperator_gt): New. > (class foperator_ge): New. > (class foperator_unordered): New. > (class foperator_ordered): New. > (class foperator_relop_unknown): New. > (floating_op_table::floating_op_table): Add above classes to > floating op table. > * value-range.h (frange::supports_p): Enable. > > gcc/testsuite/ChangeLog: > > * g++.dg/opt/pr94589-2.C: Add notes. > * gcc.dg/tree-ssa/vrp-float-1.c: New test. > * gcc.dg/tree-ssa/vrp-float-11.c: New test. > * gcc.dg/tree-ssa/vrp-float-3.c: New test. > * gcc.dg/tree-ssa/vrp-float-4.c: New test. > * gcc.dg/tree-ssa/vrp-float-6.c: New test. > * gcc.dg/tree-ssa/vrp-float-7.c: New test. > * gcc.dg/tree-ssa/vrp-float-8.c: New test. > --- > gcc/range-op-float.cc | 564 +++++++++++++++++++ > gcc/testsuite/g++.dg/opt/pr94589-2.C | 25 + > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c | 19 + > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c | 26 + > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c | 18 + > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c | 16 + > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c | 20 + > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c | 14 + > gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c | 26 + > gcc/value-range.h | 3 +- > 10 files changed, 729 insertions(+), 2 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c > > diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc > index 8e9d83e3827..d94ff6f915a 100644 > --- a/gcc/range-op-float.cc > +++ b/gcc/range-op-float.cc > @@ -150,6 +150,50 @@ range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) cons > return VREL_VARYING; > } > > +// Return TRUE if OP1 and OP2 are known to be free of NANs. > + > +static inline bool > +finite_operands_p (const frange &op1, const frange &op2) > +{ > + return (flag_finite_math_only > + || (op1.get_nan ().no_p () > + && op2.get_nan ().no_p ())); > +} > + > +// Floating version of relop_early_resolve that takes into account NAN > +// and -ffinite-math-only. > + > +inline bool > +frelop_early_resolve (irange &r, tree type, > + const frange &op1, const frange &op2, > + relation_kind rel, relation_kind my_rel) > +{ > + // 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 (finite_operands_p (op1, op2) > + && relop_early_resolve (r, type, op1, op2, rel, my_rel)); > +} > + > +// Default implementation of fold_range for relational operators. > +// This amounts to passing on any known relations from the oracle, iff > +// we know the operands are not NAN or -ffinite-math-only holds. > + > +static inline bool > +default_frelop_fold_range (irange &r, tree type, > + const frange &op1, const frange &op2, > + relation_kind rel, relation_kind my_rel) > +{ > + if (frelop_early_resolve (r, type, op1, op2, rel, my_rel)) > + return true; > + > + r.set_varying (type); > + return true; > +} > + > class foperator_identity : public range_operator_float > { > using range_operator_float::fold_range; > @@ -172,6 +216,509 @@ class foperator_identity : public range_operator_float > public: > } fop_identity; > > +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; > + > + 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 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; > + 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); > + } > +} fop_equal; > + > +bool > +foperator_equal::op1_range (frange &r, tree type, > + const irange &lhs, > + const frange &op2 ATTRIBUTE_UNUSED, > + relation_kind rel) 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. > + r.set_nan (fp_prop::NO); > + break; > + > + case BRS_FALSE: > + r.set_varying (type); > + // The FALSE side of op1 ORDERED op1 implies op1 is a NAN. > + if (rel == VREL_EQ) > + r.set_nan (fp_prop::YES); > + break; > + > + default: > + break; > + } > + return true; > +} > + > +class foperator_not_equal : public range_operator_float > +{ > + using range_operator_float::fold_range; > + using range_operator_float::op1_range; > + > + 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 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; > +} fop_not_equal; > + > +bool > +foperator_not_equal::op1_range (frange &r, tree type, > + const irange &lhs, > + const frange &op2 ATTRIBUTE_UNUSED, > + relation_kind) const > +{ > + switch (get_bool_state (r, lhs, type)) > + { > + case BRS_TRUE: > + r.set_varying (type); > + break; > + > + case BRS_FALSE: > + r.set_varying (type); > + // The FALSE side of op1 != op2 implies op1 is !NAN. > + r.set_nan (fp_prop::NO); > + break; > + > + default: > + break; > + } > + return true; > +} > + > +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; > + > + 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 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; > + bool op2_range (frange &r, tree type, > + const irange &lhs, const frange &op1, > + relation_kind rel) const final override; > +} fop_lt; > + > +bool > +foperator_lt::op1_range (frange &r, > + tree type, > + const irange &lhs, > + const frange &op2 ATTRIBUTE_UNUSED, > + 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 and !INF. > + r.set_nan (fp_prop::NO); > + r.set_inf (fp_prop::NO); > + break; > + > + case BRS_FALSE: > + r.set_varying (type); > + break; > + > + default: > + break; > + } > + return true; > +} > + > +bool > +foperator_lt::op2_range (frange &r, > + tree type, > + const irange &lhs, > + const frange &op1 ATTRIBUTE_UNUSED, > + relation_kind) const > +{ > + 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. > + r.set_nan (fp_prop::NO); > + r.set_ninf (fp_prop::NO); > + break; > + > + case BRS_FALSE: > + r.set_varying (type); > + break; > + > + default: > + break; > + } > + return true; > +} > + > +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; > + > + 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 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; > + 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); > + } > +} fop_le; > + > +bool > +foperator_le::op1_range (frange &r, > + tree type, > + const irange &lhs, > + const frange &op2 ATTRIBUTE_UNUSED, > + 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. > + r.set_nan (fp_prop::NO); > + break; > + > + case BRS_FALSE: > + r.set_varying (type); > + break; > + > + default: > + break; > + } > + return true; > +} > + > +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; > + > + 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 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; > + bool op2_range (frange &r, tree type, > + const irange &lhs, const frange &op1, > + relation_kind rel) const final override; > +} fop_gt; > + > +bool > +foperator_gt::op1_range (frange &r, > + tree type, > + const irange &lhs, > + const frange &op2 ATTRIBUTE_UNUSED, > + 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 and !NINF. > + r.set_nan (fp_prop::NO); > + r.set_ninf (fp_prop::NO); > + break; > + > + case BRS_FALSE: > + r.set_varying (type); > + break; > + > + default: > + break; > + } > + return true; > +} > + > +bool > +foperator_gt::op2_range (frange &r, > + tree type, > + const irange &lhs, > + const frange &op1 ATTRIBUTE_UNUSED, > + relation_kind) const > +{ > + 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. > + r.set_nan (fp_prop::NO); > + r.set_inf (fp_prop::NO); > + break; > + > + case BRS_FALSE: > + r.set_varying (type); > + break; > + > + default: > + break; > + } > + return true; > +} > + > +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; > + > + 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 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; > + 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); > + } > +} fop_ge; > + > +bool > +foperator_ge::op1_range (frange &r, > + tree type, > + const irange &lhs, > + const frange &op2 ATTRIBUTE_UNUSED, > + 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. > + r.set_nan (fp_prop::NO); > + break; > + > + case BRS_FALSE: > + r.set_varying (type); > + break; > + > + default: > + break; > + } > + return true; > +} > + > +// UNORDERED_EXPR comparison. > + > +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; > + bool op1_range (frange &r, tree type, > + const irange &lhs, const frange &op2, > + 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); > + } > +} fop_unordered; > + > +bool > +foperator_unordered::fold_range (irange &r, tree type, > + const frange &op1, const frange &op2, > + relation_kind) const > +{ > + // UNORDERED is TRUE if either operand is a NAN. > + if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ()) > + r = range_true (type); > + // UNORDERED is FALSE if neither operand is a NAN. > + else if (op1.get_nan ().no_p () && op2.get_nan ().no_p ()) > + r = range_false (type); > + else > + r = range_true_and_false (type); > + return true; > +} > + > +bool > +foperator_unordered::op1_range (frange &r, tree type, > + const irange &lhs, > + const frange &op2 ATTRIBUTE_UNUSED, > + 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.get_nan ().no_p ()) > + r.set_nan (fp_prop::YES); > + break; > + > + case BRS_FALSE: > + r.set_varying (type); > + // A false UNORDERED means both operands are !NAN. > + r.set_nan (fp_prop::NO); > + break; > + > + default: > + break; > + } > + return true; > +} > + > +// ORDERED_EXPR comparison. > + > +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; > + bool op1_range (frange &r, tree type, > + const irange &lhs, const frange &op2, > + 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); > + } > +} fop_ordered; > + > +bool > +foperator_ordered::fold_range (irange &r, tree type, > + const frange &op1, const frange &op2, > + relation_kind) const > +{ > + // ORDERED is TRUE if neither operand is a NAN. > + if (op1.get_nan ().no_p () && op2.get_nan ().no_p ()) > + r = range_true (type); > + // ORDERED is FALSE if either operand is a NAN. > + 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_ordered::op1_range (frange &r, tree type, > + const irange &lhs, > + const frange &op2 ATTRIBUTE_UNUSED, > + relation_kind rel) const > +{ > + switch (get_bool_state (r, lhs, type)) > + { > + case BRS_TRUE: > + r.set_varying (type); > + // The TRUE side of op1 UNORDERED op2 implies op1 is !NAN. > + r.set_nan (fp_prop::NO); > + break; > + > + case BRS_FALSE: > + r.set_varying (type); > + // The FALSE side of op1 UNORDERED op1 implies op1 is !NAN. > + if (rel == VREL_EQ) > + r.set_nan (fp_prop::NO); > + break; > + > + default: > + break; > + } > + return true; > +} > + > +// Placeholder for unimplemented relational operators. > + > +class foperator_relop_unknown : public range_operator_float > +{ > + using range_operator_float::fold_range; > + > +public: > + bool fold_range (irange &r, tree type, > + const frange &, const frange &, > + relation_kind) const final override > + { > + r.set_varying (type); > + return true; > + } > +} fop_relop_unknown; > + > > // Instantiate a range_op_table for floating point operations. > static floating_op_table global_floating_table; > @@ -185,6 +732,23 @@ floating_op_table::floating_op_table () > set (PAREN_EXPR, fop_identity); > set (OBJ_TYPE_REF, fop_identity); > set (REAL_CST, fop_identity); > + > + // All the relational operators are expected to work, because the > + // calculation of ranges on outgoing edges expect the handlers to be > + // present. > + set (EQ_EXPR, fop_equal); > + set (NE_EXPR, fop_not_equal); > + set (LT_EXPR, fop_lt); > + 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 (ORDERED_EXPR, fop_ordered); > + set (UNORDERED_EXPR, fop_unordered); > } > > // Return a pointer to the range_operator_float instance, if there is > diff --git a/gcc/testsuite/g++.dg/opt/pr94589-2.C b/gcc/testsuite/g++.dg/opt/pr94589-2.C > index e9ef84b1912..1caa725061e 100644 > --- a/gcc/testsuite/g++.dg/opt/pr94589-2.C > +++ b/gcc/testsuite/g++.dg/opt/pr94589-2.C > @@ -4,6 +4,31 @@ > // { dg-final { scan-tree-dump-times "\[ij]_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) \[ij]_\[0-9]+\\(D\\)" 12 "optimized" } } > // { dg-final { scan-tree-dump-times "i_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) 5\\.0" 12 "optimized" } } > > +/* This is failing on f5() because the spaceship operator is no longer > + folded. What happens is that evrp folds away the final condition > + here as always true. > + > + : > + if (i_2(D) != j_4(D)) > + goto ; [INV] > + else > + goto ; [INV] > + > + : > + if (i_2(D) >= j_4(D)) > + goto ; [INV] > + else > + goto ; [INV] > + > + : > + if (i_2(D) > j_4(D)) <<== ALWAYS TRUE > + goto ; [INV] > + else > + goto ; [INV] > + > + This causes phiopt to no longer be able to fold the total sequence > + into i_2 >= j_4. */ > + > #include > > #define A __attribute__((noipa)) > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c > new file mode 100644 > index 00000000000..88faf72ac42 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c > @@ -0,0 +1,19 @@ > +// { dg-do compile } > +// { dg-options "-O2 -fdisable-tree-ethread -fdisable-tree-fre1 -fdump-tree-evrp" } > + > +void bar (); > +void george (); > + > +float > +foo (float x, float y) > +{ > + if (x == x) > + { > + if (x > y) > + bar(); > + if (x == x) > + george(); > + } > +} > + > +// { dg-final { scan-tree-dump-times "Folding predicate x_*to 1" "evrp" 1 } } > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c > new file mode 100644 > index 00000000000..2f4dc8757a3 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c > @@ -0,0 +1,26 @@ > +// { dg-do compile } > +// { dg-options "-O2 -fno-thread-jumps -fdump-tree-evrp" } > + > +extern void link_error (); > + > +void fast_sqrt (float); > + > +float test (float x) > +{ > + float y = x*x; > + if (y >= 0.f) > + { > + if (__builtin_isnan (y)) > + link_error (); > + else > + fast_sqrt (y); > + > + if (!__builtin_isnan (y)) > + fast_sqrt (y); > + else > + link_error (); > + } > +} > + > +// { dg-final { scan-tree-dump-times "fast_sqrt" 2 "evrp" } } > +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c > new file mode 100644 > index 00000000000..c659abb6cc0 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c > @@ -0,0 +1,18 @@ > +// { dg-do compile } > +// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" } > + > +void link_error (); > + > +void > +foo (double x, double y) > +{ > + if (x == y) > + { > + if (__builtin_isnan (x)) > + link_error (); > + if (__builtin_isnan (y)) > + link_error (); > + } > +} > + > +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c > new file mode 100644 > index 00000000000..86436742e0a > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c > @@ -0,0 +1,16 @@ > +// { dg-do compile } > +// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" } > + > +void link_error (); > + > +void > +foo (double x, double y) > +{ > + if (x > y) > + { > + if (__builtin_isnan (x) || __builtin_isnan (y)) > + link_error (); > + } > +} > + > +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c > new file mode 100644 > index 00000000000..145d1861804 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c > @@ -0,0 +1,20 @@ > +// { dg-do compile } > +// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" } > + > +void bar (); > + > +void > +foo (double x, double y) > +{ > + if (x > y) > + ; > + else if (!__builtin_isnan (x) && !__builtin_isnan (y)) > + { > + // If x and y are not NAN, the x <= y relationship holds, and the > + // following conditional can be folded away. > + if (x <= y) > + bar (); > + } > +} > + > +// { dg-final { scan-tree-dump-times "Folding predicate x_.* <= y_.* to 1" 1 "evrp" } } > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c > new file mode 100644 > index 00000000000..92af87091a8 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c > @@ -0,0 +1,14 @@ > +// { dg-do compile } > +// { dg-options "-O2 -fno-tree-forwprop -fno-tree-ccp -fno-tree-fre -fdump-tree-evrp" } > + > +extern void link_error (); > + > +void > +foo () > +{ > + float z = 0.0; > + if (__builtin_isnan (z)) > + link_error (); > +} > + > +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c > new file mode 100644 > index 00000000000..9170150d453 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c > @@ -0,0 +1,26 @@ > +// { dg-do compile } > +// { dg-options "-O2 -fno-thread-jumps -fdump-tree-evrp" } > + > +extern void link_error (); > + > +void fast_sqrt (float); > + > +float test (float x) > +{ > + float y = x*x; > + if (y >= 0.f) > + { > + if (__builtin_isnan (y)) > + link_error (); > + else > + fast_sqrt (y); > + > + if (!__builtin_isnan (y)) > + fast_sqrt (y); > + else > + link_error (); > + } > +} > + > +// { dg-final { scan-tree-dump-times "fast_sqrt" 2 "evrp" } } > +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } > diff --git a/gcc/value-range.h b/gcc/value-range.h > index e43fbe30f27..478f165e02b 100644 > --- a/gcc/value-range.h > +++ b/gcc/value-range.h > @@ -338,8 +338,7 @@ public: > frange (const frange &); > static bool supports_p (tree type) > { > - // Disabled until floating point range-ops come live. > - return 0 && SCALAR_FLOAT_TYPE_P (type); > + return SCALAR_FLOAT_TYPE_P (type); > } > virtual tree type () const override; > virtual void set (tree, tree, value_range_kind = VR_RANGE) override; > -- > 2.36.1 >