/* Floating point range operators.
   Copyright (C) 2022-2023 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, tree type,
				  const frange &op1, const frange &op2,
				  relation_trio trio) const
{
  if (empty_range_varying (r, type, op1, op2))
    return true;
  if (op1.known_isnan () || op2.known_isnan ())
    {
      r.set_nan (op1.type ());
      return true;
    }

  REAL_VALUE_TYPE lb, ub;
  bool maybe_nan;
  rv_fold (lb, ub, maybe_nan, type,
	   op1.lower_bound (), op1.upper_bound (),
	   op2.lower_bound (), op2.upper_bound (), trio.op1_op2 ());

  // Handle possible NANs by saturating to the appropriate INF if only
  // one end is a NAN.  If both ends are a NAN, just return a NAN.
  bool lb_nan = real_isnan (&lb);
  bool ub_nan = real_isnan (&ub);
  if (lb_nan && ub_nan)
    {
      r.set_nan (type);
      return true;
    }
  if (lb_nan)
    lb = dconstninf;
  else if (ub_nan)
    ub = dconstinf;

  r.set (type, lb, ub);

  if (lb_nan || ub_nan || maybe_nan
      || op1.maybe_isnan ()
      || op2.maybe_isnan ())
    // Keep the default NAN (with a varying sign) set by the setter.
    ;
  else
    r.clear_nan ();

  // If the result has overflowed and flag_trapping_math, folding this
  // operation could elide an overflow or division by zero exception.
  // Avoid returning a singleton +-INF, to keep the propagators (DOM
  // and substitute_and_fold_engine) from folding.  See PR107608.
  if (flag_trapping_math
      && MODE_HAS_INFINITIES (TYPE_MODE (type))
      && r.known_isinf () && !op1.known_isinf () && !op2.known_isinf ())
    {
      REAL_VALUE_TYPE inf = r.lower_bound ();
      if (real_isneg (&inf))
	{
	  REAL_VALUE_TYPE min = real_min_representable (type);
	  r.set (type, inf, min);
	}
      else
	{
	  REAL_VALUE_TYPE max = real_max_representable (type);
	  r.set (type, max, inf);
	}
    }

  r.flush_denormals_to_zero ();

  return true;
}

// For a given operation, fold two sets of ranges into [lb, ub].
// MAYBE_NAN is set to TRUE if, in addition to any result in LB or
// UB, the final range has the possibility of a NAN.
void
range_operator_float::rv_fold (REAL_VALUE_TYPE &lb,
			       REAL_VALUE_TYPE &ub,
			       bool &maybe_nan,
			       tree type ATTRIBUTE_UNUSED,
			       const REAL_VALUE_TYPE &lh_lb ATTRIBUTE_UNUSED,
			       const REAL_VALUE_TYPE &lh_ub ATTRIBUTE_UNUSED,
			       const REAL_VALUE_TYPE &rh_lb ATTRIBUTE_UNUSED,
			       const REAL_VALUE_TYPE &rh_ub ATTRIBUTE_UNUSED,
			       relation_kind) const
{
  lb = dconstninf;
  ub = dconstinf;
  maybe_nan = true;
}

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

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

// Return TRUE if OP1 and OP2 may be a NAN.

static inline bool
maybe_isnan (const frange &op1, const frange &op2)
{
  return 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_trio 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 (!maybe_isnan (op1, op2)
	  && relop_early_resolve (r, type, op1, op2, rel, my_rel));
}

// Set VALUE to its next real value, or INF if the operation overflows.

inline void
frange_nextafter (enum machine_mode mode,
		  REAL_VALUE_TYPE &value,
		  const REAL_VALUE_TYPE &inf)
{
  if (MODE_COMPOSITE_P (mode)
      && (real_isdenormal (&value, mode) || real_iszero (&value)))
    {
      // IBM extended denormals only have DFmode precision.
      REAL_VALUE_TYPE tmp, tmp2;
      real_convert (&tmp2, DFmode, &value);
      real_nextafter (&tmp, REAL_MODE_FORMAT (DFmode), &tmp2, &inf);
      real_convert (&value, mode, &tmp);
    }
  else
    {
      REAL_VALUE_TYPE tmp;
      real_nextafter (&tmp, REAL_MODE_FORMAT (mode), &value, &inf);
      value = tmp;
    }
}

// Like real_arithmetic, but round the result to INF if the operation
// produced inexact results.
//
// ?? There is still one problematic case, i387.  With
// -fexcess-precision=standard we perform most SF/DFmode arithmetic in
// XFmode (long_double_type_node), so that case is OK.  But without
// -mfpmath=sse, all the SF/DFmode computations are in XFmode
// precision (64-bit mantissa) and only occasionally rounded to
// SF/DFmode (when storing into memory from the 387 stack).  Maybe
// this is ok as well though it is just occasionally more precise. ??

static void
frange_arithmetic (enum tree_code code, tree type,
		   REAL_VALUE_TYPE &result,
		   const REAL_VALUE_TYPE &op1,
		   const REAL_VALUE_TYPE &op2,
		   const REAL_VALUE_TYPE &inf)
{
  REAL_VALUE_TYPE value;
  enum machine_mode mode = TYPE_MODE (type);
  bool mode_composite = MODE_COMPOSITE_P (mode);

  bool inexact = real_arithmetic (&value, code, &op1, &op2);
  real_convert (&result, mode, &value);

  // Be extra careful if there may be discrepancies between the
  // compile and runtime results.
  bool round = false;
  if (mode_composite)
    round = true;
  else
    {
      bool low = real_isneg (&inf);
      round = (low ? !real_less (&result, &value)
		   : !real_less (&value, &result));
      if (real_isinf (&result, !low)
	  && !real_isinf (&value)
	  && !flag_rounding_math)
	{
	  // Use just [+INF, +INF] rather than [MAX, +INF]
	  // even if value is larger than MAX and rounds to
	  // nearest to +INF.  Similarly just [-INF, -INF]
	  // rather than [-INF, +MAX] even if value is smaller
	  // than -MAX and rounds to nearest to -INF.
	  // Unless INEXACT is true, in that case we need some
	  // extra buffer.
	  if (!inexact)
	    round = false;
	  else
	    {
	      REAL_VALUE_TYPE tmp = result, tmp2;
	      frange_nextafter (mode, tmp, inf);
	      // TMP is at this point the maximum representable
	      // number.
	      real_arithmetic (&tmp2, MINUS_EXPR, &value, &tmp);
	      if (real_isneg (&tmp2) != low
		  && (REAL_EXP (&tmp2) - REAL_EXP (&tmp)
		      >= 2 - REAL_MODE_FORMAT (mode)->p))
		round = false;
	    }
	}
    }
  if (round && (inexact || !real_identical (&result, &value)))
    {
      if (mode_composite
	  && (real_isdenormal (&result, mode) || real_iszero (&result)))
	{
	  // IBM extended denormals only have DFmode precision.
	  REAL_VALUE_TYPE tmp, tmp2;
	  real_convert (&tmp2, DFmode, &value);
	  real_nextafter (&tmp, REAL_MODE_FORMAT (DFmode), &tmp2, &inf);
	  real_convert (&result, mode, &tmp);
	}
      else
	frange_nextafter (mode, result, inf);
    }
  if (mode_composite)
    switch (code)
      {
      case PLUS_EXPR:
      case MINUS_EXPR:
	// ibm-ldouble-format documents 1ulp for + and -.
	frange_nextafter (mode, result, inf);
	break;
      case MULT_EXPR:
	// ibm-ldouble-format documents 2ulps for *.
	frange_nextafter (mode, result, inf);
	frange_nextafter (mode, result, inf);
	break;
      case RDIV_EXPR:
	// ibm-ldouble-format documents 3ulps for /.
	frange_nextafter (mode, result, inf);
	frange_nextafter (mode, result, inf);
	frange_nextafter (mode, result, inf);
	break;
      default:
	break;
      }
}

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

// Crop R to [MIN, MAX] where MAX is the maximum representable number
// for TYPE and MIN the minimum representable number for TYPE.

static inline void
frange_drop_infs (frange &r, tree type)
{
  REAL_VALUE_TYPE max = real_max_representable (type);
  REAL_VALUE_TYPE min = real_min_representable (type);
  frange tmp (type, min, max);
  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.  Return TRUE if
// further changes may be needed for R, or FALSE if R is in its final
// form.

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.  Return TRUE if
// further changes may be needed for R, or FALSE if R is in its final
// form.

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

  REAL_VALUE_TYPE ninf = frange_val_min (type);
  REAL_VALUE_TYPE prev = val.upper_bound ();
  machine_mode mode = TYPE_MODE (type);
  // Default to the conservatively correct closed ranges for
  // MODE_COMPOSITE_P, otherwise use nextafter.  Note that for
  // !HONOR_INFINITIES, nextafter will yield -INF, but frange::set()
  // will crop the range appropriately.
  if (!MODE_COMPOSITE_P (mode))
    frange_nextafter (mode, prev, ninf);
  r.set (type, ninf, prev);
  return true;
}

// Build a range that is >= VAL and store it in R.  Return TRUE if
// further changes may be needed for R, or FALSE if R is in its final
// form.

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.  Return TRUE if
// further changes may be needed for R, or FALSE if R is in its final
// form.

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

  REAL_VALUE_TYPE inf = frange_val_max (type);
  REAL_VALUE_TYPE next = val.lower_bound ();
  machine_mode mode = TYPE_MODE (type);
  // Default to the conservatively correct closed ranges for
  // MODE_COMPOSITE_P, otherwise use nextafter.  Note that for
  // !HONOR_INFINITIES, nextafter will yield +INF, but frange::set()
  // will crop the range appropriately.
  if (!MODE_COMPOSITE_P (mode))
    frange_nextafter (mode, next, inf);
  r.set (type, next, inf);
  return true;
}


class foperator_identity : public range_operator_float
{
  using range_operator_float::fold_range;
  using range_operator_float::op1_range;
public:
  bool fold_range (frange &r, tree type ATTRIBUTE_UNUSED,
		   const frange &op1, const frange &op2 ATTRIBUTE_UNUSED,
		   relation_trio = TRIO_VARYING) 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_trio = TRIO_VARYING) 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;
  using range_operator_float::op1_op2_relation;
public:
  bool fold_range (irange &r, tree type,
		   const frange &op1, const frange &op2,
		   relation_trio = TRIO_VARYING) 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_trio = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio rel = TRIO_VARYING) const final override
  {
    return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
  }
} fop_equal;

bool
foperator_equal::fold_range (irange &r, tree type,
			     const frange &op1, const frange &op2,
			     relation_trio 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);
      // If one operand is -0.0 and other 0.0, they are still equal.
      else if (real_iszero (&op1.lower_bound ())
	       && real_iszero (&op2.lower_bound ()))
	r = range_true (type);
      else
	r = range_false (type);
    }
  else if (!maybe_isnan (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 ())
	{
	  // If one range is [whatever, -0.0] and another
	  // [0.0, whatever2], we don't know anything either,
	  // because -0.0 == 0.0.
	  if ((real_iszero (&op1.upper_bound ())
	       && real_iszero (&op2.lower_bound ()))
	      || (real_iszero (&op1.lower_bound ())
		  && real_iszero (&op2.upper_bound ())))
	    r = range_true_and_false (type);
	  else
	    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_trio trio) const
{
  relation_kind rel = trio.op1_op2 ();
  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 ()
	       || (!op2.maybe_isnan () && 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;
  using range_operator_float::op1_op2_relation;
public:
  bool fold_range (irange &r, tree type,
		   const frange &op1, const frange &op2,
		   relation_trio rel = TRIO_VARYING) 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_trio = TRIO_VARYING) const final override;
} fop_not_equal;

bool
foperator_not_equal::fold_range (irange &r, tree type,
				 const frange &op1, const frange &op2,
				 relation_trio 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_false (type);
      // If one operand is -0.0 and other 0.0, they are still equal.
      else if (real_iszero (&op1.lower_bound ())
	       && real_iszero (&op2.lower_bound ()))
	r = range_false (type);
      else
	r = range_true (type);
    }
  else if (!maybe_isnan (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 ())
	{
	  // If one range is [whatever, -0.0] and another
	  // [0.0, whatever2], we don't know anything either,
	  // because -0.0 == 0.0.
	  if ((real_iszero (&op1.upper_bound ())
	       && real_iszero (&op2.lower_bound ()))
	      || (real_iszero (&op1.lower_bound ())
		  && real_iszero (&op2.upper_bound ())))
	    r = range_true_and_false (type);
	  else
	    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_trio trio) const
{
  relation_kind rel = trio.op1_op2 ();
  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);
	}
      // The TRUE side of op1 != op1 implies op1 is NAN.
      else if (rel == VREL_EQ)
	r.set_nan (type);
      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;
  using range_operator_float::op1_op2_relation;
public:
  bool fold_range (irange &r, tree type,
		   const frange &op1, const frange &op2,
		   relation_trio = TRIO_VARYING) 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_trio = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio = TRIO_VARYING) const final override;
} fop_lt;

bool
foperator_lt::fold_range (irange &r, tree type,
			  const frange &op1, const frange &op2,
			  relation_trio 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 (!maybe_isnan (op1, op2))
    {
      if (real_less (&op1.upper_bound (), &op2.lower_bound ()))
	r = range_true (type);
      else if (!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_trio) 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 (op2.undefined_p ())
	return false;
      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_trio) 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 (op1.undefined_p ())
	return false;
      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;
  using range_operator_float::op1_op2_relation;
public:
  bool fold_range (irange &r, tree type,
		   const frange &op1, const frange &op2,
		   relation_trio rel = TRIO_VARYING) 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_trio rel = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio rel = TRIO_VARYING) const final override;
} fop_le;

bool
foperator_le::fold_range (irange &r, tree type,
			  const frange &op1, const frange &op2,
			  relation_trio 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 (!maybe_isnan (op1, op2))
    {
      if (real_compare (LE_EXPR, &op1.upper_bound (), &op2.lower_bound ()))
	r = range_true (type);
      else if (!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_trio) 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 (op2.undefined_p ())
	return false;
      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_trio) 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 (op1.undefined_p ())
	return false;
      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 if (op1.undefined_p ())
	return false;
      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;
  using range_operator_float::op1_op2_relation;
public:
  bool fold_range (irange &r, tree type,
		   const frange &op1, const frange &op2,
		   relation_trio = TRIO_VARYING) 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_trio = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio = TRIO_VARYING) const final override;
} fop_gt;

bool
foperator_gt::fold_range (irange &r, tree type,
			  const frange &op1, const frange &op2,
			  relation_trio 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 (!maybe_isnan (op1, op2))
    {
      if (real_compare (GT_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
	r = range_true (type);
      else if (!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_trio) 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 (op2.undefined_p ())
	return false;
      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 if (op2.undefined_p ())
	return false;
      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_trio) 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 (op1.undefined_p ())
	return false;
      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 if (op1.undefined_p ())
	return false;
      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;
  using range_operator_float::op1_op2_relation;
public:
  bool fold_range (irange &r, tree type,
		   const frange &op1, const frange &op2,
		   relation_trio = TRIO_VARYING) 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_trio = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio = TRIO_VARYING) const final override;
} fop_ge;

bool
foperator_ge::fold_range (irange &r, tree type,
			  const frange &op1, const frange &op2,
			  relation_trio 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 (!maybe_isnan (op1, op2))
    {
      if (real_compare (GE_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
	r = range_true (type);
      else if (!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_trio) 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 (op2.undefined_p ())
	return false;
      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 if (op2.undefined_p ())
	return false;
      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_trio) 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 (op1.undefined_p ())
	return false;
      else if (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 if (op1.undefined_p ())
	return false;
      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_trio = TRIO_VARYING) const final override;
  bool op1_range (frange &r, tree type,
		  const irange &lhs, const frange &op2,
		  relation_trio = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio rel = TRIO_VARYING) const final override
  {
    return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
  }
} fop_unordered;

bool
foperator_unordered::fold_range (irange &r, tree type,
				 const frange &op1, const frange &op2,
				 relation_trio) 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_trio trio) const
{
  relation_kind rel = trio.op1_op2 ();
  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 (rel == VREL_EQ || !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_trio = TRIO_VARYING) const final override;
  bool op1_range (frange &r, tree type,
		  const irange &lhs, const frange &op2,
		  relation_trio = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio rel = TRIO_VARYING) const final override
  {
    return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
  }
} fop_ordered;

bool
foperator_ordered::fold_range (irange &r, tree type,
			       const frange &op1, const frange &op2,
			       relation_trio) 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_trio trio) const
{
  relation_kind rel = trio.op1_op2 ();
  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:
      // The FALSE side of op1 ORDERED op1 implies op1 is NAN.
      if (rel == VREL_EQ)
	r.set_nan (type);
      else
	r.set_varying (type);
      break;

    default:
      break;
    }
  return true;
}

class foperator_negate : public range_operator_float
{
  using range_operator_float::fold_range;
  using range_operator_float::op1_range;
public:
  bool fold_range (frange &r, tree type,
		   const frange &op1, const frange &op2,
		   relation_trio = TRIO_VARYING) const final override
  {
    if (empty_range_varying (r, type, op1, op2))
      return true;
    if (op1.known_isnan ())
      {
	bool sign;
	if (op1.nan_signbit_p (sign))
	  r.set_nan (type, !sign);
	else
	  r.set_nan (type);
	return true;
      }

    REAL_VALUE_TYPE lh_lb = op1.lower_bound ();
    REAL_VALUE_TYPE lh_ub = op1.upper_bound ();
    lh_lb = real_value_negate (&lh_lb);
    lh_ub = real_value_negate (&lh_ub);
    r.set (type, lh_ub, lh_lb);
    if (op1.maybe_isnan ())
      {
	bool sign;
	if (op1.nan_signbit_p (sign))
	  r.update_nan (!sign);
	else
	  r.update_nan ();
      }
    else
      r.clear_nan ();
    return true;
  }
  bool op1_range (frange &r, tree type,
		  const frange &lhs, const frange &op2,
		  relation_trio rel = TRIO_VARYING) const final override
  {
    return fold_range (r, type, lhs, op2, rel);
  }
} fop_negate;

class foperator_abs : public range_operator_float
{
  using range_operator_float::fold_range;
  using range_operator_float::op1_range;
public:
  bool fold_range (frange &r, tree type,
		   const frange &op1, const frange &,
		   relation_trio = TRIO_VARYING) const final override;
  bool op1_range (frange &r, tree type,
		  const frange &lhs, const frange &op2,
		  relation_trio rel = TRIO_VARYING) const final override;
} fop_abs;

bool
foperator_abs::fold_range (frange &r, tree type,
			   const frange &op1, const frange &op2,
			   relation_trio) const
{
  if (empty_range_varying (r, type, op1, op2))
    return true;
  if (op1.known_isnan ())
    {
      r.set_nan (type, /*sign=*/false);
      return true;
    }

  const REAL_VALUE_TYPE lh_lb = op1.lower_bound ();
  const REAL_VALUE_TYPE lh_ub = op1.upper_bound ();
  // Handle the easy case where everything is positive.
  if (real_compare (GE_EXPR, &lh_lb, &dconst0)
      && !real_iszero (&lh_lb, /*sign=*/true)
      && !op1.maybe_isnan (/*sign=*/true))
    {
      r = op1;
      return true;
    }

  REAL_VALUE_TYPE min = real_value_abs (&lh_lb);
  REAL_VALUE_TYPE max = real_value_abs (&lh_ub);
  // If the range contains zero then we know that the minimum value in the
  // range will be zero.
  if (real_compare (LE_EXPR, &lh_lb, &dconst0)
      && real_compare (GE_EXPR, &lh_ub, &dconst0))
    {
      if (real_compare (GT_EXPR, &min, &max))
	max = min;
      min = dconst0;
    }
  else
    {
      // If the range was reversed, swap MIN and MAX.
      if (real_compare (GT_EXPR, &min, &max))
	std::swap (min, max);
    }

  r.set (type, min, max);
  if (op1.maybe_isnan ())
    r.update_nan (/*sign=*/false);
  else
    r.clear_nan ();
  return true;
}

