/* Run some tests on various mpn routines.
THIS IS A TEST PROGRAM USED ONLY FOR DEVELOPMENT. IT'S ALMOST CERTAIN TO
BE SUBJECT TO INCOMPATIBLE CHANGES IN FUTURE VERSIONS OF GMP.
Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of the GNU MP Library.
The GNU MP Library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or (at your
option) any later version.
The GNU MP Library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the GNU MP Library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA. */
/* Usage: try [options] <function>...
For example, "./try mpn_add_n" to run tests of that function.
Combinations of alignments and overlaps are tested, with redzones above
or below the destinations, and with the sources write-protected.
The number of tests performed becomes ridiculously large with all the
combinations, and for that reason this can't be a part of a "make check",
it's meant only for development. The code isn't very pretty either.
During development it can help to disable the redzones, since seeing the
rest of the destination written can show where the wrong part is, or if
the dst pointers are off by 1 or whatever. The magic DEADVAL initial
fill (see below) will show locations never written.
The -s option can be used to test only certain size operands, which is
useful if some new code doesn't yet support say sizes less than the
unrolling, or whatever.
When a problem occurs it'll of course be necessary to run the program
under gdb to find out quite where, how and why it's going wrong. Disable
the spinner with the -W option when doing this, or single stepping won't
work. Using the "-1" option to run with simple data can be useful.
New functions to test can be added in try_array[]. If a new TYPE is
required then add it to the existing constants, set up its parameters in
param_init(), and add it to the call() function. Extra parameter fields
can be added if necessary, or further interpretations given to existing
fields.
Portability:
This program is not designed for use on Cray vector systems under Unicos,
it will fail to compile due to missing _SC_PAGE_SIZE. Those systems
don't really have pages or mprotect. We could arrange to run the tests
without the redzones, but we haven't bothered currently.
Enhancements:
umul_ppmm support is not very good, lots of source data is generated
whereas only two limbs are needed.
Make a little scheme for interpreting the "SIZE" selections uniformly.
Make tr->size==SIZE_2 work, for the benefit of find_a which wants just 2
source limbs. Possibly increase the default repetitions in that case.
Automatically detect gdb and disable the spinner (use -W for now).
Make a way to re-run a failing case in the debugger. Have an option to
snapshot each test case before it's run so the data is available if a
segv occurs. (This should be more reliable than the current print_all()
in the signal handler.)
When alignment means a dst isn't hard against the redzone, check the
space in between remains unchanged.
When a source overlaps a destination, don't run both s[i].high 0 and 1,
as s[i].high has no effect. Maybe encode s[i].high into overlap->s[i].
When partial overlaps aren't done, don't loop over source alignments
during overlaps.
Try to make the looping code a bit less horrible. Right now it's pretty
hard to see what iterations are actually done.
Perhaps specific setups and loops for each style of function under test
would be clearer than a parameterized general loop. There's lots of
stuff common to all functions, but the exceptions get messy.
When there's no overlap, run with both src>dst and src<dst. A subtle
calling-conventions violation occured in a P6 copy which depended on the
relative location of src and dst.
multiplier_N is more or less a third source region for the addmul_N
routines, and could be done with the redzoned region scheme.
*/
/* always do assertion checking */
#define WANT_ASSERT 1
#include "config.h"
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include "gmp.h"
#include "gmp-impl.h"
#include "longlong.h"
#include "tests.h"
#if !HAVE_DECL_OPTARG
extern char *optarg;
extern int optind, opterr;
#endif
#if ! HAVE_DECL_SYS_NERR
extern int sys_nerr;
#endif
#if ! HAVE_DECL_SYS_ERRLIST
extern char *sys_errlist[];
#endif
#if ! HAVE_STRERROR
char *
strerror (int n)
{
if (n < 0 || n >= sys_nerr)
return "errno out of range";
else
return sys_errlist[n];
}
#endif
/* Rumour has it some systems lack a define of PROT_NONE. */
#ifndef PROT_NONE
#define PROT_NONE 0
#endif
/* Dummy defines for when mprotect doesn't exist. */
#ifndef PROT_READ
#define PROT_READ 0
#endif
#ifndef PROT_WRITE
#define PROT_WRITE 0
#endif
/* _SC_PAGESIZE is standard, but hpux 9 and possibly other systems have
_SC_PAGE_SIZE instead. */
#if defined (_SC_PAGE_SIZE) && ! defined (_SC_PAGESIZE)
#define _SC_PAGESIZE _SC_PAGE_SIZE
#endif
#ifdef EXTRA_PROTOS
EXTRA_PROTOS
#endif
#ifdef EXTRA_PROTOS2
EXTRA_PROTOS2
#endif
#define DEFAULT_REPETITIONS 10
int option_repetitions = DEFAULT_REPETITIONS;
int option_spinner = 1;
int option_redzones = 1;
int option_firstsize = 0;
int option_lastsize = 500;
int option_firstsize2 = 0;
#define ALIGNMENTS 4
#define OVERLAPS 4
#define CARRY_RANDOMS 5
#define MULTIPLIER_RANDOMS 5
#define DIVISOR_RANDOMS 5
#define FRACTION_COUNT 4
int option_print = 0;
#define DATA_TRAND 0
#define DATA_ZEROS 1
#define DATA_SEQ 2
#define DATA_FFS 3
#define DATA_2FD 4
int option_data = DATA_TRAND;
mp_size_t pagesize;
#define PAGESIZE_LIMBS (pagesize / BYTES_PER_MP_LIMB)
/* must be a multiple of the page size */
#define REDZONE_BYTES (pagesize * 16)
#define REDZONE_LIMBS (REDZONE_BYTES / BYTES_PER_MP_LIMB)
#define MAX3(x,y,z) (MAX (x, MAX (y, z)))
#if BITS_PER_MP_LIMB == 32
#define DEADVAL CNST_LIMB(0xDEADBEEF)
#else
#define DEADVAL CNST_LIMB(0xDEADBEEFBADDCAFE)
#endif
struct region_t {
mp_ptr ptr;
mp_size_t size;
};
#define TRAP_NOWHERE 0
#define TRAP_REF 1
#define TRAP_FUN 2
#define TRAP_SETUPS 3
int trap_location = TRAP_NOWHERE;
#define NUM_SOURCES 2
#define NUM_DESTS 2
struct source_t {
struct region_t region;
int high;
mp_size_t align;
mp_ptr p;
};
struct source_t s[NUM_SOURCES];
struct dest_t {
int high;
mp_size_t align;
mp_size_t size;
};
struct dest_t d[NUM_DESTS];
struct source_each_t {
mp_ptr p;
};
struct dest_each_t {
struct region_t region;
mp_ptr p;
};
mp_size_t size;
mp_size_t size2;
unsigned long shift;
mp_limb_t carry;
mp_limb_t divisor;
mp_limb_t multiplier;
mp_limb_t multiplier_N[8];
struct each_t {
const char *name;
struct dest_each_t d[NUM_DESTS];
struct source_each_t s[NUM_SOURCES];
mp_limb_t retval;
};
struct each_t ref = { "Ref" };
struct each_t fun = { "Fun" };
#define SRC_SIZE(n) ((n) == 1 && tr->size2 ? size2 : size)
void validate_fail _PROTO ((void));
#if HAVE_TRY_NEW_C
#include "try-new.c"
#endif
typedef mp_limb_t (*tryfun_t)