[range-op-float] Abstract out binary operator code out of PLUS_EXPR entry.

The PLUS_EXPR was always meant to be a template for further
development, since most of the binary operators will share a similar
structure.  This patch abstracts out the common bits into the default
definition for range_operator_float::fold_range() and provides an
rv_fold() to be implemented by the individual entries wishing to use
the generic folder.  This is akin to what we do with fold_range() and
wi_fold() in the integer version of range-ops.

gcc/ChangeLog:

	* range-op-float.cc (range_operator_float::fold_range): Abstract
	out from foperator_plus.
	(range_operator_float::rv_fold): New.
	(foperator_plus::fold_range): Remove.
	(foperator_plus::rv_fold): New.
	(propagate_nans): Remove.
	* range-op.h (class range_operator_float): Add rv_fold.
This commit is contained in:
Aldy Hernandez 2022-11-08 23:49:04 +01:00
parent 68b0615be2
commit 0ef5649e9b
2 changed files with 83 additions and 78 deletions

View file

@ -49,13 +49,66 @@ along with GCC; see the file COPYING3. If not see
// 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,
range_operator_float::fold_range (frange &r, tree type,
const frange &op1, const frange &op2,
relation_trio) const
{
return false;
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 ());
// 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)
// Keep the default NAN (with a varying sign) set by the setter.
;
else if (!op1.maybe_isnan () && !op2.maybe_isnan ())
r.clear_nan ();
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 possiblity 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)
const
{
lb = dconstninf;
ub = dconstinf;
maybe_nan = true;
}
bool
@ -192,19 +245,6 @@ frelop_early_resolve (irange &r, tree type,
&& relop_early_resolve (r, type, op1, op2, rel, my_rel));
}
// If either operand is a NAN, set R to NAN and return TRUE.
inline bool
propagate_nans (frange &r, const frange &op1, const frange &op2)
{
if (op1.known_isnan () || op2.known_isnan ())
{
r.set_nan (op1.type ());
return true;
}
return false;
}
// Set VALUE to its next real value, or INF if the operation overflows.
inline void
@ -1822,69 +1862,27 @@ foperator_unordered_equal::op1_range (frange &r, tree type,
class foperator_plus : public range_operator_float
{
using range_operator_float::fold_range;
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) 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);
public:
bool fold_range (frange &r, tree type,
const frange &lh,
const frange &rh,
relation_trio = TRIO_VARYING) const final override;
// [-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;
bool
foperator_plus::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 (propagate_nans (r, op1, op2))
return true;
REAL_VALUE_TYPE lb, ub;
frange_arithmetic (PLUS_EXPR, type, lb,
op1.lower_bound (), op2.lower_bound (), dconstninf);
frange_arithmetic (PLUS_EXPR, type, ub,
op1.upper_bound (), op2.upper_bound (), dconstinf);
// 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);
// Some combinations can yield a NAN even if no operands have the
// possibility of a NAN.
bool maybe_nan;
// [-INF] + [+INF] = NAN
if (real_isinf (&op1.lower_bound (), true)
&& real_isinf (&op2.upper_bound (), false))
maybe_nan = true;
// [+INF] + [-INF] = NAN
else if (real_isinf (&op1.upper_bound (), false)
&& real_isinf (&op2.lower_bound (), true))
maybe_nan = true;
else
maybe_nan = false;
if (lb_nan || ub_nan || maybe_nan)
// Keep the default NAN (with a varying sign) set by the setter.
;
else if (!op1.maybe_isnan () && !op2.maybe_isnan ())
r.clear_nan ();
return true;
}
// Instantiate a range_op_table for floating point operations.
static floating_op_table global_floating_table;

View file

@ -117,6 +117,13 @@ public:
const frange &lh,
const frange &rh,
relation_trio = TRIO_VARYING) const;
virtual 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) const;
// Unary operations have the range of the LHS as op2.
virtual bool fold_range (irange &r, tree type,
const frange &lh,