bool
foperator_abs::op1_range (frange &r, tree type,
			  const frange &lhs, const frange &op2,
			  relation_trio) const
{
  if (empty_range_varying (r, type, lhs, op2))
    return true;
  if (lhs.known_isnan ())
    {
      r.set_nan (type);
      return true;
    }

  // Start with the positives because negatives are an impossible result.
  frange positives (type, dconst0, frange_val_max (type));
  positives.update_nan (/*sign=*/false);
  positives.intersect (lhs);
  r = positives;
  // Add -NAN if relevant.
  if (r.maybe_isnan ())
    {
      frange neg_nan;
      neg_nan.set_nan (type, true);
      r.union_ (neg_nan);
    }
  if (r.known_isnan () || r.undefined_p ())
    return true;
  // Then add the negative of each pair:
  // ABS(op1) = [5,20] would yield op1 => [-20,-5][5,20].
  frange negatives (type, real_value_negate (&positives.upper_bound ()),
		    real_value_negate (&positives.lower_bound ()));
  negatives.clear_nan ();
  r.union_ (negatives);
  return true;
}

class foperator_unordered_lt : 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_trio rel = TRIO_VARYING) const final override
  {
    if (op1.known_isnan () || op2.known_isnan ())
      {
	r = range_true (type);
	return true;
      }
    frange op1_no_nan = op1;
    frange op2_no_nan = op2;
    if (op1.maybe_isnan ())
      op1_no_nan.clear_nan ();
    if (op2.maybe_isnan ())
      op2_no_nan.clear_nan ();
    if (!fop_lt.fold_range (r, type, op1_no_nan, op2_no_nan, rel))
      return false;
    // The result is the same as the ordered version when the
    // comparison is true or when the operands cannot be NANs.
    if (!maybe_isnan (op1, op2) || r == range_true (type))
      return true;
    else
      {
	r = range_true_and_false (type);
	return true;
      }
  }
  bool op1_range (frange &r, tree type,
		  const irange &lhs,
		  const frange &op2,
		  relation_trio trio) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs,
		  const frange &op1,
		  relation_trio trio) const final override;
} fop_unordered_lt;

