From: Pip Cet <pipcet@protonmail.com>
To: "Gerd Möllmann" <gerd.moellmann@gmail.com>
Cc: Ihor Radchenko <yantar92@posteo.net>,
Eli Zaretskii <eliz@gnu.org>,
monnier@iro.umontreal.ca, emacs-devel@gnu.org,
eller.helmut@gmail.com
Subject: Re: MPS: dangling markers
Date: Sun, 30 Jun 2024 20:15:36 +0000 [thread overview]
Message-ID: <HA0pkHH7MG2SiNbZPsDK_2WAs3QStL9QKftC23ZPbTJX6F-bzskcn9sOqIMqggB1LYvRnPUBGLE0pzGJpAd-UsJpfSb6yR5f_CYcUxYK-II=@protonmail.com> (raw)
In-Reply-To: <m21q4egrqz.fsf@pro2.fritz.box>
[-- Attachment #1: Type: text/plain, Size: 962 bytes --]
On Sunday, June 30th, 2024 at 19:22, Gerd Möllmann <gerd.moellmann@gmail.com> wrote:
> Thanks! What do youo think about making a patch containing only your
> weak hash tables, and leaving the BUF_MARKERS alone for now?
I think that's the best way forward. Patch attached.
> That way
> igc could support the existing uses of weak hash tables (I remember one
> in the CLOS department somehwere), and they would be somewhat tested.
> Don't remember if we have unit tests for them.
It seems MPS isn't very eager about splatting weak references during ordinary automatic GC, FWIW. What I'm observing with
(while t
(dotimes (i 10000)
(puthash (cons 1 2) (cons 3 4) table))
(message "%S" (hash-table-count table))
(sit-for 0.1))
is that the hash table starts out at 0, grows quickly, resets to count=0 once, then keeps growing and never splats any references after that. It's quite possible this is a bug in my code, of course.
Pip
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0003-mps-weak-hash-tables.patch --]
[-- Type: text/x-patch; name=0003-mps-weak-hash-tables.patch, Size: 47583 bytes --]
diff --git a/src/alloc.c b/src/alloc.c
index 09b51ba2a08..5d47d3f7851 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -6230,6 +6230,16 @@ purecopy (Lisp_Object obj)
obj = make_lisp_hash_table (purecopy_hash_table (table));
}
+ else if (WEAK_HASH_TABLE_P (obj))
+ {
+ /* Instead, add the hash table to the list of pinned objects,
+ so that it will be marked during GC. */
+ struct pinned_object *o = xmalloc (sizeof *o);
+ o->object = obj;
+ o->next = pinned_objects;
+ pinned_objects = o;
+ return obj; /* Don't hash cons it. */
+ }
else if (CLOSUREP (obj) || VECTORP (obj) || RECORDP (obj))
{
struct Lisp_Vector *objp = XVECTOR (obj);
diff --git a/src/data.c b/src/data.c
index dcf869c1a0e..996f57e2123 100644
--- a/src/data.c
+++ b/src/data.c
@@ -251,6 +251,7 @@ DEFUN ("cl-type-of", Fcl_type_of, Scl_type_of, 1, 1, 0,
case PVEC_BOOL_VECTOR: return Qbool_vector;
case PVEC_FRAME: return Qframe;
case PVEC_HASH_TABLE: return Qhash_table;
+ case PVEC_WEAK_HASH_TABLE: return Qhash_table;
case PVEC_OBARRAY: return Qobarray;
case PVEC_FONT:
if (FONT_SPEC_P (object))
diff --git a/src/fns.c b/src/fns.c
index f7603626454..3049ae37d65 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -4583,12 +4583,28 @@ set_hash_next_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val)
eassert (idx >= 0 && idx < h->table_size);
h->next[idx] = val;
}
+
+static void
+set_weak_hash_next_slot (struct Lisp_Weak_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val)
+{
+ eassert (idx >= 0 && idx < XFIXNUM (h->strong->table_size));
+ h->strong->next[idx].lisp_object = make_fixnum (val);
+}
+
static void
set_hash_hash_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, hash_hash_t val)
{
eassert (idx >= 0 && idx < h->table_size);
h->hash[idx] = val;
}
+
+static void
+set_weak_hash_hash_slot (struct Lisp_Weak_Hash_Table *h, ptrdiff_t idx, hash_hash_t val)
+{
+ eassert (idx >= 0 && idx < XFIXNUM (h->strong->table_size));
+ h->strong->hash[idx].lisp_object = make_fixnum (val);
+}
+
static void
set_hash_index_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val)
{
@@ -4596,6 +4612,13 @@ set_hash_index_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val)
h->index[idx] = val;
}
+static void
+set_weak_hash_index_slot (struct Lisp_Weak_Hash_Table *h, ptrdiff_t idx, ptrdiff_t val)
+{
+ eassert (idx >= 0 && idx < weak_hash_table_index_size (h));
+ h->strong->index[idx].lisp_object = make_fixnum (val);
+}
+
/* If OBJ is a Lisp hash table, return a pointer to its struct
Lisp_Hash_Table. Otherwise, signal an error. */
@@ -4606,6 +4629,14 @@ check_hash_table (Lisp_Object obj)
return XHASH_TABLE (obj);
}
+static struct Lisp_Weak_Hash_Table *
+check_maybe_weak_hash_table (Lisp_Object obj)
+{
+ if (WEAK_HASH_TABLE_P (obj))
+ return XWEAK_HASH_TABLE (obj);
+ return NULL;
+}
+
/* Value is the next integer I >= N, N >= 0 which is "almost" a prime
number. A number is "almost" a prime number if it is not divisible
@@ -4687,6 +4718,13 @@ HASH_NEXT (struct Lisp_Hash_Table *h, ptrdiff_t idx)
return h->next[idx];
}
+static ptrdiff_t
+WEAK_HASH_NEXT (struct Lisp_Weak_Hash_Table *h, ptrdiff_t idx)
+{
+ eassert (idx >= 0 && idx < XFIXNUM (h->strong->table_size));
+ return XFIXNUM (h->strong->next[idx].lisp_object);
+}
+
/* Return the index of the element in hash table H that is the start
of the collision list at index IDX, or -1 if the list is empty. */
@@ -4697,6 +4735,13 @@ HASH_INDEX (struct Lisp_Hash_Table *h, ptrdiff_t idx)
return h->index[idx];
}
+static ptrdiff_t
+WEAK_HASH_INDEX (struct Lisp_Weak_Hash_Table *h, ptrdiff_t idx)
+{
+ eassert (idx >= 0 && idx < weak_hash_table_index_size (h));
+ return XFIXNUM (h->strong->index[idx].lisp_object);
+}
+
/* Restore a hash table's mutability after the critical section exits. */
static void
@@ -4821,6 +4866,48 @@ allocate_hash_table (void)
return ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Hash_Table, PVEC_HASH_TABLE);
}
+static struct Lisp_Weak_Hash_Table *
+allocate_weak_hash_table (hash_table_weakness_t weak, ssize_t size, ssize_t index_bits)
+{
+ struct Lisp_Weak_Hash_Table *ret =
+ ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Weak_Hash_Table, PVEC_WEAK_HASH_TABLE);
+ ret->strong = igc_alloc_weak_hash_table_strong_part (weak, size, index_bits);
+ ret->strong->hash = ret->strong->entries + 0;
+ ret->strong->value = ret->strong->entries + 1 * size;
+ ret->strong->next = ret->strong->entries + 2 * size;
+ ret->strong->index = ret->strong->entries + 3 * size;
+ ret->weak = igc_alloc_weak_hash_table_weak_part (weak, size, index_bits);
+ ret->strong->weak = ret->weak;
+ ret->weak->strong = ret->strong;
+ ret->strong->key = ret->weak->entries;
+ return ret;
+}
+
+Lisp_Object
+strengthen_hash_table (Lisp_Object weak)
+{
+ Lisp_Object ret = make_hash_table (XWEAK_HASH_TABLE (weak)->strong->test, 0, Weak_None, 0);
+
+ Lisp_Object k, v;
+ DOHASH_WEAK (XWEAK_HASH_TABLE (weak), k, v)
+ {
+ Fputhash (k, v, ret);
+ }
+
+ return ret;
+}
+
+Lisp_Object
+strengthen_hash_table_for_dump (struct Lisp_Weak_Hash_Table *weak)
+{
+ if (!NILP (weak->dump_replacement))
+ return weak->dump_replacement;
+ Lisp_Object ret = strengthen_hash_table (make_lisp_weak_hash_table (weak));
+ weak->dump_replacement = ret;
+
+ return ret;
+}
+
/* Compute the size of the index (as log2) from the table capacity. */
static int
compute_hash_index_bits (hash_idx_t size)
@@ -4855,6 +4942,54 @@ compute_hash_index_bits (hash_idx_t size)
`purecopy' when Emacs is being dumped. Such tables can no longer be
changed after purecopy. */
+Lisp_Object
+make_weak_hash_table (const struct hash_table_test *test, EMACS_INT size,
+ hash_table_weakness_t weak, bool purecopy)
+{
+ eassert (!purecopy);
+ eassert (SYMBOLP (test->name));
+ eassert (0 <= size && size <= min (MOST_POSITIVE_FIXNUM, PTRDIFF_MAX));
+
+ if (size < 65)
+ size = 65;
+
+ struct Lisp_Weak_Hash_Table *h = allocate_weak_hash_table (weak, size, compute_hash_index_bits (size));
+
+ h->strong->test = test;
+ h->strong->weakness = weak;
+ h->strong->count = make_fixnum (0);
+ h->strong->table_size = make_fixnum (size);
+
+ if (size == 0)
+ {
+ emacs_abort ();
+ }
+ else
+ {
+ for (ptrdiff_t i = 0; i < size; i++)
+ {
+ h->strong->key[i].lisp_object = HASH_UNUSED_ENTRY_KEY;
+ h->strong->value[i].ptr = 0;
+ }
+
+ for (ptrdiff_t i = 0; i < size - 1; i++)
+ h->strong->next[i].lisp_object = make_fixnum(i + 1);
+ h->strong->next[size - 1].lisp_object = make_fixnum(-1);
+
+ int index_bits = compute_hash_index_bits (size);
+ h->strong->index_bits = make_fixnum (index_bits);
+ ptrdiff_t index_size = weak_hash_table_index_size (h);
+ for (ptrdiff_t i = 0; i < index_size; i++)
+ h->strong->index[i].lisp_object = make_fixnum (-1);
+
+ h->strong->next_free = make_fixnum (0);
+ }
+
+ h->strong->purecopy = purecopy;
+ h->strong->mutable = true;
+ return make_lisp_weak_hash_table (h);
+}
+
Lisp_Object
make_hash_table (const struct hash_table_test *test, EMACS_INT size,
hash_table_weakness_t weak, bool purecopy)
@@ -4862,6 +4997,10 @@ make_hash_table (const struct hash_table_test *test, EMACS_INT size,
eassert (SYMBOLP (test->name));
eassert (0 <= size && size <= min (MOST_POSITIVE_FIXNUM, PTRDIFF_MAX));
+ if (weak != Weak_None)
+ {
+ return make_weak_hash_table (test, size, weak, purecopy);
+ }
struct Lisp_Hash_Table *h = allocate_hash_table ();
h->test = test;
@@ -4961,6 +5100,13 @@ hash_index_index (struct Lisp_Hash_Table *h, hash_hash_t hash)
return knuth_hash (hash, h->index_bits);
}
+/* Compute index into the index vector from a hash value. */
+static inline ptrdiff_t
+weak_hash_index_index (struct Lisp_Weak_Hash_Table *h, hash_hash_t hash)
+{
+ return knuth_hash (hash, XFIXNUM (h->strong->index_bits));
+}
+
/* Resize hash table H if it's too full. If H cannot be resized
because it's already too large, throw an error. */
@@ -5042,6 +5188,71 @@ maybe_resize_hash_table (struct Lisp_Hash_Table *h)
}
}
+static void
+maybe_resize_weak_hash_table (struct Lisp_Weak_Hash_Table *h)
+{
+ if (XFIXNUM (h->strong->next_free) < 0)
+ {
+ ptrdiff_t old_size = WEAK_HASH_TABLE_SIZE (h);
+ ptrdiff_t min_size = 6;
+ ptrdiff_t base_size = min (max (old_size, min_size), PTRDIFF_MAX / 2);
+ /* Grow aggressively at small sizes, then just double. */
+ ptrdiff_t new_size =
+ old_size == 0
+ ? min_size
+ : (base_size <= 64 ? base_size * 4 : base_size * 2);
+
+ ptrdiff_t index_bits = compute_hash_index_bits (new_size);
+
+ struct Lisp_Weak_Hash_Table_Strong_Part *strong =
+ igc_alloc_weak_hash_table_strong_part (h->strong->weakness, new_size, index_bits);
+ struct Lisp_Weak_Hash_Table_Weak_Part *weak =
+ igc_alloc_weak_hash_table_weak_part (h->strong->weakness, new_size, index_bits);
+
+ memcpy (strong, h->strong, sizeof *strong);
+
+ strong->hash = strong->entries + 0;
+ strong->value = strong->entries + 1 * new_size;
+ strong->next = strong->entries + 2 * new_size;
+ strong->index = strong->entries + 3 * new_size;
+ strong->key = weak->entries;
+ strong->count = make_fixnum (0);
+ weak->strong = strong;
+ strong->weak = weak;
+
+ for (ptrdiff_t i = 0; i < new_size - 1; i++)
+ strong->next[i].lisp_object = make_fixnum (i + 1);
+ strong->next[new_size - 1].lisp_object = make_fixnum (-1);
+
+ for (ptrdiff_t i = 0; i < new_size; i++)
+ {
+ strong->key[i].lisp_object = HASH_UNUSED_ENTRY_KEY;
+ strong->value[i].lisp_object = Qnil;
+ }
+
+ ptrdiff_t index_size = (ptrdiff_t)1 << index_bits;
+ for (ptrdiff_t i = 0; i < index_size; i++)
+ strong->index[i].lisp_object = make_fixnum (-1);
+
+ strong->index_bits = make_fixnum (index_bits);
+ strong->table_size = make_fixnum (new_size);
+ strong->next_free = make_fixnum (0);
+
+ struct Lisp_Weak_Hash_Table *pseudo =
+ ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Weak_Hash_Table, PVEC_WEAK_HASH_TABLE);
+ pseudo->strong = strong;
+ pseudo->weak = weak;
+ Lisp_Object k, v;
+ DOHASH_WEAK (h, k, v)
+ {
+ Fputhash (k, v, make_lisp_weak_hash_table (pseudo));
+ }
+
+ h->strong = strong;
+ h->weak = weak;
+ }
+}
+
static const struct hash_table_test *
hash_table_test_from_std (hash_table_std_test_t test)
{
@@ -5058,6 +5269,7 @@ hash_table_test_from_std (hash_table_std_test_t test)
void
hash_table_thaw (Lisp_Object hash_table)
{
+ eassert (HASH_TABLE_P (hash_table));
struct Lisp_Hash_Table *h = XHASH_TABLE (hash_table);
/* Freezing discarded most non-essential information; recompute it.
@@ -5173,6 +5385,24 @@ hash_lookup_with_hash (struct Lisp_Hash_Table *h,
return -1;
}
+/* Look up KEY with hash HASH in weak hash table H.
+ Return entry index or -1 if none. */
+static ptrdiff_t
+weak_hash_lookup_with_hash (struct Lisp_Weak_Hash_Table *h,
+ Lisp_Object key, hash_hash_t hash)
+{
+ ptrdiff_t start_of_bucket = weak_hash_index_index (h, hash);
+ for (ptrdiff_t i = WEAK_HASH_INDEX (h, start_of_bucket);
+ 0 <= i; i = WEAK_HASH_NEXT (h, i))
+ if (EQ (key, WEAK_HASH_KEY (h, i))
+ || (h->strong->test->cmpfn
+ && hash == WEAK_HASH_HASH (h, i)
+ && !NILP (h->strong->test->cmpfn (key, WEAK_HASH_KEY (h, i), NULL))))
+ return i;
+
+ return -1;
+}
+
/* Look up KEY in table H. Return entry index or -1 if none. */
ptrdiff_t
hash_lookup (struct Lisp_Hash_Table *h, Lisp_Object key)
@@ -5180,6 +5410,12 @@ hash_lookup (struct Lisp_Hash_Table *h, Lisp_Object key)
return hash_lookup_with_hash (h, key, hash_from_key (h, key));
}
+ptrdiff_t
+weak_hash_lookup (struct Lisp_Weak_Hash_Table *h, Lisp_Object key)
+{
+ return weak_hash_lookup_with_hash (h, key, weak_hash_from_key (h, key));
+}
+
/* Look up KEY in hash table H. Return its hash value in *PHASH.
Value is the index of the entry in H matching KEY, or -1 if not found. */
ptrdiff_t
@@ -5229,6 +5465,36 @@ hash_put (struct Lisp_Hash_Table *h, Lisp_Object key, Lisp_Object value,
return i;
}
+/* Put an entry into weak hash table H that associates KEY with VALUE.
+ HASH is a previously computed hash code of KEY.
+ Value is the index of the entry in H matching KEY. */
+
+ptrdiff_t
+weak_hash_put (struct Lisp_Weak_Hash_Table *h, Lisp_Object key, Lisp_Object value,
+ hash_hash_t hash)
+{
+ //eassert (!hash_unused_entry_key_p (key));
+ /* Increment count after resizing because resizing may fail. */
+ maybe_resize_weak_hash_table (h);
+ h->strong->count = make_fixnum (XFIXNUM (h->strong->count) + 1);
+
+ /* Store key/value in the key_and_value vector. */
+ ptrdiff_t i = XFIXNUM (h->strong->next_free);
+ //eassert (hash_unused_entry_key_p (HASH_KEY (h, i)));
+ h->strong->next_free = make_fixnum (WEAK_HASH_NEXT (h, i));
+ set_weak_hash_key_slot (h, i, key);
+ set_weak_hash_value_slot (h, i, value);
+
+ /* Remember its hash code. */
+ set_weak_hash_hash_slot (h, i, hash);
+
+ /* Add new entry to its collision chain. */
+ ptrdiff_t start_of_bucket = weak_hash_index_index (h, hash);
+ set_weak_hash_next_slot (h, i, WEAK_HASH_INDEX (h, start_of_bucket));
+ set_weak_hash_index_slot (h, start_of_bucket, i);
+ return i;
+}
+
/* Remove the entry matching KEY from hash table H, if there is one. */
@@ -5270,6 +5536,82 @@ hash_remove_from_table (struct Lisp_Hash_Table *h, Lisp_Object key)
}
+/* Remove the entry matching KEY from weak hash table H, if there is one. */
+
+void
+weak_hash_remove_from_table (struct Lisp_Weak_Hash_Table *h, Lisp_Object key)
+{
+ hash_hash_t hashval = weak_hash_from_key (h, key);
+ ptrdiff_t start_of_bucket = weak_hash_index_index (h, hashval);
+ ptrdiff_t prev = -1;
+
+ for (ptrdiff_t i = WEAK_HASH_INDEX (h, start_of_bucket);
+ 0 <= i;
+ i = WEAK_HASH_NEXT (h, i))
+ {
+ if (EQ (key, WEAK_HASH_KEY (h, i))
+ || (h->strong->test->cmpfn
+ && hashval == WEAK_HASH_HASH (h, i)
+ && !NILP (h->strong->test->cmpfn (key, WEAK_HASH_KEY (h, i), NULL))))
+ {
+ /* Take entry out of collision chain. */
+ if (prev < 0)
+ set_weak_hash_index_slot (h, start_of_bucket, WEAK_HASH_NEXT (h, i));
+ else
+ set_weak_hash_next_slot (h, prev, WEAK_HASH_NEXT (h, i));
+
+ /* Clear slots in key_and_value and add the slots to
+ the free list. */
+ set_weak_hash_key_slot (h, i, HASH_UNUSED_ENTRY_KEY);
+ set_weak_hash_value_slot (h, i, Qnil);
+ set_weak_hash_next_slot (h, i, XFIXNUM (h->strong->next_free));
+ h->strong->next_free = make_fixnum (i);
+ h->strong->count = make_fixnum (XFIXNUM (h->strong->count) - 1);
+ break;
+ }
+
+ prev = i;
+ }
+}
+
+
+/* Remove the entry at ID0 from weak hash table H. Called from GC with H
+ being a pointer to a structure on the stack. */
+
+void
+weak_hash_splat_from_table (struct Lisp_Weak_Hash_Table *h, ptrdiff_t i0)
+{
+ hash_hash_t hashval = WEAK_HASH_HASH (h, i0);
+ ptrdiff_t start_of_bucket = weak_hash_index_index (h, hashval);
+ ptrdiff_t prev = -1;
+
+ for (ptrdiff_t i = WEAK_HASH_INDEX (h, start_of_bucket);
+ 0 <= i;
+ i = WEAK_HASH_NEXT (h, i))
+ {
+ if (i == i0)
+ {
+ /* Take entry out of collision chain. */
+ if (prev < 0)
+ set_weak_hash_index_slot (h, start_of_bucket, WEAK_HASH_NEXT (h, i));
+ else
+ set_weak_hash_next_slot (h, prev, WEAK_HASH_NEXT (h, i));
+
+ /* Clear slots in key_and_value and add the slots to
+ the free list. */
+ set_weak_hash_key_slot (h, i, HASH_UNUSED_ENTRY_KEY);
+ set_weak_hash_value_slot (h, i, Qnil);
+ set_weak_hash_next_slot (h, i, XFIXNUM (h->strong->next_free));
+ h->strong->next_free = make_fixnum (i);
+ h->strong->count = make_fixnum (XFIXNUM (h->strong->count) - 1);
+ break;
+ }
+
+ prev = i;
+ }
+}
+
+
/* Clear hash table H. */
static void
@@ -5294,6 +5636,30 @@ hash_clear (struct Lisp_Hash_Table *h)
}
}
+/* Clear weak hash table H. */
+
+static void
+weak_hash_clear (struct Lisp_Weak_Hash_Table *h)
+{
+ if (XFIXNUM (h->strong->count) > 0)
+ {
+ ptrdiff_t size = WEAK_HASH_TABLE_SIZE (h);
+ for (ptrdiff_t i = 0; i < size; i++)
+ {
+ set_weak_hash_next_slot (h, i, i < size - 1 ? i + 1 : -1);
+ set_weak_hash_key_slot (h, i, HASH_UNUSED_ENTRY_KEY);
+ set_weak_hash_value_slot (h, i, Qnil);
+ }
+
+ ptrdiff_t index_size = weak_hash_table_index_size (h);
+ for (ptrdiff_t i = 0; i < index_size; i++)
+ h->strong->index[i].lisp_object = make_fixnum (-1);
+
+ h->strong->next_free = make_fixnum (0);
+ h->strong->count = make_fixnum (0);
+ }
+}
+
\f
/************************************************************************
Weak Hash Tables
@@ -5920,6 +6286,11 @@ DEFUN ("hash-table-count", Fhash_table_count, Shash_table_count, 1, 1, 0,
doc: /* Return the number of elements in TABLE. */)
(Lisp_Object table)
{
+ struct Lisp_Weak_Hash_Table *wh = check_maybe_weak_hash_table (table);
+ if (wh)
+ {
+ return wh->strong->count;
+ }
struct Lisp_Hash_Table *h = check_hash_table (table);
return make_fixnum (h->count);
}
@@ -5999,7 +6370,7 @@ DEFUN ("hash-table-p", Fhash_table_p, Shash_table_p, 1, 1, 0,
doc: /* Return t if OBJ is a Lisp hash table object. */)
(Lisp_Object obj)
{
- return HASH_TABLE_P (obj) ? Qt : Qnil;
+ return (HASH_TABLE_P (obj) || WEAK_HASH_TABLE_P (obj)) ? Qt : Qnil;
}
@@ -6007,6 +6378,12 @@ DEFUN ("clrhash", Fclrhash, Sclrhash, 1, 1, 0,
doc: /* Clear hash table TABLE and return it. */)
(Lisp_Object table)
{
+ struct Lisp_Weak_Hash_Table *wh = check_maybe_weak_hash_table (table);
+ if (wh)
+ {
+ weak_hash_clear (wh);
+ return table;
+ }
struct Lisp_Hash_Table *h = check_hash_table (table);
check_mutable_hash_table (table, h);
hash_clear (h);
@@ -6020,6 +6397,12 @@ DEFUN ("gethash", Fgethash, Sgethash, 2, 3, 0,
If KEY is not found, return DFLT which defaults to nil. */)
(Lisp_Object key, Lisp_Object table, Lisp_Object dflt)
{
+ struct Lisp_Weak_Hash_Table *wh = check_maybe_weak_hash_table (table);
+ if (wh)
+ {
+ ptrdiff_t i = weak_hash_lookup (wh, key);
+ return i >= 0 ? WEAK_HASH_VALUE (wh, i) : dflt;
+ }
struct Lisp_Hash_Table *h = check_hash_table (table);
ptrdiff_t i = hash_lookup (h, key);
return i >= 0 ? HASH_VALUE (h, i) : dflt;
@@ -6032,6 +6415,17 @@ DEFUN ("puthash", Fputhash, Sputhash, 3, 3, 0,
VALUE. In any case, return VALUE. */)
(Lisp_Object key, Lisp_Object value, Lisp_Object table)
{
+ struct Lisp_Weak_Hash_Table *wh = check_maybe_weak_hash_table (table);
+ if (wh)
+ {
+ EMACS_UINT hash = weak_hash_from_key (wh, key);
+ ptrdiff_t i = weak_hash_lookup_with_hash (wh, key, hash);
+ if (i >= 0)
+ set_weak_hash_value_slot (wh, i, value);
+ else
+ weak_hash_put (wh, key, value, hash);
+ return value;
+ }
struct Lisp_Hash_Table *h = check_hash_table (table);
check_mutable_hash_table (table, h);
@@ -6050,6 +6444,12 @@ DEFUN ("remhash", Fremhash, Sremhash, 2, 2, 0,
doc: /* Remove KEY from TABLE. */)
(Lisp_Object key, Lisp_Object table)
{
+ struct Lisp_Weak_Hash_Table *wh = check_maybe_weak_hash_table (table);
+ if (wh)
+ {
+ weak_hash_remove_from_table (wh, key);
+ return Qnil;
+ }
struct Lisp_Hash_Table *h = check_hash_table (table);
check_mutable_hash_table (table, h);
hash_remove_from_table (h, key);
@@ -6065,6 +6465,13 @@ DEFUN ("maphash", Fmaphash, Smaphash, 2, 2, 0,
`maphash' always returns nil. */)
(Lisp_Object function, Lisp_Object table)
{
+ struct Lisp_Weak_Hash_Table *wh = check_maybe_weak_hash_table (table);
+ if (wh)
+ {
+ DOHASH_WEAK_SAFE (wh, i)
+ call2 (function, WEAK_HASH_KEY (wh, i), WEAK_HASH_VALUE (wh, i));
+ return Qnil;
+ }
struct Lisp_Hash_Table *h = check_hash_table (table);
/* We can't use DOHASH here since FUNCTION may violate the rules and
we shouldn't crash as a result (although the effects are
diff --git a/src/igc.c b/src/igc.c
index 2165a69cacf..482272e9a89 100644
--- a/src/igc.c
+++ b/src/igc.c
@@ -372,6 +372,8 @@ #define IGC_DEFINE_LIST(data) \
"IGC_OBJ_DUMPED_BUFFER_TEXT",
"IGC_OBJ_DUMPED_BIGNUM_DATA",
"IGC_OBJ_DUMPED_BYTES",
+ "IGC_OBJ_WEAK_HASH_TABLE_WEAK_PART",
+ "IGC_OBJ_WEAK_HASH_TABLE_STRONG_PART",
};
static_assert (ARRAYELTS (obj_type_names) == IGC_OBJ_NUM_TYPES);
@@ -399,6 +401,7 @@ obj_type_name (enum igc_obj_type type)
"PVEC_BOOL_VECTOR",
"PVEC_BUFFER",
"PVEC_HASH_TABLE",
+ "PVEC_WEAK_HASH_TABLE",
#ifndef IN_MY_FORK
"PVEC_OBARRAY",
#endif
@@ -460,8 +463,8 @@ pvec_type_name (enum pvec_type type)
enum
{
- IGC_TYPE_BITS = 5,
- IGC_HASH_BITS = 27,
+ IGC_TYPE_BITS = 6,
+ IGC_HASH_BITS = 26,
IGC_SIZE_BITS = 32,
IGC_HASH_MASK = (1 << IGC_HASH_BITS) - 1,
};
@@ -562,6 +565,32 @@ object_nelems (void *client, size_t elem_size)
return obj_client_size (h) / elem_size;
}
+Lisp_Object
+igc_ptr_to_lisp (void *client)
+{
+ if (client == 0)
+ return Qnil;
+ mps_addr_t base = client_to_base (client);
+ struct igc_header *h = base;
+ switch (h->obj_type)
+ {
+ case IGC_OBJ_STRING:
+ return make_lisp_ptr (client, Lisp_String);
+
+ case IGC_OBJ_VECTOR:
+ return make_lisp_ptr (client, Lisp_Vectorlike);
+
+ case IGC_OBJ_CONS:
+ return make_lisp_ptr (client, Lisp_Cons);
+
+ return make_lisp_ptr (client, Lisp_Float);
+
+ default:
+ IGC_NOT_IMPLEMENTED ();
+ emacs_abort ();
+ }
+}
+
/* Round NBYTES to the next multiple of ALIGN. */
static size_t
@@ -638,6 +667,8 @@ IGC_DEFINE_LIST (igc_root);
mps_ap_t leaf_ap;
mps_ap_t weak_strong_ap;
mps_ap_t weak_weak_ap;
+ mps_ap_t weak_hash_strong_ap;
+ mps_ap_t weak_hash_weak_ap;
mps_ap_t immovable_ap;
/* Quick access to the roots used for specpdl, bytecode stack and
@@ -674,6 +705,8 @@ IGC_DEFINE_LIST (igc_thread);
mps_pool_t leaf_pool;
mps_fmt_t weak_fmt;
mps_pool_t weak_pool;
+ mps_fmt_t weak_hash_fmt;
+ mps_pool_t weak_hash_pool;
mps_fmt_t immovable_fmt;
mps_pool_t immovable_pool;
@@ -1522,6 +1555,8 @@ fix_charset_table (mps_ss_t ss, struct charset *table, size_t nbytes)
static mps_res_t fix_vector (mps_ss_t ss, struct Lisp_Vector *v);
static mps_res_t fix_vector_weak (mps_ss_t ss, struct Lisp_Vector *v);
+static mps_res_t fix_weak_hash_table_strong_part (mps_ss_t ss, struct Lisp_Weak_Hash_Table_Strong_Part *t);
+static mps_res_t fix_weak_hash_table_weak_part (mps_ss_t ss, struct Lisp_Weak_Hash_Table_Weak_Part *w);
static mps_res_t
dflt_scan_obj (mps_ss_t ss, mps_addr_t base_start, mps_addr_t base_limit,
@@ -1645,6 +1680,15 @@ dflt_scan_obj (mps_ss_t ss, mps_addr_t base_start, mps_addr_t base_limit,
IGC_FIX_CALL (ss, fix_charset_table (ss, (struct charset *)client,
obj_size (header)));
break;
+
+ case IGC_OBJ_WEAK_HASH_TABLE_STRONG_PART:
+ IGC_FIX_CALL_FN (ss, struct Lisp_Weak_Hash_Table_Strong_Part, client,
+ fix_weak_hash_table_strong_part);
+ break;
+ case IGC_OBJ_WEAK_HASH_TABLE_WEAK_PART:
+ IGC_FIX_CALL_FN (ss, struct Lisp_Weak_Hash_Table_Weak_Part, client,
+ fix_weak_hash_table_weak_part);
+ break;
}
}
MPS_SCAN_END (ss);
@@ -1841,6 +1885,60 @@ fix_hash_table (mps_ss_t ss, struct Lisp_Hash_Table *h)
return MPS_RES_OK;
}
+static mps_res_t
+fix_weak_hash_table (mps_ss_t ss, struct Lisp_Weak_Hash_Table *h)
+{
+ MPS_SCAN_BEGIN (ss)
+ {
+ IGC_FIX12_RAW (ss, &h->strong);
+ IGC_FIX12_RAW (ss, &h->weak);
+ }
+ MPS_SCAN_END (ss);
+ return MPS_RES_OK;
+}
+
+static mps_res_t
+fix_weak_hash_table_strong_part (mps_ss_t ss, struct Lisp_Weak_Hash_Table_Strong_Part *t)
+{
+ MPS_SCAN_BEGIN (ss)
+ {
+ for (ssize_t i = 0; i < 4 * XFIXNUM (t->table_size); i++)
+ {
+ IGC_FIX12_OBJ (ss, &t->entries[i].lisp_object);
+ }
+ }
+ MPS_SCAN_END (ss);
+ return MPS_RES_OK;
+}
+
+static mps_res_t
+fix_weak_hash_table_weak_part (mps_ss_t ss, struct Lisp_Weak_Hash_Table_Weak_Part *w)
+{
+ MPS_SCAN_BEGIN (ss)
+ {
+ IGC_FIX12_RAW (ss, &w->strong);
+ struct Lisp_Weak_Hash_Table_Strong_Part *t = w->strong;
+ for (ssize_t i = 0; i < 4 * XFIXNUM (t->table_size); i++)
+ {
+ bool was_nil = NILP (w->entries[i].lisp_object);
+ IGC_FIX12_OBJ (ss, &w->entries[i].lisp_object);
+ bool is_now_nil = NILP (w->entries[i].lisp_object);
+
+ if (is_now_nil && !was_nil)
+ {
+ struct Lisp_Weak_Hash_Table pseudo_h =
+ {
+ .strong = t,
+ .weak = w,
+ };
+ weak_hash_splat_from_table (&pseudo_h, i);
+ }
+ }
+ }
+ MPS_SCAN_END (ss);
+ return MPS_RES_OK;
+}
+
static mps_res_t
fix_char_table (mps_ss_t ss, struct Lisp_Vector *v)
{
@@ -2133,6 +2231,10 @@ fix_vector (mps_ss_t ss, struct Lisp_Vector *v)
IGC_FIX_CALL_FN (ss, struct Lisp_Hash_Table, v, fix_hash_table);
break;
+ case PVEC_WEAK_HASH_TABLE:
+ IGC_FIX_CALL_FN (ss, struct Lisp_Weak_Hash_Table, v, fix_weak_hash_table);
+ break;
+
case PVEC_CHAR_TABLE:
case PVEC_SUB_CHAR_TABLE:
IGC_FIX_CALL_FN (ss, struct Lisp_Vector, v, fix_char_table);
@@ -2261,6 +2363,7 @@ fix_vector_weak (mps_ss_t ss, struct Lisp_Vector *v)
case PVEC_FRAME:
case PVEC_WINDOW:
case PVEC_HASH_TABLE:
+ case PVEC_WEAK_HASH_TABLE:
case PVEC_CHAR_TABLE:
case PVEC_SUB_CHAR_TABLE:
case PVEC_BOOL_VECTOR:
@@ -2564,6 +2667,22 @@ create_weak_ap (mps_ap_t *ap, struct igc_thread *t, bool weak)
return res;
}
+static mps_res_t
+create_weak_hash_ap (mps_ap_t *ap, struct igc_thread *t, bool weak)
+{
+ struct igc *gc = t->gc;
+ mps_res_t res;
+ mps_pool_t pool = gc->weak_hash_pool;
+ MPS_ARGS_BEGIN (args)
+ {
+ MPS_ARGS_ADD (args, MPS_KEY_RANK,
+ weak ? mps_rank_weak () : mps_rank_exact ());
+ res = mps_ap_create_k (ap, pool, args);
+ }
+ MPS_ARGS_END (args);
+ return res;
+}
+
static void
create_thread_aps (struct igc_thread *t)
{
@@ -2576,8 +2695,10 @@ create_thread_aps (struct igc_thread *t)
res = mps_ap_create_k (&t->immovable_ap, gc->immovable_pool, mps_args_none);
IGC_CHECK_RES (res);
res = create_weak_ap (&t->weak_strong_ap, t, false);
+ res = create_weak_hash_ap (&t->weak_hash_strong_ap, t, false);
IGC_CHECK_RES (res);
res = create_weak_ap (&t->weak_weak_ap, t, true);
+ res = create_weak_hash_ap (&t->weak_hash_weak_ap, t, true);
IGC_CHECK_RES (res);
}
@@ -2637,6 +2758,8 @@ igc_thread_remove (void **pinfo)
mps_ap_destroy (t->d.leaf_ap);
mps_ap_destroy (t->d.weak_strong_ap);
mps_ap_destroy (t->d.weak_weak_ap);
+ mps_ap_destroy (t->d.weak_hash_strong_ap);
+ mps_ap_destroy (t->d.weak_hash_weak_ap);
mps_ap_destroy (t->d.immovable_ap);
mps_thread_dereg (deregister_thread (t));
}
@@ -2956,6 +3079,7 @@ finalize_vector (mps_addr_t v)
case PVEC_OBARRAY:
#endif
case PVEC_HASH_TABLE:
+ case PVEC_WEAK_HASH_TABLE:
case PVEC_SYMBOL_WITH_POS:
case PVEC_PROCESS:
case PVEC_RECORD:
@@ -3005,6 +3129,8 @@ finalize (struct igc *gc, mps_addr_t base)
case IGC_OBJ_DUMPED_BIGNUM_DATA:
case IGC_OBJ_DUMPED_BYTES:
case IGC_OBJ_BYTES:
+ case IGC_OBJ_WEAK_HASH_TABLE_WEAK_PART:
+ case IGC_OBJ_WEAK_HASH_TABLE_STRONG_PART:
case IGC_OBJ_NUM_TYPES:
emacs_abort ();
@@ -3060,6 +3186,7 @@ maybe_finalize (mps_addr_t client, enum pvec_type tag)
case PVEC_OBARRAY:
#endif
case PVEC_HASH_TABLE:
+ case PVEC_WEAK_HASH_TABLE:
case PVEC_NORMAL_VECTOR:
case PVEC_FREE:
case PVEC_MARKER:
@@ -3300,6 +3427,12 @@ thread_ap (enum igc_obj_type type)
case IGC_OBJ_VECTOR_WEAK:
return t->d.weak_weak_ap;
+ case IGC_OBJ_WEAK_HASH_TABLE_WEAK_PART:
+ return t->d.weak_hash_weak_ap;
+
+ case IGC_OBJ_WEAK_HASH_TABLE_STRONG_PART:
+ return t->d.weak_hash_strong_ap;
+
case IGC_OBJ_VECTOR:
case IGC_OBJ_CONS:
case IGC_OBJ_SYMBOL:
@@ -3664,12 +3797,51 @@ igc_alloc_lisp_obj_vec (size_t n)
return alloc (n * sizeof (Lisp_Object), IGC_OBJ_OBJ_VEC);
}
+static mps_addr_t
+weak_hash_find_dependent (mps_addr_t base)
+{
+ struct igc_header *h = base;
+ switch (h->obj_type)
+ {
+ case IGC_OBJ_WEAK_HASH_TABLE_WEAK_PART:
+ {
+ mps_addr_t client = base_to_client (base);
+ struct Lisp_Weak_Hash_Table_Weak_Part *w = client;
+ return client_to_base (w->strong);
+ }
+ case IGC_OBJ_WEAK_HASH_TABLE_STRONG_PART:
+ {
+ mps_addr_t client = base_to_client (base);
+ struct Lisp_Weak_Hash_Table_Strong_Part *w = client;
+ return client_to_base (w->weak);
+ }
+ default:
+ emacs_abort ();
+ }
+
+ return 0;
+}
+
Lisp_Object *
igc_make_hash_table_vec (size_t n)
{
return alloc (n * sizeof (Lisp_Object), IGC_OBJ_HASH_VEC);
}
+struct Lisp_Weak_Hash_Table_Strong_Part *
+igc_alloc_weak_hash_table_strong_part (hash_table_weakness_t weak, size_t size, size_t index_bits)
+{
+ return alloc (sizeof (struct Lisp_Weak_Hash_Table_Strong_Part) + 5 * size * sizeof (union Lisp_Weak_Hash_Table_Entry),
+ IGC_OBJ_WEAK_HASH_TABLE_STRONG_PART);
+}
+
+struct Lisp_Weak_Hash_Table_Weak_Part *
+igc_alloc_weak_hash_table_weak_part (hash_table_weakness_t weak, size_t size, size_t index_bits)
+{
+ return alloc (sizeof (struct Lisp_Weak_Hash_Table_Weak_Part) + 5 * size * sizeof (union Lisp_Weak_Hash_Table_Entry),
+ IGC_OBJ_WEAK_HASH_TABLE_WEAK_PART);
+}
+
/* Like xpalloc, but uses 'alloc' instead of xrealloc, and should only
be used for growing a vector of pointers whose current size is N
pointers. */
@@ -3865,6 +4037,7 @@ DEFUN ("igc-info", Figc_info, Sigc_info, 0, 0, 0, doc : /* */)
walk_pool (gc, gc->dflt_pool, &st);
walk_pool (gc, gc->leaf_pool, &st);
walk_pool (gc, gc->weak_pool, &st);
+ walk_pool (gc, gc->weak_hash_pool, &st);
walk_pool (gc, gc->immovable_pool, &st);
Lisp_Object result = Qnil;
@@ -3964,7 +4137,7 @@ make_dflt_fmt (struct igc *gc)
}
static mps_pool_t
-make_pool_with_class (struct igc *gc, mps_fmt_t fmt, mps_class_t cls)
+make_pool_with_class (struct igc *gc, mps_fmt_t fmt, mps_class_t cls, mps_awl_find_dependent_t find_dependent)
{
mps_res_t res;
mps_pool_t pool;
@@ -3973,6 +4146,8 @@ make_pool_with_class (struct igc *gc, mps_fmt_t fmt, mps_class_t cls)
MPS_ARGS_ADD (args, MPS_KEY_FORMAT, fmt);
MPS_ARGS_ADD (args, MPS_KEY_CHAIN, gc->chain);
MPS_ARGS_ADD (args, MPS_KEY_INTERIOR, true);
+ if (find_dependent)
+ MPS_ARGS_ADD (args, MPS_KEY_AWL_FIND_DEPENDENT, find_dependent);
res = mps_pool_create_k (&pool, gc->arena, cls, args);
}
MPS_ARGS_END (args);
@@ -3983,25 +4158,25 @@ make_pool_with_class (struct igc *gc, mps_fmt_t fmt, mps_class_t cls)
static mps_pool_t
make_pool_amc (struct igc *gc, mps_fmt_t fmt)
{
- return make_pool_with_class (gc, fmt, mps_class_amc ());
+ return make_pool_with_class (gc, fmt, mps_class_amc (), NULL);
}
static mps_pool_t
make_pool_ams (struct igc *gc, mps_fmt_t fmt)
{
- return make_pool_with_class (gc, fmt, mps_class_ams ());
+ return make_pool_with_class (gc, fmt, mps_class_ams (), NULL);
}
static mps_pool_t
-make_pool_awl (struct igc *gc, mps_fmt_t fmt)
+make_pool_awl (struct igc *gc, mps_fmt_t fmt, mps_awl_find_dependent_t find_dependent)
{
- return make_pool_with_class (gc, fmt, mps_class_awl ());
+ return make_pool_with_class (gc, fmt, mps_class_awl (), find_dependent);
}
static mps_pool_t
make_pool_amcz (struct igc *gc, mps_fmt_t fmt)
{
- return make_pool_with_class (gc, fmt, mps_class_amcz ());
+ return make_pool_with_class (gc, fmt, mps_class_amcz (), NULL);
}
static struct igc *
@@ -4020,7 +4195,9 @@ make_igc (void)
gc->leaf_fmt = make_dflt_fmt (gc);
gc->leaf_pool = make_pool_amcz (gc, gc->leaf_fmt);
gc->weak_fmt = make_dflt_fmt (gc);
- gc->weak_pool = make_pool_awl (gc, gc->weak_fmt);
+ gc->weak_pool = make_pool_awl (gc, gc->weak_fmt, NULL);
+ gc->weak_hash_fmt = make_dflt_fmt (gc);
+ gc->weak_hash_pool = make_pool_awl (gc, gc->weak_hash_fmt, weak_hash_find_dependent);
gc->immovable_fmt = make_dflt_fmt (gc);
gc->immovable_pool = make_pool_ams (gc, gc->immovable_fmt);
diff --git a/src/igc.h b/src/igc.h
index 95a0d25cbba..036fec5e238 100644
--- a/src/igc.h
+++ b/src/igc.h
@@ -56,6 +56,8 @@ #define EMACS_IGC_H
IGC_OBJ_DUMPED_BUFFER_TEXT,
IGC_OBJ_DUMPED_BIGNUM_DATA,
IGC_OBJ_DUMPED_BYTES,
+ IGC_OBJ_WEAK_HASH_TABLE_WEAK_PART,
+ IGC_OBJ_WEAK_HASH_TABLE_STRONG_PART,
IGC_OBJ_NUM_TYPES
};
@@ -122,6 +124,8 @@ #define EMACS_IGC_H
void *igc_grow_ptr_vec (void *v, ptrdiff_t *n, ptrdiff_t n_incr_min, ptrdiff_t n_max);
void igc_grow_rdstack (struct read_stack *rs);
Lisp_Object *igc_make_hash_table_vec (size_t n);
+struct Lisp_Weak_Hash_Table_Strong_Part *igc_alloc_weak_hash_table_strong_part(hash_table_weakness_t, size_t, size_t);
+struct Lisp_Weak_Hash_Table_Weak_Part *igc_alloc_weak_hash_table_weak_part(hash_table_weakness_t, size_t, size_t);
void *igc_alloc_bytes (size_t nbytes);
struct image_cache *igc_make_image_cache (void);
struct interval *igc_make_interval (void);
diff --git a/src/lisp.h b/src/lisp.h
index 54f0a1715ea..525a87b01d9 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -1039,6 +1039,7 @@ DEFINE_GDB_SYMBOL_END (PSEUDOVECTOR_FLAG)
PVEC_BOOL_VECTOR,
PVEC_BUFFER,
PVEC_HASH_TABLE,
+ PVEC_WEAK_HASH_TABLE,
PVEC_OBARRAY,
PVEC_TERMINAL,
PVEC_WINDOW_CONFIGURATION,
@@ -2558,6 +2559,7 @@ #define DOOBARRAY(oa, it) \
/* The structure of a Lisp hash table. */
+struct Lisp_Weak_Hash_Table;
struct Lisp_Hash_Table;
struct hash_impl;
@@ -2605,6 +2607,55 @@ #define DOOBARRAY(oa, it) \
(hash) indices. It's signed and a subtype of ptrdiff_t. */
typedef int32_t hash_idx_t;
+/* The reason for this unusual union is an MPS peculiarity on 32-bit x86 systems. */
+union Lisp_Weak_Hash_Table_Entry
+{
+ void *ptr;
+ Lisp_Object lisp_object; /* must be a fixnum or HASH_UNUSED_ENTRY_KEY! */
+};
+
+struct Lisp_Weak_Hash_Table_Strong_Part
+{
+ Lisp_Object index_bits;
+ Lisp_Object count;
+ Lisp_Object next_free;
+ Lisp_Object table_size;
+ struct Lisp_Weak_Hash_Table_Weak_Part *weak;
+ const struct hash_table_test *test;
+ union Lisp_Weak_Hash_Table_Entry *index; /* internal pointer */
+ union Lisp_Weak_Hash_Table_Entry *hash; /* either internal pointer or pointer to dependent object */
+ union Lisp_Weak_Hash_Table_Entry *key; /* either internal pointer or pointer to dependent object */
+ union Lisp_Weak_Hash_Table_Entry *value; /* either internal pointer or pointer to dependent object */
+ union Lisp_Weak_Hash_Table_Entry *next; /* internal pointer */
+ hash_table_weakness_t weakness : 3;
+ hash_table_std_test_t frozen_test : 2;
+
+ /* True if the table can be purecopied. The table cannot be
+ changed afterwards. */
+ bool_bf purecopy : 1;
+
+ /* True if the table is mutable. Ordinarily tables are mutable, but
+ pure tables are not, and while a table is being mutated it is
+ immutable for recursive attempts to mutate it. */
+ bool_bf mutable : 1;
+ union Lisp_Weak_Hash_Table_Entry entries[FLEXIBLE_ARRAY_MEMBER];
+};
+
+struct Lisp_Weak_Hash_Table_Weak_Part
+{
+ struct Lisp_Weak_Hash_Table_Strong_Part *strong;
+ union Lisp_Weak_Hash_Table_Entry entries[FLEXIBLE_ARRAY_MEMBER];
+};
+
+struct Lisp_Weak_Hash_Table
+{
+ union vectorlike_header header;
+
+ struct Lisp_Weak_Hash_Table_Strong_Part *strong;
+ struct Lisp_Weak_Hash_Table_Weak_Part *weak;
+ Lisp_Object dump_replacement;
+};
+
struct Lisp_Hash_Table
{
union vectorlike_header header;
@@ -2725,6 +2776,23 @@ XHASH_TABLE (Lisp_Object a)
return h;
}
+INLINE bool
+WEAK_HASH_TABLE_P (Lisp_Object a)
+{
+ return PSEUDOVECTORP (a, PVEC_WEAK_HASH_TABLE);
+}
+
+INLINE struct Lisp_Weak_Hash_Table *
+XWEAK_HASH_TABLE (Lisp_Object a)
+{
+ eassert (WEAK_HASH_TABLE_P (a));
+ struct Lisp_Weak_Hash_Table *h
+ = XUNTAG (a, Lisp_Vectorlike, struct Lisp_Weak_Hash_Table);
+ igc_check_fwd (h);
+ return h;
+}
+
+extern Lisp_Object igc_ptr_to_lisp (void *ptr);
INLINE Lisp_Object
make_lisp_hash_table (struct Lisp_Hash_Table *h)
{
@@ -2732,6 +2800,13 @@ make_lisp_hash_table (struct Lisp_Hash_Table *h)
return make_lisp_ptr (h, Lisp_Vectorlike);
}
+INLINE Lisp_Object
+make_lisp_weak_hash_table (struct Lisp_Weak_Hash_Table *h)
+{
+ eassert (PSEUDOVECTOR_TYPEP (&h->header, PVEC_WEAK_HASH_TABLE));
+ return make_lisp_ptr (h, Lisp_Vectorlike);
+}
+
/* Value is the key part of entry IDX in hash table H. */
INLINE Lisp_Object
HASH_KEY (const struct Lisp_Hash_Table *h, ptrdiff_t idx)
@@ -2740,6 +2815,14 @@ HASH_KEY (const struct Lisp_Hash_Table *h, ptrdiff_t idx)
return h->key[idx];
}
+/* Value is the key part of entry IDX in hash table H. */
+INLINE Lisp_Object
+WEAK_HASH_KEY (const struct Lisp_Weak_Hash_Table *h, ptrdiff_t idx)
+{
+ eassert (idx >= 0 && idx < XFIXNUM (h->strong->table_size));
+ return h->strong->key[idx].lisp_object;
+}
+
/* Value is the value part of entry IDX in hash table H. */
INLINE Lisp_Object
HASH_VALUE (const struct Lisp_Hash_Table *h, ptrdiff_t idx)
@@ -2748,6 +2831,12 @@ HASH_VALUE (const struct Lisp_Hash_Table *h, ptrdiff_t idx)
return h->value[idx];
}
+INLINE Lisp_Object
+WEAK_HASH_VALUE (const struct Lisp_Weak_Hash_Table *h, ptrdiff_t idx)
+{
+ return h->strong->value[idx].lisp_object;
+}
+
/* Value is the hash code computed for entry IDX in hash table H. */
INLINE hash_hash_t
HASH_HASH (const struct Lisp_Hash_Table *h, ptrdiff_t idx)
@@ -2756,6 +2845,14 @@ HASH_HASH (const struct Lisp_Hash_Table *h, ptrdiff_t idx)
return h->hash[idx];
}
+/* Value is the hash code computed for entry IDX in hash table H. */
+INLINE hash_hash_t
+WEAK_HASH_HASH (const struct Lisp_Weak_Hash_Table *h, ptrdiff_t idx)
+{
+ eassert (idx >= 0 && idx < XFIXNUM (h->strong->table_size));
+ return XFIXNUM (h->strong->hash[idx].lisp_object);
+}
+
/* Value is the size of hash table H. */
INLINE ptrdiff_t
HASH_TABLE_SIZE (const struct Lisp_Hash_Table *h)
@@ -2763,6 +2860,13 @@ HASH_TABLE_SIZE (const struct Lisp_Hash_Table *h)
return h->table_size;
}
+/* Value is the size of hash table H. */
+INLINE ptrdiff_t
+WEAK_HASH_TABLE_SIZE (const struct Lisp_Weak_Hash_Table *h)
+{
+ return XFIXNUM (h->strong->table_size);
+}
+
/* Size of the index vector in hash table H. */
INLINE ptrdiff_t
hash_table_index_size (const struct Lisp_Hash_Table *h)
@@ -2770,6 +2874,12 @@ hash_table_index_size (const struct Lisp_Hash_Table *h)
return (ptrdiff_t)1 << h->index_bits;
}
+INLINE ptrdiff_t
+weak_hash_table_index_size (const struct Lisp_Weak_Hash_Table *h)
+{
+ return (ptrdiff_t)1 << XFIXNUM (h->strong->index_bits);
+}
+
/* Hash value for KEY in hash table H. */
INLINE hash_hash_t
hash_from_key (struct Lisp_Hash_Table *h, Lisp_Object key)
@@ -2777,6 +2887,13 @@ hash_from_key (struct Lisp_Hash_Table *h, Lisp_Object key)
return h->test->hashfn (key, h);
}
+/* Hash value for KEY in hash table H. */
+INLINE hash_hash_t
+weak_hash_from_key (struct Lisp_Weak_Hash_Table *h, Lisp_Object key)
+{
+ return h->strong->test->hashfn (key, NULL);
+}
+
/* Iterate K and V as key and value of valid entries in hash table H.
The body may remove the current entry or alter its value slot, but not
mutate TABLE in any other way. */
@@ -2800,6 +2917,28 @@ hash_from_key (struct Lisp_Hash_Table *h, Lisp_Object key)
; \
else
+/* Iterate K and V as key and value of valid entries in hash table H.
+ The body may remove the current entry or alter its value slot, but not
+ mutate TABLE in any other way. */
+# define DOHASH_WEAK(h, k, v) \
+ for (union Lisp_Weak_Hash_Table_Entry *dohash_##k##_##v##_k = (h)->strong->key, \
+ *dohash_##k##_##v##_v = (h)->strong->value, \
+ *dohash_##k##_##v##_end = dohash_##k##_##v##_k \
+ + WEAK_HASH_TABLE_SIZE (h), \
+ *dohash_##k##_##v##_base = dohash_##k##_##v##_k; \
+ dohash_##k##_##v##_k < dohash_##k##_##v##_end \
+ && (k = dohash_##k##_##v##_k[0].lisp_object, \
+ v = dohash_##k##_##v##_v[0].lisp_object, /*maybe unused*/ (void)v, \
+ true); \
+ eassert (dohash_##k##_##v##_base == (h)->strong->key \
+ && dohash_##k##_##v##_end \
+ == dohash_##k##_##v##_base \
+ + WEAK_HASH_TABLE_SIZE (h)), \
+ ++dohash_##k##_##v##_k, ++dohash_##k##_##v##_v) \
+ if (hash_unused_entry_key_p (k)) \
+ ; \
+ else
+
/* Iterate I as index of valid entries in hash table H.
Unlike DOHASH, this construct copes with arbitrary table mutations
in the body. The consequences of such mutations are limited to
@@ -2812,6 +2951,18 @@ #define DOHASH_SAFE(h, i) \
; \
else
+/* Iterate I as index of valid entries in hash table H.
+ Unlike DOHASH, this construct copes with arbitrary table mutations
+ in the body. The consequences of such mutations are limited to
+ whether and in what order entries are encountered by the loop
+ (which is usually bad enough), but not crashing or corrupting the
+ Lisp state. */
+#define DOHASH_WEAK_SAFE(h, i) \
+ for (ptrdiff_t i = 0; i < WEAK_HASH_TABLE_SIZE (h); i++) \
+ if (hash_unused_entry_key_p (WEAK_HASH_KEY (h, i))) \
+ ; \
+ else
+
void hash_table_thaw (Lisp_Object hash_table);
void hash_table_rehash (struct Lisp_Hash_Table *h);
@@ -4086,6 +4237,13 @@ set_hash_key_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val)
h->key[idx] = val;
}
+INLINE void
+set_weak_hash_key_slot (struct Lisp_Weak_Hash_Table *h, ptrdiff_t idx, Lisp_Object val)
+{
+ eassert (idx >= 0 && idx < XFIXNUM (h->strong->table_size));
+ h->strong->key[idx].lisp_object = val;
+}
+
INLINE void
set_hash_value_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val)
{
@@ -4093,6 +4251,13 @@ set_hash_value_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val)
h->value[idx] = val;;
}
+INLINE void
+set_weak_hash_value_slot (struct Lisp_Weak_Hash_Table *h, ptrdiff_t idx, Lisp_Object val)
+{
+ eassert (idx >= 0 && idx < XFIXNUM (h->strong->table_size) );
+ h->strong->value[idx].lisp_object = val;
+}
+
/* Use these functions to set Lisp_Object
or pointer slots of struct Lisp_Symbol. */
@@ -4354,13 +4519,23 @@ #define CONS_TO_INTEGER(cons, type, var) \
EMACS_UINT sxhash (Lisp_Object);
Lisp_Object make_hash_table (const struct hash_table_test *, EMACS_INT,
hash_table_weakness_t, bool);
+Lisp_Object make_weak_hash_table (const struct hash_table_test *, EMACS_INT,
+ hash_table_weakness_t, bool);
Lisp_Object hash_table_weakness_symbol (hash_table_weakness_t weak);
+Lisp_Object strengthen_hash_table (Lisp_Object weak);
+Lisp_Object strengthen_hash_table_for_dump (struct Lisp_Weak_Hash_Table *);
ptrdiff_t hash_lookup (struct Lisp_Hash_Table *, Lisp_Object);
+ptrdiff_t weak_hash_lookup (struct Lisp_Weak_Hash_Table *, Lisp_Object);
ptrdiff_t hash_lookup_get_hash (struct Lisp_Hash_Table *h, Lisp_Object key,
hash_hash_t *phash);
ptrdiff_t hash_put (struct Lisp_Hash_Table *, Lisp_Object, Lisp_Object,
hash_hash_t);
+ptrdiff_t weak_hash_put (struct Lisp_Weak_Hash_Table *, Lisp_Object, Lisp_Object,
+ hash_hash_t);
void hash_remove_from_table (struct Lisp_Hash_Table *, Lisp_Object);
+void weak_hash_remove_from_table (struct Lisp_Weak_Hash_Table *, Lisp_Object);
+void weak_hash_splat_from_table (struct Lisp_Weak_Hash_Table *h, ptrdiff_t i0);
+
extern struct hash_table_test const hashtest_eq, hashtest_eql, hashtest_equal;
extern void validate_subarray (Lisp_Object, Lisp_Object, Lisp_Object,
ptrdiff_t, ptrdiff_t *, ptrdiff_t *);
diff --git a/src/pdumper.c b/src/pdumper.c
index a560dd06ba8..8f4d3f3dc25 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -1441,6 +1441,11 @@ dump_enqueue_object (struct dump_context *ctx,
Lisp_Object object,
struct link_weight weight)
{
+ if (WEAK_HASH_TABLE_P (object))
+ {
+ strengthen_hash_table_for_dump (XWEAK_HASH_TABLE (object));
+ object = XWEAK_HASH_TABLE (object)->dump_replacement;
+ }
if (dump_object_needs_dumping_p (object))
{
dump_off state = dump_recall_object (ctx, object);
@@ -1943,6 +1948,11 @@ dump_field_lv_or_rawptr (struct dump_context *ctx,
/* We don't know about the target object yet, so add a fixup.
When we process the fixup, we'll have dumped the target
object. */
+ if (WEAK_HASH_TABLE_P (value))
+ {
+ strengthen_hash_table_for_dump (XWEAK_HASH_TABLE (value));
+ value = XWEAK_HASH_TABLE (value)->dump_replacement;
+ }
out_value = (intptr_t) 0xDEADF00D;
dump_remember_fixup_lv (ctx,
out_field_offset,
@@ -3129,6 +3139,13 @@ dump_vectorlike (struct dump_context *ctx,
return dump_vectorlike_generic (ctx, &v->header);
case PVEC_BOOL_VECTOR:
return dump_bool_vector(ctx, v);
+ case PVEC_WEAK_HASH_TABLE:
+ if (WEAK_HASH_TABLE_P (lv))
+ {
+ strengthen_hash_table_for_dump (XWEAK_HASH_TABLE (lv));
+ lv = XWEAK_HASH_TABLE (lv)->dump_replacement;
+ }
+ return dump_hash_table (ctx, lv);
case PVEC_HASH_TABLE:
return dump_hash_table (ctx, lv);
case PVEC_OBARRAY:
diff --git a/src/print.c b/src/print.c
index 2840252246f..f0453b72188 100644
--- a/src/print.c
+++ b/src/print.c
@@ -2188,6 +2188,7 @@ print_vectorlike_unreadable (Lisp_Object obj, Lisp_Object printcharfun,
case PVEC_CHAR_TABLE:
case PVEC_SUB_CHAR_TABLE:
case PVEC_HASH_TABLE:
+ case PVEC_WEAK_HASH_TABLE:
case PVEC_BIGNUM:
case PVEC_BOOL_VECTOR:
/* Impossible cases. */
@@ -2786,6 +2787,54 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag)
goto next_obj;
}
+ case PVEC_WEAK_HASH_TABLE:
+ {
+ struct Lisp_Weak_Hash_Table *h = XWEAK_HASH_TABLE (obj);
+ /* Implement a readable output, e.g.:
+ #s(hash-table test equal data (k1 v1 k2 v2)) */
+ print_c_string ("#s(hash-table", printcharfun);
+
+ if (!BASE_EQ (h->strong->test->name, Qeql))
+ {
+ print_c_string (" test ", printcharfun);
+ print_object (h->strong->test->name, printcharfun, escapeflag);
+ }
+
+ if (h->strong->weakness != Weak_None)
+ {
+ print_c_string (" weakness ", printcharfun);
+ print_object (hash_table_weakness_symbol (h->strong->weakness),
+ printcharfun, escapeflag);
+ }
+
+ /* XXX: strengthen first, then count */
+ ptrdiff_t size = XFIXNUM (h->strong->count);
+ if (size > 0)
+ {
+ print_c_string (" data (", printcharfun);
+
+ /* Don't print more elements than the specified maximum. */
+ if (FIXNATP (Vprint_length) && XFIXNAT (Vprint_length) < size)
+ size = XFIXNAT (Vprint_length);
+
+ print_stack_push ((struct print_stack_entry){
+ .type = PE_hash,
+ .u.hash.obj = strengthen_hash_table (obj),
+ .u.hash.nobjs = size * 2,
+ .u.hash.idx = 0,
+ .u.hash.printed = 0,
+ .u.hash.truncated = (size < XFIXNUM (h->strong->count)),
+ });
+ }
+ else
+ {
+ /* Empty table: we can omit the data entirely. */
+ printchar (')', printcharfun);
+ --print_depth; /* Done with this. */
+ }
+ goto next_obj;
+ }
+
case PVEC_BIGNUM:
print_bignum (obj, printcharfun);
break;
next prev parent reply other threads:[~2024-06-30 20:15 UTC|newest]
Thread overview: 82+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-27 21:01 MPS: dangling markers Ihor Radchenko
2024-06-27 21:24 ` Stefan Monnier
2024-06-28 4:14 ` Gerd Möllmann
2024-06-28 16:37 ` Ihor Radchenko
2024-06-28 16:47 ` Gerd Möllmann
2024-06-28 16:52 ` Ihor Radchenko
2024-06-28 16:56 ` Gerd Möllmann
2024-06-28 17:18 ` Ihor Radchenko
2024-06-28 17:44 ` Gerd Möllmann
2024-06-29 3:57 ` Gerd Möllmann
2024-06-29 14:34 ` Ihor Radchenko
2024-06-29 14:56 ` Gerd Möllmann
2024-06-29 16:29 ` Eli Zaretskii
2024-06-29 17:09 ` Gerd Möllmann
2024-06-29 17:17 ` Gerd Möllmann
2024-06-29 17:23 ` Eli Zaretskii
2024-06-29 18:02 ` Gerd Möllmann
2024-06-29 18:11 ` Eli Zaretskii
2024-06-29 18:19 ` Gerd Möllmann
2024-06-29 19:51 ` Ihor Radchenko
2024-06-29 21:50 ` Gerd Möllmann
2024-06-29 22:33 ` Pip Cet
2024-06-30 4:41 ` Gerd Möllmann
2024-06-30 6:56 ` Gerd Möllmann
2024-06-30 9:51 ` Pip Cet
2024-06-30 11:02 ` Gerd Möllmann
2024-06-30 12:54 ` Pip Cet
2024-06-30 13:15 ` Gerd Möllmann
2024-06-30 19:02 ` Pip Cet
2024-06-30 19:22 ` Gerd Möllmann
2024-06-30 20:15 ` Pip Cet [this message]
2024-07-01 4:22 ` Gerd Möllmann
2024-07-01 17:14 ` Pip Cet
2024-07-01 18:20 ` Gerd Möllmann
2024-07-01 18:50 ` Eli Zaretskii
2024-07-01 19:04 ` Pip Cet
2024-07-01 19:07 ` Eli Zaretskii
2024-07-01 19:43 ` Gerd Möllmann
2024-07-01 18:56 ` Eli Zaretskii
2024-07-01 21:08 ` Pip Cet
2024-07-02 11:25 ` Eli Zaretskii
2024-07-03 18:46 ` Pip Cet
2024-07-03 19:20 ` Eli Zaretskii
2024-06-29 22:59 ` Stefan Monnier
2024-06-30 5:02 ` Gerd Möllmann
2024-06-30 5:29 ` Eli Zaretskii
2024-06-30 15:04 ` Stefan Monnier
2024-06-30 5:11 ` Eli Zaretskii
2024-06-30 4:57 ` Eli Zaretskii
2024-06-30 5:36 ` Gerd Möllmann
2024-06-30 12:25 ` Ihor Radchenko
2024-06-29 17:19 ` Ihor Radchenko
2024-06-29 18:05 ` Gerd Möllmann
2024-06-29 18:10 ` Eli Zaretskii
2024-06-29 18:17 ` Gerd Möllmann
2024-06-29 18:28 ` Ihor Radchenko
2024-06-29 17:20 ` Eli Zaretskii
2024-06-29 18:04 ` Gerd Möllmann
2024-06-29 17:16 ` Stefan Monnier
2024-06-29 18:12 ` Gerd Möllmann
2024-06-29 18:30 ` Stefan Monnier
2024-06-29 18:52 ` Gerd Möllmann
2024-06-29 21:20 ` Gerd Möllmann
2024-06-29 21:38 ` Gerd Möllmann
2024-06-30 7:11 ` Gerd Möllmann
2024-06-30 7:27 ` Gerd Möllmann
2024-06-30 7:45 ` Ihor Radchenko
2024-06-30 10:44 ` Gerd Möllmann
2024-06-30 11:23 ` Ihor Radchenko
2024-06-30 11:25 ` Gerd Möllmann
2024-06-30 11:31 ` Ihor Radchenko
2024-06-30 12:13 ` Gerd Möllmann
2024-06-30 12:18 ` Ihor Radchenko
2024-06-30 12:17 ` Ihor Radchenko
2024-06-30 12:28 ` Gerd Möllmann
2024-06-30 12:38 ` Ihor Radchenko
2024-06-30 12:48 ` Gerd Möllmann
2024-06-30 15:21 ` Ihor Radchenko
2024-06-30 15:32 ` Gerd Möllmann
2024-06-30 12:49 ` Eli Zaretskii
2024-06-29 15:17 ` Ihor Radchenko
2024-06-28 4:07 ` Gerd Möllmann
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='HA0pkHH7MG2SiNbZPsDK_2WAs3QStL9QKftC23ZPbTJX6F-bzskcn9sOqIMqggB1LYvRnPUBGLE0pzGJpAd-UsJpfSb6yR5f_CYcUxYK-II=@protonmail.com' \
--to=pipcet@protonmail.com \
--cc=eliz@gnu.org \
--cc=eller.helmut@gmail.com \
--cc=emacs-devel@gnu.org \
--cc=gerd.moellmann@gmail.com \
--cc=monnier@iro.umontreal.ca \
--cc=yantar92@posteo.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.