Simplify (and robustify) regular PACKing
[sbcl.git] / src / runtime / cheneygc.c
index a18d878..14b5208 100644 (file)
@@ -30,6 +30,7 @@
 #include "genesis/static-symbols.h"
 #include "genesis/primitive-objects.h"
 #include "thread.h"
+#include "arch.h"
 
 /* So you need to debug? */
 #if 0
@@ -47,10 +48,6 @@ lispobj *new_space;
 lispobj *new_space_free_pointer;
 
 static void scavenge_newspace(void);
-static void scavenge_interrupt_contexts(void);
-extern struct interrupt_data * global_interrupt_data;
-
-extern unsigned long bytes_consed_between_gcs;
 
 \f
 /* collecting garbage */
@@ -64,45 +61,20 @@ tv_diff(struct timeval *x, struct timeval *y)
 }
 #endif
 
-#define BYTES_ZERO_BEFORE_END (1<<12)
-
-/* FIXME do we need this?  Doesn't it duplicate lisp code in
- * scrub-control-stack? */
-
-static void
-zero_stack(void)
-{
-    lispobj *ptr = current_control_stack_pointer;
- search:
-    do {
-        if (*ptr)
-            goto fill;
-        ptr++;
-    } while (((unsigned long)ptr) & (BYTES_ZERO_BEFORE_END-1));
-    return;
- fill:
-    do {
-        *ptr++ = 0;
-    } while (((unsigned long)ptr) & (BYTES_ZERO_BEFORE_END-1));
-
-    goto search;
-}
-
-
 void *