bool
foperator_unordered_lt::op1_range (frange &r, tree type,
				   const irange &lhs,
				   const frange &op2,
				   relation_trio) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      if (op2.known_isnan ())
	r.set_varying (type);
      else if (op2.undefined_p ())
	return false;
      else
	build_lt (r, type, op2);
      break;

    case BRS_FALSE:
      // A false UNORDERED_LT means both operands are !NAN, so it's
      // impossible for op2 to be a NAN.
      if (op2.known_isnan ())
	r.set_undefined ();
      else if (op2.undefined_p ())
	return false;
      else if (build_ge (r, type, op2))
	r.clear_nan ();
      break;

    default:
      break;
    }
  return true;
}

bool
foperator_unordered_lt::op2_range (frange &r, tree type,
				   const irange &lhs,
				   const frange &op1,
				   relation_trio) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      if (op1.known_isnan ())
	r.set_varying (type);
      else if (op1.undefined_p ())
	return false;
      else
	build_gt (r, type, op1);
      break;

    case BRS_FALSE:
      // A false UNORDERED_LT means both operands are !NAN, so it's
      // impossible for op1 to be a NAN.
      if (op1.known_isnan ())
	r.set_undefined ();
      else if (op1.undefined_p ())
	return false;
      else if (build_le (r, type, op1))
	r.clear_nan ();
      break;

    default:
      break;
    }
  return true;
}

