aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattias EngdegÄrd2023-10-27 22:15:09 +0200
committerMattias EngdegÄrd2024-01-13 20:50:38 +0100
commitfa5c07fc87d557e642fc325852e8d0c87a9c176e (patch)
treec83fc3b64e9a642d9b303a19852bc484a8b46de2 /src
parent49fd4d120deb0b878ad262aea7d849c7275bc12c (diff)
downloademacs-fa5c07fc87d557e642fc325852e8d0c87a9c176e.tar.gz
emacs-fa5c07fc87d557e642fc325852e8d0c87a9c176e.zip
Use non-Lisp allocation for internal hash-table vectors
Using xmalloc for allocating these arrays is much cheaper than using Lisp vectors since they are no longer marked or swept by the GC, and deallocated much sooner. This makes GC faster and less frequent, and improves temporal locality. Zero-sized tables use NULL for their (0-length) vectors except the index vector which has size 1 and uses a shared constant static vector since it cannot be modified anyway. This makes creation and destruction of zero-sized hash tables very fast; they consume no memory outside the base object. * src/lisp.h (struct Lisp_Hash_Table): Retype the index, next, hash and key_and_value vectors from Lisp_Object to appropriately typed arrays (although hash values are still stored as Lisp fixnums). Add explicit table_size and index_size members. All users updated. * src/alloc.c (gcstat): Add total_hash_table_bytes. (hash_table_allocated_bytes): New. (cleanup_vector): Free hash table vectors when sweeping the object. (hash_table_alloc_bytes, hash_table_free_bytes): New. (sweep_vectors): Update gcstat.total_hash_table_bytes. (total_bytes_of_live_objects): Use it. (purecopy_hash_table): Adapt allocation of hash table vectors. (process_mark_stack): No more Lisp slots in the struct to trace. * src/fns.c (empty_hash_index_vector): New. (allocate_hash_table): Allocate without automatically GCed slots. (alloc_larger_vector): Remove. (make_hash_table, copy_hash_table, maybe_resize_hash_table): Adapt vector allocation and initialisation. * src/pdumper.c (hash_table_freeze, hash_table_thaw, dump_hash_table) (dump_hash_table_contents): Adapt dumping and loading to field changes.
Diffstat (limited to 'src')
-rw-r--r--src/alloc.c86
-rw-r--r--src/fns.c217
-rw-r--r--src/lisp.h61
-rw-r--r--src/pdumper.c56
-rw-r--r--src/print.c4
5 files changed, 290 insertions, 134 deletions
diff --git a/src/alloc.c b/src/alloc.c
index 636b4972c84..7432163db25 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -359,8 +359,16 @@ static struct gcstat
359 object_ct total_floats, total_free_floats; 359 object_ct total_floats, total_free_floats;
360 object_ct total_intervals, total_free_intervals; 360 object_ct total_intervals, total_free_intervals;
361 object_ct total_buffers; 361 object_ct total_buffers;
362
363 /* Size of the ancillary arrays of live hash-table objects.
364 The objects themselves are not included (counted as vectors above). */
365 byte_ct total_hash_table_bytes;
362} gcstat; 366} gcstat;
363 367
368/* Total size of ancillary arrays of all allocated hash-table objects,
369 both dead and alive. This number is always kept up-to-date. */
370static ptrdiff_t hash_table_allocated_bytes = 0;
371
364/* Points to memory space allocated as "spare", to be freed if we run 372/* Points to memory space allocated as "spare", to be freed if we run
365 out of memory. We keep one large block, four cons-blocks, and 373 out of memory. We keep one large block, four cons-blocks, and
366 two string blocks. */ 374 two string blocks. */
@@ -3430,6 +3438,23 @@ cleanup_vector (struct Lisp_Vector *vector)
3430 } 3438 }
3431#endif 3439#endif
3432 break; 3440 break;
3441 case PVEC_HASH_TABLE:
3442 {
3443 struct Lisp_Hash_Table *h = PSEUDOVEC_STRUCT (vector, Lisp_Hash_Table);
3444 if (h->table_size > 0)
3445 {
3446 eassert (h->index_size > 1);
3447 xfree (h->index);
3448 xfree (h->key_and_value);
3449 xfree (h->next);
3450 xfree (h->hash);
3451 ptrdiff_t bytes = (h->table_size * (2 * sizeof *h->key_and_value
3452 + sizeof *h->hash
3453 + sizeof *h->next)
3454 + h->index_size * sizeof *h->index);
3455 hash_table_allocated_bytes -= bytes;
3456 }
3457 }
3433 /* Keep the switch exhaustive. */ 3458 /* Keep the switch exhaustive. */
3434 case PVEC_NORMAL_VECTOR: 3459 case PVEC_NORMAL_VECTOR:
3435 case PVEC_FREE: 3460 case PVEC_FREE:
@@ -3440,7 +3465,6 @@ cleanup_vector (struct Lisp_Vector *vector)
3440 case PVEC_WINDOW: 3465 case PVEC_WINDOW:
3441 case PVEC_BOOL_VECTOR: 3466 case PVEC_BOOL_VECTOR:
3442 case PVEC_BUFFER: 3467 case PVEC_BUFFER:
3443 case PVEC_HASH_TABLE:
3444 case PVEC_TERMINAL: 3468 case PVEC_TERMINAL:
3445 case PVEC_WINDOW_CONFIGURATION: 3469 case PVEC_WINDOW_CONFIGURATION:
3446 case PVEC_OTHER: 3470 case PVEC_OTHER:
@@ -3554,6 +3578,8 @@ sweep_vectors (void)
3554 lisp_free (lv); 3578 lisp_free (lv);
3555 } 3579 }
3556 } 3580 }
3581
3582 gcstat.total_hash_table_bytes = hash_table_allocated_bytes;
3557} 3583}
3558 3584
3559/* Maximum number of elements in a vector. This is a macro so that it 3585/* Maximum number of elements in a vector. This is a macro so that it
@@ -5606,6 +5632,28 @@ valid_lisp_object_p (Lisp_Object obj)
5606 return 0; 5632 return 0;
5607} 5633}
5608 5634
5635/* Like xmalloc, but makes allocation count toward the total consing.
5636 Return NULL for a zero-sized allocation. */
5637void *
5638hash_table_alloc_bytes (ptrdiff_t nbytes)
5639{
5640 if (nbytes == 0)
5641 return NULL;
5642 tally_consing (nbytes);
5643 hash_table_allocated_bytes += nbytes;
5644 return xmalloc (nbytes);
5645}
5646
5647/* Like xfree, but makes allocation count toward the total consing. */
5648void
5649hash_table_free_bytes (void *p, ptrdiff_t nbytes)
5650{
5651 tally_consing (-nbytes);
5652 hash_table_allocated_bytes -= nbytes;
5653 xfree (p);
5654}
5655
5656
5609/*********************************************************************** 5657/***********************************************************************
5610 Pure Storage Management 5658 Pure Storage Management
5611 ***********************************************************************/ 5659 ***********************************************************************/
@@ -5897,10 +5945,28 @@ purecopy_hash_table (struct Lisp_Hash_Table *table)
5897 pure->test.name = purecopy (table->test.name); 5945 pure->test.name = purecopy (table->test.name);
5898 pure->test.user_hash_function = purecopy (table->test.user_hash_function); 5946 pure->test.user_hash_function = purecopy (table->test.user_hash_function);
5899 pure->test.user_cmp_function = purecopy (table->test.user_cmp_function); 5947 pure->test.user_cmp_function = purecopy (table->test.user_cmp_function);
5900 pure->hash = purecopy (table->hash); 5948
5901 pure->next = purecopy (table->next); 5949 if (table->table_size > 0)
5902 pure->index = purecopy (table->index); 5950 {
5903 pure->key_and_value = purecopy (table->key_and_value); 5951 ptrdiff_t hash_bytes = table->table_size * sizeof *table->hash;
5952 pure->hash = pure_alloc (hash_bytes, -(int)sizeof *table->hash);
5953 memcpy (pure->hash, table->hash, hash_bytes);
5954
5955 ptrdiff_t next_bytes = table->table_size * sizeof *table->next;
5956 pure->next = pure_alloc (next_bytes, -(int)sizeof *table->next);
5957 memcpy (pure->next, table->next, next_bytes);
5958
5959 ptrdiff_t nvalues = table->table_size * 2;
5960 ptrdiff_t kv_bytes = nvalues * sizeof *table->key_and_value;
5961 pure->key_and_value = pure_alloc (kv_bytes,
5962 -(int)sizeof *table->key_and_value);
5963 for (ptrdiff_t i = 0; i < nvalues; i++)
5964 pure->key_and_value[i] = purecopy (table->key_and_value[i]);
5965
5966 ptrdiff_t index_bytes = table->index_size * sizeof *table->index;
5967 pure->index = pure_alloc (index_bytes, -(int)sizeof *table->index);
5968 memcpy (pure->index, table->index, index_bytes);
5969 }
5904 5970
5905 return pure; 5971 return pure;
5906} 5972}
@@ -6084,6 +6150,7 @@ total_bytes_of_live_objects (void)
6084 tot += object_bytes (gcstat.total_floats, sizeof (struct Lisp_Float)); 6150 tot += object_bytes (gcstat.total_floats, sizeof (struct Lisp_Float));
6085 tot += object_bytes (gcstat.total_intervals, sizeof (struct interval)); 6151 tot += object_bytes (gcstat.total_intervals, sizeof (struct interval));
6086 tot += object_bytes (gcstat.total_strings, sizeof (struct Lisp_String)); 6152 tot += object_bytes (gcstat.total_strings, sizeof (struct Lisp_String));
6153 tot += gcstat.total_hash_table_bytes;
6087 return tot; 6154 return tot;
6088} 6155}
6089 6156
@@ -7227,23 +7294,20 @@ process_mark_stack (ptrdiff_t base_sp)
7227 case PVEC_HASH_TABLE: 7294 case PVEC_HASH_TABLE:
7228 { 7295 {
7229 struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *)ptr; 7296 struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *)ptr;
7230 ptrdiff_t size = ptr->header.size & PSEUDOVECTOR_SIZE_MASK;
7231 set_vector_marked (ptr); 7297 set_vector_marked (ptr);
7232 mark_stack_push_values (ptr->contents, size);
7233 mark_stack_push_value (h->test.name); 7298 mark_stack_push_value (h->test.name);
7234 mark_stack_push_value (h->test.user_hash_function); 7299 mark_stack_push_value (h->test.user_hash_function);
7235 mark_stack_push_value (h->test.user_cmp_function); 7300 mark_stack_push_value (h->test.user_cmp_function);
7236 if (h->weakness == Weak_None) 7301 if (h->weakness == Weak_None)
7237 mark_stack_push_value (h->key_and_value); 7302 mark_stack_push_values (h->key_and_value,
7303 2 * h->table_size);
7238 else 7304 else
7239 { 7305 {
7240 /* For weak tables, mark only the vector and not its 7306 /* For weak tables, don't mark the
7241 contents --- that's what makes it weak. */ 7307 contents --- that's what makes it weak. */
7242 eassert (h->next_weak == NULL); 7308 eassert (h->next_weak == NULL);
7243 h->next_weak = weak_hash_tables; 7309 h->next_weak = weak_hash_tables;
7244 weak_hash_tables = h; 7310 weak_hash_tables = h;
7245 if (!PURE_P (h->key_and_value))
7246 set_vector_marked (XVECTOR (h->key_and_value));
7247 } 7311 }
7248 break; 7312 break;
7249 } 7313 }
diff --git a/src/fns.c b/src/fns.c
index a1659884b5e..3aca588a8a5 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -4275,17 +4275,20 @@ CHECK_HASH_TABLE (Lisp_Object x)
4275static void 4275static void
4276set_hash_next_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val) 4276set_hash_next_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val)
4277{ 4277{
4278 gc_aset (h->next, idx, make_fixnum (val)); 4278 eassert (idx >= 0 && idx < h->table_size);
4279 h->next[idx] = val;
4279} 4280}
4280static void 4281static void
4281set_hash_hash_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) 4282set_hash_hash_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val)
4282{ 4283{
4283 gc_aset (h->hash, idx, val); 4284 eassert (idx >= 0 && idx < h->table_size);
4285 h->hash[idx] = val;
4284} 4286}
4285static void 4287static void
4286set_hash_index_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val) 4288set_hash_index_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val)
4287{ 4289{
4288 gc_aset (h->index, idx, make_fixnum (val)); 4290 eassert (idx >= 0 && idx < h->index_size);
4291 h->index[idx] = val;
4289} 4292}
4290 4293
4291/* If OBJ is a Lisp hash table, return a pointer to its struct 4294/* If OBJ is a Lisp hash table, return a pointer to its struct
@@ -4375,7 +4378,8 @@ larger_vector (Lisp_Object vec, ptrdiff_t incr_min, ptrdiff_t nitems_max)
4375static ptrdiff_t 4378static ptrdiff_t
4376HASH_NEXT (struct Lisp_Hash_Table *h, ptrdiff_t idx) 4379HASH_NEXT (struct Lisp_Hash_Table *h, ptrdiff_t idx)
4377{ 4380{
4378 return XFIXNUM (AREF (h->next, idx)); 4381 eassert (idx >= 0 && idx < h->table_size);
4382 return h->next[idx];
4379} 4383}
4380 4384
4381/* Return the index of the element in hash table H that is the start 4385/* Return the index of the element in hash table H that is the start
@@ -4384,7 +4388,8 @@ HASH_NEXT (struct Lisp_Hash_Table *h, ptrdiff_t idx)
4384static ptrdiff_t 4388static ptrdiff_t
4385HASH_INDEX (struct Lisp_Hash_Table *h, ptrdiff_t idx) 4389HASH_INDEX (struct Lisp_Hash_Table *h, ptrdiff_t idx)
4386{ 4390{
4387 return XFIXNUM (AREF (h->index, idx)); 4391 eassert (idx >= 0 && idx < h->index_size);
4392 return h->index[idx];
4388} 4393}
4389 4394
4390/* Restore a hash table's mutability after the critical section exits. */ 4395/* Restore a hash table's mutability after the critical section exits. */
@@ -4495,8 +4500,7 @@ struct hash_table_test const
4495static struct Lisp_Hash_Table * 4500static struct Lisp_Hash_Table *
4496allocate_hash_table (void) 4501allocate_hash_table (void)
4497{ 4502{
4498 return ALLOCATE_PSEUDOVECTOR (struct Lisp_Hash_Table, 4503 return ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Hash_Table, PVEC_HASH_TABLE);
4499 index, PVEC_HASH_TABLE);
4500} 4504}
4501 4505
4502/* An upper bound on the size of a hash table index. It must fit in 4506/* An upper bound on the size of a hash table index. It must fit in
@@ -4528,6 +4532,10 @@ hash_index_size (ptrdiff_t size)
4528 return index_size; 4532 return index_size;
4529} 4533}
4530 4534
4535/* Constant hash index vector used when the table size is zero.
4536 This avoids allocating it from the heap. */
4537static const ptrdiff_t empty_hash_index_vector[] = {-1};
4538
4531/* Create and initialize a new hash table. 4539/* Create and initialize a new hash table.
4532 4540
4533 TEST specifies the test the hash table will use to compare keys. 4541 TEST specifies the test the hash table will use to compare keys.
@@ -4547,36 +4555,54 @@ Lisp_Object
4547make_hash_table (struct hash_table_test test, EMACS_INT size, 4555make_hash_table (struct hash_table_test test, EMACS_INT size,
4548 hash_table_weakness_t weak, bool purecopy) 4556 hash_table_weakness_t weak, bool purecopy)
4549{ 4557{
4550 struct Lisp_Hash_Table *h;
4551 Lisp_Object table;
4552 ptrdiff_t i;
4553
4554 /* Preconditions. */
4555 eassert (SYMBOLP (test.name)); 4558 eassert (SYMBOLP (test.name));
4556 eassert (0 <= size && size <= MOST_POSITIVE_FIXNUM); 4559 eassert (0 <= size && size <= min (MOST_POSITIVE_FIXNUM, PTRDIFF_MAX));
4557 4560
4558 /* Allocate a table and initialize it. */ 4561 struct Lisp_Hash_Table *h = allocate_hash_table ();
4559 h = allocate_hash_table ();
4560 4562
4561 /* Initialize hash table slots. */
4562 h->test = test; 4563 h->test = test;
4563 h->weakness = weak; 4564 h->weakness = weak;
4564 h->count = 0; 4565 h->count = 0;
4565 h->key_and_value = make_vector (2 * size, HASH_UNUSED_ENTRY_KEY); 4566 h->table_size = size;
4566 h->hash = make_nil_vector (size); 4567 int index_size = hash_index_size (size);
4567 h->next = make_vector (size, make_fixnum (-1)); 4568 h->index_size = index_size;
4568 h->index = make_vector (hash_index_size (size), make_fixnum (-1)); 4569
4570 if (size == 0)
4571 {
4572 h->key_and_value = NULL;
4573 h->hash = NULL;
4574 h->next = NULL;
4575 eassert (index_size == 1);
4576 h->index = (ptrdiff_t *)empty_hash_index_vector;
4577 h->next_free = -1;
4578 }
4579 else
4580 {
4581 h->key_and_value = hash_table_alloc_bytes (2 * size
4582 * sizeof *h->key_and_value);
4583 for (ptrdiff_t i = 0; i < 2 * size; i++)
4584 h->key_and_value[i] = HASH_UNUSED_ENTRY_KEY;
4585
4586 h->hash = hash_table_alloc_bytes (size * sizeof *h->hash);
4587 memclear (h->hash, size * sizeof *h->hash);
4588
4589 h->next = hash_table_alloc_bytes (size * sizeof *h->next);
4590 for (ptrdiff_t i = 0; i < size - 1; i++)
4591 h->next[i] = i + 1;
4592 h->next[size - 1] = -1;
4593
4594 h->index = hash_table_alloc_bytes (index_size * sizeof *h->index);
4595 for (ptrdiff_t i = 0; i < index_size; i++)
4596 h->index[i] = -1;
4597
4598 h->next_free = 0;
4599 }
4600
4569 h->next_weak = NULL; 4601 h->next_weak = NULL;
4570 h->purecopy = purecopy; 4602 h->purecopy = purecopy;
4571 h->mutable = true; 4603 h->mutable = true;
4572 4604
4573 /* Set up the free list. */ 4605 Lisp_Object table;
4574 for (i = 0; i < size - 1; ++i)
4575 set_hash_next_slot (h, i, i + 1);
4576 if (size > 0)
4577 set_hash_next_slot (h, size - 1, -1);
4578 h->next_free = size > 0 ? 0 : -1;
4579
4580 XSET_HASH_TABLE (table, h); 4606 XSET_HASH_TABLE (table, h);
4581 eassert (HASH_TABLE_P (table)); 4607 eassert (HASH_TABLE_P (table));
4582 eassert (XHASH_TABLE (table) == h); 4608 eassert (XHASH_TABLE (table) == h);
@@ -4597,35 +4623,37 @@ copy_hash_table (struct Lisp_Hash_Table *h1)
4597 h2 = allocate_hash_table (); 4623 h2 = allocate_hash_table ();
4598 *h2 = *h1; 4624 *h2 = *h1;
4599 h2->mutable = true; 4625 h2->mutable = true;
4600 h2->key_and_value = Fcopy_sequence (h1->key_and_value); 4626
4601 h2->hash = Fcopy_sequence (h1->hash); 4627 if (h1->table_size > 0)
4602 h2->next = Fcopy_sequence (h1->next); 4628 {
4603 h2->index = Fcopy_sequence (h1->index); 4629 ptrdiff_t kv_bytes = 2 * h1->table_size * sizeof *h1->key_and_value;
4630 h2->key_and_value = hash_table_alloc_bytes (kv_bytes);
4631 memcpy (h2->key_and_value, h1->key_and_value, kv_bytes);
4632
4633 ptrdiff_t hash_bytes = h1->table_size * sizeof *h1->hash;
4634 h2->hash = hash_table_alloc_bytes (hash_bytes);
4635 memcpy (h2->hash, h1->hash, hash_bytes);
4636
4637 ptrdiff_t next_bytes = h1->table_size * sizeof *h1->next;
4638 h2->next = hash_table_alloc_bytes (next_bytes);
4639 memcpy (h2->next, h1->next, next_bytes);
4640
4641 ptrdiff_t index_bytes = h1->index_size * sizeof *h1->index;
4642 h2->index = hash_table_alloc_bytes (index_bytes);
4643 memcpy (h2->index, h1->index, index_bytes);
4644 }
4604 XSET_HASH_TABLE (table, h2); 4645 XSET_HASH_TABLE (table, h2);
4605 4646
4606 return table; 4647 return table;
4607} 4648}
4608 4649
4609 4650
4610/* Allocate a Lisp vector of NEW_SIZE elements.
4611 Copy elements from VEC and leave the rest undefined. */
4612static Lisp_Object
4613alloc_larger_vector (Lisp_Object vec, ptrdiff_t new_size)
4614{
4615 eassert (VECTORP (vec));
4616 ptrdiff_t old_size = ASIZE (vec);
4617 eassert (new_size >= old_size);
4618 struct Lisp_Vector *v = allocate_vector (new_size);
4619 memcpy (v->contents, XVECTOR (vec)->contents, old_size * sizeof *v->contents);
4620 XSETVECTOR (vec, v);
4621 return vec;
4622}
4623
4624/* Compute index into the index vector from a hash value. */ 4651/* Compute index into the index vector from a hash value. */
4625static inline ptrdiff_t 4652static inline ptrdiff_t
4626hash_index_index (struct Lisp_Hash_Table *h, Lisp_Object hash_code) 4653hash_index_index (struct Lisp_Hash_Table *h, Lisp_Object hash_code)
4627{ 4654{
4628 return XUFIXNUM (hash_code) % ASIZE (h->index); 4655 eassert (h->index_size > 0);
4656 return XUFIXNUM (hash_code) % h->index_size;
4629} 4657}
4630 4658
4631/* Resize hash table H if it's too full. If H cannot be resized 4659/* Resize hash table H if it's too full. If H cannot be resized
@@ -4650,37 +4678,56 @@ maybe_resize_hash_table (struct Lisp_Hash_Table *h)
4650 4678
4651 /* Allocate all the new vectors before updating *H, to 4679 /* Allocate all the new vectors before updating *H, to
4652 avoid problems if memory is exhausted. */ 4680 avoid problems if memory is exhausted. */
4653 Lisp_Object next = alloc_larger_vector (h->next, new_size); 4681 ptrdiff_t *next = hash_table_alloc_bytes (new_size * sizeof *next);
4654 for (ptrdiff_t i = old_size; i < new_size - 1; i++) 4682 for (ptrdiff_t i = old_size; i < new_size - 1; i++)
4655 ASET (next, i, make_fixnum (i + 1)); 4683 next[i] = i + 1;
4656 ASET (next, new_size - 1, make_fixnum (-1)); 4684 next[new_size - 1] = -1;
4657 4685
4658 /* Build the new&larger key_and_value vector, making sure the new 4686 Lisp_Object *key_and_value
4659 fields are initialized to `unbound`. */ 4687 = hash_table_alloc_bytes (2 * new_size * sizeof *key_and_value);
4660 Lisp_Object key_and_value 4688 memcpy (key_and_value, h->key_and_value,
4661 = alloc_larger_vector (h->key_and_value, 2 * new_size); 4689 2 * old_size * sizeof *key_and_value);
4662 for (ptrdiff_t i = 2 * old_size; i < 2 * new_size; i++) 4690 for (ptrdiff_t i = 2 * old_size; i < 2 * new_size; i++)
4663 ASET (key_and_value, i, HASH_UNUSED_ENTRY_KEY); 4691 key_and_value[i] = HASH_UNUSED_ENTRY_KEY;
4664 4692
4665 Lisp_Object hash = alloc_larger_vector (h->hash, new_size); 4693 Lisp_Object *hash = hash_table_alloc_bytes (new_size * sizeof *hash);
4666 memclear (XVECTOR (hash)->contents + old_size, 4694 memcpy (hash, h->hash, old_size * sizeof *hash);
4667 (new_size - old_size) * word_size); 4695 memclear (hash + old_size, (new_size - old_size) * word_size);
4696
4697 ptrdiff_t old_index_size = h->index_size;
4668 ptrdiff_t index_size = hash_index_size (new_size); 4698 ptrdiff_t index_size = hash_index_size (new_size);
4669 h->index = make_vector (index_size, make_fixnum (-1)); 4699 ptrdiff_t *index = hash_table_alloc_bytes (index_size * sizeof *index);
4700 for (ptrdiff_t i = 0; i < index_size; i++)
4701 index[i] = -1;
4702
4703 h->index_size = index_size;
4704 h->table_size = new_size;
4705 h->next_free = old_size;
4706
4707 if (old_index_size > 1)
4708 hash_table_free_bytes (h->index, old_index_size * sizeof *h->index);
4709 h->index = index;
4710
4711 hash_table_free_bytes (h->key_and_value,
4712 2 * old_size * sizeof *h->key_and_value);
4670 h->key_and_value = key_and_value; 4713 h->key_and_value = key_and_value;
4714
4715 hash_table_free_bytes (h->hash, old_size * sizeof *h->hash);
4671 h->hash = hash; 4716 h->hash = hash;
4717
4718 hash_table_free_bytes (h->next, old_size * sizeof *h->next);
4672 h->next = next; 4719 h->next = next;
4673 h->next_free = old_size;
4674 4720
4675 /* Rehash. */ 4721 h->key_and_value = key_and_value;
4722
4723 /* Rehash: all data occupy entries 0..old_size-1. */
4676 for (ptrdiff_t i = 0; i < old_size; i++) 4724 for (ptrdiff_t i = 0; i < old_size; i++)
4677 if (!NILP (HASH_HASH (h, i))) 4725 {
4678 { 4726 Lisp_Object hash_code = HASH_HASH (h, i);
4679 Lisp_Object hash_code = HASH_HASH (h, i); 4727 ptrdiff_t start_of_bucket = hash_index_index (h, hash_code);
4680 ptrdiff_t start_of_bucket = hash_index_index (h, hash_code); 4728 set_hash_next_slot (h, i, HASH_INDEX (h, start_of_bucket));
4681 set_hash_next_slot (h, i, HASH_INDEX (h, start_of_bucket)); 4729 set_hash_index_slot (h, start_of_bucket, i);
4682 set_hash_index_slot (h, start_of_bucket, i); 4730 }
4683 }
4684 4731
4685#ifdef ENABLE_CHECKING 4732#ifdef ENABLE_CHECKING
4686 if (HASH_TABLE_P (Vpurify_flag) && XHASH_TABLE (Vpurify_flag) == h) 4733 if (HASH_TABLE_P (Vpurify_flag) && XHASH_TABLE (Vpurify_flag) == h)
@@ -4710,14 +4757,22 @@ hash_table_thaw (Lisp_Object hash_table)
4710 /* Freezing discarded most non-essential information; recompute it. 4757 /* Freezing discarded most non-essential information; recompute it.
4711 The allocation is minimal with no room for growth. */ 4758 The allocation is minimal with no room for growth. */
4712 h->test = *hash_table_test_from_std (h->frozen_test); 4759 h->test = *hash_table_test_from_std (h->frozen_test);
4713 ptrdiff_t size = ASIZE (h->key_and_value) / 2; 4760 ptrdiff_t size = h->count;
4714 h->count = size; 4761 h->table_size = size;
4715 ptrdiff_t index_size = hash_index_size (size); 4762 ptrdiff_t index_size = hash_index_size (size);
4763 h->index_size = index_size;
4716 h->next_free = -1; 4764 h->next_free = -1;
4717 4765
4718 h->hash = make_nil_vector (size); 4766 h->hash = hash_table_alloc_bytes (size * sizeof *h->hash);
4719 h->next = make_vector (size, make_fixnum (-1)); 4767 memclear (h->hash, size * sizeof *h->hash);
4720 h->index = make_vector (index_size, make_fixnum (-1)); 4768
4769 h->next = hash_table_alloc_bytes (size * sizeof *h->next);
4770 for (ptrdiff_t i = 0; i < size; i++)
4771 h->next[i] = -1;
4772
4773 h->index = hash_table_alloc_bytes (index_size * sizeof *h->index);
4774 for (ptrdiff_t i = 0; i < index_size; i++)
4775 h->index[i] = -1;
4721 4776
4722 /* Recompute the actual hash codes for each entry in the table. 4777 /* Recompute the actual hash codes for each entry in the table.
4723 Order is still invalid. */ 4778 Order is still invalid. */
@@ -4843,7 +4898,7 @@ hash_clear (struct Lisp_Hash_Table *h)
4843 if (h->count > 0) 4898 if (h->count > 0)
4844 { 4899 {
4845 ptrdiff_t size = HASH_TABLE_SIZE (h); 4900 ptrdiff_t size = HASH_TABLE_SIZE (h);
4846 memclear (xvector_contents (h->hash), size * word_size); 4901 memclear (h->hash, size * word_size);
4847 for (ptrdiff_t i = 0; i < size; i++) 4902 for (ptrdiff_t i = 0; i < size; i++)
4848 { 4903 {
4849 set_hash_next_slot (h, i, i < size - 1 ? i + 1 : -1); 4904 set_hash_next_slot (h, i, i < size - 1 ? i + 1 : -1);
@@ -4851,8 +4906,8 @@ hash_clear (struct Lisp_Hash_Table *h)
4851 set_hash_value_slot (h, i, Qnil); 4906 set_hash_value_slot (h, i, Qnil);
4852 } 4907 }
4853 4908
4854 for (ptrdiff_t i = 0; i < ASIZE (h->index); i++) 4909 for (ptrdiff_t i = 0; i < h->index_size; i++)
4855 ASET (h->index, i, make_fixnum (-1)); 4910 h->index[i] = -1;
4856 4911
4857 h->next_free = 0; 4912 h->next_free = 0;
4858 h->count = 0; 4913 h->count = 0;
@@ -4890,7 +4945,7 @@ keep_entry_p (hash_table_weakness_t weakness,
4890bool 4945bool
4891sweep_weak_table (struct Lisp_Hash_Table *h, bool remove_entries_p) 4946sweep_weak_table (struct Lisp_Hash_Table *h, bool remove_entries_p)
4892{ 4947{
4893 ptrdiff_t n = gc_asize (h->index); 4948 ptrdiff_t n = h->index_size;
4894 bool marked = false; 4949 bool marked = false;
4895 4950
4896 for (ptrdiff_t bucket = 0; bucket < n; ++bucket) 4951 for (ptrdiff_t bucket = 0; bucket < n; ++bucket)
@@ -4928,8 +4983,7 @@ sweep_weak_table (struct Lisp_Hash_Table *h, bool remove_entries_p)
4928 /* Clear key, value, and hash. */ 4983 /* Clear key, value, and hash. */
4929 set_hash_key_slot (h, i, HASH_UNUSED_ENTRY_KEY); 4984 set_hash_key_slot (h, i, HASH_UNUSED_ENTRY_KEY);
4930 set_hash_value_slot (h, i, Qnil); 4985 set_hash_value_slot (h, i, Qnil);
4931 if (!NILP (h->hash)) 4986 set_hash_hash_slot (h, i, Qnil);
4932 set_hash_hash_slot (h, i, Qnil);
4933 4987
4934 eassert (h->count != 0); 4988 eassert (h->count != 0);
4935 h->count--; 4989 h->count--;
@@ -5563,7 +5617,7 @@ DEFUN ("internal--hash-table-histogram",
5563 struct Lisp_Hash_Table *h = check_hash_table (hash_table); 5617 struct Lisp_Hash_Table *h = check_hash_table (hash_table);
5564 ptrdiff_t size = HASH_TABLE_SIZE (h); 5618 ptrdiff_t size = HASH_TABLE_SIZE (h);
5565 ptrdiff_t *freq = xzalloc (size * sizeof *freq); 5619 ptrdiff_t *freq = xzalloc (size * sizeof *freq);
5566 ptrdiff_t index_size = ASIZE (h->index); 5620 ptrdiff_t index_size = h->index_size;
5567 for (ptrdiff_t i = 0; i < index_size; i++) 5621 for (ptrdiff_t i = 0; i < index_size; i++)
5568 { 5622 {
5569 ptrdiff_t n = 0; 5623 ptrdiff_t n = 0;
@@ -5591,7 +5645,7 @@ Internal use only. */)
5591{ 5645{
5592 struct Lisp_Hash_Table *h = check_hash_table (hash_table); 5646 struct Lisp_Hash_Table *h = check_hash_table (hash_table);
5593 Lisp_Object ret = Qnil; 5647 Lisp_Object ret = Qnil;
5594 ptrdiff_t index_size = ASIZE (h->index); 5648 ptrdiff_t index_size = h->index_size;
5595 for (ptrdiff_t i = 0; i < index_size; i++) 5649 for (ptrdiff_t i = 0; i < index_size; i++)
5596 { 5650 {
5597 Lisp_Object bucket = Qnil; 5651 Lisp_Object bucket = Qnil;
@@ -5612,8 +5666,7 @@ DEFUN ("internal--hash-table-index-size",
5612 (Lisp_Object hash_table) 5666 (Lisp_Object hash_table)
5613{ 5667{
5614 struct Lisp_Hash_Table *h = check_hash_table (hash_table); 5668 struct Lisp_Hash_Table *h = check_hash_table (hash_table);
5615 ptrdiff_t index_size = ASIZE (h->index); 5669 return make_int (h->index_size);
5616 return make_int (index_size);
5617} 5670}
5618 5671
5619 5672
diff --git a/src/lisp.h b/src/lisp.h
index f863df6bca0..dd457392cca 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -2450,25 +2450,28 @@ struct Lisp_Hash_Table
2450 The table is physically split into three vectors (hash, next, 2450 The table is physically split into three vectors (hash, next,
2451 key_and_value) which may or may not be beneficial. */ 2451 key_and_value) which may or may not be beneficial. */
2452 2452
2453 /* Vector of hash codes, or nil if the table needs rehashing. 2453 /* Bucket vector. An entry of -1 indicates no item is present,
2454 If the I-th entry is unused, then hash[I] should be nil. */ 2454 and a nonnegative entry is the index of the first item in
2455 Lisp_Object hash; 2455 a collision chain.
2456 This vector is index_size entries long.
2457 If index_size is 1 (and table_size is 0), then this is the
2458 constant read-only vector {-1}, shared between all instances.
2459 Otherwise it is heap-allocated. */
2460 ptrdiff_t *index;
2461 ptrdiff_t index_size; /* Size of the index vector. */
2462
2463 ptrdiff_t table_size; /* Size of the next and hash vectors. */
2464
2465 /* Vector of hash codes. Each entry is either a fixnum, or nil if unused.
2466 This vector is table_size entries long. */
2467 Lisp_Object *hash;
2456 2468
2457 /* Vector used to chain entries. If entry I is free, next[I] is the 2469 /* Vector used to chain entries. If entry I is free, next[I] is the
2458 entry number of the next free item. If entry I is non-free, 2470 entry number of the next free item. If entry I is non-free,
2459 next[I] is the index of the next entry in the collision chain, 2471 next[I] is the index of the next entry in the collision chain,
2460 or -1 if there is such entry. */ 2472 or -1 if there is no such entry.
2461 Lisp_Object next; 2473 This vector is table_size entries long. */
2462 2474 ptrdiff_t *next;
2463 /* Bucket vector. An entry of -1 indicates no item is present,
2464 and a nonnegative entry is the index of the first item in
2465 a collision chain. This vector's size can be larger than the
2466 hash table size to reduce collisions. */
2467 Lisp_Object index;
2468
2469 /* Only the fields above are traced normally by the GC. The ones after
2470 'index' are special and are either ignored by the GC or traced in
2471 a special way (e.g. because of weakness). */
2472 2475
2473 /* Number of key/value entries in the table. */ 2476 /* Number of key/value entries in the table. */
2474 ptrdiff_t count; 2477 ptrdiff_t count;
@@ -2494,8 +2497,9 @@ struct Lisp_Hash_Table
2494 /* Vector of keys and values. The key of item I is found at index 2497 /* Vector of keys and values. The key of item I is found at index
2495 2 * I, the value is found at index 2 * I + 1. 2498 2 * I, the value is found at index 2 * I + 1.
2496 If the key is HASH_UNUSED_ENTRY_KEY, then this slot is unused. 2499 If the key is HASH_UNUSED_ENTRY_KEY, then this slot is unused.
2497 This is gc_marked specially if the table is weak. */ 2500 This is gc_marked specially if the table is weak.
2498 Lisp_Object key_and_value; 2501 This vector is 2 * table_size entries long. */
2502 Lisp_Object *key_and_value;
2499 2503
2500 /* The comparison and hash functions. */ 2504 /* The comparison and hash functions. */
2501 struct hash_table_test test; 2505 struct hash_table_test test;
@@ -2506,9 +2510,6 @@ struct Lisp_Hash_Table
2506 struct Lisp_Hash_Table *next_weak; 2510 struct Lisp_Hash_Table *next_weak;
2507} GCALIGNED_STRUCT; 2511} GCALIGNED_STRUCT;
2508 2512
2509/* Sanity-check pseudovector layout. */
2510verify (offsetof (struct Lisp_Hash_Table, hash) == header_size);
2511
2512/* Key value that marks an unused hash table entry. */ 2513/* Key value that marks an unused hash table entry. */
2513#define HASH_UNUSED_ENTRY_KEY Qunbound 2514#define HASH_UNUSED_ENTRY_KEY Qunbound
2514 2515
@@ -2539,28 +2540,31 @@ XHASH_TABLE (Lisp_Object a)
2539INLINE Lisp_Object 2540INLINE Lisp_Object
2540HASH_KEY (const struct Lisp_Hash_Table *h, ptrdiff_t idx) 2541HASH_KEY (const struct Lisp_Hash_Table *h, ptrdiff_t idx)
2541{ 2542{
2542 return AREF (h->key_and_value, 2 * idx); 2543 eassert (idx >= 0 && idx < h->table_size);
2544 return h->key_and_value[2 * idx];
2543} 2545}
2544 2546
2545/* Value is the value part of entry IDX in hash table H. */ 2547/* Value is the value part of entry IDX in hash table H. */
2546INLINE Lisp_Object 2548INLINE Lisp_Object
2547HASH_VALUE (const struct Lisp_Hash_Table *h, ptrdiff_t idx) 2549HASH_VALUE (const struct Lisp_Hash_Table *h, ptrdiff_t idx)
2548{ 2550{
2549 return AREF (h->key_and_value, 2 * idx + 1); 2551 eassert (idx >= 0 && idx < h->table_size);
2552 return h->key_and_value[2 * idx + 1];
2550} 2553}
2551 2554
2552/* Value is the hash code computed for entry IDX in hash table H. */ 2555/* Value is the hash code computed for entry IDX in hash table H. */
2553INLINE Lisp_Object 2556INLINE Lisp_Object
2554HASH_HASH (const struct Lisp_Hash_Table *h, ptrdiff_t idx) 2557HASH_HASH (const struct Lisp_Hash_Table *h, ptrdiff_t idx)
2555{ 2558{
2556 return AREF (h->hash, idx); 2559 eassert (idx >= 0 && idx < h->table_size);
2560 return h->hash[idx];
2557} 2561}
2558 2562
2559/* Value is the size of hash table H. */ 2563/* Value is the size of hash table H. */
2560INLINE ptrdiff_t 2564INLINE ptrdiff_t
2561HASH_TABLE_SIZE (const struct Lisp_Hash_Table *h) 2565HASH_TABLE_SIZE (const struct Lisp_Hash_Table *h)
2562{ 2566{
2563 return ASIZE (h->next); 2567 return h->table_size;
2564} 2568}
2565 2569
2566/* Compute hash value for KEY in hash table H. */ 2570/* Compute hash value for KEY in hash table H. */
@@ -3781,13 +3785,15 @@ vcopy (Lisp_Object v, ptrdiff_t offset, Lisp_Object const *args,
3781INLINE void 3785INLINE void
3782set_hash_key_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) 3786set_hash_key_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val)
3783{ 3787{
3784 gc_aset (h->key_and_value, 2 * idx, val); 3788 eassert (idx >= 0 && idx < h->table_size);
3789 h->key_and_value[2 * idx] = val;
3785} 3790}
3786 3791
3787INLINE void 3792INLINE void
3788set_hash_value_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) 3793set_hash_value_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val)
3789{ 3794{
3790 gc_aset (h->key_and_value, 2 * idx + 1, val); 3795 eassert (idx >= 0 && idx < h->table_size);
3796 h->key_and_value[2 * idx + 1] = val;;
3791} 3797}
3792 3798
3793/* Use these functions to set Lisp_Object 3799/* Use these functions to set Lisp_Object
@@ -4458,6 +4464,9 @@ extern void syms_of_alloc (void);
4458extern struct buffer *allocate_buffer (void) ATTRIBUTE_RETURNS_NONNULL; 4464extern struct buffer *allocate_buffer (void) ATTRIBUTE_RETURNS_NONNULL;
4459extern int valid_lisp_object_p (Lisp_Object); 4465extern int valid_lisp_object_p (Lisp_Object);
4460 4466
4467void *hash_table_alloc_bytes (ptrdiff_t nbytes);
4468void hash_table_free_bytes (void *p, ptrdiff_t nbytes);
4469
4461/* Defined in gmalloc.c. */ 4470/* Defined in gmalloc.c. */
4462#if !defined DOUG_LEA_MALLOC && !defined HYBRID_MALLOC && !defined SYSTEM_MALLOC 4471#if !defined DOUG_LEA_MALLOC && !defined HYBRID_MALLOC && !defined SYSTEM_MALLOC
4463extern size_t __malloc_extra_blocks; 4472extern size_t __malloc_extra_blocks;
diff --git a/src/pdumper.c b/src/pdumper.c
index e4349f0cb17..8a93c45e07b 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -2648,12 +2648,13 @@ dump_vectorlike_generic (struct dump_context *ctx,
2648 2648
2649/* Return a vector of KEY, VALUE pairs in the given hash table H. 2649/* Return a vector of KEY, VALUE pairs in the given hash table H.
2650 No room for growth is included. */ 2650 No room for growth is included. */
2651static Lisp_Object 2651static Lisp_Object *
2652hash_table_contents (struct Lisp_Hash_Table *h) 2652hash_table_contents (struct Lisp_Hash_Table *h)
2653{ 2653{
2654 ptrdiff_t old_size = HASH_TABLE_SIZE (h); 2654 ptrdiff_t old_size = HASH_TABLE_SIZE (h);
2655 ptrdiff_t size = h->count; 2655 ptrdiff_t size = h->count;
2656 Lisp_Object key_and_value = make_uninit_vector (2 * size); 2656 Lisp_Object *key_and_value = hash_table_alloc_bytes (2 * size
2657 * sizeof *key_and_value);
2657 ptrdiff_t n = 0; 2658 ptrdiff_t n = 0;
2658 2659
2659 /* Make sure key_and_value ends up in the same order; charset.c 2660 /* Make sure key_and_value ends up in the same order; charset.c
@@ -2662,8 +2663,8 @@ hash_table_contents (struct Lisp_Hash_Table *h)
2662 for (ptrdiff_t i = 0; i < old_size; i++) 2663 for (ptrdiff_t i = 0; i < old_size; i++)
2663 if (!NILP (HASH_HASH (h, i))) 2664 if (!NILP (HASH_HASH (h, i)))
2664 { 2665 {
2665 ASET (key_and_value, n++, HASH_KEY (h, i)); 2666 key_and_value[n++] = HASH_KEY (h, i);
2666 ASET (key_and_value, n++, HASH_VALUE (h, i)); 2667 key_and_value[n++] = HASH_VALUE (h, i);
2667 } 2668 }
2668 2669
2669 return key_and_value; 2670 return key_and_value;
@@ -2698,15 +2699,38 @@ static void
2698hash_table_freeze (struct Lisp_Hash_Table *h) 2699hash_table_freeze (struct Lisp_Hash_Table *h)
2699{ 2700{
2700 h->key_and_value = hash_table_contents (h); 2701 h->key_and_value = hash_table_contents (h);
2701 eassert (ASIZE (h->key_and_value) == h->count * 2); 2702 h->next = NULL;
2702 h->next = Qnil; 2703 h->hash = NULL;
2703 h->hash = Qnil; 2704 h->index = NULL;
2704 h->index = Qnil; 2705 h->table_size = 0;
2705 h->count = 0; 2706 h->index_size = 0;
2706 h->frozen_test = hash_table_std_test (&h->test); 2707 h->frozen_test = hash_table_std_test (&h->test);
2707} 2708}
2708 2709
2709static dump_off 2710static dump_off
2711dump_hash_table_contents (struct dump_context *ctx, struct Lisp_Hash_Table *h)
2712{
2713 dump_align_output (ctx, DUMP_ALIGNMENT);
2714 dump_off start_offset = ctx->offset;
2715 ptrdiff_t n = 2 * h->count;
2716
2717 struct dump_flags old_flags = ctx->flags;
2718 ctx->flags.pack_objects = true;
2719
2720 for (ptrdiff_t i = 0; i < n; i++)
2721 {
2722 Lisp_Object out;
2723 const Lisp_Object *slot = &h->key_and_value[i];
2724 dump_object_start (ctx, &out, sizeof out);
2725 dump_field_lv (ctx, &out, slot, slot, WEIGHT_STRONG);
2726 dump_object_finish (ctx, &out, sizeof out);
2727 }
2728
2729 ctx->flags = old_flags;
2730 return start_offset;
2731}
2732
2733static dump_off
2710dump_hash_table (struct dump_context *ctx, Lisp_Object object) 2734dump_hash_table (struct dump_context *ctx, Lisp_Object object)
2711{ 2735{
2712#if CHECK_STRUCTS && !defined HASH_Lisp_Hash_Table_6D63EDB618 2736#if CHECK_STRUCTS && !defined HASH_Lisp_Hash_Table_6D63EDB618
@@ -2721,15 +2745,21 @@ dump_hash_table (struct dump_context *ctx, Lisp_Object object)
2721 2745
2722 START_DUMP_PVEC (ctx, &hash->header, struct Lisp_Hash_Table, out); 2746 START_DUMP_PVEC (ctx, &hash->header, struct Lisp_Hash_Table, out);
2723 dump_pseudovector_lisp_fields (ctx, &out->header, &hash->header); 2747 dump_pseudovector_lisp_fields (ctx, &out->header, &hash->header);
2724 /* TODO: dump the hash bucket vectors synchronously here to keep 2748 DUMP_FIELD_COPY (out, hash, count);
2725 them as close to the hash table as possible. */
2726 DUMP_FIELD_COPY (out, hash, weakness); 2749 DUMP_FIELD_COPY (out, hash, weakness);
2727 DUMP_FIELD_COPY (out, hash, purecopy); 2750 DUMP_FIELD_COPY (out, hash, purecopy);
2728 DUMP_FIELD_COPY (out, hash, mutable); 2751 DUMP_FIELD_COPY (out, hash, mutable);
2729 DUMP_FIELD_COPY (out, hash, frozen_test); 2752 DUMP_FIELD_COPY (out, hash, frozen_test);
2730 dump_field_lv (ctx, out, hash, &hash->key_and_value, WEIGHT_STRONG); 2753 if (hash->key_and_value)
2754 dump_field_fixup_later (ctx, out, hash, &hash->key_and_value);
2731 eassert (hash->next_weak == NULL); 2755 eassert (hash->next_weak == NULL);
2732 return finish_dump_pvec (ctx, &out->header); 2756 dump_off offset = finish_dump_pvec (ctx, &out->header);
2757 if (hash->key_and_value)
2758 dump_remember_fixup_ptr_raw
2759 (ctx,
2760 offset + dump_offsetof (struct Lisp_Hash_Table, key_and_value),
2761 dump_hash_table_contents (ctx, hash));
2762 return offset;
2733} 2763}
2734 2764
2735static dump_off 2765static dump_off
diff --git a/src/print.c b/src/print.c
index cc8df639f4f..c27c66ae40a 100644
--- a/src/print.c
+++ b/src/print.c
@@ -1455,8 +1455,8 @@ print_preprocess (Lisp_Object obj)
1455 if (HASH_TABLE_P (obj)) 1455 if (HASH_TABLE_P (obj))
1456 { 1456 {
1457 struct Lisp_Hash_Table *h = XHASH_TABLE (obj); 1457 struct Lisp_Hash_Table *h = XHASH_TABLE (obj);
1458 obj = h->key_and_value; 1458 pp_stack_push_values (h->key_and_value,
1459 continue; 1459 2 * h->table_size);
1460 } 1460 }
1461 break; 1461 break;
1462 } 1462 }