1.0.26.15: interrupt.c refactoring
[sbcl.git] / src / runtime / gc-common.c
index a526ded..c358039 100644 (file)
 #include "validate.h"
 #include "lispregs.h"
 #include "arch.h"
 #include "validate.h"
 #include "lispregs.h"
 #include "arch.h"
-#include "fixnump.h"
 #include "gc.h"
 #include "genesis/primitive-objects.h"
 #include "genesis/static-symbols.h"
 #include "genesis/layout.h"
 #include "gc.h"
 #include "genesis/primitive-objects.h"
 #include "genesis/static-symbols.h"
 #include "genesis/layout.h"
+#include "genesis/hash-table.h"
 #include "gc-internal.h"
 
 #ifdef LISP_FEATURE_SPARC
 #include "gc-internal.h"
 
 #ifdef LISP_FEATURE_SPARC
@@ -52,6 +52,9 @@
 #endif
 #endif
 
 #endif
 #endif
 
+size_t dynamic_space_size = DEFAULT_DYNAMIC_SPACE_SIZE;
+size_t thread_control_stack_size = DEFAULT_CONTROL_STACK_SIZE;
+
 inline static boolean
 forwarding_pointer_p(lispobj *pointer) {
     lispobj first_word=*pointer;
 inline static boolean
 forwarding_pointer_p(lispobj *pointer) {
     lispobj first_word=*pointer;
@@ -93,10 +96,9 @@ unsigned long bytes_consed_between_gcs = 12*1024*1024;
 /*
  * copying objects
  */
 /*
  * copying objects
  */
-
-/* to copy a boxed object */
+static
 lispobj
 lispobj
-copy_object(lispobj object, long nwords)
+gc_general_copy_object(lispobj object, long nwords, int page_type_flag)
 {
     int tag;
     lispobj *new;
 {
     int tag;
     lispobj *new;
@@ -109,13 +111,26 @@ copy_object(lispobj object, long nwords)
     tag = lowtag_of(object);
 
     /* Allocate space. */
     tag = lowtag_of(object);
 
     /* Allocate space. */
-    new = gc_general_alloc(nwords*N_WORD_BYTES,ALLOC_BOXED,ALLOC_QUICK);
+    new = gc_general_alloc(nwords*N_WORD_BYTES, page_type_flag, ALLOC_QUICK);
 
     /* Copy the object. */
     memcpy(new,native_pointer(object),nwords*N_WORD_BYTES);
     return make_lispobj(new,tag);
 }
 
 
     /* Copy the object. */
     memcpy(new,native_pointer(object),nwords*N_WORD_BYTES);
     return make_lispobj(new,tag);
 }
 
+/* to copy a boxed object */
+lispobj
+copy_object(lispobj object, long nwords)
+{
+    return gc_general_copy_object(object, nwords, BOXED_PAGE_FLAG);
+}
+
+lispobj
+copy_code_object(lispobj object, long nwords)
+{
+    return gc_general_copy_object(object, nwords, CODE_PAGE_FLAG);
+}
+
 static long scav_lose(lispobj *where, lispobj object); /* forward decl */
 
 /* FIXME: Most calls end up going to some trouble to compute an
 static long scav_lose(lispobj *where, lispobj object); /* forward decl */
 
 /* FIXME: Most calls end up going to some trouble to compute an
@@ -127,14 +142,16 @@ scavenge(lispobj *start, long n_words)
     lispobj *end = start + n_words;
     lispobj *object_ptr;
     long n_words_scavenged;
     lispobj *end = start + n_words;
     lispobj *object_ptr;
     long n_words_scavenged;
-    for (object_ptr = start;
 
 
+    for (object_ptr = start;
          object_ptr < end;
          object_ptr += n_words_scavenged) {
 
         lispobj object = *object_ptr;
 #ifdef LISP_FEATURE_GENCGC
          object_ptr < end;
          object_ptr += n_words_scavenged) {
 
         lispobj object = *object_ptr;
 #ifdef LISP_FEATURE_GENCGC
-        gc_assert(!forwarding_pointer_p(object_ptr));
+        if (forwarding_pointer_p(object_ptr))
+            lose("unexpect forwarding pointer in scavenge: %p, start=%p, n=%l\n",
+                 object_ptr, start, n_words);
 #endif
         if (is_lisp_pointer(object)) {
             if (from_space_p(object)) {
 #endif
         if (is_lisp_pointer(object)) {
             if (from_space_p(object)) {
@@ -156,25 +173,28 @@ scavenge(lispobj *start, long n_words)
                 n_words_scavenged = 1;
             }
         }
                 n_words_scavenged = 1;
             }
         }
-#ifndef LISP_FEATURE_GENCGC
-        /* this workaround is probably not necessary for gencgc; at least, the
-         * behaviour it describes has never been reported */
-        else if (n_words==1) {
-            /* there are some situations where an
-               other-immediate may end up in a descriptor
-               register.  I'm not sure whether this is
-               supposed to happen, but if it does then we
+#if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64)
+        /* This workaround is probably not needed for those ports
+           which don't have a partitioned register set (and therefore
+           scan the stack conservatively for roots). */
+        else if (n_words == 1) {
+            /* there are some situations where an other-immediate may
+               end up in a descriptor register.  I'm not sure whether
+               this is supposed to happen, but if it does then we
                don't want to (a) barf or (b) scavenge over the
                don't want to (a) barf or (b) scavenge over the
-               data-block, because there isn't one.  So, if
-               we're checking a single word and it's anything
-               other than a pointer, just hush it up */
-            int type=widetag_of(object);
-            n_words_scavenged=1;
-
-            if ((scavtab[type]==scav_lose) ||
-                (((scavtab[type])(start,object))>1)) {
-                fprintf(stderr,"warning: attempted to scavenge non-descriptor value %x at %p.  If you can\nreproduce this warning, send a bug report (see manual page for details)\n",
-                        object,start);
+               data-block, because there isn't one.  So, if we're
+               checking a single word and it's anything other than a
+               pointer, just hush it up */
+            int widetag = widetag_of(object);
+            n_words_scavenged = 1;
+
+            if ((scavtab[widetag] == scav_lose) ||
+                (((sizetab[widetag])(object_ptr)) > 1)) {
+                fprintf(stderr,"warning: \
+attempted to scavenge non-descriptor value %x at %p.\n\n\
+If you can reproduce this warning, please send a bug report\n\
+(see manual page for details).\n",
+                        object, object_ptr);
             }
         }
 #endif
             }
         }
 #endif
@@ -187,7 +207,8 @@ scavenge(lispobj *start, long n_words)
                 (scavtab[widetag_of(object)])(object_ptr, object);
         }
     }
                 (scavtab[widetag_of(object)])(object_ptr, object);
         }
     }