class foperator_unordered_le : 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_trio rel = TRIO_VARYING) const final override
  {
    if (op1.known_isnan () || op2.known_isnan ())
      {
	r = range_true (type);
	return true;
      }
    frange op1_no_nan = op1;
    frange op2_no_nan = op2;
    if (op1.maybe_isnan ())
      op1_no_nan.clear_nan ();
    if (op2.maybe_isnan ())
      op2_no_nan.clear_nan ();
    if (!fop_le.fold_range (r, type, op1_no_nan, op2_no_nan, rel))
      return false;
    // The result is the same as the ordered version when the
    // comparison is true or when the operands cannot be NANs.
    if (!maybe_isnan (op1, op2) || r == range_true (type))
      return true;
    else
      {
	r = range_true_and_false (type);
	return true;
      }
  }
  bool op1_range (frange &r, tree type,
		  const irange &lhs, const frange &op2,
		  relation_trio = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio = TRIO_VARYING) const final override;
} fop_unordered_le;

bool
foperator_unordered_le::op1_range (frange &r, tree type,
				   const irange &lhs, const frange &op2,
				   relation_trio) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      if (op2.known_isnan ())
	r.set_varying (type);
      else if (op2.undefined_p ())
	return false;
      else
	build_le (r, type, op2);
      break;

    case BRS_FALSE:
      // A false UNORDERED_LE means both operands are !NAN, so it's
      // impossible for op2 to be a NAN.
      if (op2.known_isnan ())
	r.set_undefined ();
      else if (build_gt (r, type, op2))
	r.clear_nan ();
      break;

    default:
      break;
    }
  return true;
}

bool
foperator_unordered_le::op2_range (frange &r,
				   tree type,
				   const irange &lhs,
				   const frange &op1,
				   relation_trio) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      if (op1.known_isnan ())
	r.set_varying (type);
      else if (op1.undefined_p ())
	return false;
      else
	build_ge (r, type, op1);
      break;

    case BRS_FALSE:
      // A false UNORDERED_LE means both operands are !NAN, so it's
      // impossible for op1 to be a NAN.
      if (op1.known_isnan ())
	r.set_undefined ();
      else if (op1.undefined_p ())
	return false;
      else if (build_lt (r, type, op1))
	r.clear_nan ();
      break;

    default:
      break;
    }
  return true;
}

class foperator_unordered_gt : 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_trio rel = TRIO_VARYING) const final override
  {
    if (op1.known_isnan () || op2.known_isnan ())
      {
	r = range_true (type);
	return true;
      }
    frange op1_no_nan = op1;
    frange op2_no_nan = op2;
    if (op1.maybe_isnan ())
      op1_no_nan.clear_nan ();
    if (op2.maybe_isnan ())
      op2_no_nan.clear_nan ();
    if (!fop_gt.fold_range (r, type, op1_no_nan, op2_no_nan, rel))
      return false;
    // The result is the same as the ordered version when the
    // comparison is true or when the operands cannot be NANs.
    if (!maybe_isnan (op1, op2) || r == range_true (type))
      return true;
    else
      {
	r = range_true_and_false (type);
	return true;
      }
  }
  bool op1_range (frange &r, tree type,
		  const irange &lhs, const frange &op2,
		  relation_trio = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio = TRIO_VARYING) const final override;
} fop_unordered_gt;

bool
foperator_unordered_gt::op1_range (frange &r,
			 tree type,
			 const irange &lhs,
			 const frange &op2,
			 relation_trio) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      if (op2.known_isnan ())
	r.set_varying (type);
      else if (op2.undefined_p ())
	return false;
      else
	build_gt (r, type, op2);
      break;

    case BRS_FALSE:
      // A false UNORDERED_GT means both operands are !NAN, so it's
      // impossible for op2 to be a NAN.
      if (op2.known_isnan ())
	r.set_undefined ();
      else if (op2.undefined_p ())
	return false;
      else if (build_le (r, type, op2))
	r.clear_nan ();
      break;

    default:
      break;
    }
  return true;
}

bool
foperator_unordered_gt::op2_range (frange &r,
				   tree type,
				   const irange &lhs,
				   const frange &op1,
				   relation_trio) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      if (op1.known_isnan ())
	r.set_varying (type);
      else if (op1.undefined_p ())
	return false;
      else
	build_lt (r, type, op1);
      break;

    case BRS_FALSE:
      // A false UNORDERED_GT means both operands are !NAN, so it's
      // impossible for op1 to be a NAN.
      if (op1.known_isnan ())
	r.set_undefined ();
      else if (op1.undefined_p ())
	return false;
      else if (build_ge (r, type, op1))
	r.clear_nan ();
      break;

    default:
      break;
    }
  return true;
}

class foperator_unordered_ge : 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_trio rel = TRIO_VARYING) const final override
  {
    if (op1.known_isnan () || op2.known_isnan ())
      {
	r = range_true (type);
	return true;
      }
    frange op1_no_nan = op1;
    frange op2_no_nan = op2;
    if (op1.maybe_isnan ())
      op1_no_nan.clear_nan ();
    if (op2.maybe_isnan ())
      op2_no_nan.clear_nan ();
    if (!fop_ge.fold_range (r, type, op1_no_nan, op2_no_nan, rel))
      return false;
    // The result is the same as the ordered version when the
    // comparison is true or when the operands cannot be NANs.
    if (!maybe_isnan (op1, op2) || r == range_true (type))
      return true;
    else
      {
	r = range_true_and_false (type);
	return true;
      }
  }
  bool op1_range (frange &r, tree type,
		  const irange &lhs, const frange &op2,
		  relation_trio = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio = TRIO_VARYING) const final override;
} fop_unordered_ge;

bool
foperator_unordered_ge::op1_range (frange &r,
				   tree type,
				   const irange &lhs,
				   const frange &op2,
				   relation_trio) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      if (op2.known_isnan ())
	r.set_varying (type);
      else if (op2.undefined_p ())
	return false;
      else
	build_ge (r, type, op2);
      break;

    case BRS_FALSE:
      // A false UNORDERED_GE means both operands are !NAN, so it's
      // impossible for op2 to be a NAN.
      if (op2.known_isnan ())
	r.set_undefined ();
      else if (op2.undefined_p ())
	return false;
      else if (build_lt (r, type, op2))
	r.clear_nan ();
      break;

    default:
      break;
    }
  return true;
}

bool
foperator_unordered_ge::op2_range (frange &r, tree type,
				   const irange &lhs,
				   const frange &op1,
				   relation_trio) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      if (op1.known_isnan ())
	r.set_varying (type);
      else if (op1.undefined_p ())
	return false;
      else
	build_le (r, type, op1);
      break;

    case BRS_FALSE:
      // A false UNORDERED_GE means both operands are !NAN, so it's
      // impossible for op1 to be a NAN.
      if (op1.known_isnan ())
	r.set_undefined ();
      else if (op1.undefined_p ())
	return false;
      else if (build_gt (r, type, op1))
	r.clear_nan ();
      break;

    default:
      break;
    }
  return true;
}

