public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
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
>


  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).