Unify MULT_EXPR range operator

Move the declaration of the class to the range-op-mixed header, add the
floating point prototypes as well, and use it in the new unified table.

	* range-op-float.cc (foperator_mult_div_base): Delete.
	(foperator_mult_div_base::find_range): Make static local function.
	(foperator_mult): Remove.  Move prototypes to range-op-mixed.h
	(operator_mult::op1_range): Rename from foperator_mult.
	(operator_mult::op2_range): Ditto.
	(operator_mult::rv_fold): Ditto.
	(float_table::float_table): Remove MULT_EXPR.
	(class foperator_div): Inherit from range_operator.
	(float_table::float_table): Delete.
	* range-op-mixed.h (class operator_mult): Combined from integer
	and float files.
	* range-op.cc (float_tree_table): Delete.
	(op_mult): New object.
	(unified_table::unified_table): Add MULT_EXPR.
	(get_op_handler): Do not check float table any longer.
	(class cross_product_operator): Move to range-op-mixed.h.
	(class operator_mult): Move to range-op-mixed.h.
	(integral_table::integral_table): Remove MULT_EXPR.
	(pointer_table::pointer_table): Remove MULT_EXPR.
	* range-op.h (float_table): Remove.
This commit is contained in:
Andrew MacLeod 2023-06-09 13:47:09 -04:00
parent 56518befc2
commit a13c444094
4 changed files with 221 additions and 230 deletions

View file

@ -2331,188 +2331,182 @@ operator_minus::rv_fold (REAL_VALUE_TYPE &lb, REAL_VALUE_TYPE &ub,
}
class foperator_mult_div_base : public range_operator
// 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])
{
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];
}
}
};
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
bool
operator_mult::op1_range (frange &r, tree type,
const frange &lhs, const frange &op2,
relation_trio) const
{
using range_operator::op1_range;
using range_operator::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.
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);
}
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));
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);
}
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;
}
bool
operator_mult::op2_range (frange &r, tree type,
const frange &lhs, const frange &op1,
relation_trio) const
{
return op1_range (r, type, lhs, op1);
}
// 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;
void
operator_mult::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
{
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));
int signbit_known = signbit_known_p (lh_lb, lh_ub, rh_lb, 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;
}
// 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);
// 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;
// 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);
int signbit_known = signbit_known_p (lh_lb, lh_ub, rh_lb, rh_ub);
// 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);
}
}
// 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);
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] = dconstm0;
}
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);
// 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);
find_range (lb, ub, cp);
}
} fop_mult;
// 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] = dconstm0;
}
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);
}
class foperator_div : public foperator_mult_div_base
class foperator_div : public range_operator
{
using range_operator::op1_range;
using range_operator::op2_range;
@ -2525,7 +2519,7 @@ public:
if (lhs.undefined_p ())
return false;
frange wlhs = float_widen_lhs_range (type, lhs);
bool ret = fop_mult.fold_range (r, type, wlhs, op2);
bool ret = range_op_handler (MULT_EXPR).fold_range (r, type, wlhs, op2);
if (!ret)
return ret;
if (wlhs.known_isnan () || op2.known_isnan () || op2.undefined_p ())
@ -2669,12 +2663,7 @@ private:
} fop_div;
float_table::float_table ()
{
set (MULT_EXPR, fop_mult);
}
// Initialize any pointer operators to the primary table
// Initialize any float operators to the primary table
void
range_op_table::initialize_float_ops ()

View file

@ -450,4 +450,55 @@ class operator_negate : public range_operator
const frange &lhs, const frange &op2,
relation_trio rel = TRIO_VARYING) const final override;
};
class cross_product_operator : public range_operator
{
public:
virtual bool wi_op_overflows (wide_int &r,
tree type,
const wide_int &,
const wide_int &) const = 0;
void wi_cross_product (irange &r, tree type,
const wide_int &lh_lb,
const wide_int &lh_ub,
const wide_int &rh_lb,
const wide_int &rh_ub) const;
};
class operator_mult : public cross_product_operator
{
public:
using range_operator::op1_range;
using range_operator::op2_range;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &op2,
relation_trio) const final override;
bool op1_range (frange &r, tree type,
const frange &lhs, const frange &op2,
relation_trio = TRIO_VARYING) const final override;
bool op2_range (irange &r, tree type,
const irange &lhs, const irange &op1,
relation_trio) const final override;
bool op2_range (frange &r, tree type,
const frange &lhs, const frange &op1,
relation_trio = TRIO_VARYING) const final override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
void wi_fold (irange &r, tree type, const wide_int &lh_lb,
const wide_int &lh_ub, const wide_int &rh_lb,
const wide_int &rh_ub) const final override;
bool wi_op_overflows (wide_int &res, tree type, const wide_int &w0,
const wide_int &w1) const final override;
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;
};
#endif // GCC_RANGE_OP_MIXED_H

