/* Precise GC for MzScheme Copyright (c) 2004-2010 PLT Scheme Inc. Copyright (c) 1999 Matthew Flatt All rights reserved. Please see the full copyright in the documentation. */ /* This implementation is currently hard-wired for 4-byte words */ #include #include #include #define USE_MMAP 1 #define GROW_FACTOR 1.5 #if defined(sparc) || defined(__sparc) || defined(__sparc__) # define ALIGN_DOUBLES 1 #else # define ALIGN_DOUBLES 0 #endif #if USE_MMAP /* For mmap: */ # include # include # include # include #endif typedef short Type_Tag; #include "gc2.h" #define TIME 0 #define SEARCH 1 #define SAFETY 1 #define RECYCLE_HEAP 0 #define KEEP_FROM_PTR 0 #define GC_EVERY_ALLOC 0 #define ALLOC_GC_PHASE 0 #define SKIP_FORCED_GC 0 #define CHECK_STACK_EVERY 5 #define CHECK_STACK_START -1 void (*GC_collect_start_callback)(void); void (*GC_collect_end_callback)(void); void (*GC_out_of_memory)(void); unsigned long (*GC_get_thread_stack_base)(void); void (*GC_mark_xtagged)(void *obj); void (*GC_fixup_xtagged)(void *obj); void **GC_variable_stack; Type_Tag weak_box_tag; #define gc_finalization_tag 256 #define gc_finalization_weak_link_tag 257 #define gc_weak_array_tag 258 #define _num_tags_ 259 Size_Proc size_table[_num_tags_]; Mark_Proc mark_table[_num_tags_]; Fixup_Proc fixup_table[_num_tags_]; #define STARTING_PLACE ((void *)0x400000) void *GC_alloc_space = STARTING_PLACE, *GC_alloc_top; static long alloc_size, heap_size = 32000; static void **tagged_high = STARTING_PLACE, **untagged_low = STARTING_PLACE; static void **new_tagged_high, **new_untagged_low; static void *old_space; static long old_size; static char *alloc_bitmap; static char zero_sized[4]; static void *park[2]; static int cycle_count = 0; #if GC_EVERY_ALLOC static int alloc_cycle = ALLOC_GC_PHASE; static int skipped_first = !SKIP_FORCED_GC; #endif #if KEEP_FROM_PTR static void *mark_source; # define FROM_STACK ((void *)0xAAAA1) # define FROM_ROOT ((void *)0xAAAA3) # define FROM_FNL ((void *)0xAAAA5) # define FROM_NEW ((void *)0xAAAA7) # define FROM_IMM ((void *)0xAAAA7) #endif /******************************************************************************/ #if USE_MMAP int fd, fd_created; #define PAGE_SIZE 4096 void *malloc_pages(size_t len) { void *r; if (!fd_created) { fd_created = 1; fd = open("/dev/zero", O_RDWR); } if (len & (PAGE_SIZE - 1)) { len += PAGE_SIZE - (len & (PAGE_SIZE - 1)); } r = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (r == (void *)-1) { printf("mmap failed: %s\n", strerror(errno)); exit(-1); } return r; } void free_pages(void *p, size_t len) { munmap(p, len); } #endif /******************************************************************************/ #if !USE_MMAP void *malloc_pages(size_t len) { return malloc(len); } void free_pages(void *p, size_t len) { free(p); } #endif /******************************************************************************/ #define PTR_ALIGNMENT 4 #define PTR_TO_INT(x) ((unsigned long)x) #define INT_TO_PTR(x) ((void *)x) static long roots_count; static long roots_size; static unsigned long *roots; static int compare_roots(const void *a, const void *b) { if (*(unsigned long *)a < *(unsigned long *)b) return -1; else return 1; } static void sort_and_merge_roots() { static int counter = 0; int i, offset, top; if (roots_count < 4) return; /* Only try this every 5 collections or so: */ if (counter--) return; counter = 5; qsort(roots, roots_count >> 1, 2 * sizeof(unsigned long), compare_roots); offset = 0; top = roots_count; for (i = 2; i < top; i += 2) { if ((roots[i - 2 - offset] <= roots[i]) && ((roots[i - 1 - offset] + (PTR_ALIGNMENT - 1)) >= roots[i])) { /* merge: */ if (roots[i + 1] > roots[i - 1 - offset]) roots[i - 1 - offset] = roots[i + 1]; offset += 2; roots_count -= 2; } else if (roots[i] == roots[i + 1]) { /* Remove empty range: */ offset += 2; roots_count -= 2; } else if (offset) { /* compact: */ roots[i - offset] = roots[i]; roots[i + 1 - offset] = roots[i + 1]; } } } void GC_add_roots(void *start, void *end) { if (roots_count >= roots_size) { unsigned long *naya; roots_size = roots_size ? 2 * roots_size : 500; naya = (unsigned long *)malloc(sizeof(unsigned long) * (roots_size + 1)); memcpy((void *)naya, (void *)roots, sizeof(unsigned long) * roots_count); if (roots) free(roots); roots = naya; } roots[roots_count++] = PTR_TO_INT(start); roots[roots_count++] = PTR_TO_INT(end) - PTR_ALIGNMENT; } typedef struct ImmobileBox { void *p; struct ImmobileBox *next, *prev; } ImmobileBox; static ImmobileBox *immobile; void *GC_malloc_immobile_box(void *p) { ImmobileBox *ib; ib = (ImmobileBox *)malloc(sizeof(ImmobileBox)); ib->p = p; ib->next = immobile; if (immobile) immobile->prev = ib; ib->prev = NULL; immobile = ib; return ib; } void GC_free_immobile_box(void *b) { ImmobileBox *ib = (ImmobileBox *)b; if (!ib) return; if (ib->prev) ib->prev->next = ib->next; else immobile = ib->next; if (ib->next) ib->next->prev = ib->prev; free(ib); } /******************************************************************************/ typedef struct GC_Weak_Array { Type_Tag type; short keyex; long count; void *replace_val; struct GC_Weak_Array *next; void *data[1]; } GC_Weak_Array; static GC_Weak_Array *weak_arrays; static int size_weak_array(void *p) { GC_Weak_Array *a = (GC_Weak_Array *)p; return gcBYTES_TO_WORDS(sizeof(GC_Weak_Array) + ((a->count - 1) * sizeof(void *))); } static int mark_weak_array(void *p) { /* Not used */ return size_weak_array(p); } static int fixup_weak_array(void *p) { GC_Weak_Array *a = (GC_Weak_Array *)p; gcFIXUP(a->replace_val); a->next = weak_arrays; weak_arrays = a; return size_weak_array(p); } void *GC_malloc_weak_array(size_t size_in_bytes, void *replace_val) { GC_Weak_Array *w; /* Allcation might trigger GC, so we use park: */ park[0] = replace_val; w = (GC_Weak_Array *)GC_malloc_one_tagged(size_in_bytes + sizeof(GC_Weak_Array) - sizeof(void *)); replace_val = park[0]; park[0] = NULL; w->type = gc_weak_array_tag; w->replace_val = replace_val; w->count = (size_in_bytes >> 2); return w; } typedef struct GC_Weak_Box { /* The first three fields are mandated by the GC spec: */ Type_Tag type; short keyex; void *val; /* The rest is up to us: */ void **secondary_erase; int soffset; struct GC_Weak_Box *next; } GC_Weak_Box; static GC_Weak_Box *weak_boxes; static int size_weak_box(void *p) { return gcBYTES_TO_WORDS(sizeof(GC_Weak_Box)); } static int mark_weak_box(void *p) { /* Not used */ return gcBYTES_TO_WORDS(sizeof(GC_Weak_Box)); } static int fixup_weak_box(void *p) { GC_Weak_Box *wb = (GC_Weak_Box *)p; gcFIXUP(wb->secondary_erase); if (wb->val) { wb->next = weak_boxes; weak_boxes = wb; } return gcBYTES_TO_WORDS(sizeof(GC_Weak_Box)); } void *GC_malloc_weak_box(void *p, void **secondary, int soffset) { GC_Weak_Box *w; /* Allcation might trigger GC, so we use park: */ park[0] = p; park[1] = secondary; w = (GC_Weak_Box *)GC_malloc_one_tagged(sizeof(GC_Weak_Box)); p = park[0]; park[0] = NULL; secondary = (void **)park[1]; park[1] = NULL; w->type = weak_box_tag; w->val = p; w->secondary_erase = secondary; w->soffset = soffset; return w; } /******************************************************************************/ typedef struct Fnl { Type_Tag type; short eager_level; void *p; void (*f)(void *p, void *data); void *data; struct Fnl *next; } Fnl; static Fnl *fnls, *run_queue, *last_in_queue; static int size_finalizer(void *p) { return gcBYTES_TO_WORDS(sizeof(Fnl)); } static int mark_finalizer(void *p) { /* Not used */ return gcBYTES_TO_WORDS(sizeof(Fnl)); } static int fixup_finalizer(void *p) { Fnl *fnl = (Fnl *)p; gcFIXUP(fnl->next); gcFIXUP(fnl->data); if (!fnl->eager_level) { /* Queued for run: */ gcFIXUP(fnl->p); } return gcBYTES_TO_WORDS(sizeof(Fnl)); } void GC_set_finalizer(void *p, int tagged, int level, void (*f)(void *p, void *data), void *data, void (**oldf)(void *p, void *data), void **olddata) { Fnl *fnl, *prev; if (((long)p & 0x1) || (p < GC_alloc_space) || (p > GC_alloc_top)) { /* Never collected. Don't finalize it. */ if (oldf) *oldf = NULL; if (olddata) *olddata = NULL; return; } fnl = fnls; prev = NULL; while (fnl) { if (fnl->p == p) { if (oldf) *oldf = fnl->f; if (olddata) *olddata = fnl->data; if (f) { fnl->f = f; fnl->data = data; fnl->eager_level = level; } else { if (prev) prev->next = fnl->next; else fnls = fnl->next; return; } return; } else { prev = fnl; fnl = fnl->next; } } if (oldf) *oldf = NULL; if (olddata) *olddata = NULL; if (!f) return; /* Allcation might trigger GC, so we use park: */ park[0] = p; park[1] = data; fnl = GC_malloc_one_tagged(sizeof(Fnl)); p = park[0]; park[0] = NULL; data = park[1]; park[1] = NULL; fnl->type = gc_finalization_tag; fnl->next = fnls; fnl->p = p; fnl->f = f; fnl->data = data; fnl->eager_level = level; fnls = fnl; } typedef struct Fnl_Weak_Link { Type_Tag type; void *p; long offset; /* offset from beginning of block */ void *saved; struct Fnl_Weak_Link *next; } Fnl_Weak_Link; static Fnl_Weak_Link *fnl_weaks; static int size_finalizer_weak_link(void *p) { return gcBYTES_TO_WORDS(sizeof(Fnl_Weak_Link)); } static int mark_finalizer_weak_link(void *p) { /* Not used */ return gcBYTES_TO_WORDS(sizeof(Fnl_Weak_Link)); } static int fixup_finalizer_weak_link(void *p) { Fnl_Weak_Link *wl = (Fnl_Weak_Link *)p; gcFIXUP(wl->next); return gcBYTES_TO_WORDS(sizeof(Fnl_Weak_Link)); } void GC_finalization_weak_ptr(void **p, int offset) { Fnl_Weak_Link *wl; #ifdef SAFETY if (((void *)p < GC_alloc_space) || (p >= GC_alloc_top)) { *(int *)0x0 = 1; } #endif /* Allcation might trigger GC, so we use park: */ park[0] = p; wl = (Fnl_Weak_Link *)GC_malloc_one_tagged(sizeof(Fnl_Weak_Link)); p = park[0]; park[0] = NULL; wl->type = gc_finalization_weak_link_tag; wl->p = p; wl->offset = offset * sizeof(void*); wl->next = fnl_weaks; fnl_weaks = wl; } /******************************************************************************/ static unsigned long stack_base; void GC_set_stack_base(void *base) { stack_base = (unsigned long)base; } unsigned long GC_get_stack_base(void) { return stack_base; } void GC_dump(void) { fprintf(stderr, "Memory use: %ld\n", GC_get_memory_use()); } long GC_get_memory_use() { return (alloc_size - ((untagged_low - tagged_high) << 2)); } void GC_init_type_tags(int count, int weakbox) { weak_box_tag = weakbox; } #define SKIP ((Type_Tag)0x7000) #define MOVED ((Type_Tag)0x3000) #if SEARCH void *search_for, *search_mark; long search_size; int detail_cycle; int atomic_detail_cycle; #endif #if SEARCH void stop() { printf("stopped\n"); } #endif /* Only works during GC: */ void *find_start(void *p) { long diff = ((char *)p - (char *)GC_alloc_space) >> 2; if (((long)p & 0x3) || !(alloc_bitmap[diff >> 3] & (1 << (diff & 0x7)))) { while (!(alloc_bitmap[diff >> 3] & (1 << (diff & 0x7)))) { diff--; } diff <<= 2; return (void *)((char *)GC_alloc_space + diff); } else return p; } #ifdef SAFETY static void middle(unsigned long p, long delta, unsigned long where) { fprintf(stderr, "Middle!: 0x%lx d: %ld at 0x%lx\n", p, delta, where); } static int check_count = CHECK_STACK_START; static void check_interior_pointer(void **pp) { void *p = *pp; if (check_count--) return; else check_count = CHECK_STACK_EVERY; if (!((long)p & 0x1) && (p >= GC_alloc_space) && (p <= GC_alloc_top)) { long diff = ((char *)p - (char *)GC_alloc_space) >> 2; if (((long)p & 0x3) || !(alloc_bitmap[diff >> 3] & (1 << (diff & 0x7)))) { long diff1 = ((char *)p - (char *)GC_alloc_space); while (!(alloc_bitmap[diff >> 3] & (1 << (diff & 0x7)))) { diff--; } diff <<= 2; if (((diff + (char *)GC_alloc_space) > (char *)tagged_high) && ((*(long *)(diff + (char *)GC_alloc_space - 4) & 0x20000000))) { /* Middle is ok. */ } else { middle((unsigned long)p, diff1 - diff, (unsigned long)pp); } } } } #endif static void *mark(void *p) { long diff = ((char *)p - (char *)GC_alloc_space) >> 2; #if SEARCH if (p == search_mark) stop(); #endif if (((long)p & 0x3) || !(alloc_bitmap[diff >> 3] & (1 << (diff & 0x7)))) { long diff1 = ((char *)p - (char *)GC_alloc_space); while (!(alloc_bitmap[diff >> 3] & (1 << (diff & 0x7)))) { diff--; } diff <<= 2; #ifdef SAFETY if (((diff + (char *)GC_alloc_space) > (char *)tagged_high) && ((*(long *)(diff + (char *)GC_alloc_space - 4) & 0x20000000) || (!(*(long *)(diff + (char *)GC_alloc_space - 4)) && (*(long **)(diff + (char *)GC_alloc_space))[-1] & 0x20000000))) { /* Middle is ok. */ } else { middle((unsigned long)p, diff1 - diff, 0); } #endif return (void *)((char *)mark(diff + (char *)GC_alloc_space) + (diff1 - diff)); } else { if (p < (void *)tagged_high) { Type_Tag tag = *(Type_Tag *)p; long size; void *naya; if (tag == MOVED) return ((void **)p)[1]; #if SAFETY if ((tag < 0) || (tag >= _num_tags_) || !size_table[tag]) { *(int *)0x0 = 1; } #endif size = size_table[tag](p); #if ALIGN_DOUBLES if (!(size & 0x1)) { if ((long)new_tagged_high & 0x4) { ((Type_Tag *)new_tagged_high)[0] = SKIP; new_tagged_high += 1; } } #endif #if KEEP_FROM_PTR *new_tagged_high = mark_source; new_tagged_high++; #endif { int i; long *a, *b; a = (long *)new_tagged_high; b = (void *)p; for (i = size; i--; ) *(a++) = *(b++); } naya = new_tagged_high; ((Type_Tag *)p)[0] = MOVED; ((void **)p)[1] = naya; new_tagged_high += size; #if SEARCH if (naya == search_for) { stop(); } #endif return naya; } else { long size; p -= 4; size = ((*(long *)p) & 0x0FFFFFFF); if (!size) return ((void **)p)[1]; #if ALIGN_DOUBLES if (!(size & 1)) { if (!((long)new_untagged_low & 0x4)) { new_untagged_low--; *(long *)new_untagged_low = 0; } } #endif size++; new_untagged_low -= size; #if SAFETY if ((unsigned long)new_untagged_low < (unsigned long)new_tagged_high) { *(int *)0x0 = 1; } #endif { int i; long *a, *b; a = (long *)new_untagged_low; b = (void *)p; for (i = size; i--; ) *(a++) = *(b++); } ((void **)p)[1] = new_untagged_low + 1; ((long *)p)[0] = 0; #if SEARCH if ((new_untagged_low + 1) == search_for) { stop(); } #endif #if SEARCH if (atomic_detail_cycle == cycle_count) { printf("%ld at %lx\n", size, (long)new_untagged_low); } #endif #if KEEP_FROM_PTR --new_untagged_low; *new_untagged_low = mark_source; return new_untagged_low + 2; #else return new_untagged_low + 1; #endif } } } void GC_mark(const void *p) { /* Not used. */ } void GC_fixup(void *_p) { void *p; p = *(void **)_p; if (!((long)p & 0x1) && (p >= GC_alloc_space) && (p <= GC_alloc_top)) *(void **)_p = mark(p); } static void **o_var_stack, **oo_var_stack; void GC_mark_variable_stack(void **var_stack, long delta, void *limit) { /* Not used. */ } void GC_trace_variable_stack(void **var_stack, long delta, void *limit, int just_check) { int stack_depth; stack_depth = 0; while (var_stack) { long size; void ***p; var_stack = (void **)((char *)var_stack + delta); if (var_stack == limit) return; size = *(long *)(var_stack + 1); oo_var_stack = o_var_stack; o_var_stack = var_stack; p = (void ***)(var_stack + 2); while (size--) { if (!*p) { /* Array */ long count = ((long *)p)[2]; void **a = ((void ***)p)[1]; p += 2; size -= 2; a = (void **)((char *)a + delta); while (count--) { #ifdef SAFETY if (just_check) { check_interior_pointer(a); } else #endif { gcFIXUP(*a); } a++; } } else { void **a = *p; a = (void **)((char *)a + delta); #ifdef SAFETY if (just_check) { check_interior_pointer(a); } else #endif { gcFIXUP(*a); } } p++; } #if SAFETY if (*var_stack && ((unsigned long)*var_stack <= (unsigned long)var_stack)) *(int *)0x0 = 1; #endif var_stack = *var_stack; stack_depth++; } } void GC_fixup_variable_stack(void **var_stack, long delta, void *limit) { GC_trace_variable_stack(var_stack, delta, limit, 0); } #if SAFETY void check_variable_stack() { void **limit, **var_stack; if (!alloc_bitmap) return; limit = (void **)(GC_get_thread_stack_base ? GC_get_thread_stack_base() : stack_base); var_stack = GC_variable_stack; GC_trace_variable_stack(var_stack, 0, limit, 1); } #endif #if 0 # define GETTIME() ((long)scheme_get_milliseconds()) #else # define GETTIME() ((long)scheme_get_process_milliseconds()) #endif #if TIME # define PRINTTIME(x) fprintf x # define STDERR stderr static long started, rightnow, old; # define INITTIME() (started = GETTIME()) # define GETTIMEREL() (rightnow = GETTIME(), old = started, started = rightnow, rightnow - old) #else # define INITTIME() /* empty */ # define PRINTTIME(x) /* empty */ #endif static int initialized; #if SAFETY static long *prev_ptr; static void **prev_var_stack; #endif void gcollect(int needsize) { /* Check old: */ long *p, *top; void *new_space; long new_size; void **tagged_mark, **untagged_mark; char *bitmap; int i, did_fnls; long diff, iterations; ImmobileBox *ib; GC_Weak_Box *wb; GC_Weak_Array *wa; INITTIME(); PRINTTIME((STDERR, "gc: start: %ld\n", GETTIMEREL())); cycle_count++; if (!initialized) { GC_register_traversers(weak_box_tag, size_weak_box, mark_weak_box, fixup_weak_box); GC_register_traversers(gc_weak_array_tag, size_weak_array, mark_weak_array, fixup_weak_array); GC_register_traversers(gc_finalization_tag, size_finalizer, mark_finalizer, fixup_finalizer); GC_register_traversers(gc_finalization_weak_link_tag, size_finalizer_weak_link, mark_finalizer_weak_link, fixup_finalizer_weak_link); GC_add_roots(&fnls, (char *)&fnls + sizeof(fnls) + 1); GC_add_roots(&fnl_weaks, (char *)&fnl_weaks + sizeof(fnl_weaks) + 1); GC_add_roots(&run_queue, (char *)&run_queue + sizeof(run_queue) + 1); GC_add_roots(&last_in_queue, (char *)&last_in_queue + sizeof(last_in_queue) + 1); GC_add_roots(&park, (char *)&park + sizeof(park) + 1); initialized = 1; } weak_boxes = NULL; weak_arrays = NULL; did_fnls = 0; if (GC_collect_start_callback) GC_collect_start_callback(); sort_and_merge_roots(); new_size = (heap_size * GROW_FACTOR); if (new_size < alloc_size) new_size = alloc_size; new_size += needsize; /* word-aligned: */ new_size = (new_size + 3) & 0xFFFFFFFC; if (old_size >= new_size) { new_size = old_size; new_space = old_space; } else { if (old_size) { free_pages(old_space, old_size); old_size = 0; } new_space = malloc_pages(new_size + 4); if (!new_space) { printf("Out of memory"); abort(); } } /******************** Mark/Copy ****************************/ tagged_mark = new_tagged_high = (void **)new_space; untagged_mark = new_untagged_low = (void **)(new_space + new_size); #if KEEP_FROM_PTR mark_source = FROM_STACK; #endif GC_fixup_variable_stack(GC_variable_stack, 0, (void *)(GC_get_thread_stack_base ? GC_get_thread_stack_base() : stack_base)); PRINTTIME((STDERR, "gc: stack: %ld\n", GETTIMEREL())); #if KEEP_FROM_PTR mark_source = FROM_ROOT; #endif for (i = 0; i < roots_count; i += 2) { void **s = (void **)roots[i]; void **e = (void **)roots[i + 1]; while (s < e) { gcFIXUP(*s); s++; } } #if KEEP_FROM_PTR mark_source = FROM_IMM; #endif /* Do immobiles: */ for (ib = immobile; ib; ib = ib->next) { gcFIXUP(ib->p); } PRINTTIME((STDERR, "gc: roots: %ld\n", GETTIMEREL())); iterations = 0; while (1) { /* Loop to do finalization */ while ((tagged_mark < new_tagged_high) || (untagged_mark > new_untagged_low)) { iterations++; while (tagged_mark < new_tagged_high) { Type_Tag tag; long size; #if KEEP_FROM_PTR tagged_mark++; #endif tag = *(Type_Tag *)tagged_mark; #if ALIGN_DOUBLES if (tag == SKIP) tagged_mark++; else { #endif #if SAFETY if ((tag < 0) || (tag >= _num_tags_) || !size_table[tag]) { *(int *)0x0 = 1; } #endif #if KEEP_FROM_PTR mark_source = tagged_mark; #endif size = size_table[tag](tagged_mark); fixup_table[tag](tagged_mark); #if SAFETY if (size <= 1) { *(int *)0x0 = 1; } #endif tagged_mark += size; #if SAFETY if ((void *)tagged_mark < new_space) { *(int *)0x0 = 1; } #endif #if ALIGN_DOUBLES } #endif } while (untagged_mark > new_untagged_low) { void **mp, **started; mp = started = new_untagged_low; while (mp < untagged_mark) { long v, size; #if KEEP_FROM_PTR mp++; #endif v = *(long *)mp; size = (v & 0x0FFFFFFF); if (v & 0xF0000000) { #if KEEP_FROM_PTR mark_source = mp; #endif mp++; if (v & 0x80000000) { /* Array of pointers */ int i; /* printf("parray: %d %lx\n", size, (long)mp); */ for (i = size; i--; mp++) { gcFIXUP(*mp); } } else if (v & 0x10000000) { /* xtagged */ GC_fixup_xtagged(mp); mp += size; } else { /* Array of tagged */ int i, elem_size; Type_Tag tag = *(Type_Tag *)mp; elem_size = size_table[tag](mp); fixup_table[tag](mp); mp += elem_size; for (i = elem_size; i < size; i += elem_size, mp += elem_size) fixup_table[tag](mp); } } else mp += v + 1; } untagged_mark = started; } } if ((did_fnls >= 3) || !fnls) { if (did_fnls == 3) { /* Finish up ordered finalization */ Fnl *f, *next, *prev; Fnl_Weak_Link *wl; /* Enqueue and mark level 3 finalizers that still haven't been marked. */ /* (Recursive marking is already done, though.) */ prev = NULL; for (f = fnls; f; f = next) { next = f->next; if (f->eager_level == 3) { void *v; v = GC_resolve(f->p); if (v == f->p) { /* Not yet marked. Mark it and enqueue it. */ #if KEEP_FROM_PTR mark_source = f; #endif gcFIXUP(f->p); if (prev) prev->next = next; else fnls = next; f->eager_level = 0; /* indicates queued */ f->next = NULL; if (last_in_queue) { last_in_queue->next = f; last_in_queue = f; } else { run_queue = last_in_queue = f; } } else { f->p = v; prev = f; } } } /* Restore zeroed out weak links, marking as we go: */ for (wl = fnl_weaks; wl; wl = wl->next) { void *wp = (void *)GC_resolve(wl->p); int markit; markit = (wp != wl->p); wp = (wp + wl->offset); if (markit) gcFIXUP(wl->saved); *(void **)wp = wl->saved; } /* We have to mark one more time, because restoring a weak link may have made something reachable. */ did_fnls++; } else break; } else { int eager_level = did_fnls + 1; if (eager_level == 3) { /* Ordered finalization */ Fnl *f; Fnl_Weak_Link *wl; /* Zero out weak links for ordered finalization */ for (wl = fnl_weaks; wl; wl = wl->next) { void *wp = (void *)GC_resolve(wl->p); wl->saved = *(void **)(wp + wl->offset); *(void **)(wp + wl->offset) = NULL; } /* Mark content of not-yet-marked finalized objects, but don't mark the finalized objects themselves. */ for (f = fnls; f; f = f->next) { if (f->eager_level == 3) { void *v; v = GC_resolve(f->p); if (v == f->p) { /* Not yet marked. Do content. */ Type_Tag tag = *(Type_Tag *)v; #if SAFETY if ((tag < 0) || (tag >= _num_tags_) || !fixup_table[tag]) { *(int *)0x0 = 1; } #endif #if KEEP_FROM_PTR mark_source = FROM_FNL; #endif fixup_table[tag](v); } } } } else { /* Unordered finalization */ Fnl *f, *prev, *queue; f = fnls; prev = NULL; queue = NULL; while (f) { if (f->eager_level == eager_level) { void *v; v = GC_resolve(f->p); if (v == f->p) { /* Not yet marked. Move finalization to run queue. */ Fnl *next = f->next; if (prev) prev->next = next; else fnls = next; f->eager_level = 0; /* indicates queued */ f->next = NULL; if (last_in_queue) { last_in_queue->next = f; last_in_queue = f; } else { run_queue = last_in_queue = f; } if (!queue) queue = f; f = next; } else { f->p = v; prev = f; f = f->next; } } else { prev = f; f = f->next; } } /* Mark items added to run queue: */ f = queue; while (f) { #if KEEP_FROM_PTR mark_source = f; #endif gcFIXUP(f->p); f = f->next; } } did_fnls++; } } PRINTTIME((STDERR, "gc: mark/copy (%d): %ld\n", iterations, GETTIMEREL())); /******************************************************/ /* Do weak boxes: */ wb = weak_boxes; while (wb) { if (!((long)wb->val & 0x1) && ((void *)wb->val >= GC_alloc_space) && ((void *)wb->val <= GC_alloc_top)) { void *v; v = GC_resolve(wb->val); if (v == wb->val) { wb->val = NULL; if (wb->secondary_erase) { *(wb->secondary_erase + wb->soffset) = NULL; wb->secondary_erase = NULL; } } else wb->val = v; } /* else not collectable */ wb = wb->next; } /* Do weak arrays: */ wa = weak_arrays; while (wa) { int i; for (i = wa->count; i--; ) { void *p = wa->data[i]; if (!((long)p & 0x1) && (p >= GC_alloc_space) && (p <= GC_alloc_top)) { void *v; v = GC_resolve(p); if (v == p) wa->data[i] = wa->replace_val; else wa->data[i] = v; } /* else not collectable */ } wa = wa->next; } /* Cleanup weak finalization links: */ { Fnl_Weak_Link *wl, *prev, *next; prev = NULL; for (wl = fnl_weaks; wl; wl = next) { void *wp; next = wl->next; wp = (void *)GC_resolve(wl->p); if (wp == wl->p) { /* Collectable. Removed this link. */ if (prev) prev->next = next; else fnl_weaks = next; } else { wl->p = wp; prev = wl; } } } /******************************************************/ #if RECYCLE_HEAP old_space = GC_alloc_space; old_size = alloc_size; #else if (alloc_size) free_pages(GC_alloc_space, alloc_size + 4); #endif free(alloc_bitmap); PRINTTIME((STDERR, "gc: free: %ld\n", GETTIMEREL())); if (new_untagged_low < new_tagged_high) { printf("Ouch: Tagged area collided with untagged area.\n"); abort(); } alloc_size = new_size; GC_alloc_space = new_space; GC_alloc_top = GC_alloc_space + alloc_size; tagged_high = new_tagged_high; untagged_low = new_untagged_low; heap_size = new_size - ((untagged_low - tagged_high) << 2); { long *p = (long *)untagged_low; while (p-- > (long *)tagged_high) *p = 0; } PRINTTIME((STDERR, "gc: restored: %ld\n", GETTIMEREL())); /******************** Make initial bitmap image: ****************************/ { alloc_bitmap = bitmap = (char *)malloc((alloc_size >> 5) + 1); memset(bitmap, 0, (alloc_size >> 5) + 1); } p = (long *)untagged_low; diff = (((char *)p - (char *)GC_alloc_space) + 4) >> 2; top = (long *)GC_alloc_top; while (p < top) { long size; #if KEEP_FROM_PTR diff++; p++; #endif size = (*p & 0x0FFFFFFF) + 1; bitmap[diff >> 3] |= (1 << (diff & 0x7)); p += size; diff += size; } p = ((long *)GC_alloc_space); diff = ((char *)p - (char *)GC_alloc_space) >> 2; while (p < (long *)tagged_high) { Type_Tag tag; long size; #if KEEP_FROM_PTR diff++; p++; #endif tag = *(Type_Tag *)p; #if ALIGN_DOUBLES if (tag == SKIP) { p++; diff++; } else { #endif bitmap[diff >> 3] |= (1 << (diff & 0x7)); #if SEARCH if (cycle_count == detail_cycle) printf("tag: %lx = %d\n", (long)p, tag); #endif #if SAFETY if ((tag < 0) || (tag >= _num_tags_) || !size_table[tag]) { fflush(NULL); *(int *)0x0 = 1; } prev_ptr = p; prev_var_stack = GC_variable_stack; #endif size = size_table[tag](p); #if SAFETY if (prev_var_stack != GC_variable_stack) { *(int *)0x0 = 1; } #endif p += size; diff += size; #if ALIGN_DOUBLES } #endif } PRINTTIME((STDERR, "gc: done (t=%d, u=%d): %ld\n", (long)((void *)tagged_high - GC_alloc_space), (long)(GC_alloc_top - (void *)untagged_low), GETTIMEREL())); if (GC_collect_start_callback) GC_collect_end_callback(); /**********************************************************************/ /* Run Finalizations. Collections may happen */ while (run_queue) { Fnl *f; void **gcs; f = run_queue; run_queue = run_queue->next; if (!run_queue) last_in_queue = NULL; gcs = GC_variable_stack; f->f(f->p, f->data); GC_variable_stack = gcs; } } void *GC_resolve(void *p) { if (!((long)p & 0x1) && (p >= GC_alloc_space) && (p <= GC_alloc_top)) { if (p < (void *)tagged_high) { Type_Tag tag = *(Type_Tag *)p; if (tag == MOVED) return ((void **)p)[1]; else return p; } else { long size; p -= 4; size = ((*(long *)p) & 0x0FFFFFFF); if (!size) return ((void **)p)[1]; else return p + 4; } } else return p; } static void *malloc_tagged(size_t size_in_bytes) { void **m, **naya; #if SAFETY check_variable_stack(); #endif #if GC_EVERY_ALLOC # if SKIP_FORCED_GC if (!skipped_first) { alloc_cycle++; if (alloc_cycle >= SKIP_FORCED_GC) { alloc_cycle = 0; skipped_first = 1; } } # endif if (skipped_first) { alloc_cycle++; if (alloc_cycle >= GC_EVERY_ALLOC) { alloc_cycle = 0; gcollect(size_in_bytes); } } #endif #if KEEP_FROM_PTR size_in_bytes += 4; #endif size_in_bytes = ((size_in_bytes + 3) & 0xFFFFFFFC); #if ALIGN_DOUBLES if (!(size_in_bytes & 0x4)) { /* Make sure memory is 8-aligned */ if (((long)tagged_high & 0x4)) { if (tagged_high == untagged_low) { gcollect(size_in_bytes); #if KEEP_FROM_PTR size_in_bytes -= 4; #endif return malloc_tagged(size_in_bytes); } ((Type_Tag *)tagged_high)[0] = SKIP; tagged_high += 1; } } #endif #if SEARCH if (size_in_bytes == search_size) stop(); #endif m = tagged_high; naya = tagged_high + (size_in_bytes >> 2); if (naya > untagged_low) { gcollect(size_in_bytes); #if KEEP_FROM_PTR size_in_bytes -= 4; #endif return malloc_tagged(size_in_bytes); } tagged_high = naya; #if KEEP_FROM_PTR *m = FROM_NEW; m++; #endif #if SEARCH if (m == search_for) { stop(); } #endif { long diff = ((char *)m - (char *)GC_alloc_space) >> 2; alloc_bitmap[diff >> 3] |= (1 << (diff & 0x7)); } return m; } static void *malloc_untagged(size_t size_in_bytes, unsigned long nonatomic) { void **naya; #if SAFETY check_variable_stack(); #endif #if GC_EVERY_ALLOC # if SKIP_FORCED_GC if (!skipped_first) { alloc_cycle++; if (alloc_cycle >= SKIP_FORCED_GC) { alloc_cycle = 0; skipped_first = 1; } } # endif if (skipped_first) { alloc_cycle++; if (alloc_cycle >= GC_EVERY_ALLOC) { alloc_cycle = 0; gcollect(size_in_bytes); } } #endif if (!size_in_bytes) return zero_sized; #if KEEP_FROM_PTR size_in_bytes += 4; #endif size_in_bytes = ((size_in_bytes + 3) & 0xFFFFFFFC); #if ALIGN_DOUBLES if (!(size_in_bytes & 0x4)) { /* Make sure memory is 8-aligned */ if ((long)untagged_low & 0x4) { if (untagged_low == tagged_high) { #if KEEP_FROM_PTR size_in_bytes -= 4; #endif gcollect(size_in_bytes); return malloc_untagged(size_in_bytes, nonatomic); } untagged_low -= 1; ((long *)untagged_low)[0] = 0; } } #endif #if SEARCH if (size_in_bytes == search_size) stop(); #endif naya = untagged_low - ((size_in_bytes >> 2) + 1); if (naya < tagged_high) { gcollect(size_in_bytes); #if KEEP_FROM_PTR size_in_bytes -= 4; #endif return malloc_untagged(size_in_bytes, nonatomic); } untagged_low = naya; #if KEEP_FROM_PTR *naya = FROM_NEW; naya++; size_in_bytes -= 4; #endif ((long *)naya)[0] = (size_in_bytes >> 2) | nonatomic; #if SEARCH if ((naya + 1) == search_for) { stop(); } #endif { long diff = ((char *)(naya + 1) - (char *)GC_alloc_space) >> 2; alloc_bitmap[diff >> 3] |= (1 << (diff & 0x7)); } return naya + 1; } /* Array of pointers: */ void *GC_malloc(size_t size_in_bytes) { return malloc_untagged(size_in_bytes, 0x80000000); } /* Tagged item: */ void *GC_malloc_one_tagged(size_t size_in_bytes) { return malloc_tagged(size_in_bytes); } void *GC_malloc_one_xtagged(size_t size_in_bytes) { return malloc_untagged(size_in_bytes, 0x10000000); } void *GC_malloc_array_tagged(size_t size_in_bytes) { return malloc_untagged(size_in_bytes, 0x40000000); } /* Pointerless */ void *GC_malloc_atomic(size_t size_in_bytes) { return malloc_untagged(size_in_bytes, 0x00000000); } /* Plain malloc: */ void *GC_malloc_atomic_uncollectable(size_t size_in_bytes) { return malloc(size_in_bytes); } /* Array of pointers: */ void *GC_malloc_allow_interior(size_t size_in_bytes) { return malloc_untagged(size_in_bytes, 0xA0000000); } void GC_free(void *s) /* noop */ { } void GC_register_traversers(Type_Tag tag, Size_Proc size, Mark_Proc mark, Fixup_Proc fixup) { size_table[tag] = size; mark_table[tag] = mark; fixup_table[tag] = fixup; } void GC_gcollect() { gcollect(0); } /*************************************************************/ #if KEEP_FROM_PTR void GC_print_back_trace(void *p) { while ((p > GC_alloc_space) && (p < GC_alloc_top)) { if (p < (void *)tagged_high) { printf("%lx = tagged: %d\n", (long)p, *(short *)p); p = ((void **)p)[-1]; } else if (p > (void *)untagged_low) { printf("%lx = untagged: %lx\n", (long)p, *(long *)p); p = ((void **)p)[-1]; } else break; } if (p == FROM_STACK) printf("stack\n"); if (p == FROM_ROOT) printf("root\n"); if (p == FROM_FNL) printf("fnl\n"); if (p == FROM_IMM) printf("immobile\n"); } #endif