class foperator_unordered_equal : 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_trio rel = TRIO_VARYING) const final override
  {
    if (op1.known_isnan () || op2.known_isnan ())
      {
	r = range_true (type);
	return true;
      }
    frange op1_no_nan = op1;
    frange op2_no_nan = op2;
    if (op1.maybe_isnan ())
      op1_no_nan.clear_nan ();
    if (op2.maybe_isnan ())
      op2_no_nan.clear_nan ();
    if (!fop_equal.fold_range (r, type, op1_no_nan, op2_no_nan, rel))
      return false;
    // The result is the same as the ordered version when the
    // comparison is true or when the operands cannot be NANs.
    if (!maybe_isnan (op1, op2) || r == range_true (type))
      return true;
    else
      {
	r = range_true_and_false (type);
	return true;
      }
  }
  bool op1_range (frange &r, tree type,
		  const irange &lhs, const frange &op2,
		  relation_trio = TRIO_VARYING) const final override;
  bool op2_range (frange &r, tree type,
		  const irange &lhs, const frange &op1,
		  relation_trio rel = TRIO_VARYING) const final override
  {
    return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ());
  }
} fop_unordered_equal;

bool
foperator_unordered_equal::op1_range (frange &r, tree type,
				      const irange &lhs,
				      const frange &op2,
				      relation_trio) const
{
  switch (get_bool_state (r, lhs, type))
    {
    case BRS_TRUE:
      // If it's true, the result is the same as OP2 plus a NAN.
      r = op2;
      // Add both zeros if there's the possibility of zero equality.
      frange_add_zeros (r, type);
      // Add the possibility of a NAN.
      r.update_nan ();
      break;

    case BRS_FALSE:
      // A false UNORDERED_EQ means both operands are !NAN, so it's
      // impossible for op2 to be a NAN.
      if (op2.known_isnan ())
	r.set_undefined ();
      else
	{
	  // The false side indicates !NAN and not equal.  We can at least
	  // represent !NAN.
	  r.set_varying (type);
	  r.clear_nan ();
	}
      break;

    default:
      break;
    }
  return true;
}

// Final tweaks for float binary op op1_range/op2_range.
// Return TRUE if the operation is performed and a valid range is available.

static bool
float_binary_op_range_finish (bool ret, frange &r, tree type,
			      const frange &lhs, bool div_op2 = false)
{
  if (!ret)
    return false;

  // If we get a known NAN from reverse op, it means either that
  // the other operand was known NAN (in that case we know nothing),
  // or the reverse operation introduced a known NAN.
  // Say for lhs = op1 * op2 if lhs is [-0, +0] and op2 is too,
  // 0 / 0 is known NAN.  Just punt in that case.
  // If NANs aren't honored, we get for 0 / 0 UNDEFINED, so punt as well.
  // Or if lhs is a known NAN, we also don't know anything.
  if (r.known_isnan () || lhs.known_isnan () || r.undefined_p ())
    {
      r.set_varying (type);
      return true;
    }

  // If lhs isn't NAN, then neither operand could be NAN,
  // even if the reverse operation does introduce a maybe_nan.
  if (!lhs.maybe_isnan ())
    {
      r.clear_nan ();
      if (div_op2
	  ? !(real_compare (LE_EXPR, &lhs.lower_bound (), &dconst0)
	      && real_compare (GE_EXPR, &lhs.upper_bound (), &dconst0))
	  : !(real_isinf (&lhs.lower_bound ())
	      || real_isinf (&lhs.upper_bound ())))
	// For reverse + or - or * or op1 of /, if result is finite, then
	// r must be finite too, as X + INF or X - INF or X * INF or
	// INF / X is always +-INF or NAN.  For op2 of /, if result is
	// non-zero and not NAN, r must be finite, as X / INF is always
	// 0 or NAN.
	frange_drop_infs (r, type);
    }
  // If lhs is a maybe or known NAN, the operand could be
  // NAN.
  else
    r.update_nan ();
  return true;
}

// True if [lb, ub] is [+-0, +-0].
static bool
zero_p (const REAL_VALUE_TYPE &lb, const REAL_VALUE_TYPE &ub)
{
  return real_iszero (&lb) && real_iszero (&ub);
}

// True if +0 or -0 is in [lb, ub] range.
static bool
contains_zero_p (const REAL_VALUE_TYPE &lb, const REAL_VALUE_TYPE &ub)
{
  return (real_compare (LE_EXPR, &lb, &dconst0)
	  && real_compare (GE_EXPR, &ub, &dconst0));
}

// True if [lb, ub] is [-INF, -INF] or [+INF, +INF].
static bool
singleton_inf_p (const REAL_VALUE_TYPE &lb, const REAL_VALUE_TYPE &ub)
{
  return real_isinf (&lb) && real_isinf (&ub, real_isneg (&lb));
}

// Return -1 if binary op result must have sign bit set,
// 1 if binary op result must have sign bit clear,
// 0 otherwise.
// Sign bit of binary op result is exclusive or of the
// operand's sign bits.
static int
signbit_known_p (const REAL_VALUE_TYPE &lh_lb, const REAL_VALUE_TYPE &lh_ub,
		 const REAL_VALUE_TYPE &rh_lb, const REAL_VALUE_TYPE &rh_ub)
{
  if (real_isneg (&lh_lb) == real_isneg (&lh_ub)
      && real_isneg (&rh_lb) == real_isneg (&rh_ub))
    {
      if (real_isneg (&lh_lb) == real_isneg (&rh_ub))
	return 1;
      else
	return -1;
    }
  return 0;
}

// Set [lb, ub] to [-0, -0], [-0, +0] or [+0, +0] depending on
// signbit_known.
static void
zero_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, int signbit_known)
{
  ub = lb = dconst0;
  if (signbit_known <= 0)
    lb = real_value_negate (&dconst0);
  if (signbit_known < 0)
    ub = lb;
}

// Set [lb, ub] to [-INF, -INF], [-INF, +INF] or [+INF, +INF] depending on
// signbit_known.
static void
inf_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, int signbit_known)
{
  if (signbit_known > 0)
    ub = lb = dconstinf;
  else if (signbit_known < 0)
    ub = lb = dconstninf;
  else
    {
      lb = dconstninf;
      ub = dconstinf;
    }
}

// Set [lb, ub] to [-INF, -0], [-INF, +INF] or [+0, +INF] depending on
// signbit_known.
static void
zero_to_inf_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, int signbit_known)
{
  if (signbit_known > 0)
    {
      lb = dconst0;
      ub = dconstinf;
    }
  else if (signbit_known < 0)
    {
      lb = dconstninf;
      ub = real_value_negate (&dconst0);
    }
  else
    {
      lb = dconstninf;
      ub = dconstinf;
    }
}

/* Extend the LHS range by 1ulp in each direction.  For op1_range
   or op2_range of binary operations just computing the inverse
   operation on ranges isn't sufficient.  Consider e.g.
   [1., 1.] = op1 + [1., 1.].  op1's range is not [0., 0.], but
   [-0x1.0p-54, 0x1.0p-53] (when not -frounding-math), any value for
   which adding 1. to it results in 1. after rounding to nearest.
   So, for op1_range/op2_range extend the lhs range by 1ulp (or 0.5ulp)
   in each direction.  See PR109008 for more details.  */

