From 150655ee3f7d67672e2cc809f8abe56923491c62 Mon Sep 17 00:00:00 2001 From: Aldy Hernandez Date: Tue, 4 Sep 2018 14:52:41 +0000 Subject: [PATCH] wide-int-range.cc (wide_int_range_convert): New. * wide-int-range.cc (wide_int_range_convert): New. * wide-int-range.h (wide_int_range_convert): New. * tree-vrp.c (extract_range_from_unary_expr): Abstract wide int code into wide_int_range_convert. (extract_range_into_wide_ints): Do not munge anti range constants into the entire domain. Just return the range back. From-SVN: r264085 --- gcc/ChangeLog | 9 ++++++ gcc/tree-vrp.c | 74 +++++++++++++++++++++---------------------- gcc/wide-int-range.cc | 33 +++++++++++++++++++ gcc/wide-int-range.h | 7 ++++ 4 files changed, 86 insertions(+), 37 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8cba0290c46..9b0d2d11771 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2018-09-04 Aldy Hernandez + + * wide-int-range.cc (wide_int_range_convert): New. + * wide-int-range.h (wide_int_range_convert): New. + * tree-vrp.c (extract_range_from_unary_expr): Abstract wide int + code into wide_int_range_convert. + (extract_range_into_wide_ints): Do not munge anti range constants + into the entire domain. Just return the range back. + 2018-09-04 Martin Liska * genmatch.c (output_line_directive): Add new argument diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index d29a2c9b507..8f16713300c 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -995,7 +995,7 @@ ranges_from_anti_range (const value_range *ar, /* Extract the components of a value range into a pair of wide ints in [WMIN, WMAX]. - If the value range is anything but a VR_RANGE of constants, the + If the value range is anything but a VR_*RANGE of constants, the resulting wide ints are set to [-MIN, +MAX] for the type. */ static void inline @@ -1003,7 +1003,10 @@ extract_range_into_wide_ints (const value_range *vr, signop sign, unsigned prec, wide_int &wmin, wide_int &wmax) { - if (range_int_cst_p (vr)) + if ((vr->type == VR_RANGE + || vr->type == VR_ANTI_RANGE) + && TREE_CODE (vr->min) == INTEGER_CST + && TREE_CODE (vr->max) == INTEGER_CST) { wmin = wi::to_wide (vr->min); wmax = wi::to_wide (vr->max); @@ -1850,44 +1853,41 @@ extract_range_from_unary_expr (value_range *vr, return; } - /* If VR0 is varying and we increase the type precision, assume - a full range for the following transformation. */ - if (vr0.type == VR_VARYING - && INTEGRAL_TYPE_P (inner_type) - && TYPE_PRECISION (inner_type) < TYPE_PRECISION (outer_type)) - { - vr0.type = VR_RANGE; - vr0.min = TYPE_MIN_VALUE (inner_type); - vr0.max = TYPE_MAX_VALUE (inner_type); - } - - /* If VR0 is a constant range or anti-range and 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 and the range is - not an anti-range. */ - if ((vr0.type == VR_RANGE - || vr0.type == VR_ANTI_RANGE) + /* We normalize everything to a VR_RANGE, but for constant + anti-ranges we must handle them by leaving the final result + as an anti range. This allows us to convert things like + ~[0,5] seamlessly. */ + value_range_type vr_type = VR_RANGE; + if (vr0.type == VR_ANTI_RANGE && TREE_CODE (vr0.min) == INTEGER_CST - && TREE_CODE (vr0.max) == INTEGER_CST - && (TYPE_PRECISION (outer_type) >= TYPE_PRECISION (inner_type) - || (vr0.type == VR_RANGE - && integer_zerop (int_const_binop (RSHIFT_EXPR, - int_const_binop (MINUS_EXPR, vr0.max, vr0.min), - size_int (TYPE_PRECISION (outer_type))))))) + && TREE_CODE (vr0.max) == INTEGER_CST) + vr_type = VR_ANTI_RANGE; + + /* NOTES: Previously we were returning VARYING for all symbolics, but + we can do better by treating them as [-MIN, +MAX]. For + example, converting [SYM, SYM] from INT to LONG UNSIGNED, + we can return: ~[0x8000000, 0xffffffff7fffffff]. + + We were also failing to convert ~[0,0] from char* to unsigned, + instead choosing to return VR_VARYING. Now we return ~[0,0]. */ + wide_int vr0_min, vr0_max, wmin, wmax; + signop inner_sign = TYPE_SIGN (inner_type); + signop outer_sign = TYPE_SIGN (outer_type); + unsigned inner_prec = TYPE_PRECISION (inner_type); + unsigned outer_prec = TYPE_PRECISION (outer_type); + extract_range_into_wide_ints (&vr0, inner_sign, inner_prec, + vr0_min, vr0_max); + if (wide_int_range_convert (wmin, wmax, + inner_sign, inner_prec, + outer_sign, outer_prec, + vr0_min, vr0_max)) { - tree new_min, new_max; - new_min = force_fit_type (outer_type, wi::to_widest (vr0.min), - 0, false); - new_max = force_fit_type (outer_type, wi::to_widest (vr0.max), - 0, false); - set_and_canonicalize_value_range (vr, vr0.type, - new_min, new_max, NULL); - return; + tree min = wide_int_to_tree (outer_type, wmin); + tree max = wide_int_to_tree (outer_type, wmax); + set_and_canonicalize_value_range (vr, vr_type, min, max, NULL); } - - set_value_range_to_varying (vr); + else + set_value_range_to_varying (vr); return; } else if (code == ABS_EXPR) diff --git a/gcc/wide-int-range.cc b/gcc/wide-int-range.cc index 04d391b33d5..8a3dfd25684 100644 --- a/gcc/wide-int-range.cc +++ b/gcc/wide-int-range.cc @@ -735,6 +735,39 @@ wide_int_range_abs (wide_int &min, wide_int &max, return true; } +/* Convert range in [VR0_MIN, VR0_MAX] with INNER_SIGN and INNER_PREC, + to a range in [MIN, MAX] with OUTER_SIGN and OUTER_PREC. + + Return TRUE if we were able to successfully calculate the new range. + + Caller is responsible for canonicalizing the resulting range. */ + +bool +wide_int_range_convert (wide_int &min, wide_int &max, + signop inner_sign, + unsigned inner_prec, + signop outer_sign, + unsigned outer_prec, + const wide_int &vr0_min, + const wide_int &vr0_max) +{ + /* 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 (vr0_max, vr0_min), + wi::uhwi (outer_prec, inner_prec), + inner_sign) == 0) + { + min = wide_int::from (vr0_min, outer_prec, inner_sign); + max = wide_int::from (vr0_max, outer_prec, inner_sign); + return (!wi::eq_p (min, wi::min_value (outer_prec, outer_sign)) + || !wi::eq_p (max, wi::max_value (outer_prec, outer_sign))); + } + return false; +} + /* Calculate a division operation on two ranges and store the result in [WMIN, WMAX] U [EXTRA_MIN, EXTRA_MAX]. diff --git a/gcc/wide-int-range.h b/gcc/wide-int-range.h index eaf47bca449..589fdea4df6 100644 --- a/gcc/wide-int-range.h +++ b/gcc/wide-int-range.h @@ -109,6 +109,13 @@ extern bool wide_int_range_abs (wide_int &min, wide_int &max, const wide_int &vr0_min, const wide_int &vr0_max, bool overflow_undefined); +extern bool wide_int_range_convert (wide_int &min, wide_int &max, + signop inner_sign, + unsigned inner_prec, + signop outer_sign, + unsigned outer_prec, + const wide_int &vr0_min, + const wide_int &vr0_max); extern bool wide_int_range_div (wide_int &wmin, wide_int &wmax, enum tree_code code, signop sign, unsigned prec, -- 2.30.2