X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fruntime%2Fgencgc.c;h=250ccee7c90cef0a05c95c2584cf7418d0dc21db;hb=26e8f33d8639e4de75e59e3aa30bbd7478d51a69;hp=9fb8e4436dc09c94a8fe206ee26f61d9aed748ce;hpb=33a6b2817f3e2a8cf98d162a2b40c50d259295c4;p=sbcl.git diff --git a/src/runtime/gencgc.c b/src/runtime/gencgc.c index 9fb8e44..250ccee 100644 --- a/src/runtime/gencgc.c +++ b/src/runtime/gencgc.c @@ -1,5 +1,5 @@ /* - * GENerational Conservative Garbage Collector for SBCL x86 + * GENerational Conservative Garbage Collector for SBCL */ /* @@ -24,6 +24,7 @@ * . */ +#include #include #include #include @@ -41,6 +42,7 @@ #include "gc.h" #include "gc-internal.h" #include "thread.h" +#include "alloc.h" #include "genesis/vector.h" #include "genesis/weak-pointer.h" #include "genesis/fdefn.h" @@ -49,9 +51,9 @@ #include "genesis/hash-table.h" #include "genesis/instance.h" #include "genesis/layout.h" - -#ifdef LUTEX_WIDETAG -#include "genesis/lutex.h" +#include "gencgc.h" +#if defined(LUTEX_WIDETAG) +#include "pthread-lutex.h" #endif /* forward declarations */ @@ -144,7 +146,6 @@ boolean gencgc_partial_pickup = 0; /* the total bytes allocated. These are seen by Lisp DYNAMIC-USAGE. */ unsigned long bytes_allocated = 0; -extern unsigned long bytes_consed_between_gcs; /* gc-common.c */ unsigned long auto_gc_trigger = 0; /* the source and destination generations. These are set before a GC starts @@ -159,21 +160,16 @@ boolean gc_active_p = 0; * saving a core), don't scan the stack / mark pages dont_move. */ static boolean conservative_stack = 1; -/* An array of page structures is statically allocated. +/* An array of page structures is allocated on gc initialization. * This helps quickly map between an address its page structure. - * NUM_PAGES is set from the size of the dynamic space. */ -struct page page_table[NUM_PAGES]; + * page_table_pages is set from the size of the dynamic space. */ +unsigned page_table_pages; +struct page *page_table; /* To map addresses to page structures the address of the first page * is needed. */ static void *heap_base = NULL; -#if N_WORD_BITS == 32 - #define SIMPLE_ARRAY_WORD_WIDETAG SIMPLE_ARRAY_UNSIGNED_BYTE_32_WIDETAG -#elif N_WORD_BITS == 64 - #define SIMPLE_ARRAY_WORD_WIDETAG SIMPLE_ARRAY_UNSIGNED_BYTE_64_WIDETAG -#endif - /* Calculate the start address for the given page number. */ inline void * page_address(page_index_t page_num) @@ -190,7 +186,7 @@ find_page_index(void *addr) if (index >= 0) { index = ((unsigned long)index)/PAGE_BYTES; - if (index < NUM_PAGES) + if (index < page_table_pages) return (index); } @@ -1076,9 +1072,9 @@ void gc_heap_exhausted_error_or_lose (long available, long requested) { /* Write basic information before doing anything else: if we don't - * call to lisp this is a must, and even if we do there is always the - * danger that we bounce back here before the error has been handled, - * or indeed even printed. + * call to lisp this is a must, and even if we do there is always + * the danger that we bounce back here before the error has been + * handled, or indeed even printed. */ fprintf(stderr, "Heap exhausted during %s: %ld bytes available, %ld requested.\n", gc_active_p ? "garbage collection" : "allocation", available, requested); @@ -1086,14 +1082,23 @@ gc_heap_exhausted_error_or_lose (long available, long requested) /* If we are in GC, or totally out of memory there is no way * to sanely transfer control to the lisp-side of things. */ + struct thread *thread = arch_os_get_current_thread(); print_generation_stats(1); + fprintf(stderr, "GC control variables:\n"); + fprintf(stderr, " *GC-INHIBIT* = %s\n *GC-PENDING* = %s\n", + SymbolValue(GC_INHIBIT,thread)==NIL ? "false" : "true", + SymbolValue(GC_PENDING,thread)==NIL ? "false" : "true"); +#ifdef LISP_FEATURE_SB_THREAD + fprintf(stderr, " *STOP-FOR-GC-PENDING* = %s\n", + SymbolValue(STOP_FOR_GC_PENDING,thread)==NIL ? "false" : "true"); +#endif lose("Heap exhausted, game over."); } else { /* FIXME: assert free_pages_lock held */ - thread_mutex_unlock(&free_pages_lock); + (void)thread_mutex_unlock(&free_pages_lock); funcall2(SymbolFunction(HEAP_EXHAUSTED_ERROR), - make_fixnum(available), make_fixnum(requested)); + alloc_number(available), alloc_number(requested)); lose("HEAP-EXHAUSTED-ERROR fell through"); } } @@ -1121,11 +1126,11 @@ gc_find_freeish_pages(page_index_t *restart_page_ptr, long nbytes, int unboxed) do { first_page = restart_page; if (large_p) - while ((first_page < NUM_PAGES) + while ((first_page < page_table_pages) && (page_table[first_page].allocated != FREE_PAGE_FLAG)) first_page++; else - while (first_page < NUM_PAGES) { + while (first_page < page_table_pages) { if(page_table[first_page].allocated == FREE_PAGE_FLAG) break; if((page_table[first_page].allocated == @@ -1140,7 +1145,7 @@ gc_find_freeish_pages(page_index_t *restart_page_ptr, long nbytes, int unboxed) first_page++; } - if (first_page >= NUM_PAGES) + if (first_page >= page_table_pages) gc_heap_exhausted_error_or_lose(0, nbytes); gc_assert(page_table[first_page].write_protected == 0); @@ -1150,7 +1155,7 @@ gc_find_freeish_pages(page_index_t *restart_page_ptr, long nbytes, int unboxed) num_pages = 1; while (((bytes_found < nbytes) || (!large_p && (num_pages < 2))) - && (last_page < (NUM_PAGES-1)) + && (last_page < (page_table_pages-1)) && (page_table[last_page+1].allocated == FREE_PAGE_FLAG)) { last_page++; num_pages++; @@ -1163,10 +1168,10 @@ gc_find_freeish_pages(page_index_t *restart_page_ptr, long nbytes, int unboxed) gc_assert(bytes_found == region_size); restart_page = last_page + 1; - } while ((restart_page < NUM_PAGES) && (bytes_found < nbytes)); + } while ((restart_page < page_table_pages) && (bytes_found < nbytes)); /* Check for a failure */ - if ((restart_page >= NUM_PAGES) && (bytes_found < nbytes)) + if ((restart_page >= page_table_pages) && (bytes_found < nbytes)) gc_heap_exhausted_error_or_lose(bytes_found, nbytes); *restart_page_ptr=first_page; @@ -1858,237 +1863,6 @@ trans_unboxed_large(lispobj object) /* - * vector-like objects - */ - - -/* FIXME: What does this mean? */ -int gencgc_hash = 1; - -#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) - -static long -scav_vector(lispobj *where, lispobj object) -{ - unsigned long kv_length; - lispobj *kv_vector; - unsigned long length = 0; /* (0 = dummy to stop GCC warning) */ - struct hash_table *hash_table; - lispobj empty_symbol; - unsigned long *index_vector = NULL; /* (NULL = dummy to stop GCC warning) */ - unsigned long *next_vector = NULL; /* (NULL = dummy to stop GCC warning) */ - unsigned long *hash_vector = NULL; /* (NULL = dummy to stop GCC warning) */ - lispobj weak_p_obj; - unsigned long next_vector_length = 0; - - /* FIXME: A comment explaining this would be nice. It looks as - * though SB-VM:VECTOR-VALID-HASHING-SUBTYPE is set for EQ-based - * hash tables in the Lisp HASH-TABLE code, and nowhere else. */ - if (HeaderValue(object) != subtype_VectorValidHashing) - return 1; - - if (!gencgc_hash) { - /* This is set for backward compatibility. FIXME: Do we need - * this any more? */ - *where = - (subtype_VectorMustRehash<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]); - } - empty_symbol = where[3]; - /* 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)); - } - - /* 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); - } - - /* WEAK-P */ - weak_p_obj = hash_table->weak_p; - - /* index vector */ - { - lispobj index_vector_obj = hash_table->index_vector; - - if (is_lisp_pointer(index_vector_obj) && - (widetag_of(*(lispobj *)native_pointer(index_vector_obj)) == - SIMPLE_ARRAY_WORD_WIDETAG)) { - index_vector = - ((unsigned long *)native_pointer(index_vector_obj)) + 2; - /*FSHOW((stderr, "/index_vector = %x\n",index_vector));*/ - length = fixnum_value(((lispobj *)native_pointer(index_vector_obj))[1]); - /*FSHOW((stderr, "/length = %d\n", length));*/ - } else { - lose("invalid index_vector %x\n", index_vector_obj); - } - } - - /* next vector */ - { - lispobj next_vector_obj = hash_table->next_vector; - - if (is_lisp_pointer(next_vector_obj) && - (widetag_of(*(lispobj *)native_pointer(next_vector_obj)) == - SIMPLE_ARRAY_WORD_WIDETAG)) { - next_vector = ((unsigned long *)native_pointer(next_vector_obj)) + 2; - /*FSHOW((stderr, "/next_vector = %x\n", next_vector));*/ - next_vector_length = fixnum_value(((lispobj *)native_pointer(next_vector_obj))[1]); - /*FSHOW((stderr, "/next_vector_length = %d\n", next_vector_length));*/ - } else { - lose("invalid next_vector %x\n", next_vector_obj); - } - } - - /* maybe hash vector */ - { - lispobj hash_vector_obj = hash_table->hash_vector; - - if (is_lisp_pointer(hash_vector_obj) && - (widetag_of(*(lispobj *)native_pointer(hash_vector_obj)) == - SIMPLE_ARRAY_WORD_WIDETAG)){ - hash_vector = - ((unsigned long *)native_pointer(hash_vector_obj)) + 2; - /*FSHOW((stderr, "/hash_vector = %x\n", hash_vector));*/ - gc_assert(fixnum_value(((lispobj *)native_pointer(hash_vector_obj))[1]) - == next_vector_length); - } else { - hash_vector = NULL; - /*FSHOW((stderr, "/no hash_vector: %x\n", hash_vector_obj));*/ - } - } - - /* 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); - - /* now all set up.. */ - - /* Work through the KV vector. */ - { - long i; - for (i = 1; i < next_vector_length; i++) { - lispobj old_key = kv_vector[2*i]; - -#if N_WORD_BITS == 32 - unsigned long old_index = (old_key & 0x1fffffff)%length; -#elif N_WORD_BITS == 64 - unsigned long old_index = (old_key & 0x1fffffffffffffff)%length; -#endif - - /* Scavenge the key and value. */ - scavenge(&kv_vector[2*i],2); - - /* Check whether the key has moved and is EQ based. */ - { - lispobj new_key = kv_vector[2*i]; -#if N_WORD_BITS == 32 - unsigned long new_index = (new_key & 0x1fffffff)%length; -#elif N_WORD_BITS == 64 - unsigned long new_index = (new_key & 0x1fffffffffffffff)%length; -#endif - - if ((old_index != new_index) && - ((!hash_vector) || - (hash_vector[i] == MAGIC_HASH_VECTOR_VALUE)) && - ((new_key != empty_symbol) || - (kv_vector[2*i] != empty_symbol))) { - - /*FSHOW((stderr, - "* EQ key %d moved from %x to %x; index %d to %d\n", - i, old_key, new_key, old_index, new_index));*/ - - if (index_vector[old_index] != 0) { - /*FSHOW((stderr, "/P1 %d\n", index_vector[old_index]));*/ - - /* Unlink the key from the old_index chain. */ - if (index_vector[old_index] == i) { - /*FSHOW((stderr, "/P2a %d\n", next_vector[i]));*/ - index_vector[old_index] = next_vector[i]; - /* Link it into the needing rehash chain. */ - next_vector[i] = fixnum_value(hash_table->needing_rehash); - hash_table->needing_rehash = make_fixnum(i); - /*SHOW("P2");*/ - } else { - unsigned long prior = index_vector[old_index]; - unsigned long next = next_vector[prior]; - - /*FSHOW((stderr, "/P3a %d %d\n", prior, next));*/ - - while (next != 0) { - /*FSHOW((stderr, "/P3b %d %d\n", prior, next));*/ - if (next == i) { - /* Unlink it. */ - next_vector[prior] = next_vector[next]; - /* Link it into the needing rehash - * chain. */ - next_vector[next] = - fixnum_value(hash_table->needing_rehash); - hash_table->needing_rehash = make_fixnum(next); - /*SHOW("/P3");*/ - break; - } - prior = next; - next = next_vector[next]; - } - } - } - } - } - } - } - return (CEILING(kv_length + 2, 2)); -} - -#else - -static long -scav_vector(lispobj *where, lispobj object) -{ - if (HeaderValue(object) == subtype_VectorValidHashing) { - *where = - (subtype_VectorMustRehash<next; if (!lutex->live) { - lutex_destroy(lutex); + lutex_destroy((tagged_lutex_t) lutex); gencgc_unregister_lutex(lutex); } lutex = next; @@ -2233,7 +2007,7 @@ scav_lutex(lispobj *where, lispobj object) static lispobj trans_lutex(lispobj object) { - struct lutex *lutex = native_pointer(object); + struct lutex *lutex = (struct lutex *) native_pointer(object); lispobj copied; size_t words = CEILING(sizeof(struct lutex)/sizeof(lispobj), 2); gc_assert(is_lisp_pointer(object)); @@ -2241,13 +2015,14 @@ trans_lutex(lispobj object) /* Update the links, since the lutex moved in memory. */ if (lutex->next) { - lutex->next->prev = native_pointer(copied); + lutex->next->prev = (struct lutex *) native_pointer(copied); } if (lutex->prev) { - lutex->prev->next = native_pointer(copied); + lutex->prev->next = (struct lutex *) native_pointer(copied); } else { - generations[lutex->gen].lutexes = native_pointer(copied); + generations[lutex->gen].lutexes = + (struct lutex *) native_pointer(copied); } return copied; @@ -2276,29 +2051,21 @@ size_lutex(lispobj *where) static long scav_weak_pointer(lispobj *where, lispobj object) { - struct weak_pointer *wp = weak_pointers; - /* Push the weak pointer onto the list of weak pointers. - * Do I have to watch for duplicates? Originally this was - * part of trans_weak_pointer but that didn't work in the - * case where the WP was in a promoted region. + /* Since we overwrite the 'next' field, we have to make + * sure not to do so for pointers already in the list. + * Instead of searching the list of weak_pointers each + * time, we ensure that next is always NULL when the weak + * pointer isn't in the list, and not NULL otherwise. + * Since we can't use NULL to denote end of list, we + * use a pointer back to the same weak_pointer. */ + struct weak_pointer * wp = (struct weak_pointer*)where; - /* Check whether it's already in the list. */ - while (wp != NULL) { - if (wp == (struct weak_pointer*)where) { - break; - } - wp = wp->next; - } - if (wp == NULL) { - /* Add it to the start of the list. */ - wp = (struct weak_pointer*)where; - if (wp->next != weak_pointers) { - wp->next = weak_pointers; - } else { - /*SHOW("avoided write to weak pointer");*/ - } + if (NULL == wp->next) { + wp->next = weak_pointers; weak_pointers = wp; + if (NULL == wp->next) + wp->next = wp; } /* Do not let GC scavenge the value slot of the weak pointer. @@ -2351,37 +2118,30 @@ search_dynamic_space(void *pointer) (lispobj *)pointer)); } -/* Is there any possibility that pointer is a valid Lisp object - * reference, and/or something else (e.g. subroutine call return - * address) which should prevent us from moving the referred-to thing? - * This is called from preserve_pointers() */ +#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) + +/* Helper for valid_lisp_pointer_p and + * possibly_valid_dynamic_space_pointer. + * + * pointer is the pointer to validate, and start_addr is the address + * of the enclosing object. + */ static int -possibly_valid_dynamic_space_pointer(lispobj *pointer) +looks_like_valid_lisp_pointer_p(lispobj *pointer, lispobj *start_addr) { - lispobj *start_addr; - - /* Find the object start address. */ - if ((start_addr = search_dynamic_space(pointer)) == NULL) { - return 0; - } - /* We need to allow raw pointers into Code objects for return * addresses. This will also pick up pointers to functions in code * objects. */ - if (widetag_of(*start_addr) == CODE_HEADER_WIDETAG) { + if (widetag_of(*start_addr) == CODE_HEADER_WIDETAG) /* XXX could do some further checks here */ return 1; - } - /* If it's not a return address then it needs to be a valid Lisp - * pointer. */ if (!is_lisp_pointer((lispobj)pointer)) { return 0; } /* Check that the object pointed to is consistent with the pointer - * low tag. - */ + * low tag. */ switch (lowtag_of((lispobj)pointer)) { case FUN_POINTER_LOWTAG: /* Start_addr should be the enclosing code object, or a closure @@ -2623,7 +2383,46 @@ possibly_valid_dynamic_space_pointer(lispobj *pointer) return 1; } -#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) +/* Used by the debugger to validate possibly bogus pointers before + * calling MAKE-LISP-OBJ on them. + * + * FIXME: We would like to make this perfect, because if the debugger + * constructs a reference to a bugs lisp object, and it ends up in a + * location scavenged by the GC all hell breaks loose. + * + * Whereas possibly_valid_dynamic_space_pointer has to be conservative + * and return true for all valid pointers, this could actually be eager + * and lie about a few pointers without bad results... but that should + * be reflected in the name. + */ +int +valid_lisp_pointer_p(lispobj *pointer) +{ + lispobj *start; + if (((start=search_dynamic_space(pointer))!=NULL) || + ((start=search_static_space(pointer))!=NULL) || + ((start=search_read_only_space(pointer))!=NULL)) + return looks_like_valid_lisp_pointer_p(pointer, start); + else + return 0; +} + +/* Is there any possibility that pointer is a valid Lisp object + * reference, and/or something else (e.g. subroutine call return + * address) which should prevent us from moving the referred-to thing? + * This is called from preserve_pointers() */ +static int +possibly_valid_dynamic_space_pointer(lispobj *pointer) +{ + lispobj *start_addr; + + /* Find the object start address. */ + if ((start_addr = search_dynamic_space(pointer)) == NULL) { + return 0; + } + + return looks_like_valid_lisp_pointer_p(pointer, start_addr); +} /* Adjust large bignum and vector objects. This will adjust the * allocated region if the size has shrunk, and move unboxed objects @@ -2799,8 +2598,6 @@ maybe_adjust_large_object(lispobj *where) return; } -#endif - /* Take a possible pointer to a Lisp object and mark its page in the * page_table so that it will not be relocated during a GC. * @@ -2814,8 +2611,6 @@ maybe_adjust_large_object(lispobj *where) * It is also assumed that the current gc_alloc() region has been * flushed and the tables updated. */ -#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) - static void preserve_pointer(void *addr) { @@ -2932,7 +2727,7 @@ preserve_pointer(void *addr) gc_assert(page_table[addr_page_index].dont_move != 0); } -#endif +#endif // defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) /* If the given page is not write-protected, then scan it for pointers @@ -3046,7 +2841,7 @@ scavenge_generations(generation_index_t from, generation_index_t to) #define SC_GEN_CK 0 #if SC_GEN_CK /* Clear the write_protected_cleared flags on all pages. */ - for (i = 0; i < NUM_PAGES; i++) + for (i = 0; i < page_table_pages; i++) page_table[i].write_protected_cleared = 0; #endif @@ -3100,7 +2895,7 @@ scavenge_generations(generation_index_t from, generation_index_t to) #if SC_GEN_CK /* Check that none of the write_protected pages in this generation * have been written to. */ - for (i = 0; i < NUM_PAGES; i++) { + for (i = 0; i < page_table_pages; i++) { if ((page_table[i].allocation != FREE_PAGE_FLAG) && (page_table[i].bytes_used != 0) && (page_table[i].gen == generation) @@ -3243,6 +3038,13 @@ scavenge_newspace_generation(generation_index_t generation) /* Record all new areas now. */ record_new_objects = 2; + /* Give a chance to weak hash tables to make other objects live. + * FIXME: The algorithm implemented here for weak hash table gcing + * is O(W^2+N) as Bruno Haible warns in + * http://www.haible.de/bruno/papers/cs/weak/WeakDatastructures-writeup.html + * see "Implementation 2". */ + scav_weak_hash_tables(); + /* Flush the current regions updating the tables. */ gc_alloc_update_all_page_tables(); @@ -3281,8 +3083,8 @@ scavenge_newspace_generation(generation_index_t generation) if (gencgc_verbose) SHOW("new_areas overflow, doing full scavenge"); - /* Don't need to record new areas that get scavenge anyway - * during scavenge_newspace_generation_one_scan. */ + /* Don't need to record new areas that get scavenged + * anyway during scavenge_newspace_generation_one_scan. */ record_new_objects = 1; scavenge_newspace_generation_one_scan(generation); @@ -3290,6 +3092,8 @@ scavenge_newspace_generation(generation_index_t generation) /* Record all new areas now. */ record_new_objects = 2; + scav_weak_hash_tables(); + /* Flush the current regions updating the tables. */ gc_alloc_update_all_page_tables(); @@ -3304,6 +3108,8 @@ scavenge_newspace_generation(generation_index_t generation) scavenge(page_address(page)+offset, size); } + scav_weak_hash_tables(); + /* Flush the current regions updating the tables. */ gc_alloc_update_all_page_tables(); } @@ -3321,7 +3127,7 @@ scavenge_newspace_generation(generation_index_t generation) #if SC_NS_GEN_CK /* Check that none of the write_protected pages in this generation * have been written to. */ - for (i = 0; i < NUM_PAGES; i++) { + for (i = 0; i < page_table_pages; i++) { if ((page_table[i].allocation != FREE_PAGE_FLAG) && (page_table[i].bytes_used != 0) && (page_table[i].gen == generation) @@ -3455,13 +3261,6 @@ print_ptr(lispobj *addr) } #endif -#if defined(LISP_FEATURE_PPC) -extern int closure_tramp; -extern int undefined_tramp; -#else -extern int undefined_tramp; -#endif - static void verify_space(lispobj *start, size_t words) { @@ -3516,14 +3315,7 @@ verify_space(lispobj *start, size_t words) */ } else { /* Verify that it points to another valid space. */ - if (!to_readonly_space && !to_static_space && -#if defined(LISP_FEATURE_PPC) - !((thing == &closure_tramp) || - (thing == &undefined_tramp)) -#else - thing != (unsigned long)&undefined_tramp -#endif - ) { + if (!to_readonly_space && !to_static_space) { lose("Ptr %x @ %x sees junk.\n", thing, start); } } @@ -3891,6 +3683,8 @@ write_protect_generation_pages(generation_index_t generation) } } +#if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64) + static void scavenge_control_stack() { @@ -3906,7 +3700,6 @@ scavenge_control_stack() scavenge(control_stack, control_stack_size); } -#if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64) /* Scavenging Interrupt Contexts */ static int boxed_registers[] = BOXED_REGISTERS; @@ -4060,11 +3853,27 @@ preserve_context_registers (os_context_t *c) preserve_pointer((void*)*os_context_register_addr(c,reg_ESI)); preserve_pointer((void*)*os_context_register_addr(c,reg_EDI)); preserve_pointer((void*)*os_context_pc_addr(c)); +#elif defined LISP_FEATURE_X86_64 + preserve_pointer((void*)*os_context_register_addr(c,reg_RAX)); + preserve_pointer((void*)*os_context_register_addr(c,reg_RCX)); + preserve_pointer((void*)*os_context_register_addr(c,reg_RDX)); + preserve_pointer((void*)*os_context_register_addr(c,reg_RBX)); + preserve_pointer((void*)*os_context_register_addr(c,reg_RSI)); + preserve_pointer((void*)*os_context_register_addr(c,reg_RDI)); + preserve_pointer((void*)*os_context_register_addr(c,reg_R8)); + preserve_pointer((void*)*os_context_register_addr(c,reg_R9)); + preserve_pointer((void*)*os_context_register_addr(c,reg_R10)); + preserve_pointer((void*)*os_context_register_addr(c,reg_R11)); + preserve_pointer((void*)*os_context_register_addr(c,reg_R12)); + preserve_pointer((void*)*os_context_register_addr(c,reg_R13)); + preserve_pointer((void*)*os_context_register_addr(c,reg_R14)); + preserve_pointer((void*)*os_context_register_addr(c,reg_R15)); + preserve_pointer((void*)*os_context_pc_addr(c)); #else #error "preserve_context_registers needs to be tweaked for non-x86 Darwin" #endif #endif - for(ptr = (void **)(c+1); ptr>=(void **)c; ptr--) { + for(ptr = ((void **)(c+1))-1; ptr>=(void **)c; ptr--) { preserve_pointer(*ptr); } } @@ -4078,12 +3887,17 @@ garbage_collect_generation(generation_index_t generation, int raise) unsigned long bytes_freed; page_index_t i; unsigned long static_space_size; +#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) struct thread *th; +#endif gc_assert(generation <= HIGHEST_NORMAL_GENERATION); /* The oldest generation can't be raised. */ gc_assert((generation != HIGHEST_NORMAL_GENERATION) || (raise == 0)); + /* Check if weak hash tables were processed in the previous GC. */ + gc_assert(weak_hash_tables == NULL); + /* Initialize the weak pointer list. */ weak_pointers = NULL; @@ -4170,7 +3984,7 @@ garbage_collect_generation(generation_index_t generation, int raise) #else esp = (void **)((void *)&raise); #endif - for (ptr = (void **)th->control_stack_end; ptr > esp; ptr--) { + for (ptr = ((void **)th->control_stack_end)-1; ptr >= esp; ptr--) { preserve_pointer(*ptr); } } @@ -4291,6 +4105,7 @@ garbage_collect_generation(generation_index_t generation, int raise) } #endif + scan_weak_hash_tables(); scan_weak_pointers(); /* Flush the current regions, updating the tables. */ @@ -4552,7 +4367,7 @@ gc_free_heap(void) if (gencgc_verbose > 1) SHOW("entering gc_free_heap"); - for (page = 0; page < NUM_PAGES; page++) { + for (page = 0; page < page_table_pages; page++) { /* Skip free pages which should already be zero filled. */ if (page_table[page].allocated != FREE_PAGE_FLAG) { void *page_start, *addr; @@ -4638,8 +4453,15 @@ gc_init(void) { page_index_t i; + /* Compute the number of pages needed for the dynamic space. + * Dynamic space size should be aligned on page size. */ + page_table_pages = dynamic_space_size/PAGE_BYTES; + gc_assert(dynamic_space_size == (size_t) page_table_pages*PAGE_BYTES); + + page_table = calloc(page_table_pages, sizeof(struct page)); + gc_assert(page_table); + gc_init_tables(); - scavtab[SIMPLE_VECTOR_WIDETAG] = scav_vector; scavtab[WEAK_POINTER_WIDETAG] = scav_weak_pointer; transother[SIMPLE_ARRAY_WIDETAG] = trans_boxed_large; @@ -4652,7 +4474,7 @@ gc_init(void) heap_base = (void*)DYNAMIC_SPACE_START; /* Initialize each page structure. */ - for (i = 0; i < NUM_PAGES; i++) { + for (i = 0; i < page_table_pages; i++) { /* Initialize all pages as free. */ page_table[i].allocated = FREE_PAGE_FLAG; page_table[i].bytes_used = 0; @@ -4760,7 +4582,7 @@ gc_initialize_pointers(void) * The check for a GC trigger is only performed when the current * region is full, so in most cases it's not needed. */ -char * +lispobj * alloc(long nbytes) { struct thread *thread=arch_os_get_current_thread(); @@ -4770,8 +4592,12 @@ alloc(long nbytes) #else &boxed_region; #endif +#ifndef LISP_FEATURE_WIN32 + lispobj alloc_signal; +#endif void *new_obj; void *new_free_pointer; + gc_assert(nbytes>0); /* Check for alignment allocation problems. */ @@ -4823,6 +4649,24 @@ alloc(long nbytes) } } new_obj = gc_alloc_with_region(nbytes,0,region,0); + +#ifndef LISP_FEATURE_WIN32 + alloc_signal = SymbolValue(ALLOC_SIGNAL,thread); + if ((alloc_signal & FIXNUM_TAG_MASK) == 0) { + if ((signed long) alloc_signal <= 0) { +#ifdef LISP_FEATURE_SB_THREAD + kill_thread_safely(thread->os_thread, SIGPROF); +#else + raise(SIGPROF); +#endif + } else { + SetSymbolValue(ALLOC_SIGNAL, + alloc_signal - (1 << N_FIXNUM_TAG_BITS), + thread); + } + } +#endif + return (new_obj); } @@ -4831,7 +4675,7 @@ alloc(long nbytes) * catch GENCGC-related write-protect violations */ -void unhandled_sigmemoryfault(void); +void unhandled_sigmemoryfault(void* addr); /* Depending on which OS we're running under, different signals might * be raised for a violation of write protection in the heap. This @@ -4858,7 +4702,7 @@ gencgc_handle_wp_violation(void* fault_addr) /* It can be helpful to be able to put a breakpoint on this * case to help diagnose low-level problems. */ - unhandled_sigmemoryfault(); + unhandled_sigmemoryfault(fault_addr); /* not within the dynamic space -- not our responsibility */ return 0; @@ -4889,7 +4733,7 @@ gencgc_handle_wp_violation(void* fault_addr) * are about to let Lisp deal with it. It's basically just a * convenient place to set a gdb breakpoint. */ void -unhandled_sigmemoryfault() +unhandled_sigmemoryfault(void *addr) {} void gc_alloc_update_all_page_tables(void) @@ -4964,7 +4808,8 @@ gc_and_save(char *filename, int prepend_runtime) void *runtime_bytes = NULL; size_t runtime_size; - file = prepare_to_save(filename, prepend_runtime, &runtime_bytes, &runtime_size); + file = prepare_to_save(filename, prepend_runtime, &runtime_bytes, + &runtime_size); if (file == NULL) return;