From: Aldy Hernandez <aldyh@redhat.com>
To: GCC patches <gcc-patches@gcc.gnu.org>
Cc: Andrew MacLeod <amacleod@redhat.com>,
Jeff Law <jeffreyalaw@gmail.com>,
Richard Biener <richard.guenther@gmail.com>,
Jakub Jelinek <jakub@redhat.com>,
Roger Sayle <roger@nextmovesoftware.com>
Subject: Re: [RFA] Implement basic range operators to enable floating point VRP.
Date: Mon, 25 Jul 2022 20:53:45 +0200 [thread overview]
Message-ID: <CAGm3qMWceho-m=qR+169d0AeGnnY6OC1hJMUqeq-=3MeESyR6Q@mail.gmail.com> (raw)
In-Reply-To: <20220725184951.2202379-1-aldyh@redhat.com>
I forgot to mention. There's a regression in g++.dg/opt/pr94589-2.C,
where an early optimization by evrp causes phiopt to no longer
optimize a spaceship operator because it no longer sees the exact
sequence of conditionals. I have included the analysis in the patch.
Hopefully a phiopt expert can opine.
Aldy
On Mon, Jul 25, 2022 at 8:50 PM Aldy Hernandez <aldyh@redhat.com> 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.
> +
> + <bb 2> :
> + if (i_2(D) != j_4(D))
> + goto <bb 3>; [INV]
> + else
> + goto <bb 6>; [INV]
> +
> + <bb 3> :
> + if (i_2(D) >= j_4(D))
> + goto <bb 4>; [INV]
> + else
> + goto <bb 6>; [INV]
> +
> + <bb 4> :
> + if (i_2(D) > j_4(D)) <<== ALWAYS TRUE
> + goto <bb 6>; [INV]
> + else
> + goto <bb 5>; [INV]
> +
> + This causes phiopt to no longer be able to fold the total sequence
> + into i_2 >= j_4. */
> +
> #include <compare>
>
> #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
>
next prev parent reply other threads:[~2022-07-25 18:54 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-07-25 18:49 Aldy Hernandez
2022-07-25 18:53 ` Aldy Hernandez [this message]
2022-07-31 18:11 ` Aldy Hernandez
2022-08-02 12:57 ` Aldy Hernandez
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='CAGm3qMWceho-m=qR+169d0AeGnnY6OC1hJMUqeq-=3MeESyR6Q@mail.gmail.com' \
--to=aldyh@redhat.com \
--cc=amacleod@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jakub@redhat.com \
--cc=jeffreyalaw@gmail.com \
--cc=richard.guenther@gmail.com \
--cc=roger@nextmovesoftware.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).