mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-24 12:35:55 +08:00
461 lines
15 KiB
C
461 lines
15 KiB
C
|
/* Self tests of the gmp-utils API.
|
||
|
|
||
|
Copyright (C) 2019-2020 Free Software Foundation, Inc.
|
||
|
|
||
|
This file is part of GDB.
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program 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 General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
|
||
|
#include "gmp-utils.h"
|
||
|
#include "gdbsupport/selftest.h"
|
||
|
|
||
|
#include <math.h>
|
||
|
|
||
|
namespace selftests {
|
||
|
|
||
|
/* Perform a series of general tests of gdb_mpz's as_integer method.
|
||
|
|
||
|
This function tries to be reasonably exhaustive, by testing the edges,
|
||
|
as well as a resonable set of values including negative ones, zero,
|
||
|
and positive values. */
|
||
|
|
||
|
static void
|
||
|
gdb_mpz_as_integer ()
|
||
|
{
|
||
|
/* Test a range of values, both as LONGEST and ULONGEST. */
|
||
|
gdb_mpz v;
|
||
|
LONGEST l_expected;
|
||
|
ULONGEST ul_expected;
|
||
|
|
||
|
/* Start with the smallest LONGEST */
|
||
|
l_expected = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1);
|
||
|
|
||
|
mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1);
|
||
|
mpz_neg (v.val, v.val);
|
||
|
|
||
|
SELF_CHECK (v.as_integer<LONGEST> () == l_expected);
|
||
|
|
||
|
/* Try with a small range of integers including negative, zero,
|
||
|
and positive values. */
|
||
|
for (int i = -256; i <= 256; i++)
|
||
|
{
|
||
|
l_expected = (LONGEST) i;
|
||
|
mpz_set_si (v.val, i);
|
||
|
SELF_CHECK (v.as_integer<LONGEST> () == l_expected);
|
||
|
|
||
|
if (i >= 0)
|
||
|
{
|
||
|
ul_expected = (ULONGEST) i;
|
||
|
mpz_set_ui (v.val, i);
|
||
|
SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Try with LONGEST_MAX. */
|
||
|
l_expected = LONGEST_MAX;
|
||
|
ul_expected = (ULONGEST) l_expected;
|
||
|
|
||
|
mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1);
|
||
|
mpz_sub_ui (v.val, v.val, 1);
|
||
|
|
||
|
SELF_CHECK (v.as_integer<LONGEST> () == l_expected);
|
||
|
SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected);
|
||
|
|
||
|
/* Try with ULONGEST_MAX. */
|
||
|
ul_expected = ULONGEST_MAX;
|
||
|
mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8);
|
||
|
mpz_sub_ui (v.val, v.val, 1);
|
||
|
|
||
|
SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected);
|
||
|
}
|
||
|
|
||
|
/* A helper function to store the given integer value into a buffer,
|
||
|
before reading it back into a gdb_mpz. Sets ACTUAL to the value
|
||
|
read back, while at the same time setting EXPECTED as the value
|
||
|
we would expect to be read back.
|
||
|
|
||
|
Note that this function does not perform the comparison between
|
||
|
EXPECTED and ACTUAL. The caller will do it inside a SELF_CHECK
|
||
|
call, allowing the line information shown when the test fails
|
||
|
to provide a bit more information about the kind of values
|
||
|
that were used when the check failed. This makes the writing
|
||
|
of the tests a little more verbose, but the debugging in case
|
||
|
of problems should hopefuly be easier. */
|
||
|
|
||
|
template<typename T>
|
||
|
void
|
||
|
store_and_read_back (T val, int buf_len, enum bfd_endian byte_order,
|
||
|
gdb_mpz &expected, gdb_mpz &actual)
|
||
|
{
|
||
|
gdb_byte *buf;
|
||
|
|
||
|
expected = val;
|
||
|
|
||
|
buf = (gdb_byte *) alloca (buf_len);
|
||
|
store_integer (buf, buf_len, byte_order, val);
|
||
|
|
||
|
/* Pre-initialize ACTUAL to something that's not the expected value. */
|
||
|
mpz_set (actual.val, expected.val);
|
||
|
mpz_sub_ui (actual.val, actual.val, 500);
|
||
|
|
||
|
actual.read (buf, buf_len, byte_order, !std::is_signed<T>::value);
|
||
|
}
|
||
|
|
||
|
/* Test the gdb_mpz::read method over a reasonable range of values.
|
||
|
|
||
|
The testing is done by picking an arbitrary buffer length, after
|
||
|
which we test every possible value that this buffer allows, both
|
||
|
with signed numbers as well as unsigned ones. */
|
||
|
|
||
|
static void
|
||
|
gdb_mpz_read_all_from_small ()
|
||
|
{
|
||
|
/* Start with a type whose size is small enough that we can afford
|
||
|
to check the complete range. */
|
||
|
|
||
|
int buf_len = 1;
|
||
|
LONGEST l_min = -pow (2, buf_len * 8 - 1);
|
||
|
LONGEST l_max = pow (2, buf_len * 8 - 1) - 1;
|
||
|
|
||
|
for (LONGEST l = l_min; l <= l_max; l++)
|
||
|
{
|
||
|
gdb_mpz expected, actual;
|
||
|
|
||
|
store_and_read_back (l, buf_len, BFD_ENDIAN_BIG, expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
store_and_read_back (l, buf_len, BFD_ENDIAN_LITTLE, expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
}
|
||
|
|
||
|
/* Do the same as above, but with an unsigned type. */
|
||
|
ULONGEST ul_min = 0;
|
||
|
ULONGEST ul_max = pow (2, buf_len * 8) - 1;
|
||
|
|
||
|
for (ULONGEST ul = ul_min; ul <= ul_max; ul++)
|
||
|
{
|
||
|
gdb_mpz expected, actual;
|
||
|
|
||
|
store_and_read_back (ul, buf_len, BFD_ENDIAN_BIG, expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
store_and_read_back (ul, buf_len, BFD_ENDIAN_LITTLE, expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Test the gdb_mpz::read the extremes of LONGEST and ULONGEST. */
|
||
|
|
||
|
static void
|
||
|
gdb_mpz_read_min_max ()
|
||
|
{
|
||
|
gdb_mpz expected, actual;
|
||
|
|
||
|
/* Start with the smallest LONGEST. */
|
||
|
|
||
|
LONGEST l_min = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1);
|
||
|
|
||
|
store_and_read_back (l_min, sizeof (LONGEST), BFD_ENDIAN_BIG,
|
||
|
expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
store_and_read_back (l_min, sizeof (LONGEST), BFD_ENDIAN_LITTLE,
|
||
|
expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
/* Same with LONGEST_MAX. */
|
||
|
|
||
|
LONGEST l_max = LONGEST_MAX;
|
||
|
|
||
|
store_and_read_back (l_max, sizeof (LONGEST), BFD_ENDIAN_BIG,
|
||
|
expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
store_and_read_back (l_max, sizeof (LONGEST), BFD_ENDIAN_LITTLE,
|
||
|
expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
/* Same with the smallest ULONGEST. */
|
||
|
|
||
|
ULONGEST ul_min = 0;
|
||
|
|
||
|
store_and_read_back (ul_min, sizeof (ULONGEST), BFD_ENDIAN_BIG,
|
||
|
expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
store_and_read_back (ul_min, sizeof (ULONGEST), BFD_ENDIAN_LITTLE,
|
||
|
expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
/* Same with ULONGEST_MAX. */
|
||
|
|
||
|
ULONGEST ul_max = ULONGEST_MAX;
|
||
|
|
||
|
store_and_read_back (ul_max, sizeof (ULONGEST), BFD_ENDIAN_BIG,
|
||
|
expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
store_and_read_back (ul_max, sizeof (ULONGEST), BFD_ENDIAN_LITTLE,
|
||
|
expected, actual);
|
||
|
SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0);
|
||
|
}
|
||
|
|
||
|
/* A helper function which creates a gdb_mpz object from the given
|
||
|
integer VAL, and then writes it using its gdb_mpz::write method.
|
||
|
|
||
|
The written value is then extracted from the buffer and returned,
|
||
|
for comparison with the original.
|
||
|
|
||
|
Note that this function does not perform the comparison between
|
||
|
VAL and the returned value. The caller will do it inside a SELF_CHECK
|
||
|
call, allowing the line information shown when the test fails
|
||
|
to provide a bit more information about the kind of values
|
||
|
that were used when the check failed. This makes the writing
|
||
|
of the tests a little more verbose, but the debugging in case
|
||
|
of problems should hopefuly be easier. */
|
||
|
|
||
|
template<typename T>
|
||
|
T
|
||
|
write_and_extract (T val, int buf_len, enum bfd_endian byte_order)
|
||
|
{
|
||
|
gdb_mpz v (val);
|
||
|
|
||
|
SELF_CHECK (v.as_integer<T> () == val);
|
||
|
|
||
|
gdb_byte *buf = (gdb_byte *) alloca (buf_len);
|
||
|
v.write (buf, buf_len, byte_order, !std::is_signed<T>::value);
|
||
|
|
||
|
return extract_integer<T> (buf, buf_len, byte_order);
|
||
|
}
|
||
|
|
||
|
/* Test the gdb_mpz::write method over a reasonable range of values.
|
||
|
|
||
|
The testing is done by picking an arbitrary buffer length, after
|
||
|
which we test every possible value that this buffer allows. */
|
||
|
|
||
|
static void
|
||
|
gdb_mpz_write_all_from_small ()
|
||
|
{
|
||
|
int buf_len = 1;
|
||
|
LONGEST l_min = -pow (2, buf_len * 8 - 1);
|
||
|
LONGEST l_max = pow (2, buf_len * 8 - 1) - 1;
|
||
|
|
||
|
for (LONGEST l = l_min; l <= l_max; l++)
|
||
|
{
|
||
|
SELF_CHECK (write_and_extract (l, buf_len, BFD_ENDIAN_BIG) == l);
|
||
|
SELF_CHECK (write_and_extract (l, buf_len, BFD_ENDIAN_LITTLE) == l);
|
||
|
}
|
||
|
|
||
|
/* Do the same as above, but with an unsigned type. */
|
||
|
ULONGEST ul_min = 0;
|
||
|
ULONGEST ul_max = pow (2, buf_len * 8) - 1;
|
||
|
|
||
|
for (ULONGEST ul = ul_min; ul <= ul_max; ul++)
|
||
|
{
|
||
|
SELF_CHECK (write_and_extract (ul, buf_len, BFD_ENDIAN_BIG) == ul);
|
||
|
SELF_CHECK (write_and_extract (ul, buf_len, BFD_ENDIAN_LITTLE) == ul);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Test the gdb_mpz::write the extremes of LONGEST and ULONGEST. */
|
||
|
|
||
|
static void
|
||
|
gdb_mpz_write_min_max ()
|
||
|
{
|
||
|
/* Start with the smallest LONGEST. */
|
||
|
|
||
|
LONGEST l_min = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1);
|
||
|
SELF_CHECK (write_and_extract (l_min, sizeof (LONGEST), BFD_ENDIAN_BIG)
|
||
|
== l_min);
|
||
|
SELF_CHECK (write_and_extract (l_min, sizeof (LONGEST), BFD_ENDIAN_LITTLE)
|
||
|
== l_min);
|
||
|
|
||
|
/* Same with LONGEST_MAX. */
|
||
|
|
||
|
LONGEST l_max = LONGEST_MAX;
|
||
|
SELF_CHECK (write_and_extract (l_max, sizeof (LONGEST), BFD_ENDIAN_BIG)
|
||
|
== l_max);
|
||
|
SELF_CHECK (write_and_extract (l_max, sizeof (LONGEST), BFD_ENDIAN_LITTLE)
|
||
|
== l_max);
|
||
|
|
||
|
/* Same with the smallest ULONGEST. */
|
||
|
|
||
|
ULONGEST ul_min = (ULONGEST) 1 << (sizeof (ULONGEST) * 8 - 1);
|
||
|
SELF_CHECK (write_and_extract (ul_min, sizeof (ULONGEST), BFD_ENDIAN_BIG)
|
||
|
== ul_min);
|
||
|
SELF_CHECK (write_and_extract (ul_min, sizeof (ULONGEST), BFD_ENDIAN_LITTLE)
|
||
|
== ul_min);
|
||
|
|
||
|
/* Same with ULONGEST_MAX. */
|
||
|
|
||
|
ULONGEST ul_max = ULONGEST_MAX;
|
||
|
SELF_CHECK (write_and_extract (ul_max, sizeof (ULONGEST), BFD_ENDIAN_BIG)
|
||
|
== ul_max);
|
||
|
SELF_CHECK (write_and_extract (ul_max, sizeof (ULONGEST), BFD_ENDIAN_LITTLE)
|
||
|
== ul_max);
|
||
|
}
|
||
|
|
||
|
/* A helper function which stores the signed number, the unscaled value
|
||
|
of a fixed point object, into a buffer, and then uses gdb_mpq's
|
||
|
read_fixed_point to read it as a fixed_point value, with
|
||
|
the given parameters.
|
||
|
|
||
|
EXPECTED is set to the value we expected to get after the call
|
||
|
to read_fixed_point. ACTUAL is the value we actually do get.
|
||
|
|
||
|
Note that this function does not perform the comparison between
|
||
|
EXPECTED and ACTUAL. The caller will do it inside a SELF_CHECK
|
||
|
call, allowing the line information shown when the test fails
|
||
|
to provide a bit more information about the kind of values
|
||
|
that were used when the check failed. This makes the writing
|
||
|
of the tests a little more verbose, but the debugging in case
|
||
|
of problems should hopefuly be easier. */
|
||
|
|
||
|
static void
|
||
|
read_fp_test (int unscaled, const gdb_mpq &scaling_factor,
|
||
|
enum bfd_endian byte_order,
|
||
|
gdb_mpq &expected, gdb_mpq &actual)
|
||
|
{
|
||
|
/* For this kind of testing, we'll use a buffer the same size as
|
||
|
our unscaled parameter. */
|
||
|
const int len = sizeof (unscaled);
|
||
|
gdb_byte buf[len];
|
||
|
store_signed_integer (buf, len, byte_order, unscaled);
|
||
|
|
||
|
actual.read_fixed_point (buf, len, byte_order, 0, scaling_factor);
|
||
|
|
||
|
mpq_set_si (expected.val, unscaled, 1);
|
||
|
mpq_mul (expected.val, expected.val, scaling_factor.val);
|
||
|
}
|
||
|
|
||
|
/* Perform various tests of the gdb_mpq::read_fixed_point method. */
|
||
|
|
||
|
static void
|
||
|
gdb_mpq_read_fixed_point ()
|
||
|
{
|
||
|
gdb_mpq expected, actual;
|
||
|
gdb_mpq scaling_factor;
|
||
|
|
||
|
/* Pick an arbitrary scaling_factor; this operation is trivial enough
|
||
|
thanks to GMP that the value we use isn't really important. */
|
||
|
mpq_set_ui (scaling_factor.val, 3, 5);
|
||
|
|
||
|
/* Try a few values, both negative and positive... */
|
||
|
|
||
|
read_fp_test (-256, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
|
||
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
||
|
read_fp_test (-256, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
|
||
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
read_fp_test (-1, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
|
||
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
||
|
read_fp_test (-1, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
|
||
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
read_fp_test (0, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
|
||
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
||
|
read_fp_test (0, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
|
||
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
read_fp_test (1, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
|
||
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
||
|
read_fp_test (1, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
|
||
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
||
|
|
||
|
read_fp_test (1025, scaling_factor, BFD_ENDIAN_BIG, expected, actual);
|
||
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
||
|
read_fp_test (1025, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual);
|
||
|
SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0);
|
||
|
}
|
||
|
|
||
|
/* A helper function which builds a gdb_mpq object from the given
|
||
|
NUMERATOR and DENOMINATOR, and then calls gdb_mpq's write_fixed_point
|
||
|
method to write it to a buffer.
|
||
|
|
||
|
The value written into the buffer is then read back as is,
|
||
|
and returned. */
|
||
|
|
||
|
static LONGEST
|
||
|
write_fp_test (int numerator, unsigned int denominator,
|
||
|
const gdb_mpq &scaling_factor,
|
||
|
enum bfd_endian byte_order)
|
||
|
{
|
||
|
/* For this testing, we'll use a buffer the size of LONGEST.
|
||
|
This is really an arbitrary decision, as long as the buffer
|
||
|
is long enough to hold the unscaled values that we'll be
|
||
|
writing. */
|
||
|
const int len = sizeof (LONGEST);
|
||
|
gdb_byte buf[len];
|
||
|
memset (buf, 0, len);
|
||
|
|
||
|
gdb_mpq v;
|
||
|
mpq_set_ui (v.val, numerator, denominator);
|
||
|
mpq_canonicalize (v.val);
|
||
|
v.write_fixed_point (buf, len, byte_order, 0, scaling_factor);
|
||
|
|
||
|
return extract_unsigned_integer (buf, len, byte_order);
|
||
|
}
|
||
|
|
||
|
/* Perform various tests of the gdb_mpq::write_fixed_point method. */
|
||
|
|
||
|
static void
|
||
|
gdb_mpq_write_fixed_point ()
|
||
|
{
|
||
|
/* Pick an arbitrary factor; this operations is sufficiently trivial
|
||
|
with the use of GMP that the value of this factor is not really
|
||
|
all that important. */
|
||
|
gdb_mpq scaling_factor;
|
||
|
mpq_set_ui (scaling_factor.val, 1, 3);
|
||
|
|
||
|
gdb_mpq vq;
|
||
|
|
||
|
/* Try a few multiples of the scaling factor, both negative,
|
||
|
and positive... */
|
||
|
|
||
|
SELF_CHECK (write_fp_test (-8, 1, scaling_factor, BFD_ENDIAN_BIG) == -24);
|
||
|
SELF_CHECK (write_fp_test (-8, 1, scaling_factor, BFD_ENDIAN_LITTLE) == -24);
|
||
|
|
||
|
SELF_CHECK (write_fp_test (-2, 3, scaling_factor, BFD_ENDIAN_BIG) == -2);
|
||
|
SELF_CHECK (write_fp_test (-2, 3, scaling_factor, BFD_ENDIAN_LITTLE) == -2);
|
||
|
|
||
|
SELF_CHECK (write_fp_test (0, 3, scaling_factor, BFD_ENDIAN_BIG) == 0);
|
||
|
SELF_CHECK (write_fp_test (0, 3, scaling_factor, BFD_ENDIAN_LITTLE) == 0);
|
||
|
|
||
|
SELF_CHECK (write_fp_test (5, 3, scaling_factor, BFD_ENDIAN_BIG) == 5);
|
||
|
SELF_CHECK (write_fp_test (5, 3, scaling_factor, BFD_ENDIAN_LITTLE) == 5);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void _initialize_gmp_utils_selftests ();
|
||
|
|
||
|
void
|
||
|
_initialize_gmp_utils_selftests ()
|
||
|
{
|
||
|
selftests::register_test ("gdb_mpz_as_integer",
|
||
|
selftests::gdb_mpz_as_integer);
|
||
|
selftests::register_test ("gdb_mpz_read_all_from_small",
|
||
|
selftests::gdb_mpz_read_all_from_small);
|
||
|
selftests::register_test ("gdb_mpz_read_min_max",
|
||
|
selftests::gdb_mpz_read_min_max);
|
||
|
selftests::register_test ("gdb_mpz_write_all_from_small",
|
||
|
selftests::gdb_mpz_write_all_from_small);
|
||
|
selftests::register_test ("gdb_mpz_write_min_max",
|
||
|
selftests::gdb_mpz_write_min_max);
|
||
|
selftests::register_test ("gdb_mpq_read_fixed_point",
|
||
|
selftests::gdb_mpq_read_fixed_point);
|
||
|
selftests::register_test ("gdb_mpq_write_fixed_point",
|
||
|
selftests::gdb_mpq_write_fixed_point);
|
||
|
}
|