aboutsummaryrefslogtreecommitdiffstats
path: root/test/data
diff options
context:
space:
mode:
authorPhilipp Stephani2019-11-02 10:54:57 +0100
committerPhilipp Stephani2019-12-04 21:17:10 +0100
commit096be9c4541329af259273fe604dc4f8669fbd8a (patch)
tree9a93e99ec78c598bfa42b73c30e7ff349a3bb489 /test/data
parent0ca32d1270bd5d494e365f3525fa65ac423f6658 (diff)
downloademacs-096be9c4541329af259273fe604dc4f8669fbd8a.tar.gz
emacs-096be9c4541329af259273fe604dc4f8669fbd8a.zip
Change module interface to no longer use GMP objects directly.
As described in the new comment added to emacs-module.c, using GMP directly in the module interface has significant downsides: it couples the module interface directly to the implementation and requires module authors to link their module against the same GMP library as Emacs itself, which is often difficult and an unnecessary burden. By picking a representation for the magnitude that often matches the one used by GMP, we can avoid overhead when converting from and to GMP in most cases. Loading the test module in test/data/emacs-module and evaluating (dotimes (_ 10000) (mod-test-double (* 2 most-negative-fixnum))) under Callgrind shows that on my (GNU/Linux) machine Emacs only spends 10% of the CPU time of mod-test-double in mpz_import and mpz_export combined, even though that function does little else. (By contrast, 30% is spent in allocate_pseudovector.) * src/emacs-module.h.in: Don't check EMACS_MODULE_GMP. Don't include gmp.h. Remove emacs_mpz structure. Instead, define type alias emacs_limb_t and macro EMACS_LIMB_MAX. * src/module-env-27.h: Change interface of extract_big_integer and make_big_integer to take a sign-magnitude representation instead of mpz_t. * src/emacs-module.c: Don't check EMACS_MODULE_GMP or EMACS_MODULE_HAVE_MPZ_T. Add a comment about the chosen implementation. (module_extract_big_integer, module_make_big_integer): Reimplement without using mpz_t in the interface. * doc/lispref/internals.texi (Module Values): Adapt function documentation and example. Stop mentioning GMP and EMACS_MODULE_GMP. * test/data/emacs-module/mod-test.c: Don't define EMACS_MODULE_GMP or EMACS_MODULE_HAVE_MPZ_T. (memory_full, extract_big_integer, make_big_integer): New helper functions, identical to example in the Info documentation. (Fmod_test_nanoseconds, Fmod_test_double): Adapt to new interface.
Diffstat (limited to 'test/data')
-rw-r--r--test/data/emacs-module/mod-test.c111
1 files changed, 97 insertions, 14 deletions
diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c
index 2891b73c1a0..b579c8a6278 100644
--- a/test/data/emacs-module/mod-test.c
+++ b/test/data/emacs-module/mod-test.c
@@ -33,10 +33,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
33#include <gmp.h> 33#include <gmp.h>
34#else 34#else
35#include "mini-gmp.h" 35#include "mini-gmp.h"
36#define EMACS_MODULE_HAVE_MPZ_T
37#endif 36#endif
38 37
39#define EMACS_MODULE_GMP
40#include <emacs-module.h> 38#include <emacs-module.h>
41 39
42#include "timespec.h" 40#include "timespec.h"
@@ -66,6 +64,8 @@ int plugin_is_GPL_compatible;
66# error "INTPTR_MAX too large" 64# error "INTPTR_MAX too large"
67#endif 65#endif
68 66
67/* Smoke test to verify that EMACS_LIMB_MAX is defined. */
68_Static_assert (0 < EMACS_LIMB_MAX, "EMACS_LIMB_MAX missing or incorrect");
69 69
70/* Always return symbol 't'. */ 70/* Always return symbol 't'. */
71static emacs_value 71static emacs_value
@@ -372,23 +372,106 @@ Fmod_test_add_nanosecond (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
372 return env->make_time (env, time); 372 return env->make_time (env, time);
373} 373}
374 374
375static void
376memory_full (emacs_env *env)
377{
378 const char *message = "Memory exhausted";
379 emacs_value data = env->make_string (env, message, strlen (message));
380 env->non_local_exit_signal (env, env->intern (env, "error"),
381 env->funcall (env, env->intern (env, "list"), 1,
382 &data));
383}
384
385enum
386{
387 max_count = ((SIZE_MAX < PTRDIFF_MAX ? SIZE_MAX : PTRDIFF_MAX)
388 / sizeof (emacs_limb_t))
389};
390
391static bool
392extract_big_integer (emacs_env *env, emacs_value arg, mpz_t result)
393{
394 int sign;
395 ptrdiff_t count;
396 bool success = env->extract_big_integer (env, arg, &sign, &count, NULL);
397 if (!success)
398 return false;
399 if (sign == 0)
400 {
401 mpz_set_ui (result, 0);
402 return true;
403 }
404 enum { order = -1, size = sizeof (unsigned long), endian = 0, nails = 0 };
405 assert (0 < count && count <= max_count);
406 emacs_limb_t *magnitude = malloc (count * size);
407 if (magnitude == NULL)
408 {
409 memory_full (env);
410 return false;
411 }
412 success = env->extract_big_integer (env, arg, NULL, &count, magnitude);
413 assert (success);
414 mpz_import (result, count, order, size, endian, nails, magnitude);
415 free (magnitude);
416 if (sign < 0)
417 mpz_neg (result, result);
418 return true;
419}
420
421static emacs_value
422make_big_integer (emacs_env *env, const mpz_t value)
423{
424 if (mpz_sgn (value) == 0)
425 return env->make_integer (env, 0);
426 /* See
427 https://gmplib.org/manual/Integer-Import-and-Export.html#index-Export. */
428 enum
429 {
430 order = -1,
431 size = sizeof (emacs_limb_t),
432 endian = 0,
433 nails = 0,
434 numb = 8 * size - nails
435 };
436 size_t count = (mpz_sizeinbase (value, 2) + numb - 1) / numb;
437 if (max_count < count)
438 {
439 memory_full (env);
440 return NULL;
441 }
442 emacs_limb_t *magnitude = malloc (count * size);
443 if (magnitude == NULL)
444 {
445 memory_full (env);
446 return NULL;
447 }
448 size_t written;
449 mpz_export (magnitude, &written, order, size, endian, nails, value);
450 assert (written == count);
451 assert (count <= PTRDIFF_MAX);
452 emacs_value result = env->make_big_integer (env, mpz_sgn (value),
453 (ptrdiff_t) count, magnitude);
454 free (magnitude);
455 return result;
456}
457
375static emacs_value 458static emacs_value
376Fmod_test_nanoseconds (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data) { 459Fmod_test_nanoseconds (emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data) {
377 assert (nargs == 1); 460 assert (nargs == 1);
378 struct timespec time = env->extract_time (env, args[0]); 461 struct timespec time = env->extract_time (env, args[0]);
379 struct emacs_mpz nanoseconds; 462 mpz_t nanoseconds;
380 assert (LONG_MIN <= time.tv_sec && time.tv_sec <= LONG_MAX); 463 assert (LONG_MIN <= time.tv_sec && time.tv_sec <= LONG_MAX);
381 mpz_init_set_si (nanoseconds.value, time.tv_sec); 464 mpz_init_set_si (nanoseconds, time.tv_sec);
382#ifdef __MINGW32__ 465#ifdef __MINGW32__
383 _Static_assert (1000000000 <= ULONG_MAX, "unsupported architecture"); 466 _Static_assert (1000000000 <= ULONG_MAX, "unsupported architecture");
384#else 467#else
385 static_assert (1000000000 <= ULONG_MAX, "unsupported architecture"); 468 static_assert (1000000000 <= ULONG_MAX, "unsupported architecture");
386#endif 469#endif
387 mpz_mul_ui (nanoseconds.value, nanoseconds.value, 1000000000); 470 mpz_mul_ui (nanoseconds, nanoseconds, 1000000000);
388 assert (0 <= time.tv_nsec && time.tv_nsec <= ULONG_MAX); 471 assert (0 <= time.tv_nsec && time.tv_nsec <= ULONG_MAX);
389 mpz_add_ui (nanoseconds.value, nanoseconds.value, time.tv_nsec); 472 mpz_add_ui (nanoseconds, nanoseconds, time.tv_nsec);
390 emacs_value result = env->make_big_integer (env, &nanoseconds); 473 emacs_value result = make_big_integer (env, nanoseconds);
391 mpz_clear (nanoseconds.value); 474 mpz_clear (nanoseconds);
392 return result; 475 return result;
393} 476}
394 477
@@ -398,12 +481,12 @@ Fmod_test_double (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
398{ 481{
399 assert (nargs == 1); 482 assert (nargs == 1);
400 emacs_value arg = args[0]; 483 emacs_value arg = args[0];
401 struct emacs_mpz value; 484 mpz_t value;
402 mpz_init (value.value); 485 mpz_init (value);
403 env->extract_big_integer (env, arg, &value); 486 extract_big_integer (env, arg, value);
404 mpz_mul_ui (value.value, value.value, 2); 487 mpz_mul_ui (value, value, 2);
405 emacs_value result = env->make_big_integer (env, &value); 488 emacs_value result = make_big_integer (env, value);
406 mpz_clear (value.value); 489 mpz_clear (value);
407 return result; 490 return result;
408} 491}
409 492