]> gitweb.factorcode.org Git - factor.git/blobdiff - vm/data_gc.c
Initial import
[factor.git] / vm / data_gc.c
index 451561dbd68bc8053dcad52ad54f955666c1397b..eaf88f31c5c029995d59e3913e6b4f4a54c57097 100644 (file)
-#include "factor.h"
+#include "master.h"
 
-/* Test if 'fault' is in the guard page at the top or bottom (depending on
-offset being 0 or -1) of area+area_size */
-bool in_page(CELL fault, CELL area, CELL area_size, int offset)
+CELL init_zone(F_ZONE *z, CELL size, CELL start)
 {
-       const int pagesize = getpagesize();
-       area += area_size;
-       area += offset * pagesize;
+       z->size = size;
+       z->start = z->here = start;
+       z->end = start + size;
+       return z->end;
+}
 
-       return fault >= area && fault <= area + pagesize;
+void init_cards_offset(void)
+{
+       cards_offset = (CELL)data_heap->cards
+               - (data_heap->segment->start >> CARD_BITS);
 }
 
-/* If memory allocation fails, bail out */
-void *safe_malloc(size_t size)
+F_DATA_HEAP *alloc_data_heap(CELL gens, CELL young_size, CELL aging_size)
 {
-       void *ptr = malloc(size);
-       if(ptr == 0)
-               fatal_error("malloc() failed", 0);
-       return ptr;
+       young_size = align_page(young_size);
+       aging_size = align_page(aging_size);
+
+       F_DATA_HEAP *data_heap = safe_malloc(sizeof(F_DATA_HEAP));
+       data_heap->young_size = young_size;
+       data_heap->aging_size = aging_size;
+       data_heap->gen_count = gens;
+
+       CELL total_size;
+       if(data_heap->gen_count == 1)
+               total_size = 2 * aging_size;
+       else if(data_heap->gen_count == 2)
+               total_size = (gens - 1) * young_size + 2 * aging_size;
+       else if(data_heap->gen_count == 3)
+               total_size = gens * young_size + 2 * aging_size;
+       else
+       {
+               fatal_error("Invalid number of generations",data_heap->gen_count);
+               return NULL; /* can't happen */
+       }
+
+       data_heap->segment = alloc_segment(total_size);
+
+       data_heap->generations = safe_malloc(sizeof(F_ZONE) * gens);
+       data_heap->semispaces = safe_malloc(sizeof(F_ZONE) * gens);
+
+       CELL cards_size = total_size / CARD_SIZE;
+       data_heap->cards = safe_malloc(cards_size);
+       data_heap->cards_end = data_heap->cards + cards_size;
+
+       CELL alloter = data_heap->segment->start;
+
+       alloter = init_zone(&data_heap->semispaces[NURSERY],0,alloter);
+
+       alloter = init_zone(&data_heap->generations[TENURED],aging_size,alloter);
+       alloter = init_zone(&data_heap->semispaces[TENURED],aging_size,alloter);
+
+       int i;
+
+       if(data_heap->gen_count > 2)
+       {
+               alloter = init_zone(&data_heap->generations[AGING],young_size,alloter);
+               alloter = init_zone(&data_heap->semispaces[AGING],young_size,alloter);
+
+               for(i = gens - 3; i >= 0; i--)
+               {
+                       alloter = init_zone(&data_heap->generations[i],
+                               young_size,alloter);
+               }
+       }
+       else
+       {
+               for(i = gens - 2; i >= 0; i--)
+               {
+                       alloter = init_zone(&data_heap->generations[i],
+                               young_size,alloter);
+               }
+       }
+
+       if(alloter != data_heap->segment->end)
+               critical_error("Bug in alloc_data_heap",alloter);
+
+       return data_heap;
+}
+
+F_DATA_HEAP *grow_data_heap(F_DATA_HEAP *data_heap, CELL requested_bytes)
+{
+       CELL new_young_size = (data_heap->young_size * 2) + requested_bytes;
+       CELL new_aging_size = (data_heap->aging_size * 2) + requested_bytes;
+
+       return alloc_data_heap(data_heap->gen_count,
+               new_young_size,
+               new_aging_size);
+}
+
+void dealloc_data_heap(F_DATA_HEAP *data_heap)
+{
+       dealloc_segment(data_heap->segment);
+       free(data_heap->generations);
+       free(data_heap->semispaces);
+       free(data_heap->cards);
+       free(data_heap);
+}
+
+/* Every card stores the offset of the first object in that card, which must be
+cleared when a generation has been cleared */
+void clear_cards(CELL from, CELL to)
+{
+       /* NOTE: reverse order due to heap layout. */
+       F_CARD *last_card = ADDR_TO_CARD(data_heap->generations[from].end);
+       F_CARD *ptr = ADDR_TO_CARD(data_heap->generations[to].start);
+       for(; ptr < last_card; ptr++)
+               clear_card(ptr);
+}
+
+void set_data_heap(F_DATA_HEAP *data_heap_)
+{
+       data_heap = data_heap_;
+       nursery = &data_heap->generations[NURSERY];
+       init_cards_offset();
+       clear_cards(NURSERY,TENURED);
+}
+
+void init_data_heap(CELL gens,
+       CELL young_size,
+       CELL aging_size,
+       bool secure_gc_)
+{
+       set_data_heap(alloc_data_heap(gens,young_size,aging_size));
+
+       extra_roots_region = alloc_segment(getpagesize());
+       extra_roots = extra_roots_region->start - CELLS;
+
+       gc_time = 0;
+       minor_collections = 0;
+       cards_scanned = 0;
+       secure_gc = secure_gc_;
 }
 
 /* Size of the object pointed to by a tagged pointer */
 CELL object_size(CELL tagged)
 {
-       if(tagged == F || TAG(tagged) == FIXNUM_TYPE)
+       if(immediate_p(tagged))
                return 0;
        else
                return untagged_object_size(UNTAG(tagged));
@@ -40,20 +155,29 @@ CELL unaligned_object_size(CELL pointer)
 {
        switch(untag_header(get(pointer)))
        {
-       case WORD_TYPE:
-               return sizeof(F_WORD);
        case ARRAY_TYPE:
        case TUPLE_TYPE:
        case BIGNUM_TYPE:
+               return array_size(array_capacity((F_ARRAY*)pointer));
        case BYTE_ARRAY_TYPE:
+               return byte_array_size(
+                       byte_array_capacity((F_BYTE_ARRAY*)pointer));
+       case BIT_ARRAY_TYPE:
+               return bit_array_size(
+                       bit_array_capacity((F_BIT_ARRAY*)pointer));
+       case FLOAT_ARRAY_TYPE:
+               return float_array_size(
+                       float_array_capacity((F_FLOAT_ARRAY*)pointer));
+       case STRING_TYPE:
+               return string_size(string_capacity((F_STRING*)pointer));
        case QUOTATION_TYPE:
-               return array_size(array_capacity((F_ARRAY*)(pointer)));
+               return sizeof(F_QUOTATION);
+       case WORD_TYPE:
+               return sizeof(F_WORD);
        case HASHTABLE_TYPE:
                return sizeof(F_HASHTABLE);
        case VECTOR_TYPE:
                return sizeof(F_VECTOR);
-       case STRING_TYPE:
-               return string_size(string_capacity((F_STRING*)(pointer)));
        case SBUF_TYPE:
                return sizeof(F_SBUF);
        case RATIO_TYPE:
@@ -68,84 +192,90 @@ CELL unaligned_object_size(CELL pointer)
                return sizeof(F_ALIEN);
        case WRAPPER_TYPE:
                return sizeof(F_WRAPPER);
+       case CURRY_TYPE:
+               return sizeof(F_CURRY);
+       case CALLSTACK_TYPE:
+               return callstack_size(
+                       untag_fixnum_fast(((F_CALLSTACK *)pointer)->length));
        default:
-               critical_error("Cannot determine untagged_object_size",pointer);
+               critical_error("Invalid header",pointer);
                return -1; /* can't happen */
        }
 }
 
-void primitive_size(void)
+DEFINE_PRIMITIVE(size)
 {
-       drepl(tag_fixnum(object_size(dpeek())));
+       box_unsigned_cell(object_size(dpop()));
 }
 
 /* Push memory usage statistics in data heap */
-void primitive_data_room(void)
+DEFINE_PRIMITIVE(data_room)
 {
-       F_ARRAY *a = allot_array(ARRAY_TYPE,gen_count * 2,F);
+       F_ARRAY *a = allot_array(ARRAY_TYPE,data_heap->gen_count * 2,F);
        int gen;
 
-       dpush(tag_fixnum((cards_end - cards) >> 10));
-       dpush(tag_fixnum((prior.limit - prior.base) >> 10));
+       dpush(tag_fixnum((data_heap->cards_end - data_heap->cards) >> 10));
 
-       for(gen = 0; gen < gen_count; gen++)
+       for(gen = 0; gen < data_heap->gen_count; gen++)
        {
-               F_ZONE *z = &generations[gen];
-               set_array_nth(a,gen * 2,tag_fixnum((z->limit - z->here) >> 10));
-               set_array_nth(a,gen * 2 + 1,tag_fixnum((z->limit - z->base) >> 10));
+               F_ZONE *z = &data_heap->generations[gen];
+               set_array_nth(a,gen * 2,tag_fixnum((z->end - z->here) >> 10));
+               set_array_nth(a,gen * 2 + 1,tag_fixnum((z->size) >> 10));
        }
 
        dpush(tag_object(a));
 }
 
 /* Disables GC and activates next-object ( -- obj ) primitive */
-void primitive_begin_scan(void)
+void begin_scan(void)
 {
-       garbage_collection(TENURED,false);
-       heap_scan_ptr = tenured.base;
+       heap_scan_ptr = data_heap->generations[TENURED].start;
        gc_off = true;
 }
 
-/* Push object at heap scan cursor and advance; pushes f when done */
-void primitive_next_object(void)
+DEFINE_PRIMITIVE(begin_scan)
+{
+       data_gc();
+       begin_scan();
+}
+
+CELL next_object(void)
 {
+       if(!gc_off)
+               general_error(ERROR_HEAP_SCAN,F,F,NULL);
+
        CELL value = get(heap_scan_ptr);
        CELL obj = heap_scan_ptr;
        CELL type;
 
-       if(!gc_off)
-               simple_error(ERROR_HEAP_SCAN,F,F);
-
-       if(heap_scan_ptr >= tenured.here)
-       {
-               dpush(F);
-               return;
-       }
+       if(heap_scan_ptr >= data_heap->generations[TENURED].here)
+               return F;
        
        type = untag_header(value);
        heap_scan_ptr += untagged_object_size(heap_scan_ptr);
 
-       if(type <= HEADER_TYPE)
-               dpush(RETAG(obj,type));
-       else
-               dpush(RETAG(obj,OBJECT_TYPE));
+       return RETAG(obj,type <= HEADER_TYPE ? type : OBJECT_TYPE);
+}
+
+/* Push object at heap scan cursor and advance; pushes f when done */
+DEFINE_PRIMITIVE(next_object)
+{
+       dpush(next_object());
 }
 
 /* Re-enables GC */
-void primitive_end_scan(void)
+DEFINE_PRIMITIVE(end_scan)
 {
        gc_off = false;
 }
 
 /* Scan all the objects in the card */
-INLINE void collect_card(F_CARD *ptr, CELL here)
+INLINE void collect_card(F_CARD *ptr, CELL gen, CELL here)
 {
        F_CARD c = *ptr;
        CELL offset = (c & CARD_BASE_MASK);
-       CELL card_scan = (CELL)CARD_TO_ADDR(ptr) + offset;
-       CELL card_end = (CELL)CARD_TO_ADDR(ptr + 1);
 
-       if(offset == 0x7f)
+       if(offset == CARD_BASE_MASK)
        {
                if(c == 0xff)
                        critical_error("bad card",(CELL)ptr);
@@ -153,138 +283,89 @@ INLINE void collect_card(F_CARD *ptr, CELL here)
                        return;
        }
 
+       CELL card_scan = (CELL)CARD_TO_ADDR(ptr) + offset;
+       CELL card_end = (CELL)CARD_TO_ADDR(ptr + 1);
+
        while(card_scan < card_end && card_scan < here)
                card_scan = collect_next(card_scan);
-       
+
        cards_scanned++;
 }
 
 /* Copy all newspace objects referenced from marked cards to the destination */
 INLINE void collect_gen_cards(CELL gen)
 {
-       F_CARD *ptr = ADDR_TO_CARD(generations[gen].base);
-       CELL here = generations[gen].here;
-       F_CARD *last_card = ADDR_TO_CARD(here);
-       
-       if(generations[gen].here == generations[gen].limit)
-               last_card--;
-       
-       for(; ptr <= last_card; ptr++)
+       F_CARD *ptr = ADDR_TO_CARD(data_heap->generations[gen].start);
+       CELL here = data_heap->generations[gen].here;
+       F_CARD *last_card = ADDR_TO_CARD(here - 1);
+
+       CELL mask, unmask;
+
+       /* if we are collecting the nursery, we care about old->nursery pointers
+       but not old->aging pointers */
+       if(collecting_gen == NURSERY)
+       {
+               mask = CARD_POINTS_TO_NURSERY;
+
+               /* after the collection, no old->nursery pointers remain
+               anywhere, but old->aging pointers might remain in tenured
+               space */
+               if(gen == TENURED)
+                       unmask = CARD_POINTS_TO_NURSERY;
+               /* after the collection, all cards in aging space can be
+               cleared */
+               else if(HAVE_AGING_P && gen == AGING)
+                       unmask = CARD_MARK_MASK;
+               else
+               {
+                       critical_error("bug in collect_gen_cards",gen);
+                       return;
+               }
+       }
+       /* if we are collecting aging space into tenured space, we care about
+       all old->nursery and old->aging pointers. no old->aging pointers can
+       remain */
+       else if(HAVE_AGING_P && collecting_gen == AGING)
        {
-               if(card_marked(*ptr))
-                       collect_card(ptr,here);
+               if(collecting_aging_again)
+               {
+                       mask = CARD_POINTS_TO_AGING;
+                       unmask = CARD_MARK_MASK;
+               }
+               /* after we collect aging space into the aging semispace, no
+               old->nursery pointers remain but tenured space might still have
+               pointers to aging space. */
+               else
+               {
+                       mask = CARD_POINTS_TO_AGING;
+                       unmask = CARD_POINTS_TO_NURSERY;
+               }
+       }
+       else
+       {
+               critical_error("bug in collect_gen_cards",gen);
+               return;
        }
-}
 
-/* After all old->new forward references have been copied over, we must unmark
-the cards */
-void unmark_cards(CELL from, CELL to)
-{
-       F_CARD *ptr = ADDR_TO_CARD(generations[from].base);
-       F_CARD *last_card = ADDR_TO_CARD(generations[to].here);
-       if(generations[to].here == generations[to].limit)
-               last_card--;
        for(; ptr <= last_card; ptr++)
-               unmark_card(ptr);
-}
-
-/* Every card stores the offset of the first object in that card, which must be
-cleared when a generation has been cleared */
-void clear_cards(CELL from, CELL to)
-{
-       /* NOTE: reverse order due to heap layout. */
-       F_CARD *last_card = ADDR_TO_CARD(generations[from].limit);
-       F_CARD *ptr = ADDR_TO_CARD(generations[to].base);
-       for(; ptr < last_card; ptr++)
-               clear_card(ptr);
+       {
+               if(*ptr & mask)
+               {
+                       collect_card(ptr,gen,here);
+                       *ptr &= ~unmask;
+               }
+       }
 }
 
 /* Scan cards in all generations older than the one being collected, copying
 old->new references */
-void collect_cards(CELL gen)
+void collect_cards(void)
 {
        int i;
-       for(i = gen + 1; i < gen_count; i++)
+       for(i = collecting_gen + 1; i < data_heap->gen_count; i++)
                collect_gen_cards(i);
 }
 
-CELL init_zone(F_ZONE *z, CELL size, CELL base)
-{
-       z->base = z->here = base;
-       z->limit = z->base + size;
-       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 - (data_heap_start >> CARD_BITS);
-}
-
-/* input parameters must be 8 byte aligned */
-/* the data heap layout is important:
-- two semispaces: tenured and prior
-- younger generations follow
-this is so that we can easily check if a pointer is in some generation or a
-younger one */
-void init_data_heap(CELL gens,
-       CELL young_size,
-       CELL aging_size,
-       bool secure_gc_)
-{
-       int i;
-       CELL alloter;
-
-       CELL total_size = (gens - 1) * young_size + 2 * aging_size;
-       CELL cards_size = total_size / CARD_SIZE;
-
-       gen_count = gens;
-       generations = safe_malloc(sizeof(F_ZONE) * gen_count);
-
-       data_heap_start = (CELL)(alloc_segment(total_size)->start);
-       data_heap_end = data_heap_start + total_size;
-
-       cards = safe_malloc(cards_size);
-       cards_end = cards + cards_size;
-       update_cards_offset();
-
-       alloter = data_heap_start;
-
-       alloter = init_zone(&tenured,aging_size,alloter);
-       alloter = init_zone(&prior,aging_size,alloter);
-
-       for(i = gen_count - 2; i >= 0; i--)
-               alloter = init_zone(&generations[i],young_size,alloter);
-
-       clear_cards(NURSERY,TENURED);
-
-       if(alloter != data_heap_start + total_size)
-               fatal_error("Oops",alloter);
-
-       gc_off = false;
-       gc_time = 0;
-       minor_collections = 0;
-       cards_scanned = 0;
-       secure_gc = secure_gc_;
-
-       data_heap_end = data_heap_start + total_size;
-
-       extra_roots_region = alloc_segment(getpagesize());
-       extra_roots = extra_roots_region->start - CELLS;
-}
-
-void collect_callframe_triple(CELL *callframe,
-       CELL *callframe_scan, CELL *callframe_end)
-{
-       *callframe_scan -= *callframe;
-       *callframe_end -= *callframe;
-       copy_handle(callframe);
-       *callframe_scan += *callframe;
-       *callframe_end += *callframe;
-}
-
 /* Copy all tagged pointers in a range of memory */
 void collect_stack(F_SEGMENT *region, CELL top)
 {
@@ -295,15 +376,27 @@ void collect_stack(F_SEGMENT *region, CELL top)
                copy_handle((CELL*)ptr);
 }
 
-/* The callstack has a special format */
-void collect_callstack(F_SEGMENT *region, CELL top)
+void collect_stack_frame(F_STACK_FRAME *frame)
 {
-       CELL bottom = region->start;
-       CELL ptr;
+       if(frame_type(frame) == QUOTATION_TYPE)
+       {
+               CELL scan = frame->scan - frame->array;
+               copy_handle(&frame->array);
+               frame->scan = scan + frame->array;
+       }
+
+       if(collecting_code)
+               recursive_mark(frame->xt);
+}
 
-       for(ptr = bottom; ptr <= top; ptr += CELLS * 3)
-               collect_callframe_triple((CELL*)ptr,
-                       (CELL*)ptr + 1, (CELL*)ptr + 2);
+/* The base parameter allows us to adjust for a heap-allocated
+callstack snapshot */
+void collect_callstack(F_CONTEXT *stacks)
+{
+       CELL top = (CELL)stacks->callstack_top;
+       CELL bottom = (CELL)stacks->callstack_bottom;
+       CELL base = bottom;
+       iterate_callstack(top,bottom,base,collect_stack_frame);
 }
 
 /* Copy roots over at the start of GC, namely various constants, stacks,
@@ -317,8 +410,6 @@ void collect_roots(void)
        copy_handle(&bignum_zero);
        copy_handle(&bignum_pos_one);
        copy_handle(&bignum_neg_one);
-       
-       collect_callframe_triple(&callframe,&callframe_scan,&callframe_end);
 
        collect_stack(extra_roots_region,extra_roots);
 
@@ -327,18 +418,13 @@ void collect_roots(void)
 
        while(stacks)
        {
-               collect_stack(stacks->data_region,stacks->data);
-               collect_stack(stacks->retain_region,stacks->retain);
-               
-               collect_callstack(stacks->call_region,stacks->call);
+               collect_stack(stacks->datastack_region,stacks->datastack);
+               collect_stack(stacks->retainstack_region,stacks->retainstack);
 
-               if(stacks->next != NULL)
-               {
-                       collect_callframe_triple(&stacks->callframe,
-                               &stacks->callframe_scan,&stacks->callframe_end);
-               }
+               copy_handle(&stacks->catchstack_save);
+               copy_handle(&stacks->current_callback_save);
 
-               copy_handle(&stacks->catch_save);
+               collect_callstack(stacks);
 
                stacks = stacks->next;
        }
@@ -351,21 +437,25 @@ void collect_roots(void)
 INLINE void *copy_untagged_object(void *pointer, CELL size)
 {
        void *newpointer;
-       if(newspace->here + size >= newspace->limit)
+       if(newspace->here + size >= newspace->end)
                longjmp(gc_jmp,1);
+       allot_barrier(newspace->here);
        newpointer = allot_zone(newspace,size);
        memcpy(newpointer,pointer,size);
        return newpointer;
 }
 
-INLINE CELL copy_object_impl(CELL pointer)
+INLINE void forward_object(CELL pointer, CELL newpointer)
 {
-       CELL newpointer = (CELL)copy_untagged_object((void*)UNTAG(pointer),
-               object_size(pointer));
-
-       /* install forwarding pointer */
        put(UNTAG(pointer),RETAG(newpointer,GC_COLLECTED));
+}
 