-    gc_assert(object_ptr == end);
+    gc_assert_verbose(object_ptr == end, "Final object pointer %p, start %p, end %p\n",
+                      object_ptr, start, end);
 }
 
 static lispobj trans_fun_header(lispobj object); /* forward decls */
 }
 
 static lispobj trans_fun_header(lispobj object); /* forward decls */
@@ -259,7 +280,7 @@ trans_code(struct code *code)
     nwords = ncode_words + nheader_words;
     nwords = CEILING(nwords, 2);
 
     nwords = ncode_words + nheader_words;
     nwords = CEILING(nwords, 2);
 
-    l_new_code = copy_object(l_code, nwords);
+    l_new_code = copy_code_object(l_code, nwords);
     new_code = (struct code *) native_pointer(l_new_code);
 
 #if defined(DEBUG_CODE_GC)
     new_code = (struct code *) native_pointer(l_new_code);
 
 #if defined(DEBUG_CODE_GC)
@@ -313,11 +334,18 @@ trans_code(struct code *code)
         fheaderl = fheaderp->next;
         prev_pointer = &nfheaderp->next;
     }
         fheaderl = fheaderp->next;
         prev_pointer = &nfheaderp->next;
     }
+#ifdef LISP_FEATURE_GENCGC
+    /* Cheneygc doesn't need this os_flush_icache, it flushes the whole
+       spaces once when all copying is done. */
     os_flush_icache((os_vm_address_t) (((long *)new_code) + nheader_words),
                     ncode_words * sizeof(long));
     os_flush_icache((os_vm_address_t) (((long *)new_code) + nheader_words),
                     ncode_words * sizeof(long));
-#ifdef LISP_FEATURE_GENCGC
+
+#endif
+
+#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
     gencgc_apply_code_fixups(code, new_code);
 #endif
     gencgc_apply_code_fixups(code, new_code);
 #endif
+
     return new_code;
 }
 
     return new_code;
 }
 
@@ -344,7 +372,9 @@ scav_code_header(lispobj *where, lispobj object)
          entry_point != NIL;
          entry_point = function_ptr->next) {
 
          entry_point != NIL;
          entry_point = function_ptr->next) {
 
-        gc_assert(is_lisp_pointer(entry_point));
+        gc_assert_verbose(is_lisp_pointer(entry_point),
+                          "Entry point %lx\n is not a lisp pointer.",
+                          (long)entry_point);
 
         function_ptr = (struct simple_fun *) native_pointer(entry_point);
         gc_assert(widetag_of(function_ptr->header)==SIMPLE_FUN_HEADER_WIDETAG);
 
         function_ptr = (struct simple_fun *) native_pointer(entry_point);
         gc_assert(widetag_of(function_ptr->header)==SIMPLE_FUN_HEADER_WIDETAG);
@@ -352,6 +382,7 @@ scav_code_header(lispobj *where, lispobj object)
         scavenge(&function_ptr->name, 1);
         scavenge(&function_ptr->arglist, 1);
         scavenge(&function_ptr->type, 1);
         scavenge(&function_ptr->name, 1);
         scavenge(&function_ptr->arglist, 1);
         scavenge(&function_ptr->type, 1);
+        scavenge(&function_ptr->xrefs, 1);
     }
 
     return n_words;
     }
 
     return n_words;
