diff options
| author | Paul Eggert | 2019-11-13 12:10:31 -0800 |
|---|---|---|
| committer | Paul Eggert | 2019-11-13 13:10:09 -0800 |
| commit | 02e637ecca3b1419d2a6c433eca72c5728c65051 (patch) | |
| tree | f3a213c730c7893513a69b5f9e2e69a429f837da /src | |
| parent | ff10e9517d98aa606e007d3aa4d5aef1c423ab77 (diff) | |
| download | emacs-02e637ecca3b1419d2a6c433eca72c5728c65051.tar.gz emacs-02e637ecca3b1419d2a6c433eca72c5728c65051.zip | |
Refactor bignum multiplication, exponentiation
This doesn’t alter behavior, and simplifies the next commit.
* src/bignum.c (GMP_NLIMBS_MAX, NLIMBS_LIMIT, emacs_mpz_size)
(emacs_mpz_mul, emacs_mpz_mul_2exp, emacs_mpz_pow_ui): Move here ...
* src/data.c: ... from here.
Diffstat (limited to 'src')
| -rw-r--r-- | src/bignum.c | 78 | ||||
| -rw-r--r-- | src/bignum.h | 6 | ||||
| -rw-r--r-- | src/data.c | 74 |
3 files changed, 84 insertions, 74 deletions
diff --git a/src/bignum.c b/src/bignum.c index 167b73eee02..c31cf3d59a4 100644 --- a/src/bignum.c +++ b/src/bignum.c | |||
| @@ -273,6 +273,84 @@ bignum_to_uintmax (Lisp_Object x) | |||
| 273 | return mpz_to_uintmax (*xbignum_val (x), &i) ? i : 0; | 273 | return mpz_to_uintmax (*xbignum_val (x), &i) ? i : 0; |
| 274 | } | 274 | } |
| 275 | 275 | ||
| 276 | |||
| 277 | /* Multiply and exponentiate mpz_t values without aborting due to size | ||
| 278 | limits. */ | ||
| 279 | |||
| 280 | /* GMP tests for this value and aborts (!) if it is exceeded. | ||
| 281 | This is as of GMP 6.1.2 (2016); perhaps future versions will differ. */ | ||
| 282 | enum { GMP_NLIMBS_MAX = min (INT_MAX, ULONG_MAX / GMP_NUMB_BITS) }; | ||
| 283 | |||
| 284 | /* An upper bound on limb counts, needed to prevent libgmp and/or | ||
| 285 | Emacs from aborting or otherwise misbehaving. This bound applies | ||
| 286 | to estimates of mpz_t sizes before the mpz_t objects are created, | ||
| 287 | as opposed to integer-width which operates on mpz_t values after | ||
| 288 | creation and before conversion to Lisp bignums. */ | ||
| 289 | enum | ||
| 290 | { | ||
| 291 | NLIMBS_LIMIT = min (min (/* libgmp needs to store limb counts. */ | ||
| 292 | GMP_NLIMBS_MAX, | ||
| 293 | |||
| 294 | /* Size calculations need to work. */ | ||
| 295 | min (PTRDIFF_MAX, SIZE_MAX) / sizeof (mp_limb_t)), | ||
| 296 | |||
| 297 | /* Emacs puts bit counts into fixnums. */ | ||
| 298 | MOST_POSITIVE_FIXNUM / GMP_NUMB_BITS) | ||
| 299 | }; | ||
| 300 | |||
| 301 | /* Like mpz_size, but tell the compiler the result is a nonnegative int. */ | ||
| 302 | |||
| 303 | static int | ||
| 304 | emacs_mpz_size (mpz_t const op) | ||
| 305 | { | ||
| 306 | mp_size_t size = mpz_size (op); | ||
| 307 | eassume (0 <= size && size <= INT_MAX); | ||
| 308 | return size; | ||
| 309 | } | ||
| 310 | |||
| 311 | /* Wrappers to work around GMP limitations. As of GMP 6.1.2 (2016), | ||
| 312 | the library code aborts when a number is too large. These wrappers | ||
| 313 | avoid the problem for functions that can return numbers much larger | ||
| 314 | than their arguments. For slowly-growing numbers, the integer | ||
| 315 | width checks in bignum.c should suffice. */ | ||
| 316 | |||
| 317 | void | ||
| 318 | emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2) | ||
| 319 | { | ||
| 320 | if (NLIMBS_LIMIT - emacs_mpz_size (op1) < emacs_mpz_size (op2)) | ||
| 321 | overflow_error (); | ||
| 322 | mpz_mul (rop, op1, op2); | ||
| 323 | } | ||
| 324 | |||
| 325 | void | ||
| 326 | emacs_mpz_mul_2exp (mpz_t rop, mpz_t const op1, EMACS_INT op2) | ||
| 327 | { | ||
| 328 | /* Fudge factor derived from GMP 6.1.2, to avoid an abort in | ||
| 329 | mpz_mul_2exp (look for the '+ 1' in its source code). */ | ||
| 330 | enum { mul_2exp_extra_limbs = 1 }; | ||
| 331 | enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - mul_2exp_extra_limbs) }; | ||
| 332 | |||
| 333 | EMACS_INT op2limbs = op2 / GMP_NUMB_BITS; | ||
| 334 | if (lim - emacs_mpz_size (op1) < op2limbs) | ||
| 335 | overflow_error (); | ||
| 336 | mpz_mul_2exp (rop, op1, op2); | ||
| 337 | } | ||
| 338 | |||
| 339 | void | ||
| 340 | emacs_mpz_pow_ui (mpz_t rop, mpz_t const base, unsigned long exp) | ||
| 341 | { | ||
| 342 | /* This fudge factor is derived from GMP 6.1.2, to avoid an abort in | ||
| 343 | mpz_n_pow_ui (look for the '5' in its source code). */ | ||
| 344 | enum { pow_ui_extra_limbs = 5 }; | ||
| 345 | enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - pow_ui_extra_limbs) }; | ||
| 346 | |||
| 347 | int nbase = emacs_mpz_size (base), n; | ||
| 348 | if (INT_MULTIPLY_WRAPV (nbase, exp, &n) || lim < n) | ||
| 349 | overflow_error (); | ||
| 350 | mpz_pow_ui (rop, base, exp); | ||
| 351 | } | ||
| 352 | |||
| 353 | |||
| 276 | /* Yield an upper bound on the buffer size needed to contain a C | 354 | /* Yield an upper bound on the buffer size needed to contain a C |
| 277 | string representing the NUM in base BASE. This includes any | 355 | string representing the NUM in base BASE. This includes any |
| 278 | preceding '-' and the terminating NUL. */ | 356 | preceding '-' and the terminating NUL. */ |
diff --git a/src/bignum.h b/src/bignum.h index bf7b3669537..432fcbc99e5 100644 --- a/src/bignum.h +++ b/src/bignum.h | |||
| @@ -49,6 +49,12 @@ extern bool mpz_to_intmax (mpz_t const, intmax_t *) ARG_NONNULL ((1, 2)); | |||
| 49 | extern bool mpz_to_uintmax (mpz_t const, uintmax_t *) ARG_NONNULL ((1, 2)); | 49 | extern bool mpz_to_uintmax (mpz_t const, uintmax_t *) ARG_NONNULL ((1, 2)); |
| 50 | extern void mpz_set_intmax_slow (mpz_t, intmax_t) ARG_NONNULL ((1)); | 50 | extern void mpz_set_intmax_slow (mpz_t, intmax_t) ARG_NONNULL ((1)); |
| 51 | extern void mpz_set_uintmax_slow (mpz_t, uintmax_t) ARG_NONNULL ((1)); | 51 | extern void mpz_set_uintmax_slow (mpz_t, uintmax_t) ARG_NONNULL ((1)); |
| 52 | extern void emacs_mpz_mul (mpz_t, mpz_t const, mpz_t const) | ||
| 53 | ARG_NONNULL ((1, 2, 3)); | ||
| 54 | extern void emacs_mpz_mul_2exp (mpz_t, mpz_t const, EMACS_INT) | ||
| 55 | ARG_NONNULL ((1, 2)); | ||
| 56 | extern void emacs_mpz_pow_ui (mpz_t, mpz_t const, unsigned long) | ||
| 57 | ARG_NONNULL ((1, 2)); | ||
| 52 | extern double mpz_get_d_rounded (mpz_t const); | 58 | extern double mpz_get_d_rounded (mpz_t const); |
| 53 | 59 | ||
| 54 | INLINE_HEADER_BEGIN | 60 | INLINE_HEADER_BEGIN |
diff --git a/src/data.c b/src/data.c index 649dc174f90..9efcd72f93e 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -2352,80 +2352,6 @@ bool-vector. IDX starts at 0. */) | |||
| 2352 | return newelt; | 2352 | return newelt; |
| 2353 | } | 2353 | } |
| 2354 | 2354 | ||
| 2355 | /* GMP tests for this value and aborts (!) if it is exceeded. | ||
| 2356 | This is as of GMP 6.1.2 (2016); perhaps future versions will differ. */ | ||
| 2357 | enum { GMP_NLIMBS_MAX = min (INT_MAX, ULONG_MAX / GMP_NUMB_BITS) }; | ||
| 2358 | |||
| 2359 | /* An upper bound on limb counts, needed to prevent libgmp and/or | ||
| 2360 | Emacs from aborting or otherwise misbehaving. This bound applies | ||
| 2361 | to estimates of mpz_t sizes before the mpz_t objects are created, | ||
| 2362 | as opposed to integer-width which operates on mpz_t values after | ||
| 2363 | creation and before conversion to Lisp bignums. */ | ||
| 2364 | enum | ||
| 2365 | { | ||
| 2366 | NLIMBS_LIMIT = min (min (/* libgmp needs to store limb counts. */ | ||
| 2367 | GMP_NLIMBS_MAX, | ||
| 2368 | |||
| 2369 | /* Size calculations need to work. */ | ||
| 2370 | min (PTRDIFF_MAX, SIZE_MAX) / sizeof (mp_limb_t)), | ||
| 2371 | |||
| 2372 | /* Emacs puts bit counts into fixnums. */ | ||
| 2373 | MOST_POSITIVE_FIXNUM / GMP_NUMB_BITS) | ||
| 2374 | }; | ||
| 2375 | |||
| 2376 | /* Like mpz_size, but tell the compiler the result is a nonnegative int. */ | ||
| 2377 | |||
| 2378 | static int | ||
| 2379 | emacs_mpz_size (mpz_t const op) | ||
| 2380 | { | ||
| 2381 | mp_size_t size = mpz_size (op); | ||
| 2382 | eassume (0 <= size && size <= INT_MAX); | ||
| 2383 | return size; | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | /* Wrappers to work around GMP limitations. As of GMP 6.1.2 (2016), | ||
| 2387 | the library code aborts when a number is too large. These wrappers | ||
| 2388 | avoid the problem for functions that can return numbers much larger | ||
| 2389 | than their arguments. For slowly-growing numbers, the integer | ||
| 2390 | width checks in bignum.c should suffice. */ | ||
| 2391 | |||
| 2392 | static void | ||
| 2393 | emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2) | ||
| 2394 | { | ||
| 2395 | if (NLIMBS_LIMIT - emacs_mpz_size (op1) < emacs_mpz_size (op2)) | ||
| 2396 | overflow_error (); | ||
| 2397 | mpz_mul (rop, op1, op2); | ||
| 2398 | } | ||
| 2399 | |||
| 2400 | static void | ||
| 2401 | emacs_mpz_mul_2exp (mpz_t rop, mpz_t const op1, EMACS_INT op2) | ||
| 2402 | { | ||
| 2403 | /* Fudge factor derived from GMP 6.1.2, to avoid an abort in | ||
| 2404 | mpz_mul_2exp (look for the '+ 1' in its source code). */ | ||
| 2405 | enum { mul_2exp_extra_limbs = 1 }; | ||
| 2406 | enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - mul_2exp_extra_limbs) }; | ||
| 2407 | |||
| 2408 | EMACS_INT op2limbs = op2 / GMP_NUMB_BITS; | ||
| 2409 | if (lim - emacs_mpz_size (op1) < op2limbs) | ||
| 2410 | overflow_error (); | ||
| 2411 | mpz_mul_2exp (rop, op1, op2); | ||
| 2412 | } | ||
| 2413 | |||
| 2414 | static void | ||
| 2415 | emacs_mpz_pow_ui (mpz_t rop, mpz_t const base, unsigned long exp) | ||
| 2416 | { | ||
| 2417 | /* This fudge factor is derived from GMP 6.1.2, to avoid an abort in | ||
| 2418 | mpz_n_pow_ui (look for the '5' in its source code). */ | ||
| 2419 | enum { pow_ui_extra_limbs = 5 }; | ||
| 2420 | enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - pow_ui_extra_limbs) }; | ||
| 2421 | |||
| 2422 | int nbase = emacs_mpz_size (base), n; | ||
| 2423 | if (INT_MULTIPLY_WRAPV (nbase, exp, &n) || lim < n) | ||
| 2424 | overflow_error (); | ||
| 2425 | mpz_pow_ui (rop, base, exp); | ||
| 2426 | } | ||
| 2427 | |||
| 2428 | |||
| 2429 | /* Arithmetic functions */ | 2355 | /* Arithmetic functions */ |
| 2430 | 2356 | ||
| 2431 | Lisp_Object | 2357 | Lisp_Object |