+INLINE CELL copy_object_impl(CELL pointer)
+{
+       CELL newpointer = (CELL)copy_untagged_object(
+               (void*)UNTAG(pointer),
+               object_size(pointer));
+       forward_object(pointer,newpointer);
        return newpointer;
 }
 
@@ -390,26 +480,25 @@ CELL resolve_forwarding(CELL untagged, CELL tag)
 If the object has already been copied, return the forwarding
 pointer address without copying anything; otherwise, install
 a new forwarding pointer. */
-CELL copy_object(CELL pointer)
+INLINE CELL copy_object(CELL pointer)
 {
-       CELL tag;
-       CELL header;
-
-       if(pointer == F)
-               return F;
-
-       tag = TAG(pointer);
+       CELL tag = TAG(pointer);
+       CELL header = get(UNTAG(pointer));
 
-       if(tag == FIXNUM_TYPE)
-               return pointer;
-
-       header = get(UNTAG(pointer));
        if(TAG(header) == GC_COLLECTED)
                return resolve_forwarding(UNTAG(header),tag);
        else
                return RETAG(copy_object_impl(pointer),tag);
 }
 
+void copy_handle(CELL *handle)
+{
+       CELL pointer = *handle;
+
+       if(!immediate_p(pointer) && should_copy(pointer))
+               *handle = copy_object(pointer);
+}
+
 /* The number of cells from the start of the object which should be scanned by
 the GC. Some types have a binary payload at the end (string, word, DLL) which
 we ignore. */
