blob: 6944742ecbc1015fbdb12ffec575382fb788faae [file] [log] [blame]
/* Header file for mixed range operator class.
Copyright (C) 2017-2023 Free Software Foundation, Inc.
Contributed by Andrew MacLeod <amacleod@redhat.com>
and 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/>. */
#ifndef GCC_RANGE_OP_MIXED_H
#define GCC_RANGE_OP_MIXED_H
void update_known_bitmask (irange &, tree_code, const irange &, const irange &);
bool minus_op1_op2_relation_effect (irange &lhs_range, tree type,
const irange &, const irange &,
relation_kind rel);
// Return TRUE if 0 is within [WMIN, WMAX].
inline bool
wi_includes_zero_p (tree type, const wide_int &wmin, const wide_int &wmax)
{
signop sign = TYPE_SIGN (type);
return wi::le_p (wmin, 0, sign) && wi::ge_p (wmax, 0, sign);
}
// Return TRUE if [WMIN, WMAX] is the singleton 0.
inline bool
wi_zero_p (tree type, const wide_int &wmin, const wide_int &wmax)
{
unsigned prec = TYPE_PRECISION (type);
return wmin == wmax && wi::eq_p (wmin, wi::zero (prec));
}
enum bool_range_state { BRS_FALSE, BRS_TRUE, BRS_EMPTY, BRS_FULL };
bool_range_state get_bool_state (vrange &r, const vrange &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 truly 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 (vrange &r, tree type,
const vrange &op1, const vrange & 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 vrange &op1,
const vrange &op2, relation_trio trio,
relation_kind my_rel)
{
relation_kind rel = trio.op1_op2 ();
// 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_UNDEFINED)
{
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;
}
// ----------------------------------------------------------------------
// Mixed Mode Operators.
// ----------------------------------------------------------------------
class operator_equal : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
using range_operator::op2_range;
using range_operator::op1_op2_relation;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio = TRIO_VARYING) const final override;
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
relation_trio = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &val,
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 (irange &r, tree type,
const irange &lhs, const irange &val,
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;
relation_kind op1_op2_relation (const irange &lhs) const final override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
};
class operator_not_equal : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
using range_operator::op2_range;
using range_operator::op1_op2_relation;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio = TRIO_VARYING) const final override;
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
relation_trio rel = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &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 (irange &r, tree type,
const irange &lhs, const irange &op1,
relation_trio = TRIO_VARYING) const final override;
relation_kind op1_op2_relation (const irange &lhs) const final override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
};
class operator_lt : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
using range_operator::op2_range;
using range_operator::op1_op2_relation;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio = TRIO_VARYING) const final override;
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
relation_trio = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &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 (irange &r, tree type,
const irange &lhs, const irange &op1,
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;
relation_kind op1_op2_relation (const irange &lhs) const final override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
};
class operator_le : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
using range_operator::op2_range;
using range_operator::op1_op2_relation;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio = TRIO_VARYING) const final override;
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
relation_trio rel = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &op2,
relation_trio = TRIO_VARYING) const final override;
bool op1_range (frange &r, tree type,
const irange &lhs, const frange &op2,
relation_trio rel = TRIO_VARYING) const final override;
bool op2_range (irange &r, tree type,
const irange &lhs, const irange &op1,
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;
relation_kind op1_op2_relation (const irange &lhs) const final override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
};
class operator_gt : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
using range_operator::op2_range;
using range_operator::op1_op2_relation;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio = TRIO_VARYING) const final override;
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
relation_trio = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &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 (irange &r, tree type,
const irange &lhs, const irange &op1,
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;
relation_kind op1_op2_relation (const irange &lhs) const final override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
};
class operator_ge : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
using range_operator::op2_range;
using range_operator::op1_op2_relation;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio = TRIO_VARYING) const final override;
bool fold_range (irange &r, tree type,
const frange &op1, const frange &op2,
relation_trio = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &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 (irange &r, tree type,
const irange &lhs, const irange &op1,
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;
relation_kind op1_op2_relation (const irange &lhs) const final override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
};
class operator_identity : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
using range_operator::lhs_op1_relation;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio rel = TRIO_VARYING) const final override;
bool fold_range (frange &r, tree type ATTRIBUTE_UNUSED,
const frange &op1, const frange &op2 ATTRIBUTE_UNUSED,
relation_trio = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &op2,
relation_trio rel = TRIO_VARYING) const final override;
bool op1_range (frange &r, tree type ATTRIBUTE_UNUSED,
const frange &lhs, const frange &op2 ATTRIBUTE_UNUSED,
relation_trio = TRIO_VARYING) const final override;
relation_kind lhs_op1_relation (const irange &lhs,
const irange &op1, const irange &op2,
relation_kind rel) const final override;
};
class operator_cst : public range_operator
{
public:
using range_operator::fold_range;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio rel = TRIO_VARYING) const final override;
bool fold_range (frange &r, tree type,
const frange &op1, const frange &op2,
relation_trio = TRIO_VARYING) const final override;
};
class operator_cast: public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
using range_operator::lhs_op1_relation;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio rel = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &op2,
relation_trio rel = TRIO_VARYING) const final override;
relation_kind lhs_op1_relation (const irange &lhs,
const irange &op1, const irange &op2,
relation_kind) const final override;
private:
bool truncating_cast_p (const irange &inner, const irange &outer) const;
bool inside_domain_p (const wide_int &min, const wide_int &max,
const irange &outer) const;
void fold_pair (irange &r, unsigned index, const irange &inner,
const irange &outer) const;
};
class operator_plus : public range_operator
{
public:
using range_operator::op1_range;
using range_operator::op2_range;
using range_operator::lhs_op1_relation;
using range_operator::lhs_op2_relation;
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;
relation_kind lhs_op1_relation (const irange &lhs, const irange &op1,
const irange &op2,
relation_kind rel) const final override;
relation_kind lhs_op2_relation (const irange &lhs, const irange &op1,
const irange &op2,
relation_kind rel) const final override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
private:
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;
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;
};
class operator_abs : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
bool fold_range (frange &r, tree type,
const frange &op1, const frange &,
relation_trio = TRIO_VARYING) const final override;
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 rel = TRIO_VARYING) const final override;
private:
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;
};
class operator_minus : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
using range_operator::op2_range;
using range_operator::lhs_op1_relation;
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;
relation_kind lhs_op1_relation (const irange &lhs,
const irange &op1, const irange &op2,
relation_kind rel) const final override;
bool op1_op2_relation_effect (irange &lhs_range, tree type,
const irange &op1_range,
const irange &op2_range,
relation_kind rel) const final override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
private:
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;
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;
};
class operator_negate : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio rel = TRIO_VARYING) const final override;
bool fold_range (frange &r, tree type,
const frange &op1, const frange &op2,
relation_trio = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &op2,
relation_trio rel = 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;
};
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;
};
class operator_addr_expr : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
bool fold_range (irange &r, tree type,
const irange &op1, const irange &op2,
relation_trio rel = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &op2,
relation_trio rel = TRIO_VARYING) const final override;
};
class operator_bitwise_not : public range_operator
{
public:
using range_operator::fold_range;
using range_operator::op1_range;
bool fold_range (irange &r, tree type,
const irange &lh, const irange &rh,
relation_trio rel = TRIO_VARYING) const final override;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &op2,
relation_trio rel = TRIO_VARYING) const final override;
};
class operator_bitwise_xor : public range_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 rel = TRIO_VARYING) const final override;
bool op2_range (irange &r, tree type,
const irange &lhs, const irange &op1,
relation_trio rel = TRIO_VARYING) const final override;
bool op1_op2_relation_effect (irange &lhs_range,
tree type,
const irange &op1_range,
const irange &op2_range,
relation_kind rel) const;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const final override;
private:
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;
};
class operator_bitwise_and : public range_operator
{
public:
using range_operator::op1_range;
using range_operator::op2_range;
using range_operator::lhs_op1_relation;
bool op1_range (irange &r, tree type,
const irange &lhs, const irange &op2,
relation_trio rel = TRIO_VARYING) const override;
bool op2_range (irange &r, tree type,
const irange &lhs, const irange &op1,
relation_trio rel = TRIO_VARYING) const override;
relation_kind lhs_op1_relation (const irange &lhs,
const irange &op1, const irange &op2,
relation_kind) const override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const override;
protected:
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 override;
void simple_op1_range_solver (irange &r, tree type,
const irange &lhs,
const irange &op2) const;
};
class operator_bitwise_or : public range_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 rel = TRIO_VARYING) const override;
bool op2_range (irange &r, tree type,
const irange &lhs, const irange &op1,
relation_trio rel = TRIO_VARYING) const override;
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const override;
protected:
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 override;
};
class operator_min : public range_operator
{
public:
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const override;
protected:
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 override;
};
class operator_max : public range_operator
{
public:
void update_bitmask (irange &r, const irange &lh,
const irange &rh) const override;
protected:
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 override;
};
#endif // GCC_RANGE_OP_MIXED_H