divmod_internal didn't handle unsigned division in which the inputs have implicit all-one upper bits. There were two problems:
- wi_unpack should extend implicit 1s to index blocks_needed - 1 (possibly with a zext_hwi on the last block for small_prec) regardless of signedness - when calculating m and n, divmod_internal only used the *_blocks_needed value if the division was signed. Neither is value is really signed in this context, since we've already calculated absolute values if sgnop == SIGNED, so the neg_p (and its pre-wi:: incarnation) would only have held for the minimum integer. Using a simple while loop to find the top half-HWI seems more direct. Also, divmod_internal_2 took n and m as unsigned values but used HOST_WIDE_INT iterators on m - n. m can be less than n for things like 0 / big, and the negative m - n was being treated as an unsigned int and zero-extended to HOST_WIDE_INT. The patch fixes both the types of n and m and the types of the iterators. Tested on x86_64-linux-gnu and powerpc64-linux-gnu. OK to install? Thanks, Richard Index: gcc/wide-int.cc =================================================================== --- gcc/wide-int.cc 2014-05-02 16:28:09.560842845 +0100 +++ gcc/wide-int.cc 2014-05-02 16:40:40.002916374 +0100 @@ -1126,8 +1126,7 @@ wi::add_large (HOST_WIDE_INT *val, const HOST_HALF_WIDE_INTs of RESULT. The rest of RESULT is filled by uncompressing the top bit of INPUT[IN_LEN - 1]. */ static void -wi_unpack (unsigned HOST_HALF_WIDE_INT *result, - const unsigned HOST_WIDE_INT *input, +wi_unpack (unsigned HOST_HALF_WIDE_INT *result, const HOST_WIDE_INT *input, unsigned int in_len, unsigned int out_len, unsigned int prec, signop sgn) { @@ -1145,20 +1144,24 @@ wi_unpack (unsigned HOST_HALF_WIDE_INT * else mask = 0; - for (i = 0; i < in_len; i++) + for (i = 0; i < blocks_needed - 1; i++) { - HOST_WIDE_INT x = input[i]; - if (i == blocks_needed - 1 && small_prec) - { - if (sgn == SIGNED) - x = sext_hwi (x, small_prec); - else - x = zext_hwi (x, small_prec); - } + HOST_WIDE_INT x = safe_uhwi (input, in_len, i); result[j++] = x; result[j++] = x >> HOST_BITS_PER_HALF_WIDE_INT; } + HOST_WIDE_INT x = safe_uhwi (input, in_len, i); + if (small_prec) + { + if (sgn == SIGNED) + x = sext_hwi (x, small_prec); + else + x = zext_hwi (x, small_prec); + } + result[j++] = x; + result[j++] = x >> HOST_BITS_PER_HALF_WIDE_INT; + /* Smear the sign bit. */ while (j < out_len) result[j++] = mask; @@ -1317,10 +1320,8 @@ wi::mul_internal (HOST_WIDE_INT *val, co } /* We do unsigned mul and then correct it. */ - wi_unpack (u, (const unsigned HOST_WIDE_INT *) op1val, op1len, - half_blocks_needed, prec, SIGNED); - wi_unpack (v, (const unsigned HOST_WIDE_INT *) op2val, op2len, - half_blocks_needed, prec, SIGNED); + wi_unpack (u, op1val, op1len, half_blocks_needed, prec, SIGNED); + wi_unpack (v, op2val, op2len, half_blocks_needed, prec, SIGNED); /* The 2 is for a full mult. */ memset (r, 0, half_blocks_needed * 2 @@ -1519,7 +1520,7 @@ divmod_internal_2 (unsigned HOST_HALF_WI unsigned HOST_HALF_WIDE_INT *b_remainder, unsigned HOST_HALF_WIDE_INT *b_dividend, unsigned HOST_HALF_WIDE_INT *b_divisor, - unsigned int m, unsigned int n) + int m, int n) { /* The "digits" are a HOST_HALF_WIDE_INT which the size of half of a HOST_WIDE_INT and stored in the lower bits of each word. This @@ -1530,7 +1531,8 @@ divmod_internal_2 (unsigned HOST_HALF_WI unsigned HOST_WIDE_INT qhat; /* Estimate of quotient digit. */ unsigned HOST_WIDE_INT rhat; /* A remainder. */ unsigned HOST_WIDE_INT p; /* Product of two digits. */ - HOST_WIDE_INT s, i, j, t, k; + HOST_WIDE_INT t, k; + int i, j, s; /* Single digit divisor. */ if (n == 1) @@ -1757,26 +1759,17 @@ wi::divmod_internal (HOST_WIDE_INT *quot } } - wi_unpack (b_dividend, (const unsigned HOST_WIDE_INT *) dividend.get_val (), - dividend.get_len (), dividend_blocks_needed, dividend_prec, sgn); - wi_unpack (b_divisor, (const unsigned HOST_WIDE_INT *) divisor.get_val (), - divisor.get_len (), divisor_blocks_needed, divisor_prec, sgn); - - if (wi::neg_p (dividend, sgn)) - m = dividend_blocks_needed; - else - m = 2 * dividend.get_len (); + wi_unpack (b_dividend, dividend.get_val (), dividend.get_len (), + dividend_blocks_needed, dividend_prec, sgn); + wi_unpack (b_divisor, divisor.get_val (), divisor.get_len (), + divisor_blocks_needed, divisor_prec, sgn); + + m = dividend_blocks_needed; + while (m > 1 && b_dividend[m - 1] == 0) + m--; - if (wi::neg_p (divisor, sgn)) - n = divisor_blocks_needed; - else - n = 2 * divisor.get_len (); - - /* We need to find the top non zero block of b_divisor. At most the - top two blocks are zero. */ - if (b_divisor[n - 1] == 0) - n--; - if (b_divisor[n - 1] == 0) + n = divisor_blocks_needed; + while (n > 1 && b_divisor[n - 1] == 0) n--; memset (b_quotient, 0, sizeof (b_quotient));