@@ -421,53 +510,67 @@ CELL binary_payload_start(CELL pointer)
        case STRING_TYPE:
        case FLOAT_TYPE:
        case BYTE_ARRAY_TYPE:
+       case BIT_ARRAY_TYPE:
+       case FLOAT_ARRAY_TYPE:
        case BIGNUM_TYPE:
+       case CALLSTACK_TYPE:
                return 0;
        /* these objects have some binary data at the end */
        case WORD_TYPE:
                return sizeof(F_WORD) - CELLS;
        case ALIEN_TYPE:
+               return CELLS * 3;
        case DLL_TYPE:
                return CELLS * 2;
+       case QUOTATION_TYPE:
+               return sizeof(F_QUOTATION) - CELLS;
        /* everything else consists entirely of pointers */
        default:
                return unaligned_object_size(pointer);
        }
 }
 
-/* Every object has a regular representation in the runtime, which makes GC
-much simpler. Every slot of the object until binary_payload_start is a pointer
-to some other object. */
-INLINE void collect_object(CELL start)
+void collect_callstack_object(F_CALLSTACK *callstack)
 {
-       CELL scan = start;
-       CELL payload_start = binary_payload_start(scan);
-       CELL end = scan + payload_start;
+       iterate_callstack_object(callstack,collect_stack_frame);
+}
 
