Move common code from range-op.cc to header files.

In preparation for the agnostication of ranger, this patch moves
common code that can be shared between non-integer ranges (initially
pointers) into the relevant header files.

This is a relatively non-invasive change, as any changes that would
need to be ported to GCC 12, would occur in the range-op entries
themselves, not in the supporting glue which I'm moving.

Tested and benchmarked on x86-64 Linux.

gcc/ChangeLog:

	* range-op.cc (empty_range_varying): Move to range-op.h.
	(range_true): Move to range.h.
	(range_false): Same.
	(range_true_and_false): Same.
	(enum bool_range_state): Move to range-op.h.
	(relop_early_resolve): Same.
	(operator_equal::op1_op2_relation): Abstract code to...
	(equal_op1_op2_relation): ...here.
	(operator_not_equal::op1_op2_relation): Abstract code to...
	(not_equal_op1_op2_relation): ...here.
	(operator_lt::op1_op2_relation): Abstract code to...
	(lt_op1_op2_relation): ...here.
	(operator_le::op1_op2_relation): Abstract code to...
	(le_op1_op2_relation): ...here.
	(operator_gt::op1_op2_relation): Abstract code to...
	(gt_op1_op2_relation): ...here.
	(operator_ge::op1_op2_relation): Abstract code to...
	(ge_op1_op2_relation): ...here.
	(class range_op_table): Move to range-op.h.
	* range-op.h (equal_op1_op2_relation): Moved from range-op.cc.
	(not_equal_op1_op2_relation): Same.
	(lt_op1_op2_relation): Same.
	(le_op1_op2_relation): Same.
	(gt_op1_op2_relation): Same.
	(ge_op1_op2_relation): Same.
	(enum bool_range_state): Same.
	(get_bool_state): Same.
	(empty_range_varying): Same.
	(relop_early_resolve): Same.
	(class range_op_table): Same.
	* range.h (range_true): Same.
	(range_false): Same.
	(range_true_and_false): Same.
This commit is contained in:
Aldy Hernandez 2022-04-28 20:19:14 +02:00
parent c13fd1b8fd
commit 9eb38e88ce
3 changed files with 143 additions and 97 deletions

View File

