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));