From 00a95f15bc840814baeaec8d970acf998d70e273 Mon Sep 17 00:00:00 2001 From: akr Date: Sat, 15 Jun 2013 23:46:07 +0000 Subject: [PATCH] * bignum.c (bary_divmod): New function. (absint_numwords_generic): Use bary_divmod. (bigdivrem_num_extra_words): Extracted from bigdivrem. (bigdivrem_single): Ditto. (bigdivrem_normal): Ditto. (BIGDIVREM_EXTRA_WORDS): Defined. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@41326 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 19 +++-- bignum.c | 233 +++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 184 insertions(+), 68 deletions(-) diff --git a/ChangeLog b/ChangeLog index ab65b1d32e..195830469c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,18 @@ +Sun Jun 16 08:43:59 2013 Tanaka Akira + + * bignum.c (bary_divmod): New function. + (absint_numwords_generic): Use bary_divmod. + (bigdivrem_num_extra_words): Extracted from bigdivrem. + (bigdivrem_single): Ditto. + (bigdivrem_normal): Ditto. + (BIGDIVREM_EXTRA_WORDS): Defined. + Sun Jun 16 05:51:51 2013 Masaya Tarui - * gc.c: Fixup around GC by MALLOC. - Add allocate size to malloc_increase before GC + * gc.c: Fixup around GC by MALLOC. + Add allocate size to malloc_increase before GC for updating limit in after_gc_sweep. - Reset malloc_increase into garbage_collect() + Reset malloc_increase into garbage_collect() for preventing GC again soon. Sun Jun 16 05:15:36 2013 Masaya Tarui @@ -14,7 +23,7 @@ Sun Jun 16 05:15:36 2013 Masaya Tarui Sun Jun 16 02:04:40 2013 Masaya Tarui * gc.c (gc_prof_timer_stop): Merge function codes of GC_PROFILE_MORE_DETAIL and !GC_PROFILE_MORE_DETAIL. - * gc.c (gc_prof_mark_timer_start): Ditto. + * gc.c (gc_prof_mark_timer_start): Ditto. * gc.c (gc_prof_mark_timer_stop): Ditto. * gc.c (gc_prof_sweep_slot_timer_start): Ditto. * gc.c (gc_prof_sweep_slot_timer_stop): Ditto. @@ -102,7 +111,7 @@ Fri Jun 14 18:18:07 2013 Koichi Sasada * constant.h: constify rb_const_entry_t::value and file to detect assignment. - * variable.c, internal.h (rb_st_insert_id_and_value, rb_st_copy): + * variable.c, internal.h (rb_st_insert_id_and_value, rb_st_copy): added. update table with write barrier. * method.h: constify some variables to detect assignment. diff --git a/bignum.c b/bignum.c index cd42d08ed4..136318b618 100644 --- a/bignum.c +++ b/bignum.c @@ -50,6 +50,7 @@ static VALUE big_three = Qnil; (BDIGITS(x)[0] == 0 && \ (RBIGNUM_LEN(x) == 1 || bigzero_p(x)))) +#define BIGDIVREM_EXTRA_WORDS 2 #define roomof(n, m) ((int)(((n)+(m)-1) / (m))) #define bdigit_roomof(n) roomof(n, sizeof(BDIGIT)) #define BARY_ARGS(ary) ary, numberof(ary) @@ -60,6 +61,7 @@ static void bdigs_small_rshift(BDIGIT *zds, BDIGIT *xds, long n, int shift, int static void bary_unpack(BDIGIT *bdigits, size_t num_bdigits, const void *words, size_t numwords, size_t wordsize, size_t nails, int flags); static void bary_mul(BDIGIT *zds, size_t zl, BDIGIT *xds, size_t xl, BDIGIT *yds, size_t yl); static void bary_sub(BDIGIT *zds, size_t zn, BDIGIT *xds, size_t xn, BDIGIT *yds, size_t yn); +static void bary_divmod(BDIGIT *qds, long nq, BDIGIT *rds, long nr, BDIGIT *xds, long nx, BDIGIT *yds, long ny); #define BIGNUM_DEBUG 0 #if BIGNUM_DEBUG @@ -601,7 +603,10 @@ absint_numwords_generic(size_t numbytes, int nlz_bits_in_msbyte, size_t word_num BDIGIT char_bit[1] = { CHAR_BIT }; BDIGIT val_numbits_bary[bdigit_roomof(sizeof(numbytes) + 1)]; BDIGIT nlz_bits_in_msbyte_bary[1] = { nlz_bits_in_msbyte }; - VALUE v; + BDIGIT word_numbits_bary[bdigit_roomof(sizeof(word_numbits))]; + BDIGIT div_bary[numberof(val_numbits_bary) + BIGDIVREM_EXTRA_WORDS]; + BDIGIT mod_bary[numberof(word_numbits_bary)]; + VALUE vd, vm; /* * val_numbits = numbytes * CHAR_BIT - nlz_bits_in_msbyte @@ -615,19 +620,25 @@ absint_numwords_generic(size_t numbytes, int nlz_bits_in_msbyte, size_t word_num bary_mul(BARY_ARGS(val_numbits_bary), BARY_ARGS(numbytes_bary), BARY_ARGS(char_bit)); if (nlz_bits_in_msbyte) bary_sub(BARY_ARGS(val_numbits_bary), BARY_ARGS(val_numbits_bary), BARY_ARGS(nlz_bits_in_msbyte_bary)); + bary_unpack(BARY_ARGS(word_numbits_bary), &word_numbits, 1, sizeof(word_numbits), 0, + INTEGER_PACK_NATIVE_BYTE_ORDER); + bary_divmod(BARY_ARGS(div_bary), BARY_ARGS(mod_bary), BARY_ARGS(val_numbits_bary), BARY_ARGS(word_numbits_bary)); - v = rb_integer_unpack(val_numbits_bary, numberof(val_numbits_bary), sizeof(BDIGIT), 0, + vd = rb_integer_unpack(div_bary, numberof(div_bary), sizeof(BDIGIT), 0, + INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER); + vm = rb_integer_unpack(mod_bary, numberof(mod_bary), sizeof(BDIGIT), 0, INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER); val_numbits = SIZET2NUM(numbytes); val_numbits = rb_funcall(val_numbits, '*', 1, LONG2FIX(CHAR_BIT)); if (nlz_bits_in_msbyte) val_numbits = rb_funcall(val_numbits, '-', 1, LONG2FIX(nlz_bits_in_msbyte)); - assert(rb_equal(val_numbits, v)); word_numbits_v = SIZET2NUM(word_numbits); div_mod = rb_funcall(val_numbits, rb_intern("divmod"), 1, word_numbits_v); div = RARRAY_AREF(div_mod, 0); mod = RARRAY_AREF(div_mod, 1); + assert(rb_equal(div, vd)); + assert(rb_equal(mod, vm)); if (mod == LONG2FIX(0)) { nlz_bits = 0; } @@ -3831,67 +3842,47 @@ rb_big_stop(void *ptr) bds->stop = Qtrue; } -static VALUE -bigdivrem(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp) +static inline int +bigdivrem_num_extra_words(long nx, long ny) +{ + int ret = nx==ny ? 2 : 1; + assert(ret <= BIGDIVREM_EXTRA_WORDS); + return ret; +} + +static BDIGIT +bigdivrem_single(BDIGIT *qds, BDIGIT *xds, long nx, BDIGIT y) +{ + long i; + BDIGIT_DBL t2; + t2 = 0; + i = nx; + while (i--) { + t2 = BIGUP(t2) + xds[i]; + qds[i] = (BDIGIT)(t2 / y); + t2 %= y; + } + return (BDIGIT)t2; +} + +static void +bigdivrem_normal(BDIGIT *zds, long nz, BDIGIT *xds, long nx, BDIGIT *yds, long ny, int needs_mod) { struct big_div_struct bds; - long nx = RBIGNUM_LEN(x), ny = RBIGNUM_LEN(y), nz; - long i, j; - VALUE z, zz; - VALUE tmpy = 0, tmpz = 0; - BDIGIT *xds, *yds, *zds, *tds, *qds; - BDIGIT_DBL t2; - BDIGIT dd, q; - - yds = BDIGITS(y); - while (0 < ny && !yds[ny-1]) ny--; - if (ny == 0) - rb_num_zerodiv(); - - xds = BDIGITS(x); - while (0 < nx && !xds[nx-1]) nx--; - - if (nx < ny || (nx == ny && xds[nx - 1] < yds[ny - 1])) { - if (divp) *divp = rb_int2big(0); - if (modp) *modp = x; - return Qnil; - } - if (ny == 1) { - dd = yds[0]; - z = bignew(nx, RBIGNUM_SIGN(x)==RBIGNUM_SIGN(y)); - zds = BDIGITS(z); - t2 = 0; i = nx; - while (i--) { - t2 = BIGUP(t2) + xds[i]; - zds[i] = (BDIGIT)(t2 / dd); - t2 %= dd; - } - if (modp) { - *modp = rb_uint2big((VALUE)t2); - RBIGNUM_SET_SIGN(*modp, RBIGNUM_SIGN(x)); - } - if (divp) *divp = z; - return Qnil; - } - - nz = nx==ny ? nx+2 : nx+1; - zds = ALLOCV_N(BDIGIT, tmpz, nz); - if (nx==ny) zds[nx+1] = 0; + BDIGIT q; + int shift; q = yds[ny-1]; - dd = nlz(q); - q <<= dd; - if (dd) { - tds = ALLOCV_N(BDIGIT, tmpy, ny); - bdigs_small_lshift(tds, yds, ny, dd); - yds = tds; - zds[nx] = bdigs_small_lshift(zds, xds, nx, dd); + shift = nlz(q); + if (shift) { + bdigs_small_lshift(yds, yds, ny, shift); + zds[nx] = bdigs_small_lshift(zds, xds, nx, shift); } else { + MEMCPY(zds, xds, BDIGIT, nx); zds[nx] = 0; - j = nx; - while (j--) zds[j] = xds[j]; } + if (nx+1 < nz) zds[nx+1] = 0; bds.nx = nx; bds.ny = ny; @@ -3914,20 +3905,136 @@ bigdivrem(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp) bigdivrem1(&bds); } + if (needs_mod && shift) { + bdigs_small_rshift(zds, zds, ny, shift, 0); + } +} + +static void +bary_divmod(BDIGIT *qds, long nq, BDIGIT *rds, long nr, BDIGIT *xds, long nx, BDIGIT *yds, long ny) +{ + assert(nx <= nq); + assert(ny <= nr); + + while (0 < ny && !yds[ny-1]) ny--; + if (ny == 0) + rb_num_zerodiv(); + + while (0 < nx && !xds[nx-1]) nx--; + if (nx == 0) { + MEMZERO(qds, BDIGIT, nq); + MEMZERO(rds, BDIGIT, nr); + return; + } + + if (ny == 1) { + MEMCPY(qds, xds, BDIGIT, nx); + MEMZERO(qds+nx, BDIGIT, nq-nx); + rds[0] = bigdivrem_single(qds, xds, nx, yds[0]); + MEMZERO(rds+1, BDIGIT, nr-1); + } + else { + int extra_words; + long j; + long nz; + BDIGIT *zds; + VALUE tmpz = 0; + BDIGIT *tds; + + extra_words = bigdivrem_num_extra_words(nx, ny); + nz = nx + extra_words; + if (nx + extra_words <= nq) + zds = qds; + else + zds = ALLOCV_N(BDIGIT, tmpz, nx + extra_words); + MEMCPY(zds, xds, BDIGIT, nx); + MEMZERO(zds+nx, BDIGIT, nz-nx); + + if ((yds[ny-1] >> (BITSPERDIG-1)) & 1) { + /* digits_bigdivrem_multi_sub will not modify y. + * So use yds directly. */ + tds = yds; + } + else { + /* digits_bigdivrem_multi_sub will modify y. + * So use rds as a temporary buffer. */ + MEMCPY(rds, yds, BDIGIT, ny); + tds = rds; + } + + bigdivrem_normal(zds, nz, xds, nx, tds, ny, 1); + + /* copy remainder */ + MEMCPY(rds, zds, BDIGIT, ny); + MEMZERO(rds+ny, BDIGIT, nr-ny); + + /* move quotient */ + j = nz - ny; + MEMMOVE(qds, zds+ny, BDIGIT, j); + MEMZERO(qds+j, BDIGIT, nq-j); + + if (tmpz) + ALLOCV_END(tmpz); + } +} + +static VALUE +bigdivrem(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp) +{ + long nx = RBIGNUM_LEN(x), ny = RBIGNUM_LEN(y), nz; + long j; + VALUE z, zz; + VALUE tmpy = 0, tmpz = 0; + BDIGIT *xds, *yds, *zds, *tds; + BDIGIT_DBL t2; + BDIGIT dd; + + yds = BDIGITS(y); + while (0 < ny && !yds[ny-1]) ny--; + if (ny == 0) + rb_num_zerodiv(); + + xds = BDIGITS(x); + while (0 < nx && !xds[nx-1]) nx--; + + if (nx < ny || (nx == ny && xds[nx - 1] < yds[ny - 1])) { + if (divp) *divp = rb_int2big(0); + if (modp) *modp = x; + return Qnil; + } + if (ny == 1) { + dd = yds[0]; + z = bignew(nx, RBIGNUM_SIGN(x)==RBIGNUM_SIGN(y)); + zds = BDIGITS(z); + t2 = bigdivrem_single(zds, xds, nx, dd); + if (modp) { + *modp = rb_uint2big((VALUE)t2); + RBIGNUM_SET_SIGN(*modp, RBIGNUM_SIGN(x)); + } + if (divp) *divp = z; + return Qnil; + } + + if (((yds[ny-1] >> (BITSPERDIG-1)) & 1) == 0) { + /* Make yds modifiable. */ + tds = ALLOCV_N(BDIGIT, tmpy, ny); + MEMCPY(tds, yds, BDIGIT, ny); + yds = tds; + } + + nz = nx + bigdivrem_num_extra_words(nx, ny); + zds = ALLOCV_N(BDIGIT, tmpz, nz); + bigdivrem_normal(zds, nz, xds, nx, yds, ny, modp != NULL); + if (divp) { /* move quotient down in z */ j = nz - ny; while (0 < j && !zds[j-1+ny]) j--; *divp = zz = bignew(j, RBIGNUM_SIGN(x)==RBIGNUM_SIGN(y)); - qds = BDIGITS(zz); - for (i = 0;i < j;i++) qds[i] = zds[i+ny]; + MEMCPY(BDIGITS(zz), zds+ny, BDIGIT, j); } if (modp) { /* normalize remainder */ - while (ny > 1 && !zds[ny-1]) --ny; - if (dd) { - bdigs_small_rshift(zds, zds, ny, dd, 0); - } - if (!zds[ny-1]) ny--; + while (ny > 0 && !zds[ny-1]) --ny; *modp = zz = bignew(ny, RBIGNUM_SIGN(x)); MEMCPY(BDIGITS(zz), zds, BDIGIT, ny); }