blob: dee79c97372d7c1067fc61c74c2e8ac1b714087b [file]
/* Arbitrary precision integers.
Copyright (C) 2026 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
This file is part of GCC.
GCC 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, or (at your option)
any later version.
GCC 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 GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "analyzer/common.h"
#include "diagnostics/markup.h"
#include "selftest.h"
#include "selftest-json.h"
#include "analyzer/integer.h"
#if ENABLE_ANALYZER
namespace ana {
void
integer::dump_to_pp (pretty_printer *pp) const
{
char *str = nullptr;
gmp_asprintf (&str, "%Zd", m_mpz);
gcc_assert (str);
pp_string (pp, str);
void (*free_func_ptr) (void *, size_t) = nullptr;
mp_get_memory_functions (nullptr, nullptr, &free_func_ptr);
gcc_assert (free_func_ptr);
free_func_ptr (str, 0);
}
DEBUG_FUNCTION void
integer::dump () const
{
pretty_printer pp;
pp.set_output_stream (stderr);
dump_to_pp (&pp);
pp_newline (&pp);
pp_flush (&pp);
}
std::unique_ptr<json::value>
integer::to_json () const
{
pretty_printer pp;
dump_to_pp (&pp);
return std::make_unique<json::string> (pp_formatted_text (&pp));
}
std::unique_ptr<diagnostics::markup::node>
integer::make_markup_node () const
{
pretty_printer pp;
dump_to_pp (&pp);
return diagnostics::markup::make_text_node (pp_formatted_text (&pp));
}
integer
convert_from_integer_cst (const_tree int_cst)
{
gcc_assert (int_cst);
gcc_assert (TREE_CODE (int_cst) == INTEGER_CST);
integer result;
wi::to_mpz (wi::to_wide (int_cst),
result.access_mpz (),
TYPE_SIGN (TREE_TYPE (int_cst)));
return result;
}
tree
make_integer_cst_from_integer (tree type, const integer &value, bool wrap)
{
gcc_assert (type);
/* Need to make a copy, as wi::from_mpz modifies its argument. */
integer mutable_copy (value);
wide_int w = wi::from_mpz (type, mutable_copy.access_mpz (), wrap);
return wide_int_to_tree (type, w);
}
#if CHECKING_P
namespace selftest {
static void
test_rule_of_5 ()
{
integer a;
ASSERT_EQ (a, 0);
integer b (42);
ASSERT_EQ (b, 42);
integer c (b);
ASSERT_EQ (c, 42);
// Check that writes to b don't affect c after the copy:
b = 43;
ASSERT_EQ (b, 43);
ASSERT_EQ (c, 42);
integer d (std::move (c));
ASSERT_EQ (d, 42);
ASSERT_EQ (c, 0);
c = integer (1066);
ASSERT_EQ (c, 1066);
a = std::move (d);
ASSERT_EQ (a, 42);
}
static void
test_comparisons ()
{
/* Equality. */
ASSERT_TRUE (integer (0) == integer (0));
ASSERT_FALSE (integer (0) == integer (1));
ASSERT_TRUE (integer (0) == 0);
ASSERT_FALSE (integer (0) == 42);
/* Inequality. */
ASSERT_TRUE (integer (0) != integer (1));
ASSERT_FALSE (integer (0) != integer (0));
ASSERT_TRUE (integer (0) != 1);
ASSERT_FALSE (integer (0) != 0);
// operator<
ASSERT_TRUE (integer (-1) < integer (0));
ASSERT_FALSE (integer (0) < integer (0));
ASSERT_TRUE (integer (2) < integer (3));
ASSERT_FALSE (integer (3) < integer (2));
// Default ctor should give zero:
ASSERT_TRUE (integer () == integer (0));
}
static void
test_addition ()
{
ASSERT_EQ (integer (1) + integer (2),
integer (3));
// Reflexivity
ASSERT_EQ (integer (1) + integer (2),
integer (2) + integer (1));
// Commutativity
ASSERT_EQ ((integer (1) + integer (2)) + integer (3),
integer (6));
ASSERT_EQ (integer (1) + (integer (2) + integer (3)),
integer (6));
}
static void
test_subtraction ()
{
// Identity
ASSERT_EQ (integer (2) - integer (0),
integer (2));
// Positive result
ASSERT_EQ (integer (11) - integer (7),
integer (4));
// Negative reult
ASSERT_EQ (integer (5) - integer (10),
integer (-5));
// Negative operands
ASSERT_EQ (integer (-2) - integer (2),
integer (-4));
ASSERT_EQ (integer (2) - integer (-2),
integer (4));
}
static void
test_multiplication ()
{
// Zero:
ASSERT_EQ (integer (2) * integer (0),
integer (0));
ASSERT_EQ (integer (0) * integer (2),
integer (0));
// Identity
ASSERT_EQ (integer (3) * integer (1),
integer (3));
ASSERT_EQ (integer (1) * integer (3),
integer (3));
// Combinations of positive and negative
ASSERT_EQ (integer (2) * integer (3),
integer (6));
ASSERT_EQ (integer (4) * integer (-5),
integer (-20));
ASSERT_EQ (integer (-4) * integer (5),
integer (-20));
ASSERT_EQ (integer (-4) * integer (-5),
integer (20));
}
static void
test_division ()
{
ASSERT_EQ (integer (0).floor_div (10),
integer (0));
ASSERT_EQ (integer (24).floor_div (10),
integer (2));
ASSERT_EQ (integer (3).floor_div (1),
integer (3));
ASSERT_EQ (integer (-8).floor_div (10),
integer (-1));
}
static void
test_modulus ()
{
ASSERT_EQ (integer (0).floor_mod (10),
integer (0));
ASSERT_EQ (integer (24).floor_mod (10),
integer (4));
ASSERT_EQ (integer (3).floor_mod (1),
integer (0));
ASSERT_EQ (integer (-8).floor_mod (10),
integer (2));
}
static void
test_dumping ()
{
// Positive
{
pretty_printer pp;
integer (42).dump_to_pp (&pp);
ASSERT_STREQ (pp_formatted_text (&pp), "42");
}
// Negative
{
pretty_printer pp;
integer (-100).dump_to_pp (&pp);
ASSERT_STREQ (pp_formatted_text (&pp), "-100");
}
}
static void
test_very_large ()
{
/* Large power of two. */
{
const integer two_to_the_12 (1 << 12);
const integer two_to_the_24 = two_to_the_12 * two_to_the_12;
const integer two_to_the_48 = two_to_the_24 * two_to_the_24;
const integer two_to_the_96 = two_to_the_48 * two_to_the_48;
const integer two_to_the_192 = two_to_the_96 * two_to_the_96;
{
pretty_printer pp;
two_to_the_192.dump_to_pp (&pp);
ASSERT_STREQ
(pp_formatted_text (&pp),
"6277101735386680763835789423207666416102355444464034512896");
}
// Negation
{
pretty_printer pp;
(-two_to_the_192).dump_to_pp (&pp);
ASSERT_STREQ
(pp_formatted_text (&pp),
"-6277101735386680763835789423207666416102355444464034512896");
}
}
/* Large power of ten. */
{
const integer million (1000 * 1000);
const integer ten_to_the_12 = million * million;
const integer ten_to_the_24 = ten_to_the_12 * ten_to_the_12;
const integer ten_to_the_48 = ten_to_the_24 * ten_to_the_24;
const integer ten_to_the_96 = ten_to_the_48 * ten_to_the_48;
const integer ten_to_the_192 = ten_to_the_96 * ten_to_the_96;
{
pretty_printer pp;
ten_to_the_192.dump_to_pp (&pp);
ASSERT_STREQ
(pp_formatted_text (&pp),
("1"
"000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000"));
}
// Negation
{
pretty_printer pp;
(-ten_to_the_192).dump_to_pp (&pp);
ASSERT_STREQ
(pp_formatted_text (&pp),
("-1"
"000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000"));
}
// Addition
{
pretty_printer pp;
(ten_to_the_192 + ten_to_the_192).dump_to_pp (&pp);
ASSERT_STREQ
(pp_formatted_text (&pp),
("2"
"000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000"));
}
}
}
static void
test_pp_element ()
{
pretty_printer pp;
pp_element_integer e1 (integer (42));
pp_element_integer e2 (integer (-100));
pp_printf (&pp, "positive: %e negative: %e", &e1, &e2);
ASSERT_STREQ (pp_formatted_text (&pp), "positive: 42 negative: -100");
}
static integer
get_min_value (tree integral_type)
{
gcc_assert (integral_type);
return convert_from_integer_cst (TYPE_MIN_VALUE (integral_type));
}
static integer
get_max_value (tree integral_type)
{
gcc_assert (integral_type);
return convert_from_integer_cst (TYPE_MAX_VALUE (integral_type));
}
static void
test_convert_from_integer_cst ()
{
ASSERT_TRUE (get_min_value (signed_char_type_node) < 0);
ASSERT_TRUE (get_max_value (signed_char_type_node) > 0);
ASSERT_TRUE (get_min_value (unsigned_char_type_node) == 0);
ASSERT_TRUE (get_max_value (unsigned_char_type_node) > 0);
ASSERT_TRUE (get_min_value (integer_type_node) < 0);
ASSERT_TRUE (get_max_value (integer_type_node) > 0);
ASSERT_EQ (convert_from_integer_cst (integer_zero_node), 0);
ASSERT_EQ (convert_from_integer_cst (integer_one_node), 1);
ASSERT_EQ (convert_from_integer_cst (integer_minus_one_node), -1);
ASSERT_TRUE (get_min_value (unsigned_type_node) == 0);
ASSERT_TRUE (get_max_value (unsigned_type_node) > 0);
ASSERT_TRUE (get_min_value (long_integer_type_node) < 0);
ASSERT_TRUE (get_max_value (long_integer_type_node) > 0);
ASSERT_TRUE (get_min_value (long_unsigned_type_node) == 0);
ASSERT_TRUE (get_max_value (long_unsigned_type_node) > 0);
ASSERT_TRUE (get_min_value (long_long_integer_type_node) < 0);
ASSERT_TRUE (get_max_value (long_long_integer_type_node) > 0);
ASSERT_TRUE (get_min_value (long_long_unsigned_type_node) == 0);
ASSERT_TRUE (get_max_value (long_long_unsigned_type_node) > 0);
ASSERT_TRUE (get_min_value (size_type_node) == 0);
ASSERT_TRUE (get_max_value (size_type_node) > 0);
ASSERT_EQ (convert_from_integer_cst (size_zero_node), 0);
ASSERT_EQ (convert_from_integer_cst (size_one_node), 1);
ASSERT_TRUE (get_min_value (ptrdiff_type_node) < 0);
ASSERT_TRUE (get_max_value (ptrdiff_type_node) > 0);
}
// Verify that INT_CST round-trips through integer.
static void
assert_round_trip (const ::selftest::location &loc, tree int_cst)
{
integer as_integer = convert_from_integer_cst (int_cst);
tree round_tripped_cst = make_integer_cst_from_integer (TREE_TYPE (int_cst),
as_integer, false);
ASSERT_EQ_AT (loc, TREE_TYPE (int_cst), TREE_TYPE (round_tripped_cst));
ASSERT_TRUE_AT (loc, tree_int_cst_equal (int_cst, round_tripped_cst));
}
#define ASSERT_ROUND_TRIP(INT_CST) \
SELFTEST_BEGIN_STMT \
assert_round_trip ((SELFTEST_LOCATION), (INT_CST)); \
SELFTEST_END_STMT
static void
test_round_trip_of_integer_cst ()
{
ASSERT_ROUND_TRIP (TYPE_MIN_VALUE (signed_char_type_node));
ASSERT_ROUND_TRIP (TYPE_MAX_VALUE (signed_char_type_node));
ASSERT_ROUND_TRIP (TYPE_MIN_VALUE (unsigned_char_type_node));
ASSERT_ROUND_TRIP (TYPE_MAX_VALUE (unsigned_char_type_node));
ASSERT_ROUND_TRIP (TYPE_MIN_VALUE (integer_type_node));
ASSERT_ROUND_TRIP (TYPE_MAX_VALUE (integer_type_node));
ASSERT_ROUND_TRIP (integer_zero_node);
ASSERT_ROUND_TRIP (integer_one_node);
ASSERT_ROUND_TRIP (integer_minus_one_node);
ASSERT_ROUND_TRIP (TYPE_MIN_VALUE (unsigned_type_node));
ASSERT_ROUND_TRIP (TYPE_MAX_VALUE (unsigned_type_node));
ASSERT_ROUND_TRIP (TYPE_MIN_VALUE (long_integer_type_node));
ASSERT_ROUND_TRIP (TYPE_MAX_VALUE (long_integer_type_node));
ASSERT_ROUND_TRIP (TYPE_MIN_VALUE (long_unsigned_type_node));
ASSERT_ROUND_TRIP (TYPE_MAX_VALUE (long_unsigned_type_node));
ASSERT_ROUND_TRIP (TYPE_MIN_VALUE (long_long_integer_type_node));
ASSERT_ROUND_TRIP (TYPE_MAX_VALUE (long_long_integer_type_node));
ASSERT_ROUND_TRIP (TYPE_MIN_VALUE (long_long_unsigned_type_node));
ASSERT_ROUND_TRIP (TYPE_MAX_VALUE (long_long_unsigned_type_node));
ASSERT_ROUND_TRIP (TYPE_MIN_VALUE (size_type_node));
ASSERT_ROUND_TRIP (TYPE_MAX_VALUE (size_type_node));
ASSERT_ROUND_TRIP (size_zero_node);
ASSERT_ROUND_TRIP (size_one_node);
ASSERT_ROUND_TRIP (TYPE_MIN_VALUE (ptrdiff_type_node));
ASSERT_ROUND_TRIP (TYPE_MAX_VALUE (ptrdiff_type_node));
}
static void
test_to_json ()
{
ASSERT_JSON_STRING_EQ (integer (42).to_json ().get (),
"42");
}
/* Run all of the selftests within this file. */
void
analyzer_integer_cc_tests ()
{
test_rule_of_5 ();
test_comparisons ();
test_addition ();
test_subtraction ();
test_multiplication ();
test_division ();
test_modulus ();
test_dumping ();
test_very_large ();
test_pp_element ();
test_convert_from_integer_cst ();
test_round_trip_of_integer_cst ();
test_to_json ();
}
} // namespace selftest
#endif /* CHECKING_P */
} // namespace ana
#endif /* #if ENABLE_ANALYZER */