-       scan += CELLS;
+CELL collect_next(CELL scan)
+{
+       do_slots(scan,copy_handle);
 
-       while(scan < end)
-       {
-               copy_handle((CELL*)scan);
-               scan += CELLS;
-       }
+       /* Special behaviors */
+       F_WORD *word;
+       F_QUOTATION *quot;
+       F_CALLSTACK *stack;
 
-       /* It is odd to put this hook here, but this is the only special case
-       made for any type of object by the GC. If code GC is being performed,
-       compiled code blocks referenced by this word must be marked. */
-       if(collecting_code && object_type(start) == WORD_TYPE)
+       switch(object_type(scan))
        {
-               F_WORD *word = (F_WORD *)start;
-               if(word->compiledp != F)
+       case WORD_TYPE:
+               word = (F_WORD *)scan;
+               if(collecting_code && word->compiledp != F)
                        recursive_mark(word->xt);
+               break;
+       case QUOTATION_TYPE:
+               quot = (F_QUOTATION *)scan;
+               if(collecting_code && quot->xt != NULL)
+                       recursive_mark(quot->xt);
+               break;
+       case CALLSTACK_TYPE:
+               stack = (F_CALLSTACK *)scan;
+               collect_callstack_object(stack);
+               break;
        }
+
+       return scan + untagged_object_size(scan);
 }
 
