/* Floating point range operators.
   Copyright (C) 2022 Free Software Foundation, Inc.
   Contributed by Aldy Hernandez <aldyh@redhat.com>.

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "insn-codes.h"
#include "rtl.h"
#include "tree.h"
#include "gimple.h"
#include "cfghooks.h"
#include "tree-pass.h"
#include "ssa.h"
#include "optabs-tree.h"
#include "gimple-pretty-print.h"
#include "diagnostic-core.h"
#include "flags.h"
#include "fold-const.h"
#include "stor-layout.h"
#include "calls.h"
#include "cfganal.h"
#include "gimple-iterator.h"
#include "gimple-fold.h"
#include "tree-eh.h"
#include "gimple-walk.h"
#include "tree-cfg.h"
#include "wide-int.h"
#include "value-relation.h"
#include "range-op.h"

// Default definitions for floating point operators.

bool
range_operator_float::fold_range (frange &r ATTRIBUTE_UNUSED,
				  tree type ATTRIBUTE_UNUSED,
				  const frange &lh ATTRIBUTE_UNUSED,
				  const frange &rh ATTRIBUTE_UNUSED,
				  relation_kind rel ATTRIBUTE_UNUSED) const
{
  return false;
}

bool
range_operator_float::fold_range (irange &r ATTRIBUTE_UNUSED,
				  tree type ATTRIBUTE_UNUSED,
				  const frange &lh ATTRIBUTE_UNUSED,
				  const irange &rh ATTRIBUTE_UNUSED,
				  relation_kind rel ATTRIBUTE_UNUSED) const
{
  return false;
}

bool
range_operator_float::fold_range (irange &r ATTRIBUTE_UNUSED,
				  tree type ATTRIBUTE_UNUSED,
				  const frange &lh ATTRIBUTE_UNUSED,
				  const frange &rh ATTRIBUTE_UNUSED,
				  relation_kind rel ATTRIBUTE_UNUSED) const
{
  return false;
}

bool
range_operator_float::op1_range (frange &r ATTRIBUTE_UNUSED,
				 tree type ATTRIBUTE_UNUSED,
				 const frange &lhs ATTRIBUTE_UNUSED,
				 const frange &op2 ATTRIBUTE_UNUSED,
				 relation_kind rel ATTRIBUTE_UNUSED) const
{
  return false;
}

bool
range_operator_float::op1_range (frange &r ATTRIBUTE_UNUSED,
				 tree type ATTRIBUTE_UNUSED,
				 const irange &lhs ATTRIBUTE_UNUSED,
				 const frange &op2 ATTRIBUTE_UNUSED,
				 relation_kind rel ATTRIBUTE_UNUSED) const
{
  return false;
}

bool
range_operator_float::op2_range (frange &r ATTRIBUTE_UNUSED,
				 tree type ATTRIBUTE_UNUSED,
				 const frange &lhs ATTRIBUTE_UNUSED,
				 const frange &op1 ATTRIBUTE_UNUSED,
				 relation_kind rel ATTRIBUTE_UNUSED) const
{
  return false;
}

bool
range_operator_float::op2_range (frange &r ATTRIBUTE_UNUSED,
				 tree type ATTRIBUTE_UNUSED,
				 const irange &lhs ATTRIBUTE_UNUSED,
				 const frange &op1 ATTRIBUTE_UNUSED,
				 relation_kind rel ATTRIBUTE_UNUSED) const
{
  return false;
}

relation_kind
range_operator_float::lhs_op1_relation (const frange &lhs ATTRIBUTE_UNUSED,
					const frange &op1 ATTRIBUTE_UNUSED,
					const frange &op2 ATTRIBUTE_UNUSED,
					relation_kind) const
{
  return VREL_VARYING;
}

relation_kind
range_operator_float::lhs_op1_relation (const irange &lhs ATTRIBUTE_UNUSED,
					const frange &op1 ATTRIBUTE_UNUSED,
					const frange &op2 ATTRIBUTE_UNUSED,
					relation_kind) const
{
  return VREL_VARYING;
}

relation_kind
range_operator_float::lhs_op2_relation (const irange &lhs ATTRIBUTE_UNUSED,
					const frange &op1 ATTRIBUTE_UNUSED,
					const frange &op2 ATTRIBUTE_UNUSED,
					relation_kind) const
{
  return VREL_VARYING;
}

relation_kind
range_operator_float::lhs_op2_relation (const frange &lhs ATTRIBUTE_UNUSED,
					const frange &op1 ATTRIBUTE_UNUSED,
					const frange &op2 ATTRIBUTE_UNUSED,
					relation_kind) const
{
  return VREL_VARYING;
}

relation_kind
range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) const
{
  return VREL_VARYING;
}

// Return TRUE if OP1 is known to be free of NANs.

static inline bool
finite_operand_p (const frange &op1)
{
  return flag_finite_math_only || !op1.maybe_isnan ();
}

// 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.maybe_isnan () && !op2.maybe_isnan ());
}

// 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));
}

// Crop R to [-INF, MAX] where MAX is the maximum representable number
// for TYPE.

static inline void
frange_drop_inf (frange &r, tree type)
{
  REAL_VALUE_TYPE max = real_max_representable (type);
  frange tmp (type, r.lower_bound (), max);
  r.intersect (tmp);
}

// Crop R to [MIN, +INF] where MIN is the minimum representable number
// for TYPE.

static inline void
frange_drop_ninf (frange &r, tree type)
{
  REAL_VALUE_TYPE min = real_min_representable (type);
  frange tmp (type, min, r.upper_bound ());
  r.intersect (tmp);
}

// If zero is in R, make sure both -0.0 and +0.0 are in the range.

static inline void
frange_add_zeros (frange &r, tree type)
{
  if (r.undefined_p () || r.known_isnan ())
    return;

  if (HONOR_SIGNED_ZEROS (type)
      && (real_iszero (&r.lower_bound ()) || real_iszero (&r.upper_bound ())))
    {
      frange zero;
      zero.set_zero (type);
      r.union_ (zero);
    }
}

// Build a range that is <= VAL and store it in R.

static bool
build_le (frange &r, tree type, const frange &val)
{
  gcc_checking_assert (!val.known_isnan ());

  REAL_VALUE_TYPE ninf = frange_val_min (type);
  r.set (type, ninf, val.upper_bound ());

  // Add both zeros if there's the possibility of zero equality.
  frange_add_zeros (r, type);

  return true;
}

// Build a range that is < VAL and store it in R.

static bool
build_lt (frange &r, tree type, const frange &val)
{
  gcc_checking_assert (!val.known_isnan ());

  // < -INF is outside the range.
  if (real_isinf (&val.upper_bound (), 1))
    {
      if (HONOR_NANS (type))
	r.set_nan (type);
      else
	r.set_undefined ();
      return false;
    }
  // We only support closed intervals.
  REAL_VALUE_TYPE ninf = frange_val_min (type);
  r.set (type, ninf, val.upper_bound ());
  return true;
}

// Build a range that is >= VAL and store it in R.

static bool
build_ge (frange &r, tree type, const frange &val)
{
  gcc_checking_assert (!val.known_isnan ());

  REAL_VALUE_TYPE inf = frange_val_max (type);
  r.set (type, val.lower_bound (), inf);

  // Add both zeros if there's the possibility of zero equality.
  frange_add_zeros (r, type);

  return true;
}

// Build a range that is > VAL and store it in R.

static bool
build_gt (frange &r, tree type, const frange &val)
{
  gcc_checking_assert (!val.known_isnan ());

  // > +INF is outside the range.
  if (real_isinf (&val.lower_bound (), 0))
    {
      if (HONOR_NANS (type))
	r.set_nan (type);
      else
	r.set_undefined ();
      return false;
    }

  // We only support closed intervals.
  REAL_VALUE_TYPE inf = frange_val_max (type);
  r.set (type, val.lower_bound (), inf);
  return true;
}


class foperator_identity : public range_operator_float
{
  using range_operator_float::fold_range;
  using range_operator_float::op1_range;

  bool fold_range (frange &r, tree type ATTRIBUTE_UNUSED,
		   const frange &op1, const frange &op2 ATTRIBUTE_UNUSED,
		   relation_kind) const final override
  {
    r = op1;
    return true;
  }
  bool op1_range (frange &r, tree type ATTRIBUTE_UNUSED,
		  const frange &lhs, const frange &op2 ATTRIBUTE_UNUSED,
		  relation_kind) const final override
  {
    r = lhs;
    return true;
  }
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;
  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::fold_range (irange &r, tree type,
			     const frange &op1, const frange &op2,
			     relation_kind rel) const
{
  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_EQ))
    return true;

  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.
  else if (op1.singleton_p () && op2.singleton_p ())
    {
      if (op1 == op2)
	r = range_true (type);
      else
	r = range_false (type);
    }
  else if (finite_operands_p (op1, op2))
    {
      // If ranges do not intersect, we know the range is not equal,
      // otherwise we don't know anything for sure.
      frange tmp = op1;
      tmp.intersect (op2);
      if (tmp.undefined_p ())
	r = range_false (type);
      else
	r = range_true_and_false (type);
    }
  else
    r = range_true_and_false (type);
  return true;
}

bool
foperator_equal::op1_range (frange &r, tree type,
			    const irange &lhs,
			    const frange &op2,
			    relation_kind rel) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // 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:
      // 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 ()
	       || (finite_operand_p (op2) && op2.zero_p ()))
	{
	  REAL_VALUE_TYPE tmp = op2.lower_bound ();
	  r.set (type, tmp, tmp, VR_ANTI_RANGE);
	}
      else
	r.set_varying (type);
      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;
  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::fold_range (irange &r, tree type,
				 const frange &op1, const frange &op2,
				 relation_kind rel) const
{
  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_NE))
    return true;

  // 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.
  else if (op1.singleton_p () && op2.singleton_p ())
    {
      if (op1 != op2)
	r = range_true (type);
      else
	r = range_false (type);
    }
  else if (finite_operands_p (op1, op2))
    {
      // If ranges do not intersect, we know the range is not equal,
      // otherwise we don't know anything for sure.
      frange tmp = op1;
      tmp.intersect (op2);
      if (tmp.undefined_p ())
	r = range_true (type);
      else
	r = range_true_and_false (type);
    }
  else
    r = range_true_and_false (type);
  return true;
}

bool
foperator_not_equal::op1_range (frange &r, tree type,
				const irange &lhs,
				const frange &op2,
				relation_kind) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // If the result is true, the only time we know anything is if
      // OP2 is a constant.
      if (op2.singleton_p ())
	{
	  // This is correct even if op1 is NAN, because the following
	  // range would be ~[tmp, tmp] with the NAN property set to
	  // maybe (VARYING).
	  REAL_VALUE_TYPE tmp = op2.lower_bound ();
	  r.set (type, tmp, tmp, VR_ANTI_RANGE);
	}
      else
	r.set_varying (type);
      break;

    case BRS_FALSE:
      // 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:
      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;
  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::fold_range (irange &r, tree type,
			  const frange &op1, const frange &op2,
			  relation_kind rel) const
{
  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LT))
    return true;

  if (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 if (finite_operands_p (op1, op2)
	       && !real_less (&op1.lower_bound (), &op2.upper_bound ()))
	r = range_false (type);
      else
	r = range_true_and_false (type);
    }
  else
    r = range_true_and_false (type);
  return true;
}

bool
foperator_lt::op1_range (frange &r,
			 tree type,
			 const irange &lhs,
			 const frange &op2,
			 relation_kind) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // 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.
	  frange_drop_inf (r, type);
	}
      break;

    case BRS_FALSE:
      // 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:
      break;
    }
  return true;
}

bool
foperator_lt::op2_range (frange &r,
			 tree type,
			 const irange &lhs,
			 const frange &op1,
			 relation_kind) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // 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.
	  frange_drop_ninf (r, type);
	}
      break;

    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_le (r, type, op1);
      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;
  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;
} fop_le;

bool
foperator_le::fold_range (irange &r, tree type,
			  const frange &op1, const frange &op2,
			  relation_kind rel) const
{
  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LE))
    return true;

  if (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 if (finite_operands_p (op1, op2)
	       && !real_compare (LE_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
	r = range_false (type);
      else
	r = range_true_and_false (type);
    }
  else
    r = range_true_and_false (type);
  return true;
}

bool
foperator_le::op1_range (frange &r,
			 tree type,
			 const irange &lhs,
			 const frange &op2,
			 relation_kind) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // 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:
      // 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:
      break;
    }
  return true;
}

bool
foperator_le::op2_range (frange &r,
			 tree type,
			 const irange &lhs,
			 const frange &op1,
			 relation_kind) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // 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:
      // 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:
      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;
  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::fold_range (irange &r, tree type,
			  const frange &op1, const frange &op2,
			  relation_kind rel) const
{
  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GT))
    return true;

  if (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 if (finite_operands_p (op1, op2)
	       && !real_compare (GT_EXPR, &op1.upper_bound (), &op2.lower_bound ()))
	r = range_false (type);
      else
	r = range_true_and_false (type);
    }
  else
    r = range_true_and_false (type);
  return true;
}

bool
foperator_gt::op1_range (frange &r,
			 tree type,
			 const irange &lhs,
			 const frange &op2,
			 relation_kind) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // 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.
	  frange_drop_ninf (r, type);
	}
      break;

    case BRS_FALSE:
      // 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:
      break;
    }
  return true;
}

bool
foperator_gt::op2_range (frange &r,
			 tree type,
			 const irange &lhs,
			 const frange &op1,
			 relation_kind) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // 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.
	  frange_drop_inf (r, type);
	}
      break;

    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_ge (r, type, op1);
      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;
  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;
} fop_ge;

bool
foperator_ge::fold_range (irange &r, tree type,
			  const frange &op1, const frange &op2,
			  relation_kind rel) const
{
  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GE))
    return true;

  if (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 if (finite_operands_p (op1, op2)
	       && !real_compare (GE_EXPR, &op1.upper_bound (), &op2.lower_bound ()))
	r = range_false (type);
      else
	r = range_true_and_false (type);
    }
  else
    r = range_true_and_false (type);
  return true;
}

bool
foperator_ge::op1_range (frange &r,
			 tree type,
			 const irange &lhs,
			 const frange &op2,
			 relation_kind) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // 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:
      // 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:
      break;
    }
  return true;
}

bool
foperator_ge::op2_range (frange &r, tree type,
			 const irange &lhs,
			 const frange &op1,
			 relation_kind) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_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_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:
      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.known_isnan () || op2.known_isnan ())
    r = range_true (type);
  // UNORDERED is FALSE if neither operand is a NAN.
  else if (!op1.maybe_isnan () && !op2.maybe_isnan ())
    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,
				relation_kind) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // 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:
      // 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:
      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
{
  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,
			      relation_kind rel) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // 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:
      r.set_varying (type);
      // The FALSE side of op1 ORDERED op1 implies op1 is !NAN.
      if (rel == VREL_EQ)
	r.clear_nan ();
      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 &op1, const frange &op2,
		   relation_kind) const final override
  {
    if (op1.known_isnan () || op2.known_isnan ())
      r = range_true (type);
    else
      r.set_varying (type);
    return true;
  }
} fop_unordered_relop_unknown;


// Instantiate a range_op_table for floating point operations.
static floating_op_table global_floating_table;

// Pointer to the float table so the dispatch code can access it.
floating_op_table *floating_tree_table = &global_floating_table;

floating_op_table::floating_op_table ()
{
  set (SSA_NAME, fop_identity);
  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_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);
}

// Return a pointer to the range_operator_float instance, if there is
// one associated with tree_code CODE.

range_operator_float *
floating_op_table::operator[] (enum tree_code code)
{
  return m_range_tree[code];
}

// Add OP to the handler table for CODE.

void
floating_op_table::set (enum tree_code code, range_operator_float &op)
{
  gcc_checking_assert (m_range_tree[code] == NULL);
  m_range_tree[code] = &op;
}