From c47ea0721cb6a50c90056cc3bfe49d95ad8c1fd5 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Fri, 13 Mar 2009 21:59:44 -0700 Subject: [PATCH] fix the arithmetic of multi-word division. The Multiword division was not handling some degenerate high guesses for the intermediate division result guess. The end result was an assertion. Recover from this case. (Does the addinb back of bp need to be optimized better?) (cherry picked from commit 67154268337b8589d5825e99a8143f8e7c57b68b) --- vvp/vthread.cc | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/vvp/vthread.cc b/vvp/vthread.cc index b6a0e8e82..ce7b91d33 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -1974,7 +1974,8 @@ static unsigned long divide2words(unsigned long a, unsigned long b, static unsigned long* divide_bits(unsigned long*ap, unsigned long*bp, unsigned wid) { - + // Do all our work a cpu-word at a time. The "words" variable + // is the number of words of the wid. unsigned words = (wid+CPU_WORD_BITS-1) / CPU_WORD_BITS; unsigned btop = words-1; @@ -1985,6 +1986,8 @@ static unsigned long* divide_bits(unsigned long*ap, unsigned long*bp, unsigned w if (btop==0 && bp[0]==0) return 0; + // The result array will eventually accumulate the result. The + // diff array is a difference that we use in the intermediate. unsigned long*diff = new unsigned long[words]; unsigned long*result= new unsigned long[words]; for (unsigned idx = 0 ; idx < words ; idx += 1) @@ -2012,10 +2015,11 @@ static unsigned long* divide_bits(unsigned long*ap, unsigned long*bp, unsigned w // cur_res is a guestimate of the result this far. It // may be 1 too big. (But it will also be >0) Try it, - // and if the difference comes out negative, then adjust - // then. + // and if the difference comes out negative, then adjust. + // diff = (bp * cur_res) << cur_ptr; multiply_array_imm(diff+cur_ptr, bp, words-cur_ptr, cur_res); + // ap -= diff unsigned long carry = 1; for (unsigned idx = cur_ptr ; idx < words ; idx += 1) ap[idx] = add_with_carry(ap[idx], ~diff[idx], carry); @@ -2027,12 +2031,14 @@ static unsigned long* divide_bits(unsigned long*ap, unsigned long*bp, unsigned w // case, we know that cur_res was too large by 1. Correct by // adding 1b back in and reducing cur_res. if ((carry&1) == 0) { - cur_res -= 1; - carry = 0; - for (unsigned idx = cur_ptr ; idx < words ; idx += 1) - ap[idx] = add_with_carry(ap[idx], bp[idx-cur_ptr], carry); - // The sign *must* have changed again. - assert(carry == 1); + // Keep adding b back in until the remainder + // becomes positive again. + do { + cur_res -= 1; + carry = 0; + for (unsigned idx = cur_ptr ; idx < words ; idx += 1) + ap[idx] = add_with_carry(ap[idx], bp[idx-cur_ptr], carry); + } while (carry == 0); } result[cur_ptr] = cur_res;