-CELL collect_next(CELL scan)
+INLINE void reset_generation(CELL i)
 {
-       CELL size = untagged_object_size(scan);
-       collect_object(scan);
-       return scan + size;
+       F_ZONE *z = &data_heap->generations[i];
+       z->here = z->start;
+       if(secure_gc)
+               memset((void*)z->start,69,z->size);
 }
 
 /* After garbage collection, any generations which are now empty need to have
@@ -475,124 +578,172 @@ their allocation pointers and cards reset. */
 void reset_generations(CELL from, CELL to)
 {
        CELL i;
-
-       for(i = from; i <= to; i++)
-       {
-               F_ZONE *z = &generations[i];
-               z->here = z->base;
-               if(secure_gc)
-                       memset((void*)z->base,69,z->limit - z->base);
-       }
-
+       for(i = from; i <= to; i++) reset_generation(i);
        clear_cards(from,to);
 }
 
 /* Prepare to start copying reachable objects into an unused zone */
-void begin_gc(CELL gen, bool code_gc)
+void begin_gc(CELL requested_bytes)
 {
-       collecting_gen = gen;
-       collecting_gen_start = generations[gen].base;
-       collecting_code = code_gc;
+       if(growing_data_heap)
+       {
+               if(collecting_gen != TENURED)
+                       critical_error("Invalid parameters to begin_gc",0);
 
-       if(gen == TENURED)
+               old_data_heap = data_heap;
+               set_data_heap(grow_data_heap(old_data_heap,requested_bytes));
+               newspace = &data_heap->generations[collecting_gen];
+       }
+       else if(collecting_accumulation_gen_p())
        {
-               /* when collecting the oldest generation, rotate it
+               /* when collecting one of these generations, rotate it
                with the semispace */
-               F_ZONE z = generations[gen];
-               generations[gen] = prior;
-               prior = z;
-               generations[gen].here = generations[gen].base;
-               newspace = &generations[gen];
-               clear_cards(TENURED,TENURED);
+               F_ZONE z = data_heap->generations[collecting_gen];
+               data_heap->generations[collecting_gen] = data_heap->semispaces[collecting_gen];
+               data_heap->semispaces[collecting_gen] = z;
+               reset_generation(collecting_gen);
+               newspace = &data_heap->generations[collecting_gen];
+               clear_cards(collecting_gen,collecting_gen);
        }
        else
        {
                /* when collecting a younger generation, we copy
                reachable objects to the next oldest generation,
                so we set the newspace so the next generation. */
-               newspace = &generations[gen + 1];
+               newspace = &data_heap->generations[collecting_gen + 1];
        }
 }
 