static frange
float_widen_lhs_range (tree type, const frange &lhs)
{
  frange ret = lhs;
  if (lhs.known_isnan ())
    return ret;
  REAL_VALUE_TYPE lb = lhs.lower_bound ();
  REAL_VALUE_TYPE ub = lhs.upper_bound ();
  if (real_isfinite (&lb))
    {
      frange_nextafter (TYPE_MODE (type), lb, dconstninf);
      if (real_isinf (&lb))
	{
	  /* For -DBL_MAX, instead of -Inf use
	     nexttoward (-DBL_MAX, -LDBL_MAX) in a hypothetical
	     wider type with the same mantissa precision but larger
	     exponent range; it is outside of range of double values,
	     but makes it clear it is just one ulp larger rather than
	     infinite amount larger.  */
	  lb = dconstm1;
	  SET_REAL_EXP (&lb, FLOAT_MODE_FORMAT (TYPE_MODE (type))->emax + 1);
	}
      if (!flag_rounding_math && !MODE_COMPOSITE_P (TYPE_MODE (type)))
	{
	  /* If not -frounding-math nor IBM double double, actually widen
	     just by 0.5ulp rather than 1ulp.  */
	  REAL_VALUE_TYPE tem;
	  real_arithmetic (&tem, PLUS_EXPR, &lhs.lower_bound (), &lb);
	  real_arithmetic (&lb, RDIV_EXPR, &tem, &dconst2);
	}
    }
  if (real_isfinite (&ub))
    {
      frange_nextafter (TYPE_MODE (type), ub, dconstinf);
      if (real_isinf (&ub))
	{
	  /* For DBL_MAX similarly.  */
	  ub = dconst1;
	  SET_REAL_EXP (&ub, FLOAT_MODE_FORMAT (TYPE_MODE (type))->emax + 1);
	}
      if (!flag_rounding_math && !MODE_COMPOSITE_P (TYPE_MODE (type)))
	{
	  /* If not -frounding-math nor IBM double double, actually widen
	     just by 0.5ulp rather than 1ulp.  */
	  REAL_VALUE_TYPE tem;
	  real_arithmetic (&tem, PLUS_EXPR, &lhs.upper_bound (), &ub);
	  real_arithmetic (&ub, RDIV_EXPR, &tem, &dconst2);
	}
    }
  /* Temporarily disable -ffinite-math-only, so that frange::set doesn't
     reduce the range back to real_min_representable (type) as lower bound
     or real_max_representable (type) as upper bound.  */
  bool save_flag_finite_math_only = flag_finite_math_only;
  flag_finite_math_only = false;
  ret.set (type, lb, ub, lhs.get_nan_state ());
  flag_finite_math_only = save_flag_finite_math_only;
  return ret;
}

class foperator_plus : public range_operator_float
{
  using range_operator_float::op1_range;
  using range_operator_float::op2_range;
public:
  virtual bool op1_range (frange &r, tree type,
			  const frange &lhs,
			  const frange &op2,
			  relation_trio = TRIO_VARYING) const final override
  {
    if (lhs.undefined_p ())
      return false;
    range_op_handler minus (MINUS_EXPR, type);
    if (!minus)
      return false;
    frange wlhs = float_widen_lhs_range (type, lhs);
    return float_binary_op_range_finish (minus.fold_range (r, type, wlhs, op2),
					 r, type, wlhs);
  }
  virtual bool op2_range (frange &r, tree type,
			  const frange &lhs,
			  const frange &op1,
			  relation_trio = TRIO_VARYING) const final override
  {
    return op1_range (r, type, lhs, op1);
  }
private:
  void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan,
		tree type,
		const REAL_VALUE_TYPE &lh_lb,
		const REAL_VALUE_TYPE &lh_ub,
		const REAL_VALUE_TYPE &rh_lb,
		const REAL_VALUE_TYPE &rh_ub,
		relation_kind) const final override
  {
    frange_arithmetic (PLUS_EXPR, type, lb, lh_lb, rh_lb, dconstninf);
    frange_arithmetic (PLUS_EXPR, type, ub, lh_ub, rh_ub, dconstinf);

    // [-INF] + [+INF] = NAN
    if (real_isinf (&lh_lb, true) && real_isinf (&rh_ub, false))
      maybe_nan = true;
    // [+INF] + [-INF] = NAN
    else if (real_isinf (&lh_ub, false) && real_isinf (&rh_lb, true))
      maybe_nan = true;
    else
      maybe_nan = false;
  }
} fop_plus;


class foperator_minus : public range_operator_float
{
  using range_operator_float::op1_range;
  using range_operator_float::op2_range;
public:
  virtual bool op1_range (frange &r, tree type,
			  const frange &lhs,
			  const frange &op2,
			  relation_trio = TRIO_VARYING) const final override
  {
    if (lhs.undefined_p ())
      return false;
    frange wlhs = float_widen_lhs_range (type, lhs);
    return float_binary_op_range_finish (fop_plus.fold_range (r, type, wlhs,
							      op2),
					 r, type, wlhs);
  }
  virtual bool op2_range (frange &r, tree type,
			  const frange &lhs,
			  const frange &op1,
			  relation_trio = TRIO_VARYING) const final override
  {
    if (lhs.undefined_p ())
      return false;
    frange wlhs = float_widen_lhs_range (type, lhs);
    return float_binary_op_range_finish (fold_range (r, type, op1, wlhs),
					 r, type, wlhs);
  }
private:
  void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan,
		tree type,
		const REAL_VALUE_TYPE &lh_lb,
		const REAL_VALUE_TYPE &lh_ub,
		const REAL_VALUE_TYPE &rh_lb,
		const REAL_VALUE_TYPE &rh_ub,
		relation_kind) const final override
  {
    frange_arithmetic (MINUS_EXPR, type, lb, lh_lb, rh_ub, dconstninf);
    frange_arithmetic (MINUS_EXPR, type, ub, lh_ub, rh_lb, dconstinf);

    // [+INF] - [+INF] = NAN
    if (real_isinf (&lh_ub, false) && real_isinf (&rh_ub, false))
      maybe_nan = true;
    // [-INF] - [-INF] = NAN
    else if (real_isinf (&lh_lb, true) && real_isinf (&rh_lb, true))
      maybe_nan = true;
    else
      maybe_nan = false;
  }
} fop_minus;


class foperator_mult_div_base : public range_operator_float
{
protected:
  // Given CP[0] to CP[3] floating point values rounded to -INF,
  // set LB to the smallest of them (treating -0 as smaller to +0).
  // Given CP[4] to CP[7] floating point values rounded to +INF,
  // set UB to the largest of them (treating -0 as smaller to +0).
  static void find_range (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub,
			  const REAL_VALUE_TYPE (&cp)[8])
  {
    lb = cp[0];
    ub = cp[4];
    for (int i = 1; i < 4; ++i)
      {
	if (real_less (&cp[i], &lb)
	    || (real_iszero (&lb) && real_isnegzero (&cp[i])))
	  lb = cp[i];
	if (real_less (&ub, &cp[i + 4])
	    || (real_isnegzero (&ub) && real_iszero (&cp[i + 4])))
	  ub = cp[i + 4];
      }
  }
};