-gc_general_alloc(long bytes, int unboxed_p, int quick_p) {
+gc_general_alloc(word_t bytes, int page_type_flag, int quick_p) {
     lispobj *new=new_space_free_pointer;
     new_space_free_pointer+=(bytes/N_WORD_BYTES);
     return new;
 }
 
-lispobj  copy_large_unboxed_object(lispobj object, long nwords) {
+lispobj  copy_large_unboxed_object(lispobj object, sword_t nwords) {
     return copy_object(object,nwords);
 }
-lispobj  copy_unboxed_object(lispobj object, long nwords) {
+lispobj  copy_unboxed_object(lispobj object, sword_t nwords) {
     return copy_object(object,nwords);
 }
-lispobj  copy_large_object(lispobj object, long nwords) {
+lispobj  copy_large_object(lispobj object, sword_t nwords) {
     return copy_object(object,nwords);
 }
 
@@ -110,7 +82,7 @@ lispobj  copy_large_object(lispobj object, long nwords) {
  * last_generation argument. That's meaningless for us, since we're
  * not a generational GC. So we ignore it. */
 void
-collect_garbage(unsigned ignore)
+collect_garbage(generation_index_t ignore)
 {
 #ifdef PRINTNOISE
     struct timeval start_tv, stop_tv;
@@ -125,9 +97,6 @@ collect_garbage(unsigned ignore)
     unsigned long control_stack_size, binding_stack_size;
     sigset_t tmp, old;
     struct thread *th=arch_os_get_current_thread();
-    struct interrupt_data *data=
-        th ? th->interrupt_data : global_interrupt_data;
-
 
 #ifdef PRINTNOISE
     printf("[Collecting garbage ... \n");
@@ -138,9 +107,7 @@ collect_garbage(unsigned ignore)
 
     /* it's possible that signals are blocked already if this was called
      * from a signal handler (e.g. with the sigsegv gc_trigger stuff) */
-    sigemptyset(&tmp);
-    sigaddset_blockable(&tmp);
-    thread_sigmask(SIG_BLOCK, &tmp, &old);
+    block_blockable_signals(0, &old);
 
     current_static_space_free_pointer =
         (lispobj *) ((unsigned long)
@@ -173,33 +140,27 @@ collect_garbage(unsigned ignore)
 #ifdef PRINTNOISE
     printf("Scavenging interrupt contexts ...\n");
 #endif
-    scavenge_interrupt_contexts();
+    scavenge_interrupt_contexts(th);
 
 #ifdef PRINTNOISE
     printf("Scavenging interrupt handlers (%d bytes) ...\n",
            (int)sizeof(interrupt_handlers));
 #endif
-    scavenge((lispobj *) data->interrupt_handlers,
-             sizeof(data->interrupt_handlers) / sizeof(lispobj));
+    scavenge((lispobj *) interrupt_handlers,
+             sizeof(interrupt_handlers) / sizeof(lispobj));
 
-    /* _size quantities are in units of sizeof(lispobj) - i.e. 4 */
-    control_stack_size =
-        current_control_stack_pointer-
-        (lispobj *)th->control_stack_start;
 #ifdef PRINTNOISE
-    printf("Scavenging the control stack at %p (%ld words) ...\n",
-           ((lispobj *)th->control_stack_start),
-           control_stack_size);
+    printf("Scavenging the control stack ...\n");
 #endif
-    scavenge(((lispobj *)th->control_stack_start), control_stack_size);
+    scavenge_control_stack(th);
 
 
     binding_stack_size =
-        current_binding_stack_pointer -
+        (lispobj *)get_binding_stack_pointer(th) -
         (lispobj *)th->binding_stack_start;
 #ifdef PRINTNOISE
     printf("Scavenging the binding stack %x - %x (%d words) ...\n",
-           th->binding_stack_start,current_binding_stack_pointer,
+           th->binding_stack_start,get_binding_stack_pointer(th),
            (int)(binding_stack_size));
 #endif
     scavenge(((lispobj *)th->binding_stack_start), binding_stack_size);
@@ -227,11 +188,16 @@ collect_garbage(unsigned ignore)
 
     /* Scan the weak pointers. */
 #ifdef PRINTNOISE
+    printf("Scanning weak hash tables ...\n");
+#endif
+    scan_weak_hash_tables();
+
+    /* Scan the weak pointers. */
+#ifdef PRINTNOISE
     printf("Scanning weak pointers ...\n");
 #endif
     scan_weak_pointers();
 
-
     /* Flip spaces. */
 #ifdef PRINTNOISE
     printf("Flipping spaces ...\n");
@@ -240,8 +206,12 @@ collect_garbage(unsigned ignore)
     /* Maybe FIXME: it's possible that we could significantly reduce
      * RSS by zeroing the from_space or madvise(MADV_DONTNEED) or
      * similar os-dependent tricks here */
+#ifdef LISP_FEATURE_HPUX
+    /* hpux cant handle unmapping areas that are not 100% mapped */
+    clear_auto_gc_trigger();
+#endif
     os_zero((os_vm_address_t) from_space,
-            (os_vm_size_t) DYNAMIC_SPACE_SIZE);
+            (os_vm_size_t) dynamic_space_size);
 
     current_dynamic_space = new_space;
     dynamic_space_free_pointer = new_space_free_pointer;
@@ -257,7 +227,7 @@ collect_garbage(unsigned ignore)
 #ifdef PRINTNOISE
     printf("Zeroing empty part of control stack ...\n");
 #endif
-    zero_stack();
+    scrub_control_stack();
     set_auto_gc_trigger(size_retained+bytes_consed_between_gcs);
     thread_sigmask(SIG_SETMASK, &old, 0);
 
@@ -301,139 +271,12 @@ scavenge_newspace(void)
                 here,new_space_free_pointer); */
         next = new_space_free_pointer;
         scavenge(here, next - here);
+        scav_weak_hash_tables();
         here = next;
     }
     /* printf("done with newspace\n"); */
 }
 \f
-/* scavenging interrupt contexts */
-
-static int boxed_registers[] = BOXED_REGISTERS;
-
-static void
-scavenge_interrupt_context(os_context_t *context)
-{
-    int i;
-#ifdef reg_LIP
-    unsigned long lip;
-    unsigned long lip_offset;
-    int lip_register_pair;
-#endif
-    unsigned long pc_code_offset;
-#ifdef ARCH_HAS_LINK_REGISTER
-    unsigned long lr_code_offset;
-#endif
-#ifdef ARCH_HAS_NPC_REGISTER
-    unsigned long npc_code_offset;
-#endif
-#ifdef DEBUG_SCAVENGE_VERBOSE
-    fprintf(stderr, "Scavenging interrupt context at 0x%x\n",context);
-#endif
-    /* Find the LIP's register pair and calculate its offset */
-    /* before we scavenge the context. */
-#ifdef reg_LIP
-    lip = *os_context_register_addr(context, reg_LIP);
-    /* 0x7FFFFFFF on 32-bit platforms;
-       0x7FFFFFFFFFFFFFFF on 64-bit platforms */
-    lip_offset = (((unsigned long)1) << (N_WORD_BITS - 1)) - 1;
-    lip_register_pair = -1;
-    for (i = 0; i < (sizeof(boxed_registers) / sizeof(int)); i++) {
-        unsigned long reg;
-        long offset;
-        int index;
-
-        index = boxed_registers[i];
-        reg = *os_context_register_addr(context, index);
-        /* would be using PTR if not for integer length issues */
-        if ((reg & ~((1L<<N_LOWTAG_BITS)-1)) <= lip) {
-            offset = lip - reg;
-            if (offset < lip_offset) {
-                lip_offset = offset;
-                lip_register_pair = index;
-            }
-        }
-    }
-#endif /* reg_LIP */
-
-    /* Compute the PC's offset from the start of the CODE */
-    /* register. */
-    pc_code_offset =
-        *os_context_pc_addr(context) -
-        *os_context_register_addr(context, reg_CODE);
-#ifdef ARCH_HAS_NPC_REGISTER
-    npc_code_offset =
-        *os_context_npc_addr(context) -
-        *os_context_register_addr(context, reg_CODE);
-#endif
-#ifdef ARCH_HAS_LINK_REGISTER
-    lr_code_offset =
-        *os_context_lr_addr(context) -
-        *os_context_register_addr(context, reg_CODE);
-#endif
-
-    /* Scavenge all boxed registers in the context. */
-    for (i = 0; i < (sizeof(boxed_registers) / sizeof(int)); i++) {
-        int index;
-        lispobj foo;
-
-        index = boxed_registers[i];
-        foo = *os_context_register_addr(context,index);
-        scavenge((lispobj *) &foo, 1);
-        *os_context_register_addr(context,index) = foo;
-
-        /* this is unlikely to work as intended on bigendian
-         * 64 bit platforms */
-
-        scavenge((lispobj *)
-                 os_context_register_addr(context, index), 1);
-    }
-
-#ifdef reg_LIP
-    /* Fix the LIP */
-    *os_context_register_addr(context, reg_LIP) =
-        *os_context_register_addr(context, lip_register_pair) + lip_offset;
-#endif /* reg_LIP */
-
-    /* Fix the PC if it was in from space */
-    if (from_space_p(*os_context_pc_addr(context)))
-        *os_context_pc_addr(context) =
-            *os_context_register_addr(context, reg_CODE) + pc_code_offset;
-#ifdef ARCH_HAS_LINK_REGISTER
-    /* Fix the LR ditto; important if we're being called from
-     * an assembly routine that expects to return using blr, otherwise
-     * harmless */
-    if (from_space_p(*os_context_lr_addr(context)))
-        *os_context_lr_addr(context) =
-            *os_context_register_addr(context, reg_CODE) + lr_code_offset;
-#endif
-
-#ifdef ARCH_HAS_NPC_REGISTER
-    if (from_space_p(*os_context_npc_addr(context)))
-        *os_context_npc_addr(context) =
-            *os_context_register_addr(context, reg_CODE) + npc_code_offset;
-#endif
-}
-
-void scavenge_interrupt_contexts(void)
-{
-    int i, index;
-    os_context_t *context;
-
-    struct thread *th=arch_os_get_current_thread();
-
-    index = fixnum_value(SymbolValue(FREE_INTERRUPT_CONTEXT_INDEX,0));
-
-
-#ifdef DEBUG_SCAVENGE_VERBOSE
-    fprintf(stderr, "%d interrupt contexts to scan\n",index);
-#endif
-    for (i = 0; i < index; i++) {
-        context = th->interrupt_contexts[i];
-        scavenge_interrupt_context(context);
-    }
-}
-
-\f
 /* debugging code */
 
 void
@@ -494,20 +337,6 @@ print_garbage(lispobj *from_space, lispobj *from_space_free_pointer)
 }
 
 \f
-/* vector-like objects */
-
-static long
-scav_vector(lispobj *where, lispobj object)
-{
-    if (HeaderValue(object) == subtype_VectorValidHashing) {
-        *where =
-            (subtype_VectorMustRehash<<N_WIDETAG_BITS) | SIMPLE_VECTOR_WIDETAG;
-    }
-
-    return 1;
-}
-
-\f
 /* weak pointers */
 
 #define WEAK_POINTER_NWORDS \
@@ -566,7 +395,6 @@ void
 gc_init(void)
 {
     gc_init_tables();
-    scavtab[SIMPLE_VECTOR_WIDETAG] = scav_vector;
     scavtab[WEAK_POINTER_WIDETAG] = scav_weak_pointer;
 }
 
@@ -588,34 +416,28 @@ gc_initialize_pointers(void)
 /* noise to manipulate the gc trigger stuff */
 
 /* Functions that substantially change the dynamic space free pointer
- * (collect_garbage, purify) are responsible also for resettting the
+ * (collect_garbage, purify) are responsible also for resetting the
  * auto_gc_trigger */
 void set_auto_gc_trigger(os_vm_size_t dynamic_usage)
 {
-    os_vm_address_t addr=(os_vm_address_t)current_dynamic_space
-        + dynamic_usage;
-    long length = DYNAMIC_SPACE_SIZE - dynamic_usage;
-
-    if (addr < (os_vm_address_t)dynamic_space_free_pointer) {
-        fprintf(stderr,
-           "set_auto_gc_trigger: tried to set gc trigger too low! (%ld < 0x%08lx)\n",
-                (unsigned long)dynamic_usage,
-                (unsigned long)((os_vm_address_t)dynamic_space_free_pointer
-                                - (os_vm_address_t)current_dynamic_space));
-        lose("lost");
-    }
-    else if (length < 0) {
-        fprintf(stderr,
-                "set_auto_gc_trigger: tried to set gc trigger too high! (0x%08lx)\n",
-                (unsigned long)dynamic_usage);
-        lose("lost");
-    }
-
-    addr=os_round_up_to_page(addr);
-    length=os_trunc_size_to_page(length);
-
-#if defined(SUNOS) || defined(SOLARIS)
-    os_invalidate(addr,length);
+    os_vm_address_t addr;
+    os_vm_size_t length;
+
+    addr = os_round_up_to_page((os_vm_address_t)current_dynamic_space
+                               + dynamic_usage);
+    if (addr < (os_vm_address_t)dynamic_space_free_pointer)
+        lose("set_auto_gc_trigger: tried to set gc trigger too low! (%ld < 0x%08lx)\n",
+             (unsigned long)dynamic_usage,
+             (unsigned long)((os_vm_address_t)dynamic_space_free_pointer
+                             - (os_vm_address_t)current_dynamic_space));
+
+    length = os_trunc_size_to_page(dynamic_space_size - dynamic_usage);
+    if (length < 0)
+        lose("set_auto_gc_trigger: tried to set gc trigger too high! (0x%08lx)\n",
+             (unsigned long)dynamic_usage);
+
+#if defined(SUNOS) || defined(SOLARIS) || defined(LISP_FEATURE_HPUX)
+    os_invalidate(addr, length);
 #else
     os_protect(addr, length, 0);
 #endif
@@ -625,19 +447,62 @@ void set_auto_gc_trigger(os_vm_size_t dynamic_usage)
 
 void clear_auto_gc_trigger(void)
 {
-    if (current_auto_gc_trigger!=NULL){
-#if defined(SUNOS) || defined(SOLARIS)/* don't want to force whole space into swapping mode... */
-        os_vm_address_t addr=(os_vm_address_t)current_auto_gc_trigger;
-        os_vm_size_t length=
-            DYNAMIC_SPACE_SIZE + (os_vm_address_t)current_dynamic_space - addr;
+    os_vm_address_t addr;
+    os_vm_size_t length;
+
+    if (current_auto_gc_trigger == NULL)
+        return;
+
+    addr = (os_vm_address_t)current_auto_gc_trigger;
+    length = dynamic_space_size + (os_vm_address_t)current_dynamic_space - addr;
 
-        os_validate(addr,length);
+#if defined(SUNOS) || defined(SOLARIS) || defined(LISP_FEATURE_HPUX)
+    /* don't want to force whole space into swapping mode... */
+    os_validate(addr, length);
 #else
-        os_protect((os_vm_address_t)current_dynamic_space,
-                   DYNAMIC_SPACE_SIZE,
-                   OS_VM_PROT_ALL);
+    os_protect(addr, length, OS_VM_PROT_ALL);
 #endif
 
-        current_auto_gc_trigger = NULL;
+    current_auto_gc_trigger = NULL;
+}
+
+static boolean
+gc_trigger_hit(void *addr)
+{
+    if (current_auto_gc_trigger == NULL)
+        return 0;
+    else{
+        return (addr >= (void *)current_auto_gc_trigger &&
+                addr <((void *)current_dynamic_space + dynamic_space_size));
+    }
+}
+
+boolean
+cheneygc_handle_wp_violation(os_context_t *context, void *addr)
+{
+    if(!foreign_function_call_active && gc_trigger_hit(addr)){
+        struct thread *thread=arch_os_get_current_thread();
+        clear_auto_gc_trigger();
+        /* Don't flood the system with interrupts if the need to gc is
+         * already noted. This can happen for example when SUB-GC
+         * allocates or after a gc triggered in a WITHOUT-GCING. */
+        if (SymbolValue(GC_PENDING,thread) == NIL) {
+            if (SymbolValue(GC_INHIBIT,thread) == NIL) {
+                if (arch_pseudo_atomic_atomic(context)) {
+                    /* set things up so that GC happens when we finish
+                     * the PA section */
+                    SetSymbolValue(GC_PENDING,T,thread);
+                    arch_set_pseudo_atomic_interrupted(context);
+                    maybe_save_gc_mask_and_block_deferrables
+                        (os_context_sigmask_addr(context));
+                } else {
+                    maybe_gc(context);
+                }
+            } else {
+                SetSymbolValue(GC_PENDING,T,thread);
+            }
+        }
+        return 1;
     }
+    return 0;
 }