-void end_gc()
+void major_gc_message(void)
+{
+       fprintf(stderr,"*** %s GC (%ld minor, %ld cards)\n",
+               collecting_code ? "Code and data" : "Data",
+               minor_collections,cards_scanned);
+       fflush(stderr);
+       minor_collections = 0;
+       cards_scanned = 0;
+}
+
+void end_gc(void)
 {
-       if(collecting_gen == TENURED)
+       if(growing_data_heap)
+       {
+               dealloc_data_heap(old_data_heap);
+               old_data_heap = NULL;
+               growing_data_heap = false;
+
+               fprintf(stderr,"*** Data heap resized to %lu bytes\n",
+                       data_heap->segment->size);
+       }
+
+       if(collecting_accumulation_gen_p())
        {
-               /* 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,"*** %s GC (%ld minor, %ld cards)\n",
-                       collecting_code ? "Code and data" : "Data",
-                       minor_collections,cards_scanned);
-               fflush(stderr);
-               minor_collections = 0;
-               cards_scanned = 0;
+               /* all younger generations except are now empty.
+               if collecting_gen == NURSERY here, we only have 1 generation;
+               old-school Cheney collector */
+               if(collecting_gen != NURSERY)
+                       reset_generations(NURSERY,collecting_gen - 1);
+
+               if(collecting_gen == TENURED)
+                       major_gc_message();
+               else if(HAVE_AGING_P && collecting_gen == AGING)
+                       minor_collections++;
        }
        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(collecting_gen + 1,collecting_gen + 1);
                /* all generations up to and including the one
                collected are now empty */
                reset_generations(NURSERY,collecting_gen);
 
                minor_collections++;
        }