class foperator_mult : public foperator_mult_div_base
{
  using range_operator_float::op1_range;
  using range_operator_float::op2_range;
public:
  virtual bool op1_range (frange &r, tree type,
			  const frange &lhs,
			  const frange &op2,
			  relation_trio = TRIO_VARYING) const final override
  {
    if (lhs.undefined_p ())
      return false;
    range_op_handler rdiv (RDIV_EXPR, type);
    if (!rdiv)
      return false;
    frange wlhs = float_widen_lhs_range (type, lhs);
    bool ret = rdiv.fold_range (r, type, wlhs, op2);
    if (ret == false)
      return false;
    if (wlhs.known_isnan () || op2.known_isnan () || op2.undefined_p ())
      return float_binary_op_range_finish (ret, r, type, wlhs);
    const REAL_VALUE_TYPE &lhs_lb = wlhs.lower_bound ();
    const REAL_VALUE_TYPE &lhs_ub = wlhs.upper_bound ();
    const REAL_VALUE_TYPE &op2_lb = op2.lower_bound ();
    const REAL_VALUE_TYPE &op2_ub = op2.upper_bound ();
    if ((contains_zero_p (lhs_lb, lhs_ub) && contains_zero_p (op2_lb, op2_ub))
	|| ((real_isinf (&lhs_lb) || real_isinf (&lhs_ub))
	    && (real_isinf (&op2_lb) || real_isinf (&op2_ub))))
      {
	// If both lhs and op2 could be zeros or both could be infinities,
	// we don't know anything about op1 except maybe for the sign
	// and perhaps if it can be NAN or not.
	REAL_VALUE_TYPE lb, ub;
	int signbit_known = signbit_known_p (lhs_lb, lhs_ub, op2_lb, op2_ub);
	zero_to_inf_range (lb, ub, signbit_known);
	r.set (type, lb, ub);
      }
    // Otherwise, if op2 is a singleton INF and lhs doesn't include INF,
    // or if lhs must be zero and op2 doesn't include zero, it would be
    // UNDEFINED, while rdiv.fold_range computes a zero or singleton INF
    // range.  Those are supersets of UNDEFINED, so let's keep that way.
    return float_binary_op_range_finish (ret, r, type, wlhs);
  }
  virtual bool op2_range (frange &r, tree type,
			  const frange &lhs,
			  const frange &op1,
			  relation_trio = TRIO_VARYING) const final override
  {
    return op1_range (r, type, lhs, op1);
  }
private:
  void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan,
		tree type,
		const REAL_VALUE_TYPE &lh_lb,
		const REAL_VALUE_TYPE &lh_ub,
		const REAL_VALUE_TYPE &rh_lb,
		const REAL_VALUE_TYPE &rh_ub,
		relation_kind kind) const final override
  {
    bool is_square
      = (kind == VREL_EQ
	 && real_equal (&lh_lb, &rh_lb)
	 && real_equal (&lh_ub, &rh_ub)
	 && real_isneg (&lh_lb) == real_isneg (&rh_lb)
	 && real_isneg (&lh_ub) == real_isneg (&rh_ub));

    maybe_nan = false;
    // x * x never produces a new NAN and we only multiply the same
    // values, so the 0 * INF problematic cases never appear there.
    if (!is_square)
      {
	// [+-0, +-0] * [+INF,+INF] (or [-INF,-INF] or swapped is a known NAN.
	if ((zero_p (lh_lb, lh_ub) && singleton_inf_p (rh_lb, rh_ub))
	    || (zero_p (rh_lb, rh_ub) && singleton_inf_p (lh_lb, lh_ub)))
	  {
	    real_nan (&lb, "", 0, TYPE_MODE (type));
	    ub = lb;
	    maybe_nan = true;
	    return;
	  }

	// Otherwise, if one range includes zero and the other ends with +-INF,
	// it is a maybe NAN.
	if ((contains_zero_p (lh_lb, lh_ub)
	     && (real_isinf (&rh_lb) || real_isinf (&rh_ub)))
	    || (contains_zero_p (rh_lb, rh_ub)
		&& (real_isinf (&lh_lb) || real_isinf (&lh_ub))))
	  {
	    maybe_nan = true;

	    int signbit_known = signbit_known_p (lh_lb, lh_ub, rh_lb, rh_ub);

	    // If one of the ranges that includes INF is singleton
	    // and the other range includes zero, the resulting
	    // range is INF and NAN, because the 0 * INF boundary
	    // case will be NAN, but already nextafter (0, 1) * INF
	    // is INF.
	    if (singleton_inf_p (lh_lb, lh_ub)
		|| singleton_inf_p (rh_lb, rh_ub))
	      return inf_range (lb, ub, signbit_known);

	    // If one of the multiplicands must be zero, the resulting
	    // range is +-0 and NAN.
	    if (zero_p (lh_lb, lh_ub) || zero_p (rh_lb, rh_ub))
	      return zero_range (lb, ub, signbit_known);

	    // Otherwise one of the multiplicands could be
	    // [0.0, nextafter (0.0, 1.0)] and the [DBL_MAX, INF]
	    // or similarly with different signs.  0.0 * DBL_MAX
	    // is still 0.0, nextafter (0.0, 1.0) * INF is still INF,
	    // so if the signs are always the same or always different,
	    // result is [+0.0, +INF] or [-INF, -0.0], otherwise VARYING.
	    return zero_to_inf_range (lb, ub, signbit_known);
	  }
      }

    REAL_VALUE_TYPE cp[8];
    // Do a cross-product.  At this point none of the multiplications
    // should produce a NAN.
    frange_arithmetic (MULT_EXPR, type, cp[0], lh_lb, rh_lb, dconstninf);
    frange_arithmetic (MULT_EXPR, type, cp[4], lh_lb, rh_lb, dconstinf);
    if (is_square)
      {
	// For x * x we can just do max (lh_lb * lh_lb, lh_ub * lh_ub)
	// as maximum and -0.0 as minimum if 0.0 is in the range,
	// otherwise min (lh_lb * lh_lb, lh_ub * lh_ub).
	// -0.0 rather than 0.0 because VREL_EQ doesn't prove that
	// x and y are bitwise equal, just that they compare equal.
	if (contains_zero_p (lh_lb, lh_ub))
	  {
	    if (real_isneg (&lh_lb) == real_isneg (&lh_ub))
	      cp[1] = dconst0;
	    else
	      cp[1] = real_value_negate (&dconst0);
	  }
	else
	  cp[1] = cp[0];
	cp[2] = cp[0];
	cp[5] = cp[4];
	cp[6] = cp[4];
      }
    else
      {
	frange_arithmetic (MULT_EXPR, type, cp[1], lh_lb, rh_ub, dconstninf);
	frange_arithmetic (MULT_EXPR, type, cp[5], lh_lb, rh_ub, dconstinf);
	frange_arithmetic (MULT_EXPR, type, cp[2], lh_ub, rh_lb, dconstninf);
	frange_arithmetic (MULT_EXPR, type, cp[6], lh_ub, rh_lb, dconstinf);
      }
    frange_arithmetic (MULT_EXPR, type, cp[3], lh_ub, rh_ub, dconstninf);
    frange_arithmetic (MULT_EXPR, type, cp[7], lh_ub, rh_ub, dconstinf);

    find_range (lb, ub, cp);
  }
} fop_mult;


