This code was mixing hprec and hprec*2 wide_ints. The simplest fix seemed to be to introduce a function that gives the minimum precision necessary to represent a function, which also means that no temporary wide_ints are needed.
Other places might be able to use this too, but I'd like to look at that after the merge. The patch series fixed a regression in c-c++-common/ubsan/overflow-2.c and I assume it's due to this change. Tested on x86_64-linux-gnu. OK to install? Thanks, Richard Index: gcc/internal-fn.c =================================================================== --- gcc/internal-fn.c 2014-04-22 20:31:10.516895118 +0100 +++ gcc/internal-fn.c 2014-04-22 20:31:25.842005530 +0100 @@ -478,7 +478,7 @@ ubsan_expand_si_overflow_mul_check (gimp rtx do_overflow = gen_label_rtx (); rtx hipart_different = gen_label_rtx (); - int hprec = GET_MODE_PRECISION (hmode); + unsigned int hprec = GET_MODE_PRECISION (hmode); rtx hipart0 = expand_shift (RSHIFT_EXPR, mode, op0, hprec, NULL_RTX, 0); hipart0 = gen_lowpart (hmode, hipart0); @@ -513,12 +513,11 @@ ubsan_expand_si_overflow_mul_check (gimp wide_int arg0_min, arg0_max; if (get_range_info (arg0, &arg0_min, &arg0_max) == VR_RANGE) { - if (wi::les_p (arg0_max, wi::max_value (hprec, SIGNED)) - && wi::les_p (wi::min_value (hprec, SIGNED), arg0_min)) + unsigned int mprec0 = wi::min_precision (arg0_min, SIGNED); + unsigned int mprec1 = wi::min_precision (arg0_max, SIGNED); + if (mprec0 <= hprec && mprec1 <= hprec) op0_small_p = true; - else if (wi::les_p (arg0_max, wi::max_value (hprec, UNSIGNED)) - && wi::les_p (~wi::max_value (hprec, UNSIGNED), - arg0_min)) + else if (mprec0 <= hprec + 1 && mprec1 <= hprec + 1) op0_medium_p = true; if (!wi::neg_p (arg0_min, TYPE_SIGN (TREE_TYPE (arg0)))) op0_sign = 0; @@ -531,12 +530,11 @@ ubsan_expand_si_overflow_mul_check (gimp wide_int arg1_min, arg1_max; if (get_range_info (arg1, &arg1_min, &arg1_max) == VR_RANGE) { - if (wi::les_p (arg1_max, wi::max_value (hprec, SIGNED)) - && wi::les_p (wi::min_value (hprec, SIGNED), arg1_min)) + unsigned int mprec0 = wi::min_precision (arg1_min, SIGNED); + unsigned int mprec1 = wi::min_precision (arg1_max, SIGNED); + if (mprec0 <= hprec && mprec1 <= hprec) op1_small_p = true; - else if (wi::les_p (arg1_max, wi::max_value (hprec, UNSIGNED)) - && wi::les_p (~wi::max_value (hprec, UNSIGNED), - arg1_min)) + else if (mprec0 <= hprec + 1 && mprec1 <= hprec + 1) op1_medium_p = true; if (!wi::neg_p (arg1_min, TYPE_SIGN (TREE_TYPE (arg1)))) op1_sign = 0; Index: gcc/wide-int.h =================================================================== --- gcc/wide-int.h 2014-04-22 20:31:10.516895118 +0100 +++ gcc/wide-int.h 2014-04-22 20:31:25.842005530 +0100 @@ -562,6 +562,9 @@ #define SHIFT_FUNCTION \ template <typename T> unsigned HOST_WIDE_INT extract_uhwi (const T &, unsigned int, unsigned int); + + template <typename T> + unsigned int min_precision (const T &, signop); } namespace wi @@ -2995,6 +2998,17 @@ wi::extract_uhwi (const T &x, unsigned i return zext_hwi (res, width); } +/* Return the minimum precision needed to store X with sign SGN. */ +template <typename T> +inline unsigned int +wi::min_precision (const T &x, signop sgn) +{ + if (sgn == SIGNED) + return wi::get_precision (x) - clrsb (x); + else + return wi::get_precision (x) - clz (x); +} + template<typename T> void gt_ggc_mx (generic_wide_int <T> *)