-       
+
        if(collecting_code)
        {
                /* now that all reachable code blocks have been marked,
                deallocate the rest */
-               free_unmarked(&compiling);
+               free_unmarked(&code_heap);
        }
+
+       collecting_aging_again = false;
 }
 
-/* Collect gen and all younger generations */
-void garbage_collection(CELL gen, bool code_gc)
+/* Collect gen and all younger generations.
+If growing_data_heap_ is true, we must grow the data heap to such a size that
+an allocation of requested_bytes won't fail */
+void garbage_collection(CELL gen,
+       bool code_gc,
+       bool growing_data_heap_,
+       CELL requested_bytes)
 {
-       s64 start = current_millis();
-       CELL scan;
-
        if(gc_off)
+       {
                critical_error("GC disabled",gen);
+               return;
+       }
+
+       s64 start = current_millis();
+
+       performing_gc = true;
+       collecting_code = code_gc;
+       growing_data_heap = growing_data_heap_;
+       collecting_gen = gen;
 
        /* we come back here if a generation is full */
        if(setjmp(gc_jmp))
        {
-               if(gen == TENURED)
+               /* We have no older generations we can try collecting, so we
+               resort to growing the data heap */
+               if(collecting_gen == TENURED)
+               {
+                       growing_data_heap = true;
+
+                       /* see the comment in unmark_marked() */
+                       if(collecting_code)
+                               unmark_marked(&code_heap);
+               }
+               /* we try collecting AGING space twice before going on to
+               collect TENURED */
+               else if(HAVE_AGING_P
+                       && collecting_gen == AGING
+                       && !collecting_aging_again)
                {
-                       /* oops, out of memory */
-                       critical_error("Out of memory in GC",0);
+                       collecting_aging_again = true;
                }
+               /* Collect the next oldest generation */
                else
-                       gen++;
+               {
+                       collecting_gen++;
+               }
        }
 
-       begin_gc(gen,code_gc);
+       begin_gc(requested_bytes);
 
        /* initialize chase pointer */
-       scan = newspace->here;
+       CELL scan = newspace->here;
 
        /* collect objects referenced from stacks and environment */
        collect_roots();
        
        /* collect objects referenced from older generations */
-       collect_cards(gen);
+       collect_cards();
 
-       if(!code_gc)
+       if(!collecting_code)
        {
-               /* if we are doing code GC, then we will copy over literals
-               from any code block which gets marked as live. if we are not
-               doing code GC, just consider all literals as roots. */
-               collect_literals();
+               /* don't scan code heap unless it has pointers to this
+               generation or younger */
+               if(collecting_gen >= last_code_heap_scan)
+               {
+                       /* if we are doing code GC, then we will copy over
+                       literals from any code block which gets marked as live.
+                       if we are not doing code GC, just consider all literals
+                       as roots. */
+                       collect_literals();
+                       if(collecting_accumulation_gen_p())
+                               last_code_heap_scan = collecting_gen;
+                       else
+                               last_code_heap_scan = collecting_gen + 1;
+               }
        }
 
        while(scan < newspace->here)
@@ -601,20 +752,21 @@ void garbage_collection(CELL gen, bool code_gc)
        end_gc();
 
        gc_time += (current_millis() - start);
+       performing_gc = false;
+}
+
+void data_gc(void)
+{
+       garbage_collection(TENURED,false,false,0);
 }
 