@ -63,24 +63,6 @@ min_limit (const_tree type)
return wi::min_value (TYPE_PRECISION (type) , TYPE_SIGN (type));
}
// If the range of either op1 or op2 is undefined, set the result to
// varying and return TRUE. If the caller truely cares about a result,
// they should pass in a varying if it has an undefined that it wants
// treated as a varying.
inline bool
empty_range_varying (irange &r, tree type,
const irange &op1, const irange & op2)
{
if (op1.undefined_p () || op2.undefined_p ())
{
r.set_varying (type);
return true;
}
else
return false;
}
// Return false if shifting by OP is undefined behavior. Otherwise, return
// true and the range it is to be shifted by. This allows trimming out of
// undefined ranges, leaving only valid ranges if there are any.
@ -432,39 +414,10 @@ create_possibly_reversed_range (irange &r, tree type,
r.set (wide_int_to_tree (type, new_lb), wide_int_to_tree (type, new_ub));
}
// Return an irange instance that is a boolean TRUE.
static inline int_range<1>
range_true (tree type)
{
unsigned prec = TYPE_PRECISION (type);
return int_range<1> (type, wi::one (prec), wi::one (prec));
}
// Return an irange instance that is a boolean FALSE.
static inline int_range<1>
range_false (tree type)
{
unsigned prec = TYPE_PRECISION (type);
return int_range<1> (type, wi::zero (prec), wi::zero (prec));
}
// Return an irange that covers both true and false.
static inline int_range<1>
range_true_and_false (tree type)
{
unsigned prec = TYPE_PRECISION (type);
return int_range<1> (type, wi::zero (prec), wi::one (prec));
}
enum bool_range_state { BRS_FALSE, BRS_TRUE, BRS_EMPTY, BRS_FULL };
// Return the summary information about boolean range LHS. If EMPTY/FULL,
// return the equivalent range for TYPE in R; if FALSE/TRUE, do nothing.
static bool_range_state
bool_range_state
get_bool_state (irange &r, const irange &lhs, tree val_type)
{
// If there is no result, then this is unexecutable.
@ -488,37 +441,6 @@ get_bool_state (irange &r, const irange &lhs, tree val_type)
return BRS_TRUE;
}
// For relation opcodes, first try to see if the supplied relation
// forces a true or false result, and return that.
// Then check for undefined operands. If none of this applies,
// return false.
static inline bool
relop_early_resolve (irange &r, tree type, const irange &op1,
const irange &op2, relation_kind rel,
relation_kind my_rel)
{
// If known relation is a complete subset of this relation, always true.
if (relation_union (rel, my_rel) == my_rel)
{
r = range_true (type);
return true;
}
// If known relation has no subset of this relation, always false.
if (relation_intersect (rel, my_rel) == VREL_EMPTY)
{
r = range_false (type);
return true;
}
// If either operand is undefined, return VARYING.
if (empty_range_varying (r, type, op1, op2))
return true;
return false;
}
class operator_equal : public range_operator
{
@ -541,7 +463,7 @@ public:
// Check if the LHS range indicates a relation between OP1 and OP2.
enum tree_code
operator_equal::op1_op2_relation (const irange &lhs) const
equal_op1_op2_relation (const irange &lhs)
{
if (lhs.undefined_p ())
return VREL_EMPTY;
@ -556,6 +478,12 @@ operator_equal::op1_op2_relation (const irange &lhs) const
return VREL_NONE;
}
enum tree_code
operator_equal::op1_op2_relation (const irange &lhs) const
{
return equal_op1_op2_relation (lhs);
}
bool
operator_equal::fold_range (irange &r, tree type,
@ -651,7 +579,7 @@ public:
// Check if the LHS range indicates a relation between OP1 and OP2.
enum tree_code
operator_not_equal::op1_op2_relation (const irange &lhs) const
not_equal_op1_op2_relation (const irange &lhs)
{
if (lhs.undefined_p ())
return VREL_EMPTY;
@ -666,6 +594,12 @@ operator_not_equal::op1_op2_relation (const irange &lhs) const
return VREL_NONE;
}
enum tree_code
operator_not_equal::op1_op2_relation (const irange &lhs) const
{
return not_equal_op1_op2_relation (lhs);
}
bool
operator_not_equal::fold_range (irange &r, tree type,
const irange &op1,
@ -821,7 +755,7 @@ public:
// Check if the LHS range indicates a relation between OP1 and OP2.
enum tree_code
operator_lt::op1_op2_relation (const irange &lhs) const
lt_op1_op2_relation (const irange &lhs)
{
if (lhs.undefined_p ())
return VREL_EMPTY;
@ -836,6 +770,12 @@ operator_lt::op1_op2_relation (const irange &lhs) const
return VREL_NONE;
}
enum tree_code
operator_lt::op1_op2_relation (const irange &lhs) const
{
return lt_op1_op2_relation (lhs);
}
bool
operator_lt::fold_range (irange &r, tree type,
const irange &op1,
@ -923,7 +863,7 @@ public:
// Check if the LHS range indicates a relation between OP1 and OP2.
enum tree_code
operator_le::op1_op2_relation (const irange &lhs) const
le_op1_op2_relation (const irange &lhs)
{
if (lhs.undefined_p ())
return VREL_EMPTY;
@ -938,6 +878,12 @@ operator_le::op1_op2_relation (const irange &lhs) const
return VREL_NONE;
}
enum tree_code
operator_le::op1_op2_relation (const irange &lhs) const
{
return le_op1_op2_relation (lhs);
}
bool
operator_le::fold_range (irange &r, tree type,
const irange &op1,
@ -1025,7 +971,7 @@ public:
// Check if the LHS range indicates a relation between OP1 and OP2.
enum tree_code
operator_gt::op1_op2_relation (const irange &lhs) const
gt_op1_op2_relation (const irange &lhs)
{
if (lhs.undefined_p ())
return VREL_EMPTY;
@ -1040,6 +986,12 @@ operator_gt::op1_op2_relation (const irange &lhs) const
return VREL_NONE;
}
enum tree_code
operator_gt::op1_op2_relation (const irange &lhs) const
{
return gt_op1_op2_relation (lhs);
}
bool
operator_gt::fold_range (irange &r, tree type,
@ -1126,7 +1078,7 @@ public:
// Check if the LHS range indicates a relation between OP1 and OP2.
enum tree_code
operator_ge::op1_op2_relation (const irange &lhs) const
ge_op1_op2_relation (const irange &lhs)
{
if (lhs.undefined_p ())
return VREL_EMPTY;
@ -1141,6 +1093,12 @@ operator_ge::op1_op2_relation (const irange &lhs) const
return VREL_NONE;
}
enum tree_code
operator_ge::op1_op2_relation (const irange &lhs) const
{
return ge_op1_op2_relation (lhs);
}
bool
operator_ge::fold_range (irange &r, tree type,
const irange &op1,
@ -3993,18 +3951,6 @@ pointer_or_operator::wi_fold (irange &r, tree type,
r.set_varying (type);
}
// This implements the range operator tables as local objects in this file.
class range_op_table
{
public:
inline range_operator *operator[] (enum tree_code code);
protected:
void set (enum tree_code code, range_operator &op);
private:
range_operator *m_range_tree[MAX_TREE_CODES];
};
// Return a pointer to the range_operator instance, if there is one
// associated with tree_code CODE.

View File

@ -112,4 +112,76 @@ extern void wi_set_zero_nonzero_bits (tree type,
wide_int &maybe_nonzero,
wide_int &mustbe_nonzero);
// op1_op2_relation methods that are the same across irange and frange.
enum tree_code equal_op1_op2_relation (const irange &lhs);
enum tree_code not_equal_op1_op2_relation (const irange &lhs);
enum tree_code lt_op1_op2_relation (const irange &lhs);
enum tree_code le_op1_op2_relation (const irange &lhs);
enum tree_code gt_op1_op2_relation (const irange &lhs);
enum tree_code ge_op1_op2_relation (const irange &lhs);
enum bool_range_state { BRS_FALSE, BRS_TRUE, BRS_EMPTY, BRS_FULL };
bool_range_state get_bool_state (irange &r, const irange &lhs, tree val_type);
// If the range of either op1 or op2 is undefined, set the result to
// varying and return TRUE. If the caller truely cares about a result,
// they should pass in a varying if it has an undefined that it wants
// treated as a varying.
inline bool
empty_range_varying (irange &r, tree type,
const irange &op1, const irange & op2)
{
if (op1.undefined_p () || op2.undefined_p ())
{
r.set_varying (type);
return true;
}
else
return false;
}
// For relation opcodes, first try to see if the supplied relation
// forces a true or false result, and return that.
// Then check for undefined operands. If none of this applies,
// return false.
inline bool
relop_early_resolve (irange &r, tree type, const irange &op1,
const irange &op2, relation_kind rel,
relation_kind my_rel)
{
// If known relation is a complete subset of this relation, always true.
if (relation_union (rel, my_rel) == my_rel)
{
r = range_true (type);
return true;
}
// If known relation has no subset of this relation, always false.
if (relation_intersect (rel, my_rel) == VREL_EMPTY)
{
r = range_false (type);
return true;
}
// If either operand is undefined, return VARYING.
if (empty_range_varying (r, type, op1, op2))
return true;
return false;
}
// This implements the range operator tables as local objects.
class range_op_table
{
public:
range_operator *operator[] (enum tree_code code);
protected:
void set (enum tree_code code, range_operator &op);
private:
range_operator *m_range_tree[MAX_TREE_CODES];
};
#endif // GCC_RANGE_OP_H

View File

@ -25,4 +25,32 @@ value_range range_zero (tree type);
value_range range_nonzero (tree type);
value_range range_positives (tree type);
value_range range_negatives (tree type);
// Return an irange instance that is a boolean TRUE.
static inline int_range<1>
range_true (tree type)
{
unsigned prec = TYPE_PRECISION (type);
return int_range<1> (type, wi::one (prec), wi::one (prec));
}
// Return an irange instance that is a boolean FALSE.
static inline int_range<1>
range_false (tree type)
{
unsigned prec = TYPE_PRECISION (type);
return int_range<1> (type, wi::zero (prec), wi::zero (prec));
}
// Return an irange that covers both true and false.
static inline int_range<1>
range_true_and_false (tree type)
{
unsigned prec = TYPE_PRECISION (type);
return int_range<1> (type, wi::zero (prec), wi::one (prec));
}
#endif // GCC_RANGE_H