aboutsummaryrefslogtreecommitdiff
path: root/gcc/hash-map-tests.c
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2021-09-13 10:37:49 -0700
committerIan Lance Taylor <iant@golang.org>2021-09-13 10:37:49 -0700
commite252b51ccde010cbd2a146485d8045103cd99533 (patch)
treee060f101cdc32bf5e520de8e5275db9d4236b74c /gcc/hash-map-tests.c
parentf10c7c4596dda99d2ee872c995ae4aeda65adbdf (diff)
parent104c05c5284b7822d770ee51a7d91946c7e56d50 (diff)
downloadgcc-e252b51ccde010cbd2a146485d8045103cd99533.zip
gcc-e252b51ccde010cbd2a146485d8045103cd99533.tar.gz
gcc-e252b51ccde010cbd2a146485d8045103cd99533.tar.bz2
Merge from trunk revision 104c05c5284b7822d770ee51a7d91946c7e56d50.
Diffstat (limited to 'gcc/hash-map-tests.c')
-rw-r--r--gcc/hash-map-tests.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/gcc/hash-map-tests.c b/gcc/hash-map-tests.c
index 5b6b192..6acc0d4 100644
--- a/gcc/hash-map-tests.c
+++ b/gcc/hash-map-tests.c
@@ -278,6 +278,167 @@ test_map_of_type_with_ctor_and_dtor ()
ASSERT_TRUE (val_t::ndefault + val_t::ncopy == val_t::ndtor);
}
+
+
+ /* Verify basic construction and destruction of Value objects. */
+ {
+ /* Configure, arbitrary. */
+ const size_t N_init = 0;
+ const int N_elem = 28;
+
+ void *a[N_elem];
+ for (size_t i = 0; i < N_elem; ++i)
+ a[i] = &a[i];
+
+ val_t::ndefault = 0;
+ val_t::ncopy = 0;
+ val_t::nassign = 0;
+ val_t::ndtor = 0;
+ Map m (N_init);
+ ASSERT_EQ (val_t::ndefault
+ + val_t::ncopy
+ + val_t::nassign
+ + val_t::ndtor, 0);
+
+ for (int i = 0; i < N_elem; ++i)
+ {
+ m.get_or_insert (a[i]);
+ ASSERT_EQ (val_t::ndefault, 1 + i);
+ ASSERT_EQ (val_t::ncopy, 0);
+ ASSERT_EQ (val_t::nassign, 0);
+ ASSERT_EQ (val_t::ndtor, i);
+
+ m.remove (a[i]);
+ ASSERT_EQ (val_t::ndefault, 1 + i);
+ ASSERT_EQ (val_t::ncopy, 0);
+ ASSERT_EQ (val_t::nassign, 0);
+ ASSERT_EQ (val_t::ndtor, 1 + i);
+ }
+ }
+}
+
+/* Verify aspects of 'hash_table::expand'. */
+
+static void
+test_map_of_type_with_ctor_and_dtor_expand (bool remove_some_inline)
+{
+ /* Configure, so that hash table expansion triggers a few times. */
+ const size_t N_init = 0;
+ const int N_elem = 70;
+ size_t expand_c_expected = 4;
+ size_t expand_c = 0;
+
+ /* For stability of this testing, we need all Key values 'k' to produce
+ unique hash values 'Traits::hash (k)', as otherwise the dynamic
+ insert/remove behavior may diverge across different architectures. This
+ is, for example, a problem when using the standard 'pointer_hash::hash',
+ which is simply doing a 'k >> 3' operation, which is fine on 64-bit
+ architectures, but on 32-bit architectures produces the same hash value
+ for subsequent 'a[i] = &a[i]' array elements. Therefore, use an
+ 'int_hash'. */
+
+ int a[N_elem];
+ for (size_t i = 0; i < N_elem; ++i)
+ a[i] = i;
+
+ const int EMPTY = -1;
+ const int DELETED = -2;
+ typedef hash_map<int_hash<int, EMPTY, DELETED>, val_t> Map;
+
+ /* Note that we are starting with a fresh 'Map'. Even if an existing one has
+ been cleared out completely, there remain 'deleted' elements, and these
+ would disturb the following logic, where we don't have access to the
+ actual 'm_n_deleted' value. */
+ size_t m_n_deleted = 0;
+
+ val_t::ndefault = 0;
+ val_t::ncopy = 0;
+ val_t::nassign = 0;
+ val_t::ndtor = 0;
+ Map m (N_init);
+
+ /* In the following, in particular related to 'expand', we're adapting from
+ the internal logic of 'hash_table', glossing over "some details" not
+ relevant for this testing here. */
+
+ /* Per 'hash_table::hash_table'. */
+ size_t m_size;
+ {
+ unsigned int size_prime_index_ = hash_table_higher_prime_index (N_init);
+ m_size = prime_tab[size_prime_index_].prime;
+ }
+
+ int n_expand_moved = 0;
+
+ for (int i = 0; i < N_elem; ++i)
+ {
+ size_t elts = m.elements ();
+
+ /* Per 'hash_table::find_slot_with_hash'. */
+ size_t m_n_elements = elts + m_n_deleted;
+ bool expand = m_size * 3 <= m_n_elements * 4;
+
+ m.get_or_insert (a[i]);
+ if (expand)
+ {
+ ++expand_c;
+
+ /* Per 'hash_table::expand'. */
+ {
+ unsigned int nindex = hash_table_higher_prime_index (elts * 2);
+ m_size = prime_tab[nindex].prime;
+ }
+ m_n_deleted = 0;
+
+ /* All non-deleted elements have been moved. */
+ n_expand_moved += i;
+ if (remove_some_inline)
+ n_expand_moved -= (i + 2) / 3;
+ }
+
+ ASSERT_EQ (val_t::ndefault, 1 + i);
+ ASSERT_EQ (val_t::ncopy, n_expand_moved);
+ ASSERT_EQ (val_t::nassign, 0);
+ if (remove_some_inline)
+ ASSERT_EQ (val_t::ndtor, (i + 2) / 3);
+ else
+ ASSERT_EQ (val_t::ndtor, 0);
+
+ /* Remove some inline. This never triggers an 'expand' here, but via
+ 'm_n_deleted' does influence any following one. */
+ if (remove_some_inline
+ && !(i % 3))
+ {
+ m.remove (a[i]);
+ /* Per 'hash_table::remove_elt_with_hash'. */
+ m_n_deleted++;
+
+ ASSERT_EQ (val_t::ndefault, 1 + i);
+ ASSERT_EQ (val_t::ncopy, n_expand_moved);
+ ASSERT_EQ (val_t::nassign, 0);
+ ASSERT_EQ (val_t::ndtor, 1 + (i + 2) / 3);
+ }
+ }
+ ASSERT_EQ (expand_c, expand_c_expected);
+
+ int ndefault = val_t::ndefault;
+ int ncopy = val_t::ncopy;
+ int nassign = val_t::nassign;
+ int ndtor = val_t::ndtor;
+
+ for (int i = 0; i < N_elem; ++i)
+ {
+ if (remove_some_inline
+ && !(i % 3))
+ continue;
+
+ m.remove (a[i]);
+ ++ndtor;
+ ASSERT_EQ (val_t::ndefault, ndefault);
+ ASSERT_EQ (val_t::ncopy, ncopy);
+ ASSERT_EQ (val_t::nassign, nassign);
+ ASSERT_EQ (val_t::ndtor, ndtor);
+ }
}
/* Test calling empty on a hash_map that has a key type with non-zero
@@ -309,6 +470,8 @@ hash_map_tests_c_tests ()
test_map_of_strings_to_int ();
test_map_of_int_to_strings ();
test_map_of_type_with_ctor_and_dtor ();
+ test_map_of_type_with_ctor_and_dtor_expand (false);
+ test_map_of_type_with_ctor_and_dtor_expand (true);
test_nonzero_empty_key ();
}