@@ -387,7 +418,7 @@ size_code_header(lispobj *where)
 static long
 scav_return_pc_header(lispobj *where, lispobj object)
 {
 static long
 scav_return_pc_header(lispobj *where, lispobj object)
 {
-    lose("attempted to scavenge a return PC header where=0x%08x object=0x%08x",
+    lose("attempted to scavenge a return PC header where=0x%08x object=0x%08x\n",
          (unsigned long) where,
          (unsigned long) object);
     return 0; /* bogus return value to satisfy static type checking */
          (unsigned long) where,
          (unsigned long) object);
     return 0; /* bogus return value to satisfy static type checking */
@@ -442,7 +473,7 @@ scav_closure_header(lispobj *where, lispobj object)
 static long
 scav_fun_header(lispobj *where, lispobj object)
 {
 static long
 scav_fun_header(lispobj *where, lispobj object)
 {
-    lose("attempted to scavenge a function header where=0x%08x object=0x%08x",
+    lose("attempted to scavenge a function header where=0x%08x object=0x%08x\n",
          (unsigned long) where,
          (unsigned long) object);
     return 0; /* bogus return value to satisfy static type checking */
          (unsigned long) where,
          (unsigned long) object);
     return 0; /* bogus return value to satisfy static type checking */
@@ -533,7 +564,7 @@ trans_list(lispobj object)
 
     /* Copy 'object'. */
     new_cons = (struct cons *)
 
     /* Copy 'object'. */
     new_cons = (struct cons *)
-        gc_general_alloc(sizeof(struct cons),ALLOC_BOXED,ALLOC_QUICK);
+        gc_general_alloc(sizeof(struct cons), BOXED_PAGE_FLAG, ALLOC_QUICK);
     new_cons->car = cons->car;
     new_cons->cdr = cons->cdr; /* updated later */
     new_list_pointer = make_lispobj(new_cons,lowtag_of(object));
     new_cons->car = cons->car;
     new_cons->cdr = cons->cdr; /* updated later */
     new_list_pointer = make_lispobj(new_cons,lowtag_of(object));
@@ -558,7 +589,7 @@ trans_list(lispobj object)
 
         /* Copy 'cdr'. */
         new_cdr_cons = (struct cons*)
 
         /* Copy 'cdr'. */
         new_cdr_cons = (struct cons*)
-            gc_general_alloc(sizeof(struct cons),ALLOC_BOXED,ALLOC_QUICK);
+            gc_general_alloc(sizeof(struct cons), BOXED_PAGE_FLAG, ALLOC_QUICK);
         new_cdr_cons->car = cdr_cons->car;
         new_cdr_cons->cdr = cdr_cons->cdr;
         new_cdr = make_lispobj(new_cdr_cons, lowtag_of(cdr));
         new_cdr_cons->car = cdr_cons->car;
         new_cdr_cons->cdr = cdr_cons->cdr;
         new_cdr = make_lispobj(new_cdr_cons, lowtag_of(cdr));
@@ -627,7 +658,7 @@ scav_immediate(lispobj *where, lispobj object)
 static lispobj
 trans_immediate(lispobj object)
 {
 static lispobj
 trans_immediate(lispobj object)
 {
-    lose("trying to transport an immediate");
+    lose("trying to transport an immediate\n");
     return NIL; /* bogus return value to satisfy static type checking */
 }
 
     return NIL; /* bogus return value to satisfy static type checking */
 }
 
@@ -649,7 +680,7 @@ scav_instance(lispobj *where, lispobj object)
 {
     lispobj nuntagged;
     long ntotal = HeaderValue(object);
 {
     lispobj nuntagged;
     long ntotal = HeaderValue(object);
-    lispobj layout = ((struct instance *)native_pointer(where))->slots[0];
+    lispobj layout = ((struct instance *)where)->slots[0];
 
     if (!layout)
         return 1;
 
     if (!layout)
         return 1;
@@ -693,7 +724,7 @@ size_boxed(lispobj *where)
 
 /* Note: on the sparc we don't have to do anything special for fdefns, */
 /* 'cause the raw-addr has a function lowtag. */
 
 /* Note: on the sparc we don't have to do anything special for fdefns, */
 /* 'cause the raw-addr has a function lowtag. */
-#ifndef LISP_FEATURE_SPARC
+#if !defined(LISP_FEATURE_SPARC)
 static long
 scav_fdefn(lispobj *where, lispobj object)
 {
 static long
 scav_fdefn(lispobj *where, lispobj object)
 {
@@ -704,8 +735,7 @@ scav_fdefn(lispobj *where, lispobj object)
     /* FSHOW((stderr, "scav_fdefn, function = %p, raw_addr = %p\n",
        fdefn->fun, fdefn->raw_addr)); */
 
     /* FSHOW((stderr, "scav_fdefn, function = %p, raw_addr = %p\n",
        fdefn->fun, fdefn->raw_addr)); */
 
-    if ((char *)(fdefn->fun + FUN_RAW_ADDR_OFFSET)
-        == (char *)((unsigned long)(fdefn->raw_addr))) {
+    if ((char *)(fdefn->fun + FUN_RAW_ADDR_OFFSET) == fdefn->raw_addr) {
         scavenge(where + 1, sizeof(struct fdefn)/sizeof(lispobj) - 1);
 
         /* Don't write unnecessarily. */
         scavenge(where + 1, sizeof(struct fdefn)/sizeof(lispobj) - 1);
 
         /* Don't write unnecessarily. */
@@ -1491,7 +1521,7 @@ trans_weak_pointer(lispobj object)
 
     gc_assert(widetag_of(wp->header)==WEAK_POINTER_WIDETAG);
     /* Push the weak pointer onto the list of weak pointers. */
 
     gc_assert(widetag_of(wp->header)==WEAK_POINTER_WIDETAG);
     /* Push the weak pointer onto the list of weak pointers. */
-    wp->next = LOW_WORD(weak_pointers);
+    wp->next = (struct weak_pointer *)LOW_WORD(weak_pointers);
     weak_pointers = wp;
 #endif
     return copy;
     weak_pointers = wp;
 #endif
     return copy;
@@ -1506,12 +1536,17 @@ size_weak_pointer(lispobj *where)
 
 void scan_weak_pointers(void)
 {
 
 void scan_weak_pointers(void)
 {
-    struct weak_pointer *wp;
-    for (wp = weak_pointers; wp != NULL;
-         wp=(struct weak_pointer *)native_pointer(wp->next)) {
+    struct weak_pointer *wp, *next_wp;
+    for (wp = weak_pointers, next_wp = NULL; wp != NULL; wp = next_wp) {
         lispobj value = wp->value;
         lispobj *first_pointer;
         gc_assert(widetag_of(wp->header)==WEAK_POINTER_WIDETAG);
         lispobj value = wp->value;
         lispobj *first_pointer;
         gc_assert(widetag_of(wp->header)==WEAK_POINTER_WIDETAG);
+
+        next_wp = wp->next;
+        wp->next = NULL;
+        if (next_wp == wp) /* gencgc uses a ref to self for end of list */
+            next_wp = NULL;
+
         if (!(is_lisp_pointer(value) && from_space_p(value)))
             continue;
 
         if (!(is_lisp_pointer(value) && from_space_p(value)))
             continue;
 
@@ -1533,6 +1568,312 @@ void scan_weak_pointers(void)
     }
 }
 
     }
 }
 
+\f
+/* Hash tables */
+
+#if N_WORD_BITS == 32
+#define EQ_HASH_MASK 0x1fffffff
+#elif N_WORD_BITS == 64
+#define EQ_HASH_MASK 0x1fffffffffffffff
+#endif
+
+/* Compute the EQ-hash of KEY. This must match POINTER-HASH in
+ * target-hash-table.lisp.  */
+#define EQ_HASH(key) ((key) & EQ_HASH_MASK)
+
+/* List of weak hash tables chained through their NEXT-WEAK-HASH-TABLE
+ * slot. Set to NULL at the end of a collection.
+ *
+ * This is not optimal because, when a table is tenured, it won't be
+ * processed automatically; only the yougest generation is GC'd by
+ * default. On the other hand, all applications will need an
+ * occasional full GC anyway, so it's not that bad either.  */
+struct hash_table *weak_hash_tables = NULL;
+
+/* Return true if OBJ has already survived the current GC. */
+static inline int
+survived_gc_yet (lispobj obj)
+{
+    return (!is_lisp_pointer(obj) || !from_space_p(obj) ||
+            forwarding_pointer_p(native_pointer(obj)));
+}
+
+static inline int
+weak_hash_entry_alivep (lispobj weakness, lispobj key, lispobj value)
+{
+    switch (weakness) {
+    case KEY:
+        return survived_gc_yet(key);
+    case VALUE:
+        return survived_gc_yet(value);
+    case KEY_OR_VALUE:
+        return (survived_gc_yet(key) || survived_gc_yet(value));
+    case KEY_AND_VALUE:
+        return (survived_gc_yet(key) && survived_gc_yet(value));
+    default:
+        gc_assert(0);
+        /* Shut compiler up. */
+        return 0;
+    }
+}
+
+/* Return the beginning of data in ARRAY (skipping the header and the
+ * length) or NULL if it isn't an array of the specified widetag after
+ * all. */
+static inline lispobj *
+get_array_data (lispobj array, int widetag, unsigned long *length)
+{
+    if (is_lisp_pointer(array) &&
+        (widetag_of(*(lispobj *)native_pointer(array)) == widetag)) {
+        if (length != NULL)
+            *length = fixnum_value(((lispobj *)native_pointer(array))[1]);
+        return ((lispobj *)native_pointer(array)) + 2;
+    } else {
+        return NULL;
+    }
+}
+
+/* Only need to worry about scavenging the _real_ entries in the
+ * table. Phantom entries such as the hash table itself at index 0 and
+ * the empty marker at index 1 were scavenged by scav_vector that
+ * either called this function directly or arranged for it to be
+ * called later by pushing the hash table onto weak_hash_tables. */
+static void
+scav_hash_table_entries (struct hash_table *hash_table)
+{
+    lispobj *kv_vector;
+    unsigned long kv_length;
+    lispobj *index_vector;
+    unsigned long length;
+    lispobj *next_vector;
+    unsigned long next_vector_length;
+    lispobj *hash_vector;
+    unsigned long hash_vector_length;
+    lispobj empty_symbol;
+    lispobj weakness = hash_table->weakness;
+    unsigned long i;
+
+    kv_vector = get_array_data(hash_table->table,
+                               SIMPLE_VECTOR_WIDETAG, &kv_length);
+    if (kv_vector == NULL)
+        lose("invalid kv_vector %x\n", hash_table->table);
+
+    index_vector = get_array_data(hash_table->index_vector,
+                                  SIMPLE_ARRAY_WORD_WIDETAG, &length);
+    if (index_vector == NULL)
+        lose("invalid index_vector %x\n", hash_table->index_vector);
+
+    next_vector = get_array_data(hash_table->next_vector,
+                                 SIMPLE_ARRAY_WORD_WIDETAG,
+                                 &next_vector_length);
+    if (next_vector == NULL)
+        lose("invalid next_vector %x\n", hash_table->next_vector);
+
+    hash_vector = get_array_data(hash_table->hash_vector,
+                                 SIMPLE_ARRAY_WORD_WIDETAG,
+                                 &hash_vector_length);
+    if (hash_vector != NULL)
+        gc_assert(hash_vector_length == next_vector_length);
+
+     /* These lengths could be different as the index_vector can be a
+      * different length from the others, a larger index_vector could
+      * help reduce collisions. */
+     gc_assert(next_vector_length*2 == kv_length);
+
+    empty_symbol = kv_vector[1];
+    /* fprintf(stderr,"* empty_symbol = %x\n", empty_symbol);*/
+    if (widetag_of(*(lispobj *)native_pointer(empty_symbol)) !=
+        SYMBOL_HEADER_WIDETAG) {
+        lose("not a symbol where empty-hash-table-slot symbol expected: %x\n",
+             *(lispobj *)native_pointer(empty_symbol));
+    }
+
+    /* Work through the KV vector. */
+    for (i = 1; i < next_vector_length; i++) {
+        lispobj old_key = kv_vector[2*i];
+        lispobj value = kv_vector[2*i+1];
+        if ((weakness == NIL) ||
+            weak_hash_entry_alivep(weakness, old_key, value)) {
+
+            /* Scavenge the key and value. */
+            scavenge(&kv_vector[2*i],2);
+
+            /* If an EQ-based key has moved, mark the hash-table for
+             * rehashing. */
+            if (!hash_vector || hash_vector[i] == MAGIC_HASH_VECTOR_VALUE) {
+                lispobj new_key = kv_vector[2*i];
+
+                if (old_key != new_key && new_key != empty_symbol) {
+                    hash_table->needs_rehash_p = T;
+                }
+            }
+        }
+    }
+}
+
+long
+scav_vector (lispobj *where, lispobj object)
+{
+    unsigned long kv_length;
+    lispobj *kv_vector;
+    struct hash_table *hash_table;
+
+    /* SB-VM:VECTOR-VALID-HASHING-SUBTYPE is set for EQ-based and weak
+     * hash tables in the Lisp HASH-TABLE code to indicate need for
+     * special GC support. */
+    if (HeaderValue(object) == subtype_VectorNormal)
+        return 1;
+
+    kv_length = fixnum_value(where[1]);
+    kv_vector = where + 2;  /* Skip the header and length. */
+    /*FSHOW((stderr,"/kv_length = %d\n", kv_length));*/
+
+    /* Scavenge element 0, which may be a hash-table structure. */
+    scavenge(where+2, 1);
+    if (!is_lisp_pointer(where[2])) {
+        /* This'll happen when REHASH clears the header of old-kv-vector
+         * and fills it with zero, but some other thread simulatenously
+         * sets the header in %%PUTHASH.
+         */
+        fprintf(stderr,
+                "Warning: no pointer at %lx in hash table: this indicates "
+                "non-fatal corruption caused by concurrent access to a "
+                "hash-table from multiple threads. Any accesses to "
+                "hash-tables shared between threads should be protected "
+                "by locks.\n", (unsigned long)&where[2]);
+        // We've scavenged three words.
+        return 3;
+    }
+    hash_table = (struct hash_table *)native_pointer(where[2]);
+    /*FSHOW((stderr,"/hash_table = %x\n", hash_table));*/
+    if (widetag_of(hash_table->header) != INSTANCE_HEADER_WIDETAG) {
+        lose("hash table not instance (%x at %x)\n",
+             hash_table->header,
+             hash_table);
+    }
+
+    /* Scavenge element 1, which should be some internal symbol that
+     * the hash table code reserves for marking empty slots. */
+    scavenge(where+3, 1);
+    if (!is_lisp_pointer(where[3])) {
+        lose("not empty-hash-table-slot symbol pointer: %x\n", where[3]);
+    }
+
+    /* Scavenge hash table, which will fix the positions of the other
+     * needed objects. */
+    scavenge((lispobj *)hash_table,
+             sizeof(struct hash_table) / sizeof(lispobj));
+
+    /* Cross-check the kv_vector. */
+    if (where != (lispobj *)native_pointer(hash_table->table)) {
+        lose("hash_table table!=this table %x\n", hash_table->table);
+    }
+
+    if (hash_table->weakness == NIL) {
+        scav_hash_table_entries(hash_table);
+    } else {
+        /* Delay scavenging of this table by pushing it onto
+         * weak_hash_tables (if it's not there already) for the weak
+         * object phase. */
+        if (hash_table->next_weak_hash_table == NIL) {
+            hash_table->next_weak_hash_table = (lispobj)weak_hash_tables;
+            weak_hash_tables = hash_table;
+        }
+    }
+
+    return (CEILING(kv_length + 2, 2));
+}
+
+void
+scav_weak_hash_tables (void)
+{
+    struct hash_table *table;
+
+    /* Scavenge entries whose triggers are known to survive. */
+    for (table = weak_hash_tables; table != NULL;
+         table = (struct hash_table *)table->next_weak_hash_table) {
+        scav_hash_table_entries(table);
+    }
+}
+
+/* Walk through the chain whose first element is *FIRST and remove
+ * dead weak entries. */
+static inline void
+scan_weak_hash_table_chain (struct hash_table *hash_table, lispobj *prev,
+                            lispobj *kv_vector, lispobj *index_vector,
+                            lispobj *next_vector, lispobj *hash_vector,
+                            lispobj empty_symbol, lispobj weakness)
+{
+    unsigned index = *prev;
+    while (index) {
+        unsigned next = next_vector[index];
+        lispobj key = kv_vector[2 * index];
+        lispobj value = kv_vector[2 * index + 1];
+        gc_assert(key != empty_symbol);
+        gc_assert(value != empty_symbol);
+        if (!weak_hash_entry_alivep(weakness, key, value)) {
+            unsigned count = fixnum_value(hash_table->number_entries);
+            gc_assert(count > 0);
+            *prev = next;
+            hash_table->number_entries = make_fixnum(count - 1);
+            next_vector[index] = fixnum_value(hash_table->next_free_kv);
+            hash_table->next_free_kv = make_fixnum(index);
+            kv_vector[2 * index] = empty_symbol;
+            kv_vector[2 * index + 1] = empty_symbol;
+            if (hash_vector)
+                hash_vector[index] = MAGIC_HASH_VECTOR_VALUE;
+        } else {
+            prev = &next_vector[index];
+        }
+        index = next;
+    }
+}
+
+static void
+scan_weak_hash_table (struct hash_table *hash_table)
+{
+    lispobj *kv_vector;
+    lispobj *index_vector;
+    unsigned long length = 0; /* prevent warning */
+    lispobj *next_vector;
+    unsigned long next_vector_length = 0; /* prevent warning */
+    lispobj *hash_vector;
+    lispobj empty_symbol;
+    lispobj weakness = hash_table->weakness;
+    unsigned long i;
+
+    kv_vector = get_array_data(hash_table->table,
+                               SIMPLE_VECTOR_WIDETAG, NULL);
+    index_vector = get_array_data(hash_table->index_vector,
+                                  SIMPLE_ARRAY_WORD_WIDETAG, &length);
+    next_vector = get_array_data(hash_table->next_vector,
+                                 SIMPLE_ARRAY_WORD_WIDETAG,
+                                 &next_vector_length);
+    hash_vector = get_array_data(hash_table->hash_vector,
+                                 SIMPLE_ARRAY_WORD_WIDETAG, NULL);
+    empty_symbol = kv_vector[1];
+
+    for (i = 0; i < length; i++) {
+        scan_weak_hash_table_chain(hash_table, &index_vector[i],
+                                   kv_vector, index_vector, next_vector,
+                                   hash_vector, empty_symbol, weakness);
+    }
+}
+
+/* Remove dead entries from weak hash tables. */
+void
+scan_weak_hash_tables (void)
+{
+    struct hash_table *table, *next;
+
+    for (table = weak_hash_tables; table != NULL; table = next) {
+        next = (struct hash_table *)table->next_weak_hash_table;
+        table->next_weak_hash_table = NIL;
+        scan_weak_hash_table(table);
+    }
+
+    weak_hash_tables = NULL;
+}
 
 \f
 /*
 
 \f
 /*
@@ -1542,9 +1883,9 @@ void scan_weak_pointers(void)
 static long
 scav_lose(lispobj *where, lispobj object)
 {
 static long
 scav_lose(lispobj *where, lispobj object)
 {
-    lose("no scavenge function for object 0x%08x (widetag 0x%x)",
+    lose("no scavenge function for object 0x%08x (widetag 0x%x)\n",
          (unsigned long)object,
          (unsigned long)object,
-         widetag_of(*(lispobj*)native_pointer(object)));
+         widetag_of(object));
 
     return 0; /* bogus return value to satisfy static type checking */
 }
 
     return 0; /* bogus return value to satisfy static type checking */
 }
@@ -1552,7 +1893,7 @@ scav_lose(lispobj *where, lispobj object)
 static lispobj
 trans_lose(lispobj object)
 {
 static lispobj
 trans_lose(lispobj object)
 {
-    lose("no transport function for object 0x%08x (widetag 0x%x)",
+    lose("no transport function for object 0x%08x (widetag 0x%x)\n",
          (unsigned long)object,
          widetag_of(*(lispobj*)native_pointer(object)));
     return NIL; /* bogus return value to satisfy static type checking */
          (unsigned long)object,
          widetag_of(*(lispobj*)native_pointer(object)));
     return NIL; /* bogus return value to satisfy static type checking */
@@ -1561,7 +1902,7 @@ trans_lose(lispobj object)
 static long
 size_lose(lispobj *where)
 {
 static long
 size_lose(lispobj *where)
 {
-    lose("no size function for object at 0x%08x (widetag 0x%x)",
+    lose("no size function for object at 0x%08x (widetag 0x%x)\n",
          (unsigned long)where,
          widetag_of(LOW_WORD(where)));
     return 1; /* bogus return value to satisfy static type checking */
          (unsigned long)where,
          widetag_of(LOW_WORD(where)));
     return 1; /* bogus return value to satisfy static type checking */
@@ -1575,7 +1916,7 @@ size_lose(lispobj *where)
 void
 gc_init_tables(void)
 {
 void
 gc_init_tables(void)
 {
-    long i;
+    unsigned long i;
 
     /* Set default value in all slots of scavenge table.  FIXME
      * replace this gnarly sizeof with something based on
 
     /* Set default value in all slots of scavenge table.  FIXME
      * replace this gnarly sizeof with something based on
@@ -1595,7 +1936,8 @@ gc_init_tables(void)
         /* skipping OTHER_IMMEDIATE_0_LOWTAG */
         scavtab[LIST_POINTER_LOWTAG|(i<<N_LOWTAG_BITS)] = scav_list_pointer;
         scavtab[ODD_FIXNUM_LOWTAG|(i<<N_LOWTAG_BITS)] = scav_immediate;
         /* skipping OTHER_IMMEDIATE_0_LOWTAG */
         scavtab[LIST_POINTER_LOWTAG|(i<<N_LOWTAG_BITS)] = scav_list_pointer;
         scavtab[ODD_FIXNUM_LOWTAG|(i<<N_LOWTAG_BITS)] = scav_immediate;
-        scavtab[INSTANCE_POINTER_LOWTAG|(i<<N_LOWTAG_BITS)] = scav_instance_pointer;
+        scavtab[INSTANCE_POINTER_LOWTAG|(i<<N_LOWTAG_BITS)] =
+            scav_instance_pointer;
         /* skipping OTHER_IMMEDIATE_1_LOWTAG */
         scavtab[OTHER_POINTER_LOWTAG|(i<<N_LOWTAG_BITS)] = scav_other_pointer;
     }
         /* skipping OTHER_IMMEDIATE_1_LOWTAG */
         scavtab[OTHER_POINTER_LOWTAG|(i<<N_LOWTAG_BITS)] = scav_other_pointer;
     }
@@ -1711,28 +2053,29 @@ gc_init_tables(void)
     scavtab[COMPLEX_VECTOR_WIDETAG] = scav_boxed;
     scavtab[COMPLEX_ARRAY_WIDETAG] = scav_boxed;
     scavtab[CODE_HEADER_WIDETAG] = scav_code_header;
     scavtab[COMPLEX_VECTOR_WIDETAG] = scav_boxed;
     scavtab[COMPLEX_ARRAY_WIDETAG] = scav_boxed;
     scavtab[CODE_HEADER_WIDETAG] = scav_code_header;
-#ifndef LISP_FEATURE_GENCGC     /* FIXME ..._X86 ? */
+#if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64)
     scavtab[SIMPLE_FUN_HEADER_WIDETAG] = scav_fun_header;
     scavtab[RETURN_PC_HEADER_WIDETAG] = scav_return_pc_header;
 #endif
     scavtab[SIMPLE_FUN_HEADER_WIDETAG] = scav_fun_header;
     scavtab[RETURN_PC_HEADER_WIDETAG] = scav_return_pc_header;
 #endif
+    scavtab[FUNCALLABLE_INSTANCE_HEADER_WIDETAG] = scav_boxed;
 #if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
     scavtab[CLOSURE_HEADER_WIDETAG] = scav_closure_header;
 #if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
     scavtab[CLOSURE_HEADER_WIDETAG] = scav_closure_header;
-    scavtab[FUNCALLABLE_INSTANCE_HEADER_WIDETAG] = scav_closure_header;
 #else
     scavtab[CLOSURE_HEADER_WIDETAG] = scav_boxed;
 #else
     scavtab[CLOSURE_HEADER_WIDETAG] = scav_boxed;
-    scavtab[FUNCALLABLE_INSTANCE_HEADER_WIDETAG] = scav_boxed;
 #endif
     scavtab[VALUE_CELL_HEADER_WIDETAG] = scav_boxed;
     scavtab[SYMBOL_HEADER_WIDETAG] = scav_boxed;
     scavtab[CHARACTER_WIDETAG] = scav_immediate;
     scavtab[SAP_WIDETAG] = scav_unboxed;
     scavtab[UNBOUND_MARKER_WIDETAG] = scav_immediate;
 #endif
     scavtab[VALUE_CELL_HEADER_WIDETAG] = scav_boxed;
     scavtab[SYMBOL_HEADER_WIDETAG] = scav_boxed;
     scavtab[CHARACTER_WIDETAG] = scav_immediate;
     scavtab[SAP_WIDETAG] = scav_unboxed;
     scavtab[UNBOUND_MARKER_WIDETAG] = scav_immediate;
+    scavtab[NO_TLS_VALUE_MARKER_WIDETAG] = scav_immediate;
     scavtab[INSTANCE_HEADER_WIDETAG] = scav_instance;
     scavtab[INSTANCE_HEADER_WIDETAG] = scav_instance;
-#ifdef LISP_FEATURE_SPARC
+#if defined(LISP_FEATURE_SPARC)
     scavtab[FDEFN_WIDETAG] = scav_boxed;
 #else
     scavtab[FDEFN_WIDETAG] = scav_fdefn;
 #endif
     scavtab[FDEFN_WIDETAG] = scav_boxed;
 #else
     scavtab[FDEFN_WIDETAG] = scav_fdefn;
 #endif
+    scavtab[SIMPLE_VECTOR_WIDETAG] = scav_vector;
 
     /* transport other table, initialized same way as scavtab */
     for (i = 0; i < ((sizeof transother)/(sizeof transother[0])); i++)
 
     /* transport other table, initialized same way as scavtab */
     for (i = 0; i < ((sizeof transother)/(sizeof transother[0])); i++)
@@ -1861,6 +2204,7 @@ gc_init_tables(void)
     transother[CHARACTER_WIDETAG] = trans_immediate;
     transother[SAP_WIDETAG] = trans_unboxed;
     transother[UNBOUND_MARKER_WIDETAG] = trans_immediate;
     transother[CHARACTER_WIDETAG] = trans_immediate;
     transother[SAP_WIDETAG] = trans_unboxed;
     transother[UNBOUND_MARKER_WIDETAG] = trans_immediate;
+    transother[NO_TLS_VALUE_MARKER_WIDETAG] = trans_immediate;
     transother[WEAK_POINTER_WIDETAG] = trans_weak_pointer;
     transother[INSTANCE_HEADER_WIDETAG] = trans_boxed;
     transother[FDEFN_WIDETAG] = trans_boxed;
     transother[WEAK_POINTER_WIDETAG] = trans_weak_pointer;
     transother[INSTANCE_HEADER_WIDETAG] = trans_boxed;
     transother[FDEFN_WIDETAG] = trans_boxed;
@@ -2000,6 +2344,7 @@ gc_init_tables(void)
     sizetab[CHARACTER_WIDETAG] = size_immediate;
     sizetab[SAP_WIDETAG] = size_unboxed;
     sizetab[UNBOUND_MARKER_WIDETAG] = size_immediate;
     sizetab[CHARACTER_WIDETAG] = size_immediate;
     sizetab[SAP_WIDETAG] = size_unboxed;
     sizetab[UNBOUND_MARKER_WIDETAG] = size_immediate;
+    sizetab[NO_TLS_VALUE_MARKER_WIDETAG] = size_immediate;
     sizetab[WEAK_POINTER_WIDETAG] = size_weak_pointer;
     sizetab[INSTANCE_HEADER_WIDETAG] = size_boxed;
     sizetab[FDEFN_WIDETAG] = size_boxed;
     sizetab[WEAK_POINTER_WIDETAG] = size_weak_pointer;
     sizetab[INSTANCE_HEADER_WIDETAG] = size_boxed;
     sizetab[FDEFN_WIDETAG] = size_boxed;
@@ -2037,13 +2382,7 @@ gc_search_space(lispobj *start, size_t words, lispobj *pointer)
         lispobj thing = *start;
 
         /* If thing is an immediate then this is a cons. */
         lispobj thing = *start;
 
         /* If thing is an immediate then this is a cons. */
-        if (is_lisp_pointer(thing)
-            || (fixnump(thing))
-            || (widetag_of(thing) == CHARACTER_WIDETAG)
-#if N_WORD_BITS == 64
-            || (widetag_of(thing) == SINGLE_FLOAT_WIDETAG)
-#endif
-            || (widetag_of(thing) == UNBOUND_MARKER_WIDETAG))
+        if (is_lisp_pointer(thing) || is_lisp_immediate(thing))
             count = 2;
         else
             count = (sizetab[widetag_of(thing)])(start);
             count = 2;
         else
             count = (sizetab[widetag_of(thing)])(start);
@@ -2063,3 +2402,72 @@ gc_search_space(lispobj *start, size_t words, lispobj *pointer)
     }
     return (NULL);
 }
     }
     return (NULL);
 }
+
+boolean
+maybe_gc(os_context_t *context)
+{
+    lispobj gc_happened;
+    struct thread *thread = arch_os_get_current_thread();
+
+    fake_foreign_function_call(context);
+    /* SUB-GC may return without GCing if *GC-INHIBIT* is set, in
+     * which case we will be running with no gc trigger barrier
+     * thing for a while.  But it shouldn't be long until the end
+     * of WITHOUT-GCING.
+     *
+     * FIXME: It would be good to protect the end of dynamic space for
+     * CheneyGC and signal a storage condition from there.
+     */
+
+    /* Restore the signal mask from the interrupted context before
+     * calling into Lisp if interrupts are enabled. Why not always?
+     *
+     * Suppose there is a WITHOUT-INTERRUPTS block far, far out. If an
+     * interrupt hits while in SUB-GC, it is deferred and the
+     * os_context_sigmask of that interrupt is set to block further
+     * deferrable interrupts (until the first one is
+     * handled). Unfortunately, that context refers to this place and
+     * when we return from here the signals will not be blocked.
+     *
+     * A kludgy alternative is to propagate the sigmask change to the
+     * outer context.
+     */
+#ifndef LISP_FEATURE_WIN32
+    check_gc_signals_unblocked_or_lose(os_context_sigmask_addr(context));
+    unblock_gc_signals(0, 0);
+#endif
+    FSHOW((stderr, "/maybe_gc: calling SUB_GC\n"));
+    /* FIXME: Nothing must go wrong during GC else we end up running
+     * the debugger, error handlers, and user code in general in a
+     * potentially unsafe place. Running out of the control stack or
+     * the heap in SUB-GC are ways to lose. Of course, deferrables
+     * cannot be unblocked because there may be a pending handler, or
+     * we may even be in a WITHOUT-INTERRUPTS. */
+    gc_happened = funcall0(StaticSymbolFunction(SUB_GC));
+    FSHOW((stderr, "/maybe_gc: gc_happened=%s\n",
+           (gc_happened == NIL) ? "NIL" : "T"));
+    if ((gc_happened != NIL) &&
+        /* See if interrupts are enabled or it's possible to enable
+         * them. POST-GC has a similar check, but we don't want to
+         * unlock deferrables in that case and get a pending interrupt
+         * here. */
+        ((SymbolValue(INTERRUPTS_ENABLED,thread) != NIL) ||
+         (SymbolValue(ALLOW_WITH_INTERRUPTS,thread) != NIL))) {
+#ifndef LISP_FEATURE_WIN32
+        sigset_t *context_sigmask = os_context_sigmask_addr(context);
+        if (!deferrables_blocked_p(context_sigmask)) {
+            thread_sigmask(SIG_SETMASK, context_sigmask, 0);
+            check_gc_signals_unblocked_or_lose(0);
+#endif
+            FSHOW((stderr, "/maybe_gc: calling POST_GC\n"));
+            funcall0(StaticSymbolFunction(POST_GC));
+#ifndef LISP_FEATURE_WIN32
+        } else {
+            FSHOW((stderr, "/maybe_gc: punting on POST_GC due to blockage\n"));
+        }
+#endif
+    }
+    undo_fake_foreign_function_call(context);
+    FSHOW((stderr, "/maybe_gc: returning\n"));
+    return (gc_happened != NIL);
+}