factor/native/gc.c

375 lines
7.8 KiB
C
Raw Normal View History

2004-07-16 02:26:21 -04:00
#include "factor.h"
2005-05-10 22:30:58 -04:00
/* Generational copying garbage collector */
2004-07-16 02:26:21 -04:00
2005-05-13 00:09:49 -04:00
CELL init_zone(ZONE *z, CELL size, CELL base)
{
z->base = z->here = base;
z->limit = z->base + size;
z->alarm = z->base + (size * 3) / 4;
return z->limit;
}
/* update this global variable. since it is stored in a non-volatile register,
we need to save its contents and re-initialize it when entering a callback,
and restore its contents when leaving the callback. see stack.c */
void update_cards_offset(void)
{
cards_offset = (CELL)cards - (heap_start >> CARD_BITS);
}
2005-05-13 00:09:49 -04:00
/* input parameters must be 8 byte aligned */
/* the heap layout is important:
- two semispaces: tenured and prior
- younger generations follow
there are two reasons for this:
- we can easily check if a pointer is in some generation or a younger one
- the nursery grows into the guard page, so allot() does not have to
check for out of memory, whereas allot_zone() (used by the GC) longjmp()s
back to collecting a higher generation */
2005-07-13 15:14:57 -04:00
void init_arena(CELL gens, CELL young_size, CELL aging_size)
2005-05-13 00:09:49 -04:00
{
int i;
CELL alloter;
2005-07-13 15:14:57 -04:00
CELL total_size = (gens - 1) * young_size + 2 * aging_size;
2005-05-13 00:09:49 -04:00
CELL cards_size = total_size / CARD_SIZE;
2005-07-13 15:14:57 -04:00
gen_count = gens;
generations = safe_malloc(sizeof(ZONE) * gen_count);
2005-07-13 15:14:57 -04:00
heap_start = (CELL)(alloc_bounded_block(total_size)->start);
2005-05-13 00:09:49 -04:00
heap_end = heap_start + total_size;
cards = safe_malloc(cards_size);
2005-05-13 00:09:49 -04:00
cards_end = cards + cards_size;
update_cards_offset();
2005-05-13 00:09:49 -04:00
alloter = heap_start;
alloter = init_zone(&tenured,aging_size,alloter);
alloter = init_zone(&prior,aging_size,alloter);
2005-07-13 15:14:57 -04:00
for(i = gen_count - 2; i >= 0; i--)
2005-05-13 00:09:49 -04:00
alloter = init_zone(&generations[i],young_size,alloter);
clear_cards(NURSERY,TENURED);
2005-05-13 00:09:49 -04:00
if(alloter != heap_start + total_size)
fatal_error("Oops",alloter);
heap_scan = false;
gc_time = 0;
minor_collections = 0;
cards_scanned = 0;
2005-05-13 00:09:49 -04:00
}
void collect_roots(void)
{
int i;
CELL ptr;
STACKS *stacks;
2005-05-12 01:02:39 -04:00
copy_handle(&T);
copy_handle(&bignum_zero);
copy_handle(&bignum_pos_one);
copy_handle(&bignum_neg_one);
copy_handle(&executing);
copy_handle(&callframe);
save_stacks();
stacks = stack_chain;
while(stacks)
{
CELL bottom = stacks->data_region->start;
CELL top = stacks->data;
for(ptr = bottom; ptr <= top; ptr += CELLS)
copy_handle((CELL*)ptr);
bottom = stacks->call_region->start;
top = stacks->call;
for(ptr = bottom; ptr <= top; ptr += CELLS)
copy_handle((CELL*)ptr);
2006-03-11 03:17:24 -05:00
copy_handle(&stacks->callframe);
copy_handle(&stacks->catch_save);
stacks = stacks->next;
}
for(i = 0; i < USER_ENV; i++)
2005-02-19 23:25:21 -05:00
copy_handle(&userenv[i]);
}
2005-05-12 01:02:39 -04:00
/* Given a pointer to oldspace, copy it to newspace. */
2005-05-12 03:52:56 -04:00
INLINE void *copy_untagged_object(void *pointer, CELL size)
2005-05-12 01:02:39 -04:00
{
2005-05-12 03:52:56 -04:00
void *newpointer;
if(newspace->here + size >= newspace->limit)
longjmp(gc_jmp,1);
newpointer = allot_zone(newspace,size);
2005-05-12 01:02:39 -04:00
memcpy(newpointer,pointer,size);
return newpointer;
}
INLINE CELL copy_object_impl(CELL pointer)
{
CELL newpointer = (CELL)copy_untagged_object((void*)UNTAG(pointer),
2005-05-12 01:02:39 -04:00
object_size(pointer));
/* install forwarding pointer */
put(UNTAG(pointer),RETAG(newpointer,GC_COLLECTED));
return newpointer;
}
/* follow a chain of forwarding pointers */
CELL resolve_forwarding(CELL untagged, CELL tag)
{
CELL header = get(untagged);
/* another forwarding pointer */
if(TAG(header) == GC_COLLECTED)
return resolve_forwarding(UNTAG(header),tag);
/* we've found the destination */
else
{
CELL pointer = RETAG(untagged,tag);
if(should_copy(untagged))
pointer = RETAG(copy_object_impl(pointer),tag);
return pointer;
}
}
2004-07-16 02:26:21 -04:00
/*
Given a pointer to a tagged pointer to oldspace, copy it to newspace.
If the object has already been copied, return the forwarding
pointer address without copying anything; otherwise, install
a new forwarding pointer.
*/
2005-05-12 01:02:39 -04:00
CELL copy_object(CELL pointer)
2004-07-16 02:26:21 -04:00
{
2005-05-12 01:02:39 -04:00
CELL tag;
CELL header;
2005-05-12 01:02:39 -04:00
gc_debug("copy object",pointer);
2005-05-12 01:02:39 -04:00
if(pointer == F)
return F;
tag = TAG(pointer);
if(tag == FIXNUM_TYPE)
return pointer;
header = get(UNTAG(pointer));
if(TAG(header) == GC_COLLECTED)
2005-05-13 00:09:49 -04:00
return resolve_forwarding(UNTAG(header),tag);
2005-05-12 01:02:39 -04:00
else
return RETAG(copy_object_impl(pointer),tag);
2004-07-16 02:26:21 -04:00
}
INLINE void collect_object(CELL scan)
2004-07-16 02:26:21 -04:00
{
switch(untag_header(get(scan)))
{
2004-07-27 23:29:37 -04:00
case WORD_TYPE:
collect_word((F_WORD*)scan);
2004-07-27 23:29:37 -04:00
break;
2004-07-16 02:26:21 -04:00
case ARRAY_TYPE:
case TUPLE_TYPE:
collect_array((F_ARRAY*)scan);
2004-07-16 02:26:21 -04:00
break;
2005-01-27 20:06:10 -05:00
case HASHTABLE_TYPE:
collect_hashtable((F_HASHTABLE*)scan);
break;
2004-07-16 02:26:21 -04:00
case VECTOR_TYPE:
collect_vector((F_VECTOR*)scan);
2004-07-16 02:26:21 -04:00
break;
case SBUF_TYPE:
collect_sbuf((F_SBUF*)scan);
2004-07-16 02:26:21 -04:00
break;
2004-12-25 18:08:20 -05:00
case DLL_TYPE:
collect_dll((DLL*)scan);
2004-12-25 18:08:20 -05:00
break;
case ALIEN_TYPE:
collect_alien((ALIEN*)scan);
2005-04-09 18:30:46 -04:00
break;
case WRAPPER_TYPE:
collect_wrapper((F_WRAPPER*)scan);
break;
2004-07-16 02:26:21 -04:00
}
}
2005-05-13 00:09:49 -04:00
CELL collect_next(CELL scan)
2004-07-16 02:26:21 -04:00
{
CELL size;
2005-05-12 03:52:56 -04:00
if(headerp(get(scan)))
2004-07-16 02:26:21 -04:00
{
size = untagged_object_size(scan);
collect_object(scan);
}
else
{
size = CELLS;
2005-02-19 23:25:21 -05:00
copy_handle((CELL*)scan);
2004-07-16 02:26:21 -04:00
}
return scan + size;
2004-07-16 02:26:21 -04:00
}
2005-05-12 01:02:39 -04:00
void reset_generations(CELL from, CELL to)
{
CELL i;
for(i = from; i <= to; i++)
generations[i].here = generations[i].base;
clear_cards(from,to);
}
2005-05-11 00:43:52 -04:00
void begin_gc(CELL gen)
{
collecting_gen = gen;
collecting_gen_start = generations[gen].base;
2005-05-11 00:43:52 -04:00
if(gen == TENURED)
{
/* when collecting the oldest generation, rotate it
with the semispace */
ZONE z = generations[gen];
generations[gen] = prior;
prior = z;
generations[gen].here = generations[gen].base;
2005-05-12 03:52:56 -04:00
newspace = &generations[gen];
2005-05-12 01:02:39 -04:00
clear_cards(TENURED,TENURED);
2005-05-11 00:43:52 -04:00
}
else
{
/* when collecting a younger generation, we copy
reachable objects to the next oldest generation,
2005-05-12 03:52:56 -04:00
so we set the newspace so the next generation. */
newspace = &generations[gen + 1];
2005-05-11 00:43:52 -04:00
}
}
void end_gc(CELL gen)
{
2005-05-12 01:02:39 -04:00
if(gen == TENURED)
{
/* we did a full collection; no more
old-to-new pointers remain since everything
is in tenured space */
unmark_cards(TENURED,TENURED);
/* all generations except tenured space are
now empty */
reset_generations(NURSERY,TENURED - 1);
fprintf(stderr,"*** Major GC (%ld minor, %ld cards)\n",
minor_collections,cards_scanned);
minor_collections = 0;
cards_scanned = 0;
2005-05-12 01:02:39 -04:00
}
else
{
/* we collected a younger generation. so the
next-oldest generation no longer has any
pointers into the younger generation (the
younger generation is empty!) */
unmark_cards(gen + 1,gen + 1);
/* all generations up to and including the one
collected are now empty */
reset_generations(NURSERY,gen);
minor_collections++;
2005-05-12 01:02:39 -04:00
}
2005-05-11 00:43:52 -04:00
}
/* collect gen and all younger generations */
void garbage_collection(CELL gen)
2004-07-16 02:26:21 -04:00
{
2005-03-21 20:59:30 -05:00
s64 start = current_millis();
CELL scan;
2004-11-22 19:15:14 -05:00
2005-02-17 21:19:27 -05:00
if(heap_scan)
2005-05-13 00:09:49 -04:00
critical_error("GC disabled during heap scan",gen);
2004-08-29 03:20:19 -04:00
2005-05-12 03:52:56 -04:00
/* we come back here if a generation is full */
if(setjmp(gc_jmp))
{
if(gen == TENURED)
{
/* oops, out of memory */
critical_error("Out of memory",0);
}
else
gen++;
}
2005-05-11 00:43:52 -04:00
begin_gc(gen);
/* initialize chase pointer */
2005-05-12 03:52:56 -04:00
scan = newspace->here;
2005-05-10 22:30:58 -04:00
2005-05-12 01:02:39 -04:00
/* collect objects referenced from stacks and environment */
2004-08-13 02:19:22 -04:00
collect_roots();
2005-05-12 01:02:39 -04:00
/* collect objects referenced from older generations */
collect_cards(gen);
2005-05-10 22:30:58 -04:00
/* collect literal objects referenced from compiled code */
collect_literals();
2005-05-12 03:52:56 -04:00
while(scan < newspace->here)
scan = collect_next(scan);
2005-05-11 00:43:52 -04:00
end_gc(gen);
2005-05-12 01:02:39 -04:00
gc_debug("gc done",gen);
2004-08-29 03:20:19 -04:00
2004-11-22 19:15:14 -05:00
gc_time += (current_millis() - start);
2005-05-12 01:02:39 -04:00
gc_debug("total gc time",gc_time);
2004-07-16 02:26:21 -04:00
}
2005-05-11 00:43:52 -04:00
void primitive_gc(void)
{
2005-05-12 01:02:39 -04:00
CELL gen = to_fixnum(dpop());
2005-06-08 22:06:33 -04:00
if(gen <= NURSERY)
gen = NURSERY;
else if(gen >= TENURED)
gen = TENURED;
2005-05-12 01:02:39 -04:00
garbage_collection(gen);
2005-05-11 00:43:52 -04:00
}
/* WARNING: only call this from a context where all local variables
are also reachable via the GC roots. */
2005-06-16 18:50:49 -04:00
void maybe_gc(CELL size)
{
2005-06-16 18:50:49 -04:00
if(nursery.here + size > nursery.alarm)
{
CELL gen = NURSERY;
while(gen < TENURED)
{
ZONE *z = &generations[gen + 1];
if(z->here < z->alarm)
break;
gen++;
}
garbage_collection(gen);
}
}
2004-11-22 19:15:14 -05:00
void simple_gc(void)
2004-11-22 19:15:14 -05:00
{
2005-06-16 18:50:49 -04:00
maybe_gc(0);
}
void primitive_gc_time(void)
{
simple_gc();
dpush(tag_bignum(s48_long_long_to_bignum(gc_time)));
2004-11-22 19:15:14 -05:00
}