View file

@ -51,7 +51,6 @@ along with GCC; see the file COPYING3. If not see
integral_table integral_tree_table;
pointer_table pointer_tree_table;
float_table float_tree_table;
// Instantiate a range_op_table for unified operations.
class unified_table : public range_op_table
@ -75,6 +74,7 @@ operator_plus op_plus;
operator_abs op_abs;
operator_minus op_minus;
operator_negate op_negate;
operator_mult op_mult;
// Invoke the initialization routines for each class of range.
@ -101,6 +101,7 @@ unified_table::unified_table ()
set (ABS_EXPR, op_abs);
set (MINUS_EXPR, op_minus);
set (NEGATE_EXPR, op_negate);
set (MULT_EXPR, op_mult);
}
// The tables are hidden and accessed via a simple extern function.
@ -113,7 +114,6 @@ get_op_handler (enum tree_code code, tree type)
// Should not be in any other table if it is in the unified table.
gcc_checking_assert (!pointer_tree_table[code]);
gcc_checking_assert (!integral_tree_table[code]);
gcc_checking_assert (!float_tree_table[code]);
return unified_tree_table[code];
}
@ -121,8 +121,6 @@ get_op_handler (enum tree_code code, tree type)
return pointer_tree_table[code];
if (INTEGRAL_TYPE_P (type))
return integral_tree_table[code];
if (frange::supports_p (type))
return float_tree_table[code];
return NULL;
}
@ -2012,24 +2010,6 @@ operator_max::wi_fold (irange &r, tree type,
}
class cross_product_operator : public range_operator
{
public:
// Perform an operation between two wide-ints and place the result
// in R. Return true if the operation overflowed.
virtual bool wi_op_overflows (wide_int &r,
tree type,
const wide_int &,
const wide_int &) const = 0;
// Calculate the cross product of two sets of sub-ranges and return it.
void wi_cross_product (irange &r, tree type,
const wide_int &lh_lb,
const wide_int &lh_ub,
const wide_int &rh_lb,
const wide_int &rh_ub) const;
};
// Calculate the cross product of two sets of ranges and return it.
//
// Multiplications, divisions and shifts are a bit tricky to handle,
@ -2085,30 +2065,12 @@ cross_product_operator::wi_cross_product (irange &r, tree type,
}
class operator_mult : public cross_product_operator
void
operator_mult::update_bitmask (irange &r, const irange &lh,
const irange &rh) const
{
using range_operator::op1_range;
using range_operator::op2_range;
public:
virtual void wi_fold (irange &r, tree type,
const wide_int &lh_lb,
const wide_int &lh_ub,
const wide_int &rh_lb,
const wide_int &rh_ub) const final override;
virtual bool wi_op_overflows (wide_int &res, tree type,
const wide_int &w0, const wide_int &w1)
const final override;
virtual bool op1_range (irange &r, tree type,
const irange &lhs,
const irange &op2,
relation_trio) const final override;
virtual bool op2_range (irange &r, tree type,
const irange &lhs,
const irange &op1,
relation_trio) const final override;
void update_bitmask (irange &r, const irange &lh, const irange &rh) const
{ update_known_bitmask (r, MULT_EXPR, lh, rh); }
} op_mult;
update_known_bitmask (r, MULT_EXPR, lh, rh);
}
bool
operator_mult::op1_range (irange &r, tree type,
@ -4647,7 +4609,6 @@ integral_table::integral_table ()
{
set (MIN_EXPR, op_min);
set (MAX_EXPR, op_max);
set (MULT_EXPR, op_mult);
set (BIT_AND_EXPR, op_bitwise_and);
set (BIT_IOR_EXPR, op_bitwise_or);
set (BIT_XOR_EXPR, op_bitwise_xor);

View file

@ -317,16 +317,6 @@ public:
};
extern pointer_table pointer_tree_table;
// Instantiate a range_op_table for floating point operations.
class float_table : public range_op_table
{
public:
float_table ();
};
extern float_table float_tree_table;
extern range_operator *ptr_op_widen_mult_signed;
extern range_operator *ptr_op_widen_mult_unsigned;
extern range_operator *ptr_op_widen_plus_signed;