-void primitive_data_gc(void)
+DEFINE_PRIMITIVE(data_gc)
 {
-       F_FIXNUM gen = unbox_signed_cell();
-       if(gen <= NURSERY)
-               gen = NURSERY;
-       else if(gen >= TENURED)
-               gen = TENURED;
-       garbage_collection(gen,false);
+       data_gc();
 }
 
 /* Push total time spent on GC */
-void primitive_gc_time(void)
+DEFINE_PRIMITIVE(gc_time)
 {
        box_unsigned_8(gc_time);
 }
@@ -623,3 +775,25 @@ void simple_gc(void)
 {
        maybe_gc(0);
 }
+
+DEFINE_PRIMITIVE(become)
+{
+       F_ARRAY *new_objects = untag_array(dpop());
+       F_ARRAY *old_objects = untag_array(dpop());
+
+       CELL capacity = array_capacity(new_objects);
+       if(capacity != array_capacity(old_objects))
+               critical_error("bad parameters to become",0);
+
+       CELL i;
+       
+       for(i = 0; i < capacity; i++)
+       {
+               CELL old_obj = array_nth(old_objects,i);
+               CELL new_obj = array_nth(new_objects,i);
+
+               forward_object(old_obj,new_obj);
+       }
+
+       data_gc();
+}