class foperator_div : public foperator_mult_div_base
{
  using range_operator_float::op1_range;
  using range_operator_float::op2_range;
public:
  virtual bool op1_range (frange &r, tree type,
			  const frange &lhs,
			  const frange &op2,
			  relation_trio = TRIO_VARYING) const final override
  {
    if (lhs.undefined_p ())
      return false;
    frange wlhs = float_widen_lhs_range (type, lhs);
    bool ret = fop_mult.fold_range (r, type, wlhs, op2);
    if (!ret)
      return ret;
    if (wlhs.known_isnan () || op2.known_isnan () || op2.undefined_p ())
      return float_binary_op_range_finish (ret, r, type, wlhs);
    const REAL_VALUE_TYPE &lhs_lb = wlhs.lower_bound ();
    const REAL_VALUE_TYPE &lhs_ub = wlhs.upper_bound ();
    const REAL_VALUE_TYPE &op2_lb = op2.lower_bound ();
    const REAL_VALUE_TYPE &op2_ub = op2.upper_bound ();
    if ((contains_zero_p (lhs_lb, lhs_ub)
	 && (real_isinf (&op2_lb) || real_isinf (&op2_ub)))
	|| ((contains_zero_p (op2_lb, op2_ub))
	    && (real_isinf (&lhs_lb) || real_isinf (&lhs_ub))))
      {
	// If both lhs could be zero and op2 infinity or vice versa,
	// we don't know anything about op1 except maybe for the sign
	// and perhaps if it can be NAN or not.
	REAL_VALUE_TYPE lb, ub;
	int signbit_known = signbit_known_p (lhs_lb, lhs_ub, op2_lb, op2_ub);
	zero_to_inf_range (lb, ub, signbit_known);
	r.set (type, lb, ub);
      }
    return float_binary_op_range_finish (ret, r, type, wlhs);
  }
  virtual bool op2_range (frange &r, tree type,
			  const frange &lhs,
			  const frange &op1,
			  relation_trio = TRIO_VARYING) const final override
  {
    if (lhs.undefined_p ())
      return false;
    frange wlhs = float_widen_lhs_range (type, lhs);
    bool ret = fold_range (r, type, op1, wlhs);
    if (!ret)
      return ret;
    if (wlhs.known_isnan () || op1.known_isnan () || op1.undefined_p ())
      return float_binary_op_range_finish (ret, r, type, wlhs, true);
    const REAL_VALUE_TYPE &lhs_lb = wlhs.lower_bound ();
    const REAL_VALUE_TYPE &lhs_ub = wlhs.upper_bound ();
    const REAL_VALUE_TYPE &op1_lb = op1.lower_bound ();
    const REAL_VALUE_TYPE &op1_ub = op1.upper_bound ();
    if ((contains_zero_p (lhs_lb, lhs_ub) && contains_zero_p (op1_lb, op1_ub))
	|| ((real_isinf (&lhs_lb) || real_isinf (&lhs_ub))
	    && (real_isinf (&op1_lb) || real_isinf (&op1_ub))))
      {
	// If both lhs and op1 could be zeros or both could be infinities,
	// we don't know anything about op2 except maybe for the sign
	// and perhaps if it can be NAN or not.
	REAL_VALUE_TYPE lb, ub;
	int signbit_known = signbit_known_p (lhs_lb, lhs_ub, op1_lb, op1_ub);
	zero_to_inf_range (lb, ub, signbit_known);
	r.set (type, lb, ub);
      }
    return float_binary_op_range_finish (ret, r, type, wlhs, true);
  }
private:
  void rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub, bool &maybe_nan,
		tree type,
		const REAL_VALUE_TYPE &lh_lb,
		const REAL_VALUE_TYPE &lh_ub,
		const REAL_VALUE_TYPE &rh_lb,
		const REAL_VALUE_TYPE &rh_ub,
		relation_kind) const final override
  {
    // +-0.0 / +-0.0 or +-INF / +-INF is a known NAN.
    if ((zero_p (lh_lb, lh_ub) && zero_p (rh_lb, rh_ub))
	|| (singleton_inf_p (lh_lb, lh_ub) && singleton_inf_p (rh_lb, rh_ub)))
      {
	real_nan (&lb, "", 0, TYPE_MODE (type));
	ub = lb;
	maybe_nan = true;
	return;
      }

    // If +-0.0 is in both ranges, it is a maybe NAN.
    if (contains_zero_p (lh_lb, lh_ub) && contains_zero_p (rh_lb, rh_ub))
      maybe_nan = true;
    // If +-INF is in both ranges, it is a maybe NAN.
    else if ((real_isinf (&lh_lb) || real_isinf (&lh_ub))
	     && (real_isinf (&rh_lb) || real_isinf (&rh_ub)))
      maybe_nan = true;
    else
      maybe_nan = false;

    int signbit_known = signbit_known_p (lh_lb, lh_ub, rh_lb, rh_ub);

    // If dividend must be zero, the range is just +-0
    // (including if the divisor is +-INF).
    // If divisor must be +-INF, the range is just +-0
    // (including if the dividend is zero).
    if (zero_p (lh_lb, lh_ub) || singleton_inf_p (rh_lb, rh_ub))
      return zero_range (lb, ub, signbit_known);

    // If divisor must be zero, the range is just +-INF
    // (including if the dividend is +-INF).
    // If dividend must be +-INF, the range is just +-INF
    // (including if the dividend is zero).
    if (zero_p (rh_lb, rh_ub) || singleton_inf_p (lh_lb, lh_ub))
      return inf_range (lb, ub, signbit_known);

    // Otherwise if both operands may be zero, divisor could be
    // nextafter(0.0, +-1.0) and dividend +-0.0
    // in which case result is going to INF or vice versa and
    // result +0.0.  So, all we can say for that case is if the
    // signs of divisor and dividend are always the same we have
    // [+0.0, +INF], if they are always different we have
    // [-INF, -0.0].  If they vary, VARYING.
    // If both may be +-INF, divisor could be INF and dividend FLT_MAX,
    // in which case result is going to INF or vice versa and
    // result +0.0.  So, all we can say for that case is if the
    // signs of divisor and dividend are always the same we have
    // [+0.0, +INF], if they are always different we have
    // [-INF, -0.0].  If they vary, VARYING.
    if (maybe_nan)
      return zero_to_inf_range (lb, ub, signbit_known);

    REAL_VALUE_TYPE cp[8];
    // Do a cross-division.  At this point none of the divisions should
    // produce a NAN.
    frange_arithmetic (RDIV_EXPR, type, cp[0], lh_lb, rh_lb, dconstninf);
    frange_arithmetic (RDIV_EXPR, type, cp[1], lh_lb, rh_ub, dconstninf);
    frange_arithmetic (RDIV_EXPR, type, cp[2], lh_ub, rh_lb, dconstninf);
    frange_arithmetic (RDIV_EXPR, type, cp[3], lh_ub, rh_ub, dconstninf);
    frange_arithmetic (RDIV_EXPR, type, cp[4], lh_lb, rh_lb, dconstinf);
    frange_arithmetic (RDIV_EXPR, type, cp[5], lh_lb, rh_ub, dconstinf);
    frange_arithmetic (RDIV_EXPR, type, cp[6], lh_ub, rh_lb, dconstinf);
    frange_arithmetic (RDIV_EXPR, type, cp[7], lh_ub, rh_ub, dconstinf);

    find_range (lb, ub, cp);

    // If divisor may be zero (but is not known to be only zero),
    // and dividend can't be zero, the range can go up to -INF or +INF
    // depending on the signs.
    if (contains_zero_p (rh_lb, rh_ub))
      {
	if (signbit_known <= 0)
	  real_inf (&lb, true);
	if (signbit_known >= 0)
	  real_inf (&ub, false);
      }
  }
} fop_div;

// 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_le);
  set (UNLT_EXPR, fop_unordered_lt);
  set (UNGE_EXPR, fop_unordered_ge);
  set (UNGT_EXPR, fop_unordered_gt);
  set (UNEQ_EXPR, fop_unordered_equal);
  set (ORDERED_EXPR, fop_ordered);
  set (UNORDERED_EXPR, fop_unordered);

  set (ABS_EXPR, fop_abs);
  set (NEGATE_EXPR, fop_negate);
  set (PLUS_EXPR, fop_plus);
  set (MINUS_EXPR, fop_minus);
  set (MULT_EXPR, fop_mult);
  set (RDIV_EXPR, fop_div);
}

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

#if CHECKING_P
#include "selftest.h"

namespace selftest
{

// Build an frange from string endpoints.

inline frange
frange_float (const char *lb, const char *ub, tree type = float_type_node)
{
  REAL_VALUE_TYPE min, max;
  gcc_assert (real_from_string (&min, lb) == 0);
  gcc_assert (real_from_string (&max, ub) == 0);
  return frange (type, min, max);
}

void
range_op_float_tests ()
{
  frange r, r0, r1;
  frange trange (float_type_node);

  // negate([-5, +10]) => [-10, 5]
  r0 = frange_float ("-5", "10");
  fop_negate.fold_range (r, float_type_node, r0, trange);
  ASSERT_EQ (r, frange_float ("-10", "5"));

  // negate([0, 1] -NAN) => [-1, -0] +NAN
  r0 = frange_float ("0", "1");
  r0.update_nan (true);
  fop_negate.fold_range (r, float_type_node, r0, trange);
  r1 = frange_float ("-1", "-0");
  r1.update_nan (false);
  ASSERT_EQ (r, r1);

  // [-INF,+INF] + [-INF,+INF] could be a NAN.
  range_op_handler plus (PLUS_EXPR, float_type_node);
  r0.set_varying (float_type_node);
  r1.set_varying (float_type_node);
  r0.clear_nan ();
  r1.clear_nan ();
  plus.fold_range (r, float_type_node, r0, r1);
  if (HONOR_NANS (float_type_node))
    ASSERT_TRUE (r.maybe_isnan ());
}

} // namespace selftest

#endif // CHECKING_P