diff options
Diffstat (limited to 'gcc/range-op.cc')
-rw-r--r-- | gcc/range-op.cc | 1952 |
1 files changed, 1335 insertions, 617 deletions
diff --git a/gcc/range-op.cc b/gcc/range-op.cc index 5df075b..c62e397 100644 --- a/gcc/range-op.cc +++ b/gcc/range-op.cc @@ -63,15 +63,17 @@ min_limit (const_tree type) } // If the range of either op1 or op2 is undefined, set the result to -// undefined and return TRUE. +// 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_check (value_range &r, - const value_range &op1, const value_range & op2) +empty_range_varying (irange &r, tree type, + const irange &op1, const irange & op2) { if (op1.undefined_p () || op2.undefined_p ()) { - r.set_undefined (); + r.set_varying (type); return true; } else @@ -82,11 +84,11 @@ empty_range_check (value_range &r, // the appropriate range. static inline bool -undefined_shift_range_check (value_range &r, tree type, const value_range op) +undefined_shift_range_check (irange &r, tree type, const irange &op) { if (op.undefined_p ()) { - r = value_range (); + r.set_undefined (); return true; } @@ -98,7 +100,7 @@ undefined_shift_range_check (value_range &r, tree type, const value_range op) || wi::ge_p (op.upper_bound (), TYPE_PRECISION (type), TYPE_SIGN (op.type ()))) { - r = value_range (type); + r.set_varying (type); return true; } return false; @@ -125,32 +127,43 @@ wi_zero_p (tree type, const wide_int &wmin, const wide_int &wmax) // Default wide_int fold operation returns [MIN, MAX]. void -range_operator::wi_fold (value_range &r, tree type, +range_operator::wi_fold (irange &r, tree type, const wide_int &lh_lb ATTRIBUTE_UNUSED, const wide_int &lh_ub ATTRIBUTE_UNUSED, const wide_int &rh_lb ATTRIBUTE_UNUSED, const wide_int &rh_ub ATTRIBUTE_UNUSED) const { - gcc_checking_assert (value_range::supports_type_p (type)); - r = value_range (type); + gcc_checking_assert (irange::supports_type_p (type)); + r.set_varying (type); } // The default for fold is to break all ranges into sub-ranges and // invoke the wi_fold method on each sub-range pair. bool -range_operator::fold_range (value_range &r, tree type, - const value_range &lh, - const value_range &rh) const +range_operator::fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const { - gcc_checking_assert (value_range::supports_type_p (type)); - if (empty_range_check (r, lh, rh)) + gcc_checking_assert (irange::supports_type_p (type)); + if (empty_range_varying (r, type, lh, rh)) return true; - value_range tmp; + unsigned num_lh = lh.num_pairs (); + unsigned num_rh = rh.num_pairs (); + + // If both ranges are single pairs, fold directly into the result range. + if (num_lh == 1 && num_rh == 1) + { + wi_fold (r, type, lh.lower_bound (0), lh.upper_bound (0), + rh.lower_bound (0), rh.upper_bound (0)); + return true; + } + + widest_irange tmp; r.set_undefined (); - for (unsigned x = 0; x < lh.num_pairs (); ++x) - for (unsigned y = 0; y < rh.num_pairs (); ++y) + for (unsigned x = 0; x < num_lh; ++x) + for (unsigned y = 0; y < num_rh; ++y) { wide_int lh_lb = lh.lower_bound (x); wide_int lh_ub = lh.upper_bound (x); @@ -167,10 +180,10 @@ range_operator::fold_range (value_range &r, tree type, // The default for op1_range is to return false. bool -range_operator::op1_range (value_range &r ATTRIBUTE_UNUSED, +range_operator::op1_range (irange &r ATTRIBUTE_UNUSED, tree type ATTRIBUTE_UNUSED, - const value_range &lhs ATTRIBUTE_UNUSED, - const value_range &op2 ATTRIBUTE_UNUSED) const + const irange &lhs ATTRIBUTE_UNUSED, + const irange &op2 ATTRIBUTE_UNUSED) const { return false; } @@ -178,10 +191,10 @@ range_operator::op1_range (value_range &r ATTRIBUTE_UNUSED, // The default for op2_range is to return false. bool -range_operator::op2_range (value_range &r ATTRIBUTE_UNUSED, +range_operator::op2_range (irange &r ATTRIBUTE_UNUSED, tree type ATTRIBUTE_UNUSED, - const value_range &lhs ATTRIBUTE_UNUSED, - const value_range &op1 ATTRIBUTE_UNUSED) const + const irange &lhs ATTRIBUTE_UNUSED, + const irange &op1 ATTRIBUTE_UNUSED) const { return false; } @@ -191,7 +204,7 @@ range_operator::op2_range (value_range &r ATTRIBUTE_UNUSED, // to have overflowed (or underflowed). static void -value_range_from_overflowed_bounds (value_range &r, tree type, +value_range_from_overflowed_bounds (irange &r, tree type, const wide_int &wmin, const wide_int &wmax) { @@ -214,9 +227,13 @@ value_range_from_overflowed_bounds (value_range &r, tree type, // Likewise if the anti-range bounds are outside of the types // values. if (covers || wi::cmp (tmin, tmax, sgn) > 0) - r = value_range (type); + r.set_varying (type); else - r = value_range (type, tmin, tmax, VR_ANTI_RANGE); + { + tree tree_min = wide_int_to_tree (type, tmin); + tree tree_max = wide_int_to_tree (type, tmax); + r.set (tree_min, tree_max, VR_ANTI_RANGE); + } } // Create and return a range from a pair of wide-ints. MIN_OVF and @@ -224,7 +241,7 @@ value_range_from_overflowed_bounds (value_range &r, tree type, // calculating WMIN and WMAX respectively. static void -value_range_with_overflow (value_range &r, tree type, +value_range_with_overflow (irange &r, tree type, const wide_int &wmin, const wide_int &wmax, wi::overflow_type min_ovf = wi::OVF_NONE, wi::overflow_type max_ovf = wi::OVF_NONE) @@ -237,7 +254,7 @@ value_range_with_overflow (value_range &r, tree type, // values. if (prec == 1 && wi::ne_p (wmax, wmin)) { - r = value_range (type); + r.set_varying (type); return; } @@ -252,11 +269,12 @@ value_range_with_overflow (value_range &r, tree type, // If the limits are swapped, we wrapped around and cover // the entire range. if (wi::gt_p (tmin, tmax, sgn)) - r = value_range (type); + r.set_varying (type); else // No overflow or both overflow or underflow. The range // kind stays normal. - r = value_range (type, tmin, tmax); + r.set (wide_int_to_tree (type, tmin), + wide_int_to_tree (type, tmax)); return; } @@ -265,7 +283,7 @@ value_range_with_overflow (value_range &r, tree type, value_range_from_overflowed_bounds (r, type, wmin, wmax); else // Other underflow and/or overflow, drop to VR_VARYING. - r = value_range (type); + r.set_varying (type); } else { @@ -285,7 +303,8 @@ value_range_with_overflow (value_range &r, tree type, else new_ub = wmax; - r = value_range (type, new_lb, new_ub); + r.set (wide_int_to_tree (type, new_lb), + wide_int_to_tree (type, new_ub)); } } @@ -294,7 +313,7 @@ value_range_with_overflow (value_range &r, tree type, // [10,5] into [MIN,5][10,MAX]. static inline void -create_possibly_reversed_range (value_range &r, tree type, +create_possibly_reversed_range (irange &r, tree type, const wide_int &new_lb, const wide_int &new_ub) { signop s = TYPE_SIGN (type); @@ -302,35 +321,35 @@ create_possibly_reversed_range (value_range &r, tree type, if (wi::gt_p (new_lb, new_ub, s)) value_range_from_overflowed_bounds (r, type, new_lb, new_ub); else - // Otherwise its just a normal range. - r = value_range (type, new_lb, new_ub); + // Otherwise it's just a normal range. + r.set (wide_int_to_tree (type, new_lb), wide_int_to_tree (type, new_ub)); } -// Return a value_range instance that is a boolean TRUE. +// Return an irange instance that is a boolean TRUE. -static inline value_range +static inline int_range<1> range_true (tree type) { unsigned prec = TYPE_PRECISION (type); - return value_range (type, wi::one (prec), wi::one (prec)); + return int_range<1> (type, wi::one (prec), wi::one (prec)); } -// Return a value_range instance that is a boolean FALSE. +// Return an irange instance that is a boolean FALSE. -static inline value_range +static inline int_range<1> range_false (tree type) { unsigned prec = TYPE_PRECISION (type); - return value_range (type, wi::zero (prec), wi::zero (prec)); + return int_range<1> (type, wi::zero (prec), wi::zero (prec)); } -// Return a value_range that covers both true and false. +// Return an irange that covers both true and false. -static inline value_range +static inline int_range<1> range_true_and_false (tree type) { unsigned prec = TYPE_PRECISION (type); - return value_range (type, wi::zero (prec), wi::one (prec)); + return int_range<1> (type, wi::zero (prec), wi::one (prec)); } enum bool_range_state { BRS_FALSE, BRS_TRUE, BRS_EMPTY, BRS_FULL }; @@ -341,7 +360,7 @@ enum bool_range_state { BRS_FALSE, BRS_TRUE, BRS_EMPTY, BRS_FULL }; // the bool range. static bool_range_state -get_bool_state (value_range &r, const value_range &lhs, tree val_type) +get_bool_state (irange &r, const irange &lhs, tree val_type) { // If there is no result, then this is unexecutable. if (lhs.undefined_p ()) @@ -350,16 +369,16 @@ get_bool_state (value_range &r, const value_range &lhs, tree val_type) return BRS_EMPTY; } - // If the bounds aren't the same, then it's not a constant. - if (!wi::eq_p (lhs.upper_bound (), lhs.lower_bound ())) + if (lhs.zero_p ()) + return BRS_FALSE; + + // For TRUE, we can't just test for [1,1] because Ada can have + // multi-bit booleans, and TRUE values can be: [1, MAX], ~[0], etc. + if (lhs.contains_p (build_zero_cst (lhs.type ()))) { r.set_varying (val_type); return BRS_FULL; } - - if (lhs.zero_p ()) - return BRS_FALSE; - return BRS_TRUE; } @@ -367,23 +386,23 @@ get_bool_state (value_range &r, const value_range &lhs, tree val_type) class operator_equal : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &val) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &val) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &val) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &val) const; } op_equal; bool -operator_equal::fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const +operator_equal::fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const { - if (empty_range_check (r, op1, op2)) + if (empty_range_varying (r, type, op1, op2)) return true; // We can be sure the values are always equal or not if both ranges @@ -411,9 +430,9 @@ operator_equal::fold_range (value_range &r, tree type, } bool -operator_equal::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_equal::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { switch (get_bool_state (r, lhs, type)) { @@ -441,9 +460,9 @@ operator_equal::op1_range (value_range &r, tree type, } bool -operator_equal::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_equal::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { return operator_equal::op1_range (r, type, lhs, op1); } @@ -452,23 +471,23 @@ operator_equal::op2_range (value_range &r, tree type, class operator_not_equal : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; } op_not_equal; bool -operator_not_equal::fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const +operator_not_equal::fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const { - if (empty_range_check (r, op1, op2)) + if (empty_range_varying (r, type, op1, op2)) return true; // We can be sure the values are always equal or not if both ranges @@ -496,9 +515,9 @@ operator_not_equal::fold_range (value_range &r, tree type, } bool -operator_not_equal::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_not_equal::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { switch (get_bool_state (r, lhs, type)) { @@ -527,9 +546,9 @@ operator_not_equal::op1_range (value_range &r, tree type, bool -operator_not_equal::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_not_equal::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { return operator_not_equal::op1_range (r, type, lhs, op1); } @@ -537,7 +556,7 @@ operator_not_equal::op2_range (value_range &r, tree type, // (X < VAL) produces the range of [MIN, VAL - 1]. static void -build_lt (value_range &r, tree type, const wide_int &val) +build_lt (irange &r, tree type, const wide_int &val) { wi::overflow_type ov; wide_int lim = wi::sub (val, 1, TYPE_SIGN (type), &ov); @@ -546,21 +565,21 @@ build_lt (value_range &r, tree type, const wide_int &val) if (ov) r.set_undefined (); else - r = value_range (type, min_limit (type), lim); + r = int_range<1> (type, min_limit (type), lim); } // (X <= VAL) produces the range of [MIN, VAL]. static void -build_le (value_range &r, tree type, const wide_int &val) +build_le (irange &r, tree type, const wide_int &val) { - r = value_range (type, min_limit (type), val); + r = int_range<1> (type, min_limit (type), val); } // (X > VAL) produces the range of [VAL + 1, MAX]. static void -build_gt (value_range &r, tree type, const wide_int &val) +build_gt (irange &r, tree type, const wide_int &val) { wi::overflow_type ov; wide_int lim = wi::add (val, 1, TYPE_SIGN (type), &ov); @@ -568,38 +587,38 @@ build_gt (value_range &r, tree type, const wide_int &val) if (ov) r.set_undefined (); else - r = value_range (type, lim, max_limit (type)); + r = int_range<1> (type, lim, max_limit (type)); } // (X >= val) produces the range of [VAL, MAX]. static void -build_ge (value_range &r, tree type, const wide_int &val) +build_ge (irange &r, tree type, const wide_int &val) { - r = value_range (type, val, max_limit (type)); + r = int_range<1> (type, val, max_limit (type)); } class operator_lt : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; } op_lt; bool -operator_lt::fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const +operator_lt::fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const { - if (empty_range_check (r, op1, op2)) + if (empty_range_varying (r, type, op1, op2)) return true; signop sign = TYPE_SIGN (op1.type ()); @@ -615,9 +634,9 @@ operator_lt::fold_range (value_range &r, tree type, } bool -operator_lt::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_lt::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { switch (get_bool_state (r, lhs, type)) { @@ -636,9 +655,9 @@ operator_lt::op1_range (value_range &r, tree type, } bool -operator_lt::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_lt::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { switch (get_bool_state (r, lhs, type)) { @@ -660,23 +679,23 @@ operator_lt::op2_range (value_range &r, tree type, class operator_le : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; } op_le; bool -operator_le::fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const +operator_le::fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const { - if (empty_range_check (r, op1, op2)) + if (empty_range_varying (r, type, op1, op2)) return true; signop sign = TYPE_SIGN (op1.type ()); @@ -692,9 +711,9 @@ operator_le::fold_range (value_range &r, tree type, } bool -operator_le::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_le::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { switch (get_bool_state (r, lhs, type)) { @@ -713,9 +732,9 @@ operator_le::op1_range (value_range &r, tree type, } bool -operator_le::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_le::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { switch (get_bool_state (r, lhs, type)) { @@ -737,22 +756,22 @@ operator_le::op2_range (value_range &r, tree type, class operator_gt : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; } op_gt; bool -operator_gt::fold_range (value_range &r, tree type, - const value_range &op1, const value_range &op2) const +operator_gt::fold_range (irange &r, tree type, + const irange &op1, const irange &op2) const { - if (empty_range_check (r, op1, op2)) + if (empty_range_varying (r, type, op1, op2)) return true; signop sign = TYPE_SIGN (op1.type ()); @@ -768,8 +787,8 @@ operator_gt::fold_range (value_range &r, tree type, } bool -operator_gt::op1_range (value_range &r, tree type, - const value_range &lhs, const value_range &op2) const +operator_gt::op1_range (irange &r, tree type, + const irange &lhs, const irange &op2) const { switch (get_bool_state (r, lhs, type)) { @@ -788,9 +807,9 @@ operator_gt::op1_range (value_range &r, tree type, } bool -operator_gt::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_gt::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { switch (get_bool_state (r, lhs, type)) { @@ -812,23 +831,23 @@ operator_gt::op2_range (value_range &r, tree type, class operator_ge : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; } op_ge; bool -operator_ge::fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const +operator_ge::fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const { - if (empty_range_check (r, op1, op2)) + if (empty_range_varying (r, type, op1, op2)) return true; signop sign = TYPE_SIGN (op1.type ()); @@ -844,9 +863,9 @@ operator_ge::fold_range (value_range &r, tree type, } bool -operator_ge::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_ge::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { switch (get_bool_state (r, lhs, type)) { @@ -865,9 +884,9 @@ operator_ge::op1_range (value_range &r, tree type, } bool -operator_ge::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_ge::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { switch (get_bool_state (r, lhs, type)) { @@ -889,13 +908,13 @@ operator_ge::op2_range (value_range &r, tree type, class operator_plus : public range_operator { public: - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; - virtual void wi_fold (value_range &r, tree type, + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; + virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -903,7 +922,7 @@ public: } op_plus; void -operator_plus::wi_fold (value_range &r, tree type, +operator_plus::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 { @@ -915,17 +934,17 @@ operator_plus::wi_fold (value_range &r, tree type, } bool -operator_plus::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_plus::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op2); } bool -operator_plus::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_plus::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op1); } @@ -934,13 +953,13 @@ operator_plus::op2_range (value_range &r, tree type, class operator_minus : public range_operator { public: - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; - virtual void wi_fold (value_range &r, tree type, + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; + virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -948,7 +967,7 @@ public: } op_minus; void -operator_minus::wi_fold (value_range &r, tree type, +operator_minus::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 { @@ -960,17 +979,17 @@ operator_minus::wi_fold (value_range &r, tree type, } bool -operator_minus::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_minus::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { return range_op_handler (PLUS_EXPR, type)->fold_range (r, type, lhs, op2); } bool -operator_minus::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_minus::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { return fold_range (r, type, op1, lhs); } @@ -979,7 +998,7 @@ operator_minus::op2_range (value_range &r, tree type, class operator_min : public range_operator { public: - virtual void wi_fold (value_range &r, tree type, + virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -987,7 +1006,7 @@ public: } op_min; void -operator_min::wi_fold (value_range &r, tree type, +operator_min::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 { @@ -1001,7 +1020,7 @@ operator_min::wi_fold (value_range &r, tree type, class operator_max : public range_operator { public: - virtual void wi_fold (value_range &r, tree type, + virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -1009,7 +1028,7 @@ public: } op_max; void -operator_max::wi_fold (value_range &r, tree type, +operator_max::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 { @@ -1031,7 +1050,7 @@ public: const wide_int &) const = 0; // Calculate the cross product of two sets of sub-ranges and return it. - void wi_cross_product (value_range &r, tree type, + void wi_cross_product (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -1052,7 +1071,7 @@ public: // figure the smallest and largest values to form the new range. void -cross_product_operator::wi_cross_product (value_range &r, tree type, +cross_product_operator::wi_cross_product (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -1060,7 +1079,7 @@ cross_product_operator::wi_cross_product (value_range &r, tree type, { wide_int cp1, cp2, cp3, cp4; // Default to varying. - r = value_range (type); + r.set_varying (type); // Compute the 4 cross operations, bailing if we get an overflow we // can't handle. @@ -1096,16 +1115,47 @@ cross_product_operator::wi_cross_product (value_range &r, tree type, class operator_mult : public cross_product_operator { public: - virtual void wi_fold (value_range &r, tree type, + 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; virtual bool wi_op_overflows (wide_int &res, tree type, const wide_int &w0, const wide_int &w1) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; } op_mult; bool +operator_mult::op1_range (irange &r, tree type, + const irange &lhs, const irange &op2) const +{ + tree offset; + + // We can't solve 0 = OP1 * N by dividing by N with a wrapping type. + // For example: For 0 = OP1 * 2, OP1 could be 0, or MAXINT, whereas + // for 4 = OP1 * 2, OP1 could be 2 or 130 (unsigned 8-bit) + if (TYPE_OVERFLOW_WRAPS (type)) + return false; + + if (op2.singleton_p (&offset) && !integer_zerop (offset)) + return range_op_handler (TRUNC_DIV_EXPR, type)->fold_range (r, type, + lhs, op2); + return false; +} + +bool +operator_mult::op2_range (irange &r, tree type, + const irange &lhs, const irange &op1) const +{ + return operator_mult::op1_range (r, type, lhs, op1); +} + +bool operator_mult::wi_op_overflows (wide_int &res, tree type, const wide_int &w0, const wide_int &w1) const { @@ -1126,7 +1176,7 @@ operator_mult::wi_op_overflows (wide_int &res, tree type, } void -operator_mult::wi_fold (value_range &r, tree type, +operator_mult::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 { @@ -1195,7 +1245,7 @@ operator_mult::wi_fold (value_range &r, tree type, prod2 = prod3 - prod0; if (wi::geu_p (prod2, sizem1)) // The range covers all values. - r = value_range (type); + r.set_varying (type); else { wide_int new_lb = wide_int::from (prod0, prec, sign); @@ -1209,7 +1259,7 @@ class operator_div : public cross_product_operator { public: operator_div (enum tree_code c) { code = c; } - virtual void wi_fold (value_range &r, tree type, + virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -1263,14 +1313,14 @@ operator_div::wi_op_overflows (wide_int &res, tree type, } void -operator_div::wi_fold (value_range &r, tree type, +operator_div::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 { // If we know we will divide by zero, return undefined. if (rh_lb == 0 && rh_ub == 0) { - r = value_range (); + r.set_undefined (); return; } @@ -1293,14 +1343,14 @@ operator_div::wi_fold (value_range &r, tree type, // If flag_non_call_exceptions, we must not eliminate a division by zero. if (cfun->can_throw_non_call_exceptions) { - r = value_range (type); + r.set_varying (type); return; } // If we're definitely dividing by zero, there's nothing to do. if (wi_zero_p (type, divisor_min, divisor_max)) { - r = value_range (); + r.set_undefined (); return; } @@ -1312,12 +1362,12 @@ operator_div::wi_fold (value_range &r, tree type, wi_cross_product (r, type, dividend_min, dividend_max, divisor_min, wi::minus_one (prec)); else - r = value_range (); + r.set_undefined (); // Then divide by the non-zero positive numbers, if any. if (wi::gt_p (divisor_max, wi::zero (prec), sign)) { - value_range tmp; + widest_irange tmp; wi_cross_product (tmp, type, dividend_min, dividend_max, wi::one (prec), divisor_max); r.union_ (tmp); @@ -1336,16 +1386,16 @@ class operator_exact_divide : public operator_div { public: operator_exact_divide () : operator_div (TRUNC_DIV_EXPR) { } - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; } op_exact_div; bool -operator_exact_divide::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_exact_divide::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { tree offset; // [2, 4] = op1 / [3,3] since its exact divide, no need to worry about @@ -1364,11 +1414,14 @@ operator_exact_divide::op1_range (value_range &r, tree type, class operator_lshift : public cross_product_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - - virtual void wi_fold (value_range &r, tree type, + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + + 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; virtual bool wi_op_overflows (wide_int &res, @@ -1378,9 +1431,9 @@ public: } op_lshift; bool -operator_lshift::fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const +operator_lshift::fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const { if (undefined_shift_range_check (r, type, op2)) return true; @@ -1390,15 +1443,14 @@ operator_lshift::fold_range (value_range &r, tree type, { unsigned shift = op2.lower_bound ().to_uhwi (); wide_int tmp = wi::set_bit_in_zero (shift, TYPE_PRECISION (type)); - value_range mult (type, tmp, tmp); + int_range<1> mult (type, tmp, tmp); // Force wrapping multiplication. bool saved_flag_wrapv = flag_wrapv; bool saved_flag_wrapv_pointer = flag_wrapv_pointer; flag_wrapv = 1; flag_wrapv_pointer = 1; - bool b = range_op_handler (MULT_EXPR, type)->fold_range (r, type, op1, - mult); + bool b = op_mult.fold_range (r, type, op1, mult); flag_wrapv = saved_flag_wrapv; flag_wrapv_pointer = saved_flag_wrapv_pointer; return b; @@ -1409,7 +1461,7 @@ operator_lshift::fold_range (value_range &r, tree type, } void -operator_lshift::wi_fold (value_range &r, tree type, +operator_lshift::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 { @@ -1465,7 +1517,7 @@ operator_lshift::wi_fold (value_range &r, tree type, if (in_bounds) wi_cross_product (r, type, lh_lb, lh_ub, rh_lb, rh_ub); else - r = value_range (type); + r.set_varying (type); } bool @@ -1485,14 +1537,56 @@ operator_lshift::wi_op_overflows (wide_int &res, tree type, return false; } +bool +operator_lshift::op1_range (irange &r, + tree type, + const irange &lhs, + const irange &op2) const +{ + tree shift_amount; + if (op2.singleton_p (&shift_amount)) + { + int_range<1> shifted (shift_amount, shift_amount), ub, lb; + const range_operator *rshift_op = range_op_handler (RSHIFT_EXPR, type); + rshift_op->fold_range (ub, type, lhs, shifted); + if (TYPE_UNSIGNED (type)) + { + r = ub; + return true; + } + // For signed types, we can't just do an arithmetic rshift, + // because that will propagate the sign bit. + // + // LHS + // 1110 = OP1 << 1 + // + // Assuming a 4-bit signed integer, a right shift will result in + // OP1=1111, but OP1 could have also been 0111. What we want is + // a range from 0111 to 1111. That is, a range from the logical + // rshift (0111) to the arithmetic rshift (1111). + // + // Perform a logical rshift by doing the rshift as unsigned. + tree unsigned_type = unsigned_type_for (type); + widest_irange unsigned_lhs = lhs; + range_cast (unsigned_lhs, unsigned_type); + rshift_op = range_op_handler (RSHIFT_EXPR, unsigned_type); + rshift_op->fold_range (lb, unsigned_type, unsigned_lhs, shifted); + range_cast (lb, type); + r = lb; + r.union_ (ub); + return true; + } + return false; +} + class operator_rshift : public cross_product_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual void wi_fold (value_range &r, tree type, + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -1501,9 +1595,58 @@ public: tree type, const wide_int &w0, const wide_int &w1) const; + virtual bool op1_range (irange &, tree type, + const irange &lhs, + const irange &op2) const; } op_rshift; bool +operator_rshift::op1_range (irange &r, + tree type, + const irange &lhs, + const irange &op2) const +{ + tree shift; + if (op2.singleton_p (&shift)) + { + // Folding the original operation may discard some impossible + // ranges from the LHS. + widest_irange lhs_refined; + op_rshift.fold_range (lhs_refined, type, int_range<1> (type), op2); + lhs_refined.intersect (lhs); + if (lhs_refined.undefined_p ()) + { + r.set_undefined (); + return true; + } + widest_irange shift_range (shift, shift); + widest_irange lb, ub; + op_lshift.fold_range (lb, type, lhs_refined, shift_range); + // LHS + // 0000 0111 = OP1 >> 3 + // + // OP1 is anything from 0011 1000 to 0011 1111. That is, a + // range from LHS<<3 plus a mask of the 3 bits we shifted on the + // right hand side (0x07). + tree mask = fold_build1 (BIT_NOT_EXPR, type, + fold_build2 (LSHIFT_EXPR, type, + build_minus_one_cst (type), + shift)); + widest_irange mask_range (build_zero_cst (type), mask); + op_plus.fold_range (ub, type, lb, mask_range); + r = lb; + r.union_ (ub); + if (!lhs_refined.contains_p (build_zero_cst (type))) + { + mask_range.invert (); + r.intersect (mask_range); + } + return true; + } + return false; +} + +bool operator_rshift::wi_op_overflows (wide_int &res, tree type, const wide_int &w0, @@ -1523,9 +1666,9 @@ operator_rshift::wi_op_overflows (wide_int &res, } bool -operator_rshift::fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const +operator_rshift::fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const { // Invoke the generic fold routine if not undefined.. if (undefined_shift_range_check (r, type, op2)) @@ -1535,7 +1678,7 @@ operator_rshift::fold_range (value_range &r, tree type, } void -operator_rshift::wi_fold (value_range &r, tree type, +operator_rshift::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 { @@ -1546,139 +1689,180 @@ operator_rshift::wi_fold (value_range &r, tree type, class operator_cast: public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; +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; } op_convert; +// Return TRUE if casting from INNER to OUTER is a truncating cast. + +inline bool +operator_cast::truncating_cast_p (const irange &inner, + const irange &outer) const +{ + return TYPE_PRECISION (outer.type ()) < TYPE_PRECISION (inner.type ()); +} + +// Return TRUE if [MIN,MAX] is inside the domain of RANGE's type. + bool -operator_cast::fold_range (value_range &r, tree type ATTRIBUTE_UNUSED, - const value_range &lh, - const value_range &rh) const +operator_cast::inside_domain_p (const wide_int &min, + const wide_int &max, + const irange &range) const { - if (empty_range_check (r, lh, rh)) - return true; - - tree inner = lh.type (); - tree outer = rh.type (); - gcc_checking_assert (rh.varying_p ()); - gcc_checking_assert (types_compatible_p (outer, type)); - signop inner_sign = TYPE_SIGN (inner); - signop outer_sign = TYPE_SIGN (outer); - unsigned inner_prec = TYPE_PRECISION (inner); - unsigned outer_prec = TYPE_PRECISION (outer); - - // Start with an empty range and add subranges. - r = value_range (); - for (unsigned x = 0; x < lh.num_pairs (); ++x) - { - wide_int lh_lb = lh.lower_bound (x); - wide_int lh_ub = lh.upper_bound (x); - - // If the conversion is not truncating we can convert the min - // and max values and canonicalize the resulting range. - // Otherwise, we can do the conversion if the size of the range - // is less than what the precision of the target type can - // represent. - if (outer_prec >= inner_prec - || wi::rshift (wi::sub (lh_ub, lh_lb), - wi::uhwi (outer_prec, inner_prec), - inner_sign) == 0) + wide_int domain_min = wi::to_wide (vrp_val_min (range.type ())); + wide_int domain_max = wi::to_wide (vrp_val_max (range.type ())); + signop domain_sign = TYPE_SIGN (range.type ()); + return (wi::le_p (min, domain_max, domain_sign) + && wi::le_p (max, domain_max, domain_sign) + && wi::ge_p (min, domain_min, domain_sign) + && wi::ge_p (max, domain_min, domain_sign)); +} + + +// Helper for fold_range which work on a pair at a time. + +void +operator_cast::fold_pair (irange &r, unsigned index, + const irange &inner, + const irange &outer) const +{ + tree inner_type = inner.type (); + tree outer_type = outer.type (); + signop inner_sign = TYPE_SIGN (inner_type); + unsigned outer_prec = TYPE_PRECISION (outer_type); + + // check to see if casting from INNER to OUTER is a conversion that + // fits in the resulting OUTER type. + wide_int inner_lb = inner.lower_bound (index); + wide_int inner_ub = inner.upper_bound (index); + if (truncating_cast_p (inner, outer)) + { + // We may be able to accomodate a truncating cast if the + // resulting range can be represented in the target type... + if (wi::rshift (wi::sub (inner_ub, inner_lb), + wi::uhwi (outer_prec, TYPE_PRECISION (inner.type ())), + inner_sign) != 0) { - wide_int min = wide_int::from (lh_lb, outer_prec, inner_sign); - wide_int max = wide_int::from (lh_ub, outer_prec, inner_sign); - if (!wi::eq_p (min, wi::min_value (outer_prec, outer_sign)) - || !wi::eq_p (max, wi::max_value (outer_prec, outer_sign))) - { - value_range tmp; - create_possibly_reversed_range (tmp, type, min, max); - r.union_ (tmp); - continue; - } + r.set_varying (outer_type); + return; } - r = value_range (type); - break; + } + // ...but we must still verify that the final range fits in the + // domain. This catches -fstrict-enum restrictions where the domain + // range is smaller than what fits in the underlying type. + wide_int min = wide_int::from (inner_lb, outer_prec, inner_sign); + wide_int max = wide_int::from (inner_ub, outer_prec, inner_sign); + if (inside_domain_p (min, max, outer)) + create_possibly_reversed_range (r, outer_type, min, max); + else + r.set_varying (outer_type); +} + + +bool +operator_cast::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, + const irange &inner, + const irange &outer) const +{ + if (empty_range_varying (r, type, inner, outer)) + return true; + + gcc_checking_assert (outer.varying_p ()); + gcc_checking_assert (inner.num_pairs () > 0); + + // Avoid a temporary by folding the first pair directly into the result. + fold_pair (r, 0, inner, outer); + + // Then process any additonal pairs by unioning with their results. + for (unsigned x = 1; x < inner.num_pairs (); ++x) + { + widest_irange tmp; + fold_pair (tmp, x, inner, outer); + r.union_ (tmp); + if (r.varying_p ()) + return true; } return true; } bool -operator_cast::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_cast::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { tree lhs_type = lhs.type (); - value_range tmp; gcc_checking_assert (types_compatible_p (op2.type(), type)); - // If the precision of the LHS is smaller than the precision of the - // RHS, then there would be truncation of the value on the RHS, and - // so we can tell nothing about it. - if (TYPE_PRECISION (lhs_type) < TYPE_PRECISION (type)) - { - // If we've been passed an actual value for the RHS rather than - // the type, see if it fits the LHS, and if so, then we can allow - // it. - fold_range (r, lhs_type, op2, value_range (lhs_type)); - fold_range (tmp, type, r, value_range (type)); - if (tmp == op2) + if (truncating_cast_p (op2, lhs)) + { + if (lhs.varying_p ()) + r.set_varying (type); + else { - // We know the value of the RHS fits in the LHS type, so - // convert the LHS and remove any values that arent in OP2. - fold_range (r, type, lhs, value_range (type)); - r.intersect (op2); - return true; - } - // Special case if the LHS is a boolean. A 0 means the RHS is - // zero, and a 1 means the RHS is non-zero. - if (TREE_CODE (lhs_type) == BOOLEAN_TYPE) - { - // If the LHS is unknown, the result is whatever op2 already is. - if (!lhs.singleton_p ()) - { - r = op2; - return true; - } - // Boolean casts are weird in GCC. It's actually an implied - // mask with 0x01, so all that is known is whether the - // rightmost bit is 0 or 1, which implies the only value - // *not* in the RHS is 0 or -1. - unsigned prec = TYPE_PRECISION (type); - if (lhs.zero_p ()) - r = value_range (type, wi::minus_one (prec), wi::minus_one (prec), - VR_ANTI_RANGE); - else - r = value_range (type, wi::zero (prec), wi::zero (prec), - VR_ANTI_RANGE); - // And intersect it with what we know about op2. - r.intersect (op2); + // We want to insert the LHS as an unsigned value since it + // would not trigger the signed bit of the larger type. + widest_irange converted_lhs = lhs; + range_cast (converted_lhs, unsigned_type_for (lhs_type)); + range_cast (converted_lhs, type); + // Start by building the positive signed outer range for the type. + wide_int lim = wi::set_bit_in_zero (TYPE_PRECISION (lhs_type), + TYPE_PRECISION (type)); + r = int_range<1> (type, lim, wi::max_value (TYPE_PRECISION (type), + SIGNED)); + // For the signed part, we need to simply union the 2 ranges now. + r.union_ (converted_lhs); + + // Create maximal negative number outside of LHS bits. + lim = wi::mask (TYPE_PRECISION (lhs_type), true, + TYPE_PRECISION (type)); + // Add this to the unsigned LHS range(s). + widest_irange lim_range (type, lim, lim); + widest_irange lhs_neg; + range_op_handler (PLUS_EXPR, type)->fold_range (lhs_neg, + type, + converted_lhs, + lim_range); + // And union this with the entire outer types negative range. + widest_irange neg (type, + wi::min_value (TYPE_PRECISION (type), + SIGNED), + lim - 1); + neg.union_ (lhs_neg); + // And finally, munge the signed and unsigned portions. + r.union_ (neg); } - else - // Otherwise we'll have to assume it's whatever we know about op2. - r = op2; + // And intersect with any known value passed in the extra operand. + r.intersect (op2); return true; } - // If the LHS precision is greater than the rhs precision, the LHS - // range is restricted to the range of the RHS by this - // assignment. - if (TYPE_PRECISION (lhs_type) > TYPE_PRECISION (type)) + widest_irange tmp; + if (TYPE_PRECISION (lhs_type) == TYPE_PRECISION (type)) + tmp = lhs; + else { + // The cast is not truncating, and the range is restricted to + // the range of the RHS by this assignment. + // // Cast the range of the RHS to the type of the LHS. - fold_range (tmp, lhs_type, value_range (type), value_range (lhs_type)); - // Intersect this with the LHS range will produce the range, which - // will be cast to the RHS type before returning. + fold_range (tmp, lhs_type, int_range<1> (type), int_range<1> (lhs_type)); + // Intersect this with the LHS range will produce the range, + // which will be cast to the RHS type before returning. tmp.intersect (lhs); } - else - tmp = lhs; // Cast the calculated range to the type of the RHS. - fold_range (r, type, tmp, value_range (type)); + fold_range (r, type, tmp, int_range<1> (type)); return true; } @@ -1686,24 +1870,24 @@ operator_cast::op1_range (value_range &r, tree type, class operator_logical_and : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &lh, - const value_range &rh) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; + virtual bool fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; } op_logical_and; bool -operator_logical_and::fold_range (value_range &r, tree type, - const value_range &lh, - const value_range &rh) const +operator_logical_and::fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const { - if (empty_range_check (r, lh, rh)) + if (empty_range_varying (r, type, lh, rh)) return true; // 0 && anything is 0. @@ -1721,9 +1905,9 @@ operator_logical_and::fold_range (value_range &r, tree type, } bool -operator_logical_and::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2 ATTRIBUTE_UNUSED) const +operator_logical_and::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2 ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -1742,9 +1926,9 @@ operator_logical_and::op1_range (value_range &r, tree type, } bool -operator_logical_and::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_logical_and::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { return operator_logical_and::op1_range (r, type, lhs, op1); } @@ -1753,19 +1937,106 @@ operator_logical_and::op2_range (value_range &r, tree type, class operator_bitwise_and : public range_operator { public: - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; - virtual void wi_fold (value_range &r, tree type, + virtual bool fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; + 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; +private: + void simple_op1_range_solver (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + void remove_impossible_ranges (irange &r, const irange &rh) const; } op_bitwise_and; +static bool +unsigned_singleton_p (const irange &op) +{ + tree mask; + if (op.singleton_p (&mask)) + { + wide_int x = wi::to_wide (mask); + return wi::ge_p (x, 0, TYPE_SIGN (op.type ())); + } + return false; +} + +// Remove any ranges from R that are known to be impossible when an +// range is ANDed with MASK. + +void +operator_bitwise_and::remove_impossible_ranges (irange &r, + const irange &rmask) const +{ + if (r.undefined_p () || !unsigned_singleton_p (rmask)) + return; + + wide_int mask = rmask.lower_bound (); + tree type = r.type (); + int prec = TYPE_PRECISION (type); + int leading_zeros = wi::clz (mask); + widest_irange impossible_ranges; + + /* We know that starting at the most significant bit, any 0 in the + mask means the resulting range cannot contain a 1 in that same + position. This means the following ranges are impossible: + + x & 0b1001 1010 + IMPOSSIBLE RANGES + 01xx xxxx [0100 0000, 0111 1111] + 001x xxxx [0010 0000, 0011 1111] + 0000 01xx [0000 0100, 0000 0111] + 0000 0001 [0000 0001, 0000 0001] + */ + wide_int one = wi::one (prec); + for (int i = 0; i < prec - leading_zeros - 1; ++i) + if (wi::bit_and (mask, wi::lshift (one, wi::uhwi (i, prec))) == 0) + { + tree lb = fold_build2 (LSHIFT_EXPR, type, + build_one_cst (type), + build_int_cst (type, i)); + tree ub_left = fold_build1 (BIT_NOT_EXPR, type, + fold_build2 (LSHIFT_EXPR, type, + build_minus_one_cst (type), + build_int_cst (type, i))); + tree ub_right = fold_build2 (LSHIFT_EXPR, type, + build_one_cst (type), + build_int_cst (type, i)); + tree ub = fold_build2 (BIT_IOR_EXPR, type, ub_left, ub_right); + impossible_ranges.union_ (int_range<1> (lb, ub)); + } + if (!impossible_ranges.undefined_p ()) + { + impossible_ranges.invert (); + r.intersect (impossible_ranges); + } +} + +bool +operator_bitwise_and::fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const +{ + if (range_operator::fold_range (r, type, lh, rh)) + { + // FIXME: This is temporarily disabled because, though it + // generates better ranges, it's noticeably slower for evrp. + // remove_impossible_ranges (r, rh); + return true; + } + return false; +} + + // Optimize BIT_AND_EXPR and BIT_IOR_EXPR in terms of a mask if // possible. Basically, see if we can optimize: // @@ -1777,7 +2048,7 @@ public: // return TRUE. static bool -wi_optimize_and_or (value_range &r, +wi_optimize_and_or (irange &r, enum tree_code code, tree type, const wide_int &lh_lb, const wide_int &lh_ub, @@ -1886,7 +2157,7 @@ wi_set_zero_nonzero_bits (tree type, } void -operator_bitwise_and::wi_fold (value_range &r, tree type, +operator_bitwise_and::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -1938,29 +2209,128 @@ operator_bitwise_and::wi_fold (value_range &r, tree type, } // If the limits got swapped around, return varying. if (wi::gt_p (new_lb, new_ub,sign)) - r = value_range (type); + r.set_varying (type); else value_range_with_overflow (r, type, new_lb, new_ub); } +static void +set_nonzero_range_from_mask (irange &r, tree type, const irange &lhs) +{ + if (!lhs.contains_p (build_zero_cst (type))) + r = range_nonzero (type); + else + r.set_varying (type); +} + +// This was shamelessly stolen from register_edge_assert_for_2 and +// adjusted to work with iranges. + +void +operator_bitwise_and::simple_op1_range_solver (irange &r, tree type, + const irange &lhs, + const irange &op2) const +{ + if (!op2.singleton_p ()) + { + set_nonzero_range_from_mask (r, type, lhs); + return; + } + unsigned int nprec = TYPE_PRECISION (type); + wide_int cst2v = op2.lower_bound (); + bool cst2n = wi::neg_p (cst2v, TYPE_SIGN (type)); + wide_int sgnbit; + if (cst2n) + sgnbit = wi::set_bit_in_zero (nprec - 1, nprec); + else + sgnbit = wi::zero (nprec); + + // Solve [lhs.lower_bound (), +INF] = x & MASK. + // + // Minimum unsigned value for >= if (VAL & CST2) == VAL is VAL and + // maximum unsigned value is ~0. For signed comparison, if CST2 + // doesn't have the most significant bit set, handle it similarly. If + // CST2 has MSB set, the minimum is the same, and maximum is ~0U/2. + wide_int valv = lhs.lower_bound (); + wide_int minv = valv & cst2v, maxv; + bool we_know_nothing = false; + if (minv != valv) + { + // If (VAL & CST2) != VAL, X & CST2 can't be equal to VAL. + minv = masked_increment (valv, cst2v, sgnbit, nprec); + if (minv == valv) + { + // If we can't determine anything on this bound, fall + // through and conservatively solve for the other end point. + we_know_nothing = true; + } + } + maxv = wi::mask (nprec - (cst2n ? 1 : 0), false, nprec); + if (we_know_nothing) + r.set_varying (type); + else + r = int_range<1> (type, minv, maxv); + + // Solve [-INF, lhs.upper_bound ()] = x & MASK. + // + // Minimum unsigned value for <= is 0 and maximum unsigned value is + // VAL | ~CST2 if (VAL & CST2) == VAL. Otherwise, find smallest + // VAL2 where + // VAL2 > VAL && (VAL2 & CST2) == VAL2 and use (VAL2 - 1) | ~CST2 + // as maximum. + // For signed comparison, if CST2 doesn't have most significant bit + // set, handle it similarly. If CST2 has MSB set, the maximum is + // the same and minimum is INT_MIN. + valv = lhs.upper_bound (); + minv = valv & cst2v; + if (minv == valv) + maxv = valv; + else + { + maxv = masked_increment (valv, cst2v, sgnbit, nprec); + if (maxv == valv) + { + // If we couldn't determine anything on either bound, return + // undefined. + if (we_know_nothing) + r.set_undefined (); + return; + } + maxv -= 1; + } + maxv |= ~cst2v; + minv = sgnbit; + int_range<1> upper_bits (type, minv, maxv); + r.intersect (upper_bits); +} + bool -operator_bitwise_and::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_bitwise_and::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { - // If this is really a logical wi_fold, call that. if (types_compatible_p (type, boolean_type_node)) return op_logical_and.op1_range (r, type, lhs, op2); - // For now do nothing with bitwise AND of value_range's. - r.set_varying (type); + r.set_undefined (); + for (unsigned i = 0; i < lhs.num_pairs (); ++i) + { + widest_irange chunk (lhs.type (), + lhs.lower_bound (i), + lhs.upper_bound (i)); + widest_irange res; + simple_op1_range_solver (res, type, chunk, op2); + r.union_ (res); + } + if (r.undefined_p ()) + set_nonzero_range_from_mask (r, type, lhs); return true; } bool -operator_bitwise_and::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_bitwise_and::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { return operator_bitwise_and::op1_range (r, type, lhs, op1); } @@ -1969,23 +2339,23 @@ operator_bitwise_and::op2_range (value_range &r, tree type, class operator_logical_or : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &lh, - const value_range &rh) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; + virtual bool fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; } op_logical_or; bool -operator_logical_or::fold_range (value_range &r, tree type ATTRIBUTE_UNUSED, - const value_range &lh, - const value_range &rh) const +operator_logical_or::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, + const irange &lh, + const irange &rh) const { - if (empty_range_check (r, lh, rh)) + if (empty_range_varying (r, type, lh, rh)) return true; r = lh; @@ -1994,9 +2364,9 @@ operator_logical_or::fold_range (value_range &r, tree type ATTRIBUTE_UNUSED, } bool -operator_logical_or::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2 ATTRIBUTE_UNUSED) const +operator_logical_or::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2 ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -2015,9 +2385,9 @@ operator_logical_or::op1_range (value_range &r, tree type, } bool -operator_logical_or::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_logical_or::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { return operator_logical_or::op1_range (r, type, lhs, op1); } @@ -2026,13 +2396,13 @@ operator_logical_or::op2_range (value_range &r, tree type, class operator_bitwise_or : public range_operator { public: - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; - virtual bool op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const; - virtual void wi_fold (value_range &r, tree type, + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; + virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -2040,7 +2410,7 @@ public: } op_bitwise_or; void -operator_bitwise_or::wi_fold (value_range &r, tree type, +operator_bitwise_or::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -2076,29 +2446,34 @@ operator_bitwise_or::wi_fold (value_range &r, tree type, new_lb = wi::max (new_lb, rh_lb, sign); // If the limits got swapped around, return varying. if (wi::gt_p (new_lb, new_ub,sign)) - r = value_range (type); + r.set_varying (type); else value_range_with_overflow (r, type, new_lb, new_ub); } bool -operator_bitwise_or::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_bitwise_or::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { // If this is really a logical wi_fold, call that. if (types_compatible_p (type, boolean_type_node)) return op_logical_or.op1_range (r, type, lhs, op2); - // For now do nothing with bitwise OR of value_range's. + if (lhs.zero_p ()) + { + tree zero = build_zero_cst (type); + r = int_range<1> (zero, zero); + return true; + } r.set_varying (type); return true; } bool -operator_bitwise_or::op2_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op1) const +operator_bitwise_or::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const { return operator_bitwise_or::op1_range (r, type, lhs, op1); } @@ -2107,15 +2482,21 @@ operator_bitwise_or::op2_range (value_range &r, tree type, class operator_bitwise_xor : public range_operator { public: - virtual void wi_fold (value_range &r, tree type, + 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; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; } op_bitwise_xor; void -operator_bitwise_xor::wi_fold (value_range &r, tree type, +operator_bitwise_xor::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -2142,14 +2523,55 @@ operator_bitwise_xor::wi_fold (value_range &r, tree type, if (wi::lt_p (new_lb, 0, sign) || wi::ge_p (new_ub, 0, sign)) value_range_with_overflow (r, type, new_lb, new_ub); else - r = value_range (type); + r.set_varying (type); +} + +bool +operator_bitwise_xor::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const +{ + if (lhs.undefined_p () || lhs.varying_p ()) + { + r = lhs; + return true; + } + if (types_compatible_p (type, boolean_type_node)) + { + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + if (op2.varying_p ()) + r.set_varying (type); + else if (op2.zero_p ()) + r = range_true (type); + else + r = range_false (type); + break; + case BRS_FALSE: + r = op2; + break; + default: + gcc_unreachable (); + } + return true; + } + r.set_varying (type); + return true; } +bool +operator_bitwise_xor::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const +{ + return operator_bitwise_xor::op1_range (r, type, lhs, op1); +} class operator_trunc_mod : public range_operator { public: - virtual void wi_fold (value_range &r, tree type, + virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -2157,7 +2579,7 @@ public: } op_trunc_mod; void -operator_trunc_mod::wi_fold (value_range &r, tree type, +operator_trunc_mod::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -2170,7 +2592,7 @@ operator_trunc_mod::wi_fold (value_range &r, tree type, // Mod 0 is undefined. Return undefined. if (wi_zero_p (type, rh_lb, rh_ub)) { - r = value_range (); + r.set_undefined (); return; } @@ -2204,12 +2626,12 @@ operator_trunc_mod::wi_fold (value_range &r, tree type, class operator_logical_not : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &lh, - const value_range &rh) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; + virtual bool fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; } op_logical_not; // Folding a logical NOT, oddly enough, involves doing nothing on the @@ -2227,11 +2649,11 @@ public: // which is the result we are looking for.. so.. pass it through. bool -operator_logical_not::fold_range (value_range &r, tree type, - const value_range &lh, - const value_range &rh ATTRIBUTE_UNUSED) const +operator_logical_not::fold_range (irange &r, tree type, + const irange &lh, + const irange &rh ATTRIBUTE_UNUSED) const { - if (empty_range_check (r, lh, rh)) + if (empty_range_varying (r, type, lh, rh)) return true; if (lh.varying_p () || lh.undefined_p ()) @@ -2246,10 +2668,10 @@ operator_logical_not::fold_range (value_range &r, tree type, } bool -operator_logical_not::op1_range (value_range &r, +operator_logical_not::op1_range (irange &r, tree type ATTRIBUTE_UNUSED, - const value_range &lhs, - const value_range &op2 ATTRIBUTE_UNUSED) const + const irange &lhs, + const irange &op2 ATTRIBUTE_UNUSED) const { r = lhs; if (!lhs.varying_p () && !lhs.undefined_p ()) @@ -2261,33 +2683,33 @@ operator_logical_not::op1_range (value_range &r, class operator_bitwise_not : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &lh, - const value_range &rh) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; + virtual bool fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; } op_bitwise_not; bool -operator_bitwise_not::fold_range (value_range &r, tree type, - const value_range &lh, - const value_range &rh) const +operator_bitwise_not::fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const { - if (empty_range_check (r, lh, rh)) + if (empty_range_varying (r, type, lh, rh)) return true; // ~X is simply -1 - X. - value_range minusone (type, wi::minus_one (TYPE_PRECISION (type)), - wi::minus_one (TYPE_PRECISION (type))); + int_range<1> minusone (type, wi::minus_one (TYPE_PRECISION (type)), + wi::minus_one (TYPE_PRECISION (type))); return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, minusone, lh); } bool -operator_bitwise_not::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_bitwise_not::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { // ~X is -1 - X and since bitwise NOT is involutary...do it again. return fold_range (r, type, lhs, op2); @@ -2297,15 +2719,15 @@ operator_bitwise_not::op1_range (value_range &r, tree type, class operator_cst : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; } op_integer_cst; bool -operator_cst::fold_range (value_range &r, tree type ATTRIBUTE_UNUSED, - const value_range &lh, - const value_range &rh ATTRIBUTE_UNUSED) const +operator_cst::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, + const irange &lh, + const irange &rh ATTRIBUTE_UNUSED) const { r = lh; return true; @@ -2315,48 +2737,66 @@ operator_cst::fold_range (value_range &r, tree type ATTRIBUTE_UNUSED, class operator_identity : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; } op_identity; bool -operator_identity::fold_range (value_range &r, tree type ATTRIBUTE_UNUSED, - const value_range &lh, - const value_range &rh ATTRIBUTE_UNUSED) const +operator_identity::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, + const irange &lh, + const irange &rh ATTRIBUTE_UNUSED) const { r = lh; return true; } bool -operator_identity::op1_range (value_range &r, tree type ATTRIBUTE_UNUSED, - const value_range &lhs, - const value_range &op2 ATTRIBUTE_UNUSED) const +operator_identity::op1_range (irange &r, tree type ATTRIBUTE_UNUSED, + const irange &lhs, + const irange &op2 ATTRIBUTE_UNUSED) const { r = lhs; return true; } +class operator_unknown : public range_operator +{ +public: + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; +} op_unknown; + +bool +operator_unknown::fold_range (irange &r, tree type, + const irange &lh ATTRIBUTE_UNUSED, + const irange &rh ATTRIBUTE_UNUSED) const +{ + r.set_varying (type); + return true; +} + + class operator_abs : public range_operator { public: - virtual void wi_fold (value_range &r, tree type, + 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; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; } op_abs; void -operator_abs::wi_fold (value_range &r, tree type, +operator_abs::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb ATTRIBUTE_UNUSED, const wide_int &rh_ub ATTRIBUTE_UNUSED) const @@ -2368,7 +2808,7 @@ operator_abs::wi_fold (value_range &r, tree type, // Pass through LH for the easy cases. if (sign == UNSIGNED || wi::ge_p (lh_lb, 0, sign)) { - r = value_range (type, lh_lb, lh_ub); + r = int_range<1> (type, lh_lb, lh_ub); return; } @@ -2378,7 +2818,7 @@ operator_abs::wi_fold (value_range &r, tree type, wide_int max_value = wi::max_value (prec, sign); if (!TYPE_OVERFLOW_UNDEFINED (type) && wi::eq_p (lh_lb, min_value)) { - r = value_range (type); + r.set_varying (type); return; } @@ -2416,15 +2856,15 @@ operator_abs::wi_fold (value_range &r, tree type, min = wi::zero (prec); max = max_value; } - r = value_range (type, min, max); + r = int_range<1> (type, min, max); } bool -operator_abs::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_abs::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { - if (empty_range_check (r, lhs, op2)) + if (empty_range_varying (r, type, lhs, op2)) return true; if (TYPE_UNSIGNED (type)) { @@ -2432,15 +2872,15 @@ operator_abs::op1_range (value_range &r, tree type, return true; } // Start with the positives because negatives are an impossible result. - value_range positives = range_positives (type); + widest_irange positives = range_positives (type); positives.intersect (lhs); r = positives; // Then add the negative of each pair: // ABS(op1) = [5,20] would yield op1 => [-20,-5][5,20]. for (unsigned i = 0; i < positives.num_pairs (); ++i) - r.union_ (value_range (type, - -positives.upper_bound (i), - -positives.lower_bound (i))); + r.union_ (int_range<1> (type, + -positives.upper_bound (i), + -positives.lower_bound (i))); return true; } @@ -2448,13 +2888,13 @@ operator_abs::op1_range (value_range &r, tree type, class operator_absu : public range_operator { public: - virtual void wi_fold (value_range &r, tree type, + 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; } op_absu; void -operator_absu::wi_fold (value_range &r, tree type, +operator_absu::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb ATTRIBUTE_UNUSED, const wide_int &rh_ub ATTRIBUTE_UNUSED) const @@ -2485,27 +2925,27 @@ operator_absu::wi_fold (value_range &r, tree type, } gcc_checking_assert (TYPE_UNSIGNED (type)); - r = value_range (type, new_lb, new_ub); + r = int_range<1> (type, new_lb, new_ub); } class operator_negate : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; } op_negate; bool -operator_negate::fold_range (value_range &r, tree type, - const value_range &lh, - const value_range &rh) const +operator_negate::fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const { - if (empty_range_check (r, lh, rh)) + if (empty_range_varying (r, type, lh, rh)) return true; // -X is simply 0 - X. return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, @@ -2514,9 +2954,9 @@ operator_negate::fold_range (value_range &r, tree type, } bool -operator_negate::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_negate::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { // NEGATE is involutory. return fold_range (r, type, lhs, op2); @@ -2526,20 +2966,20 @@ operator_negate::op1_range (value_range &r, tree type, class operator_addr_expr : public range_operator { public: - virtual bool fold_range (value_range &r, tree type, - const value_range &op1, - const value_range &op2) const; - virtual bool op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const; + virtual bool fold_range (irange &r, tree type, + const irange &op1, + const irange &op2) const; + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; } op_addr; bool -operator_addr_expr::fold_range (value_range &r, tree type, - const value_range &lh, - const value_range &rh) const +operator_addr_expr::fold_range (irange &r, tree type, + const irange &lh, + const irange &rh) const { - if (empty_range_check (r, lh, rh)) + if (empty_range_varying (r, type, lh, rh)) return true; // Return a non-null pointer of the LHS type (passed in op2). @@ -2548,14 +2988,14 @@ operator_addr_expr::fold_range (value_range &r, tree type, else if (!lh.contains_p (build_zero_cst (lh.type ()))) r = range_nonzero (type); else - r = value_range (type); + r.set_varying (type); return true; } bool -operator_addr_expr::op1_range (value_range &r, tree type, - const value_range &lhs, - const value_range &op2) const +operator_addr_expr::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const { return operator_addr_expr::fold_range (r, type, lhs, op2); } @@ -2564,7 +3004,7 @@ operator_addr_expr::op1_range (value_range &r, tree type, class pointer_plus_operator : public range_operator { public: - virtual void wi_fold (value_range &r, tree type, + virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -2572,7 +3012,7 @@ public: } op_pointer_plus; void -pointer_plus_operator::wi_fold (value_range &r, tree type, +pointer_plus_operator::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -2604,20 +3044,20 @@ pointer_plus_operator::wi_fold (value_range &r, tree type, && rh_lb == rh_ub && rh_lb == 0) r = range_zero (type); else - r = value_range (type); + r.set_varying (type); } class pointer_min_max_operator : public range_operator { public: - virtual void wi_fold (value_range & r, tree type, + 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; } op_ptr_min_max; void -pointer_min_max_operator::wi_fold (value_range &r, tree type, +pointer_min_max_operator::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -2633,20 +3073,20 @@ pointer_min_max_operator::wi_fold (value_range &r, tree type, else if (wi_zero_p (type, lh_lb, lh_ub) && wi_zero_p (type, rh_lb, rh_ub)) r = range_zero (type); else - r = value_range (type); + r.set_varying (type); } class pointer_and_operator : public range_operator { public: - virtual void wi_fold (value_range &r, tree type, + 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; } op_pointer_and; void -pointer_and_operator::wi_fold (value_range &r, tree type, +pointer_and_operator::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb ATTRIBUTE_UNUSED, @@ -2657,20 +3097,49 @@ pointer_and_operator::wi_fold (value_range &r, tree type, if (wi_zero_p (type, lh_lb, lh_ub) || wi_zero_p (type, lh_lb, lh_ub)) r = range_zero (type); else - r = value_range (type); + r.set_varying (type); } class pointer_or_operator : public range_operator { public: - virtual void wi_fold (value_range &r, tree type, + virtual bool op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2) const; + virtual bool op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const; + 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; } op_pointer_or; +bool +pointer_or_operator::op1_range (irange &r, tree type, + const irange &lhs, + const irange &op2 ATTRIBUTE_UNUSED) const +{ + if (lhs.zero_p ()) + { + tree zero = build_zero_cst (type); + r = int_range<1> (zero, zero); + return true; + } + r.set_varying (type); + return true; +} + +bool +pointer_or_operator::op2_range (irange &r, tree type, + const irange &lhs, + const irange &op1) const +{ + return pointer_or_operator::op1_range (r, type, lhs, op1); +} + void -pointer_or_operator::wi_fold (value_range &r, tree type, +pointer_or_operator::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, @@ -2684,7 +3153,7 @@ pointer_or_operator::wi_fold (value_range &r, tree type, else if (wi_zero_p (type, lh_lb, lh_ub) && wi_zero_p (type, rh_lb, rh_ub)) r = range_zero (type); else - r = value_range (type); + r.set_varying (type); } // This implements the range operator tables as local objects in this file. @@ -2760,6 +3229,8 @@ integral_table::integral_table () set (SSA_NAME, op_identity); set (PAREN_EXPR, op_identity); set (OBJ_TYPE_REF, op_identity); + set (IMAGPART_EXPR, op_unknown); + set (POINTER_DIFF_EXPR, op_unknown); set (ABS_EXPR, op_abs); set (ABSU_EXPR, op_absu); set (NEGATE_EXPR, op_negate); @@ -2811,13 +3282,13 @@ range_op_handler (enum tree_code code, tree type) // Cast the range in R to TYPE. void -range_cast (value_range &r, tree type) +range_cast (irange &r, tree type) { - value_range tmp = r; + widest_irange tmp = r; range_operator *op = range_op_handler (CONVERT_EXPR, type); // Call op_convert, if it fails, the result is varying. - if (!op->fold_range (r, type, tmp, value_range (type))) - r = value_range (type); + if (!op->fold_range (r, type, tmp, int_range<1> (type))) + r.set_varying (type); } #if CHECKING_P @@ -2836,37 +3307,280 @@ namespace selftest #define UCHAR(N) build_int_cstu (unsigned_char_type_node, (N)) #define SCHAR(N) build_int_cst (signed_char_type_node, (N)) +static int_range<3> +build_range3 (int a, int b, int c, int d, int e, int f) +{ + int_range<3> i1 (INT (a), INT (b)); + int_range<3> i2 (INT (c), INT (d)); + int_range<3> i3 (INT (e), INT (f)); + i1.union_ (i2); + i1.union_ (i3); + return i1; +} + +static void +range3_tests () +{ + typedef int_range<3> irange3; + irange3 r0, r1, r2; + irange3 i1, i2, i3; + + // ([10,20] U [5,8]) U [1,3] ==> [1,3][5,8][10,20]. + r0 = irange3 (INT (10), INT (20)); + r1 = irange3 (INT (5), INT (8)); + r0.union_ (r1); + r1 = irange3 (INT (1), INT (3)); + r0.union_ (r1); + ASSERT_TRUE (r0 == build_range3 (1, 3, 5, 8, 10, 20)); + + // [1,3][5,8][10,20] U [-5,0] => [-5,3][5,8][10,20]. + r1 = irange3 (INT (-5), INT (0)); + r0.union_ (r1); + ASSERT_TRUE (r0 == build_range3 (-5, 3, 5, 8, 10, 20)); + + // [10,20][30,40] U [50,60] ==> [10,20][30,40][50,60]. + r1 = irange3 (INT (50), INT (60)); + r0 = irange3 (INT (10), INT (20)); + r0.union_ (irange3 (INT (30), INT (40))); + r0.union_ (r1); + ASSERT_TRUE (r0 == build_range3 (10, 20, 30, 40, 50, 60)); + // [10,20][30,40][50,60] U [70, 80] ==> [10,20][30,40][50,60][70,80]. + r1 = irange3 (INT (70), INT (80)); + r0.union_ (r1); + + r2 = build_range3 (10, 20, 30, 40, 50, 60); + r2.union_ (irange3 (INT (70), INT (80))); + ASSERT_TRUE (r0 == r2); + + // [10,20][30,40][50,60] U [6,35] => [6,40][50,60]. + r0 = build_range3 (10, 20, 30, 40, 50, 60); + r1 = irange3 (INT (6), INT (35)); + r0.union_ (r1); + r1 = irange3 (INT (6), INT (40)); + r1.union_ (irange3 (INT (50), INT (60))); + ASSERT_TRUE (r0 == r1); + + // [10,20][30,40][50,60] U [6,60] => [6,60]. + r0 = build_range3 (10, 20, 30, 40, 50, 60); + r1 = irange3 (INT (6), INT (60)); + r0.union_ (r1); + ASSERT_TRUE (r0 == irange3 (INT (6), INT (60))); + + // [10,20][30,40][50,60] U [6,70] => [6,70]. + r0 = build_range3 (10, 20, 30, 40, 50, 60); + r1 = irange3 (INT (6), INT (70)); + r0.union_ (r1); + ASSERT_TRUE (r0 == irange3 (INT (6), INT (70))); + + // [10,20][30,40][50,60] U [35,70] => [10,20][30,70]. + r0 = build_range3 (10, 20, 30, 40, 50, 60); + r1 = irange3 (INT (35), INT (70)); + r0.union_ (r1); + r1 = irange3 (INT (10), INT (20)); + r1.union_ (irange3 (INT (30), INT (70))); + ASSERT_TRUE (r0 == r1); + + // [10,20][30,40][50,60] U [15,35] => [10,40][50,60]. + r0 = build_range3 (10, 20, 30, 40, 50, 60); + r1 = irange3 (INT (15), INT (35)); + r0.union_ (r1); + r1 = irange3 (INT (10), INT (40)); + r1.union_ (irange3 (INT (50), INT (60))); + ASSERT_TRUE (r0 == r1); + + // [10,20][30,40][50,60] U [35,35] => [10,20][30,40][50,60]. + r0 = build_range3 (10, 20, 30, 40, 50, 60); + r1 = irange3 (INT (35), INT (35)); + r0.union_ (r1); + ASSERT_TRUE (r0 == build_range3 (10, 20, 30, 40, 50, 60)); +} + +static void +widest_irange_tests () +{ + widest_irange big; + unsigned int nrange; + + // Build a huge multi-range range. + for (nrange = 0; nrange < 50; ++nrange) + { + int_range<1> tmp (INT (nrange*10), INT (nrange*10 + 5)); + big.union_ (tmp); + } + ASSERT_TRUE (big.num_pairs () == nrange); + + // Verify that we can copy it without loosing precision. + widest_irange copy (big); + ASSERT_TRUE (copy.num_pairs () == nrange); + + // Inverting it should produce one more sub-range. + big.invert (); + ASSERT_TRUE (big.num_pairs () == nrange + 1); + + int_range<1> tmp (INT (5), INT (37)); + big.intersect (tmp); + ASSERT_TRUE (big.num_pairs () == 4); + + // Test that [10,10][20,20] does NOT contain 15. + { + widest_irange i1 (build_int_cst (integer_type_node, 10), + build_int_cst (integer_type_node, 10)); + widest_irange i2 (build_int_cst (integer_type_node, 20), + build_int_cst (integer_type_node, 20)); + i1.union_ (i2); + ASSERT_FALSE (i1.contains_p (build_int_cst (integer_type_node, 15))); + } + +} + +static void +multi_precision_range_tests () +{ + // Test truncating copy to int_range<1>. + int_range<3> big = build_range3 (10, 20, 30, 40, 50, 60); + int_range<1> small = big; + ASSERT_TRUE (small == int_range<1> (INT (10), INT (60))); + + // Test truncating copy to int_range<2>. + int_range<2> medium = big; + ASSERT_TRUE (!medium.undefined_p ()); + + // Test that a truncating copy of [MIN,20][22,40][80,MAX] + // ends up as a conservative anti-range of ~[21,21]. + big = int_range<3> (vrp_val_min (integer_type_node), INT (20)); + big.union_ (int_range<1> (INT (22), INT (40))); + big.union_ (int_range<1> (INT (80), vrp_val_max (integer_type_node))); + small = big; + ASSERT_TRUE (small == int_range<1> (INT (21), INT (21), VR_ANTI_RANGE)); + + range3_tests (); +} + +static void +operator_tests () +{ + tree min = vrp_val_min (integer_type_node); + tree max = vrp_val_max (integer_type_node); + tree tiny = fold_build2 (PLUS_EXPR, integer_type_node, min, + build_one_cst (integer_type_node)); + widest_irange res; + widest_irange i1 (tiny, max); + widest_irange i2 (build_int_cst (integer_type_node, 255), + build_int_cst (integer_type_node, 255)); + + // [MIN+1, MAX] = OP1 & 255: OP1 is VARYING + op_bitwise_and.op1_range (res, integer_type_node, i1, i2); + ASSERT_TRUE (res == int_range<1> (integer_type_node)); + + // VARYING = OP1 & 255: OP1 is VARYING + i1 = int_range<1> (integer_type_node); + op_bitwise_and.op1_range (res, integer_type_node, i1, i2); + ASSERT_TRUE (res == int_range<1> (integer_type_node)); + + // Test that 0x808.... & 0x8.... still contains 0x8.... + // for a large set of numbers. + { + tree big_type = long_long_unsigned_type_node; + // big_num = 0x808,0000,0000,0000 + tree big_num = fold_build2 (LSHIFT_EXPR, big_type, + build_int_cst (big_type, 0x808), + build_int_cst (big_type, 48)); + op_bitwise_and.fold_range (res, big_type, + int_range <1> (big_type), + int_range <1> (big_num, big_num)); + // val = 0x8,0000,0000,0000 + tree val = fold_build2 (LSHIFT_EXPR, big_type, + build_int_cst (big_type, 0x8), + build_int_cst (big_type, 48)); + ASSERT_TRUE (res.contains_p (val)); + } + + // unsigned: [3, MAX] = OP1 >> 1 + { + widest_irange lhs (build_int_cst (unsigned_type_node, 3), + TYPE_MAX_VALUE (unsigned_type_node)); + widest_irange one (build_one_cst (unsigned_type_node), + build_one_cst (unsigned_type_node)); + widest_irange op1; + op_rshift.op1_range (op1, unsigned_type_node, lhs, one); + ASSERT_FALSE (op1.contains_p (UINT (3))); + } + + // signed: [3, MAX] = OP1 >> 1 + { + widest_irange lhs (INT (3), TYPE_MAX_VALUE (integer_type_node)); + widest_irange one (INT (1), INT (1)); + widest_irange op1; + op_rshift.op1_range (op1, integer_type_node, lhs, one); + ASSERT_FALSE (op1.contains_p (INT (-2))); + } + + // This is impossible, so OP1 should be []. + // signed: [MIN, MIN] = OP1 >> 1 + { + widest_irange lhs (TYPE_MIN_VALUE (integer_type_node), + TYPE_MIN_VALUE (integer_type_node)); + widest_irange one (INT (1), INT (1)); + widest_irange op1; + op_rshift.op1_range (op1, integer_type_node, lhs, one); + ASSERT_TRUE (op1.undefined_p ()); + } + + // signed: ~[-1] = OP1 >> 31 + { + widest_irange lhs (INT (-1), INT (-1), VR_ANTI_RANGE); + widest_irange shift (INT (31), INT (31)); + widest_irange op1; + op_rshift.op1_range (op1, integer_type_node, lhs, shift); + widest_irange negatives = range_negatives (integer_type_node); + negatives.intersect (op1); + ASSERT_TRUE (negatives.undefined_p ()); + } +} + // Run all of the selftests within this file. void range_tests () { tree u128_type = build_nonstandard_integer_type (128, /*unsigned=*/1); - value_range i1, i2, i3; - value_range r0, r1, rold; + int_range<1> i1, i2, i3; + int_range<1> r0, r1, rold; + + // Test 1-bit signed integer union. + // [-1,-1] U [0,0] = VARYING. + tree one_bit_type = build_nonstandard_integer_type (1, 0); + { + tree one_bit_min = vrp_val_min (one_bit_type); + tree one_bit_max = vrp_val_max (one_bit_type); + int_range<2> min (one_bit_min, one_bit_min); + int_range<2> max (one_bit_max, one_bit_max); + max.union_ (min); + ASSERT_TRUE (max.varying_p ()); + } // Test that NOT(255) is [0..254] in 8-bit land. - value_range not_255 (UCHAR (255), UCHAR (255), VR_ANTI_RANGE); - ASSERT_TRUE (not_255 == value_range (UCHAR (0), UCHAR (254))); + int_range<1> not_255 (UCHAR (255), UCHAR (255), VR_ANTI_RANGE); + ASSERT_TRUE (not_255 == int_range<1> (UCHAR (0), UCHAR (254))); // Test that NOT(0) is [1..255] in 8-bit land. - value_range not_zero = range_nonzero (unsigned_char_type_node); - ASSERT_TRUE (not_zero == value_range (UCHAR (1), UCHAR (255))); + int_range<1> not_zero = range_nonzero (unsigned_char_type_node); + ASSERT_TRUE (not_zero == int_range<1> (UCHAR (1), UCHAR (255))); // Check that [0,127][0x..ffffff80,0x..ffffff] // => ~[128, 0x..ffffff7f]. - r0 = value_range (UINT128 (0), UINT128 (127)); + r0 = int_range<1> (UINT128 (0), UINT128 (127)); tree high = build_minus_one_cst (u128_type); // low = -1 - 127 => 0x..ffffff80. tree low = fold_build2 (MINUS_EXPR, u128_type, high, UINT128(127)); - r1 = value_range (low, high); // [0x..ffffff80, 0x..ffffffff] + r1 = int_range<1> (low, high); // [0x..ffffff80, 0x..ffffffff] // r0 = [0,127][0x..ffffff80,0x..fffffff]. r0.union_ (r1); // r1 = [128, 0x..ffffff7f]. - r1 = value_range (UINT128(128), - fold_build2 (MINUS_EXPR, u128_type, - build_minus_one_cst (u128_type), - UINT128(128))); + r1 = int_range<1> (UINT128(128), + fold_build2 (MINUS_EXPR, u128_type, + build_minus_one_cst (u128_type), + UINT128(128))); r0.invert (); ASSERT_TRUE (r0 == r1); @@ -2882,38 +3596,38 @@ range_tests () tree maxuint = wide_int_to_tree (unsigned_type_node, r0.upper_bound ()); // Check that ~[0,5] => [6,MAX] for unsigned int. - r0 = value_range (UINT (0), UINT (5)); + r0 = int_range<1> (UINT (0), UINT (5)); r0.invert (); - ASSERT_TRUE (r0 == value_range (UINT(6), maxuint)); + ASSERT_TRUE (r0 == int_range<1> (UINT(6), maxuint)); // Check that ~[10,MAX] => [0,9] for unsigned int. - r0 = value_range (UINT(10), maxuint); + r0 = int_range<1> (UINT(10), maxuint); r0.invert (); - ASSERT_TRUE (r0 == value_range (UINT (0), UINT (9))); + ASSERT_TRUE (r0 == int_range<1> (UINT (0), UINT (9))); // Check that ~[0,5] => [6,MAX] for unsigned 128-bit numbers. - r0 = value_range (UINT128 (0), UINT128 (5), VR_ANTI_RANGE); - r1 = value_range (UINT128(6), build_minus_one_cst (u128_type)); + r0 = int_range<1> (UINT128 (0), UINT128 (5), VR_ANTI_RANGE); + r1 = int_range<1> (UINT128(6), build_minus_one_cst (u128_type)); ASSERT_TRUE (r0 == r1); // Check that [~5] is really [-MIN,4][6,MAX]. - r0 = value_range (INT (5), INT (5), VR_ANTI_RANGE); - r1 = value_range (minint, INT (4)); - r1.union_ (value_range (INT (6), maxint)); + r0 = int_range<1> (INT (5), INT (5), VR_ANTI_RANGE); + r1 = int_range<1> (minint, INT (4)); + r1.union_ (int_range<1> (INT (6), maxint)); ASSERT_FALSE (r1.undefined_p ()); ASSERT_TRUE (r0 == r1); - r1 = value_range (INT (5), INT (5)); - value_range r2 (r1); + r1 = int_range<1> (INT (5), INT (5)); + int_range<1> r2 (r1); ASSERT_TRUE (r1 == r2); - r1 = value_range (INT (5), INT (10)); + r1 = int_range<1> (INT (5), INT (10)); - r1 = value_range (integer_type_node, - wi::to_wide (INT (5)), wi::to_wide (INT (10))); + r1 = int_range<1> (integer_type_node, + wi::to_wide (INT (5)), wi::to_wide (INT (10))); ASSERT_TRUE (r1.contains_p (INT (7))); - r1 = value_range (SCHAR (0), SCHAR (20)); + r1 = int_range<1> (SCHAR (0), SCHAR (20)); ASSERT_TRUE (r1.contains_p (SCHAR(15))); ASSERT_FALSE (r1.contains_p (SCHAR(300))); @@ -2922,96 +3636,96 @@ range_tests () if (TYPE_PRECISION (TREE_TYPE (maxint)) > TYPE_PRECISION (short_integer_type_node)) { - r1 = value_range (integer_zero_node, maxint); + r1 = int_range<1> (integer_zero_node, maxint); range_cast (r1, short_integer_type_node); ASSERT_TRUE (r1.lower_bound () == wi::to_wide (minshort) && r1.upper_bound() == wi::to_wide (maxshort)); } // (unsigned char)[-5,-1] => [251,255]. - r0 = rold = value_range (SCHAR (-5), SCHAR (-1)); + r0 = rold = int_range<1> (SCHAR (-5), SCHAR (-1)); range_cast (r0, unsigned_char_type_node); - ASSERT_TRUE (r0 == value_range (UCHAR (251), UCHAR (255))); + ASSERT_TRUE (r0 == int_range<1> (UCHAR (251), UCHAR (255))); range_cast (r0, signed_char_type_node); ASSERT_TRUE (r0 == rold); // (signed char)[15, 150] => [-128,-106][15,127]. - r0 = rold = value_range (UCHAR (15), UCHAR (150)); + r0 = rold = int_range<1> (UCHAR (15), UCHAR (150)); range_cast (r0, signed_char_type_node); - r1 = value_range (SCHAR (15), SCHAR (127)); - r2 = value_range (SCHAR (-128), SCHAR (-106)); + r1 = int_range<1> (SCHAR (15), SCHAR (127)); + r2 = int_range<1> (SCHAR (-128), SCHAR (-106)); r1.union_ (r2); ASSERT_TRUE (r1 == r0); range_cast (r0, unsigned_char_type_node); ASSERT_TRUE (r0 == rold); // (unsigned char)[-5, 5] => [0,5][251,255]. - r0 = rold = value_range (SCHAR (-5), SCHAR (5)); + r0 = rold = int_range<1> (SCHAR (-5), SCHAR (5)); range_cast (r0, unsigned_char_type_node); - r1 = value_range (UCHAR (251), UCHAR (255)); - r2 = value_range (UCHAR (0), UCHAR (5)); + r1 = int_range<1> (UCHAR (251), UCHAR (255)); + r2 = int_range<1> (UCHAR (0), UCHAR (5)); r1.union_ (r2); ASSERT_TRUE (r0 == r1); range_cast (r0, signed_char_type_node); ASSERT_TRUE (r0 == rold); // (unsigned char)[-5,5] => [0,5][251,255]. - r0 = value_range (INT (-5), INT (5)); + r0 = int_range<1> (INT (-5), INT (5)); range_cast (r0, unsigned_char_type_node); - r1 = value_range (UCHAR (0), UCHAR (5)); - r1.union_ (value_range (UCHAR (251), UCHAR (255))); + r1 = int_range<1> (UCHAR (0), UCHAR (5)); + r1.union_ (int_range<1> (UCHAR (251), UCHAR (255))); ASSERT_TRUE (r0 == r1); // (unsigned char)[5U,1974U] => [0,255]. - r0 = value_range (UINT (5), UINT (1974)); + r0 = int_range<1> (UINT (5), UINT (1974)); range_cast (r0, unsigned_char_type_node); - ASSERT_TRUE (r0 == value_range (UCHAR (0), UCHAR (255))); + ASSERT_TRUE (r0 == int_range<1> (UCHAR (0), UCHAR (255))); range_cast (r0, integer_type_node); // Going to a wider range should not sign extend. - ASSERT_TRUE (r0 == value_range (INT (0), INT (255))); + ASSERT_TRUE (r0 == int_range<1> (INT (0), INT (255))); // (unsigned char)[-350,15] => [0,255]. - r0 = value_range (INT (-350), INT (15)); + r0 = int_range<1> (INT (-350), INT (15)); range_cast (r0, unsigned_char_type_node); - ASSERT_TRUE (r0 == (value_range + ASSERT_TRUE (r0 == (int_range<1> (TYPE_MIN_VALUE (unsigned_char_type_node), TYPE_MAX_VALUE (unsigned_char_type_node)))); // Casting [-120,20] from signed char to unsigned short. // => [0, 20][0xff88, 0xffff]. - r0 = value_range (SCHAR (-120), SCHAR (20)); + r0 = int_range<1> (SCHAR (-120), SCHAR (20)); range_cast (r0, short_unsigned_type_node); - r1 = value_range (UINT16 (0), UINT16 (20)); - r2 = value_range (UINT16 (0xff88), UINT16 (0xffff)); + r1 = int_range<1> (UINT16 (0), UINT16 (20)); + r2 = int_range<1> (UINT16 (0xff88), UINT16 (0xffff)); r1.union_ (r2); ASSERT_TRUE (r0 == r1); // A truncating cast back to signed char will work because [-120, 20] // is representable in signed char. range_cast (r0, signed_char_type_node); - ASSERT_TRUE (r0 == value_range (SCHAR (-120), SCHAR (20))); + ASSERT_TRUE (r0 == int_range<1> (SCHAR (-120), SCHAR (20))); // unsigned char -> signed short // (signed short)[(unsigned char)25, (unsigned char)250] // => [(signed short)25, (signed short)250] - r0 = rold = value_range (UCHAR (25), UCHAR (250)); + r0 = rold = int_range<1> (UCHAR (25), UCHAR (250)); range_cast (r0, short_integer_type_node); - r1 = value_range (INT16 (25), INT16 (250)); + r1 = int_range<1> (INT16 (25), INT16 (250)); ASSERT_TRUE (r0 == r1); range_cast (r0, unsigned_char_type_node); ASSERT_TRUE (r0 == rold); // Test casting a wider signed [-MIN,MAX] to a nar`rower unsigned. - r0 = value_range (TYPE_MIN_VALUE (long_long_integer_type_node), + r0 = int_range<1> (TYPE_MIN_VALUE (long_long_integer_type_node), TYPE_MAX_VALUE (long_long_integer_type_node)); range_cast (r0, short_unsigned_type_node); - r1 = value_range (TYPE_MIN_VALUE (short_unsigned_type_node), + r1 = int_range<1> (TYPE_MIN_VALUE (short_unsigned_type_node), TYPE_MAX_VALUE (short_unsigned_type_node)); ASSERT_TRUE (r0 == r1); // NOT([10,20]) ==> [-MIN,9][21,MAX]. - r0 = r1 = value_range (INT (10), INT (20)); - r2 = value_range (minint, INT(9)); - r2.union_ (value_range (INT(21), maxint)); + r0 = r1 = int_range<1> (INT (10), INT (20)); + r2 = int_range<1> (minint, INT(9)); + r2.union_ (int_range<1> (INT(21), maxint)); ASSERT_FALSE (r2.undefined_p ()); r1.invert (); ASSERT_TRUE (r1 == r2); @@ -3021,11 +3735,11 @@ range_tests () // Test that booleans and their inverse work as expected. r0 = range_zero (boolean_type_node); - ASSERT_TRUE (r0 == value_range (build_zero_cst (boolean_type_node), - build_zero_cst (boolean_type_node))); + ASSERT_TRUE (r0 == int_range<1> (build_zero_cst (boolean_type_node), + build_zero_cst (boolean_type_node))); r0.invert (); - ASSERT_TRUE (r0 == value_range (build_one_cst (boolean_type_node), - build_one_cst (boolean_type_node))); + ASSERT_TRUE (r0 == int_range<1> (build_one_cst (boolean_type_node), + build_one_cst (boolean_type_node))); // Casting NONZERO to a narrower type will wrap/overflow so // it's just the entire range for the narrower type. @@ -3038,8 +3752,8 @@ range_tests () { r0 = range_nonzero (integer_type_node); range_cast (r0, short_integer_type_node); - r1 = value_range (TYPE_MIN_VALUE (short_integer_type_node), - TYPE_MAX_VALUE (short_integer_type_node)); + r1 = int_range<1> (TYPE_MIN_VALUE (short_integer_type_node), + TYPE_MAX_VALUE (short_integer_type_node)); ASSERT_TRUE (r0 == r1); } @@ -3049,8 +3763,8 @@ range_tests () // Converting this to 32-bits signed is [-MIN_16,-1][1, +MAX_16]. r0 = range_nonzero (short_integer_type_node); range_cast (r0, integer_type_node); - r1 = value_range (INT (-32768), INT (-1)); - r2 = value_range (INT (1), INT (32767)); + r1 = int_range<1> (INT (-32768), INT (-1)); + r2 = int_range<1> (INT (1), INT (32767)); r1.union_ (r2); ASSERT_TRUE (r0 == r1); @@ -3064,34 +3778,34 @@ range_tests () ASSERT_TRUE (r0 == r1); // [10,20] U [15, 30] => [10, 30]. - r0 = value_range (INT (10), INT (20)); - r1 = value_range (INT (15), INT (30)); + r0 = int_range<1> (INT (10), INT (20)); + r1 = int_range<1> (INT (15), INT (30)); r0.union_ (r1); - ASSERT_TRUE (r0 == value_range (INT (10), INT (30))); + ASSERT_TRUE (r0 == int_range<1> (INT (10), INT (30))); // [15,40] U [] => [15,40]. - r0 = value_range (INT (15), INT (40)); + r0 = int_range<1> (INT (15), INT (40)); r1.set_undefined (); r0.union_ (r1); - ASSERT_TRUE (r0 == value_range (INT (15), INT (40))); + ASSERT_TRUE (r0 == int_range<1> (INT (15), INT (40))); // [10,20] U [10,10] => [10,20]. - r0 = value_range (INT (10), INT (20)); - r1 = value_range (INT (10), INT (10)); + r0 = int_range<1> (INT (10), INT (20)); + r1 = int_range<1> (INT (10), INT (10)); r0.union_ (r1); - ASSERT_TRUE (r0 == value_range (INT (10), INT (20))); + ASSERT_TRUE (r0 == int_range<1> (INT (10), INT (20))); // [10,20] U [9,9] => [9,20]. - r0 = value_range (INT (10), INT (20)); - r1 = value_range (INT (9), INT (9)); + r0 = int_range<1> (INT (10), INT (20)); + r1 = int_range<1> (INT (9), INT (9)); r0.union_ (r1); - ASSERT_TRUE (r0 == value_range (INT (9), INT (20))); + ASSERT_TRUE (r0 == int_range<1> (INT (9), INT (20))); // [10,20] ^ [15,30] => [15,20]. - r0 = value_range (INT (10), INT (20)); - r1 = value_range (INT (15), INT (30)); + r0 = int_range<1> (INT (10), INT (20)); + r1 = int_range<1> (INT (15), INT (30)); r0.intersect (r1); - ASSERT_TRUE (r0 == value_range (INT (15), INT (20))); + ASSERT_TRUE (r0 == int_range<1> (INT (15), INT (20))); // Test the internal sanity of wide_int's wrt HWIs. ASSERT_TRUE (wi::max_value (TYPE_PRECISION (boolean_type_node), @@ -3099,13 +3813,17 @@ range_tests () == wi::uhwi (1, TYPE_PRECISION (boolean_type_node))); // Test zero_p(). - r0 = value_range (INT (0), INT (0)); + r0 = int_range<1> (INT (0), INT (0)); ASSERT_TRUE (r0.zero_p ()); // Test nonzero_p(). - r0 = value_range (INT (0), INT (0)); + r0 = int_range<1> (INT (0), INT (0)); r0.invert (); ASSERT_TRUE (r0.nonzero_p ()); + + multi_precision_range_tests (); + widest_irange_tests (); + operator_tests (); } } // namespace selftest |