]> gitweb.factorcode.org Git - factor.git/commitdiff
Runtime code cleanups, implemented mark and sweep code GC
authorslava <slava@factorcode.org>
Tue, 26 Sep 2006 22:44:18 +0000 (22:44 +0000)
committerslava <slava@factorcode.org>
Tue, 26 Sep 2006 22:44:18 +0000 (22:44 +0000)
20 files changed:
Makefile
TODO.FACTOR.txt
library/compiler/inference/known-words.factor
library/tools/memory.factor
vm/code_gc.c [new file with mode: 0644]
vm/code_gc.h [new file with mode: 0644]
vm/compiler.c
vm/compiler.h
vm/data_gc.c [new file with mode: 0644]
vm/data_gc.h [new file with mode: 0644]
vm/debug.c
vm/factor.c
vm/factor.h
vm/heap.c [deleted file]
vm/heap.h [deleted file]
vm/image.c
vm/memory.c [deleted file]
vm/memory.h [deleted file]
vm/run.c
vm/run.h

index 7d40904cdc97590ce40617f315b4b5db460e4865..ddf59c29c55117898f8478624f8d85e529cf392f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -32,11 +32,11 @@ OBJS = $(PLAF_OBJS) \
        vm/debug.o \
        vm/factor.o \
        vm/ffi_test.o \
-       vm/heap.o \
        vm/image.o \
        vm/io.o \
        vm/math.o \
-       vm/memory.o \
+       vm/data_gc.o \
+       vm/code_gc.o \
        vm/primitives.o \
        vm/run.o \
        vm/stack.o \
index 6768f4ec4e4d19beea1a2ad5eaa7e97a53c5f7ef..a3e0f66a5ab27c2c9a8b4d18319e6e75e23326b0 100644 (file)
@@ -3,7 +3,6 @@
 - signal 4 on datastack underflow on mac intel??
 - test alien-indirect
 - code GC:
-  - discard the free block at the end of the code heap on save
   - minor GC takes too long now, card mark
 
 + ui:
index d9405239b8decabe76aa2cfd3f515756153820d9..e7f1010142c8a4101e5c69593f01b317210b714b 100644 (file)
@@ -235,7 +235,7 @@ t over set-effect-terminated?
 \ setenv { object fixnum } { } <effect> "infer-effect" set-word-prop
 \ stat { string } { object } <effect> "infer-effect" set-word-prop
 \ (directory) { string } { array } <effect> "infer-effect" set-word-prop
-\ gc { integer } { } <effect> "infer-effect" set-word-prop
+\ gc { integer object } { } <effect> "infer-effect" set-word-prop
 \ gc-time { } { integer } <effect> "infer-effect" set-word-prop
 \ save-image { string } { } <effect> "infer-effect" set-word-prop
 \ exit { integer } { } <effect> "infer-effect" set-word-prop
index 1098f153e268446213a53a50822224c0572af013..a3760030704d63d9d3c555b29b6b54ac05b4d9c3 100644 (file)
@@ -5,7 +5,7 @@ USING: arrays errors generic hashtables io kernel
 kernel-internals math namespaces parser prettyprint sequences
 strings styles vectors words ;
 
-: full-gc ( -- ) generations 1- gc ;
+: full-gc ( -- ) generations 1- gc ;
 
 ! Printing an overview of heap usage.
 
diff --git a/vm/code_gc.c b/vm/code_gc.c
new file mode 100644 (file)
index 0000000..7ebaf32
--- /dev/null
@@ -0,0 +1,234 @@
+#include "factor.h"
+
+/* This malloc-style heap code is reasonably generic. Maybe in the future, it
+will be used for the data heap too, if we ever get incremental
+mark/sweep/compact GC. */
+void new_heap(HEAP *heap, CELL size)
+{
+       heap->base = (CELL)(alloc_bounded_block(size)->start);
+       if(heap->base == 0)
+               fatal_error("Cannot allocate code heap",size);
+       heap->limit = heap->base + size;
+       heap->free_list = NULL;
+}
+
+void init_code_heap(CELL size)
+{
+       new_heap(&compiling,size);
+}
+
+INLINE void update_free_list(HEAP *heap, F_BLOCK *prev, F_BLOCK *next_free)
+{
+       if(prev)
+               prev->next_free = next_free;
+       else
+               heap->free_list = next_free;
+}
+
+/* called after reading the code heap from the image file. we must build the
+free list, and add a large free block from compiling.base + size to
+compiling.limit. */
+void build_free_list(HEAP *heap, CELL size)
+{
+       F_BLOCK *prev = NULL;
+       F_BLOCK *scan = (F_BLOCK *)heap->base;
+       F_BLOCK *end = (F_BLOCK *)(heap->base + size);
+
+       while(scan && scan < end)
+       {
+               if(scan->status == B_FREE)
+               {
+                       update_free_list(heap,prev,scan);
+                       prev = scan;
+               }
+
+               scan = next_block(heap,scan);
+       }
+
+       if((CELL)(end + 1) <= heap->limit)
+       {
+               end->status = B_FREE;
+               end->next_free = NULL;
+               end->size = heap->limit - (CELL)end;
+       }
+       else
+       {
+               end = NULL;
+
+               if(prev)
+                       prev->size = heap->limit - (CELL)prev;
+       }
+
+       update_free_list(heap,prev,end);
+}
+
+CELL heap_allot(HEAP *heap, CELL size)
+{
+       F_BLOCK *prev = NULL;
+       F_BLOCK *scan = heap->free_list;
+
+       while(scan)
+       {
+               CELL this_size = scan->size - sizeof(F_BLOCK);
+
+               if(this_size < size)
+               {
+                       prev = scan;
+                       scan = scan->next_free;
+                       continue;
+               }
+
+               /* we found a candidate block */
+               F_BLOCK *next_free;
+
+               if(this_size - size <= sizeof(F_BLOCK))
+               {
+                       /* too small to be split */
+                       next_free = scan->next_free;
+               }
+               else
+               {
+                       /* split the block in two */
+                       CELL new_size = size + sizeof(F_BLOCK);
+                       F_BLOCK *split = (F_BLOCK *)((CELL)scan + new_size);
+                       split->status = B_FREE;
+                       split->size = scan->size - new_size;
+                       split->next_free = scan->next_free;
+                       scan->size = new_size;
+                       next_free = split;
+               }
+
+               /* update the free list */
+               update_free_list(heap,prev,next_free);
+
+               /* this is our new block */
+               scan->status = B_ALLOCATED;
+
+               return (CELL)(scan + 1);
+       }
+
+       if(heap->base == 0)
+               critical_error("Code heap is full",size);
+
+       return 0; /* can't happen */
+}
+
+/* free blocks which are allocated and not marked */
+void free_unmarked(HEAP *heap)
+{
+       F_BLOCK *prev = NULL;
+       F_BLOCK *scan = (F_BLOCK *)heap->base;
+
+       while(scan)
+       {
+               if(scan->status == B_ALLOCATED)
+               {
+                       /* merge blocks? */
+                       if(next_block(heap,prev) == scan)
+                               prev->size += scan->size;
+                       else
+                       {
+                               scan->status = B_FREE;
+                               update_free_list(heap,prev,scan);
+                               prev = scan;
+                       }
+               }
+
+               scan = next_block(heap,scan);
+       }
+
+       if(prev)
+               prev->next_free = NULL;
+}
+
+CELL heap_free_space(HEAP *heap)
+{
+       CELL size = 0;
+       F_BLOCK *scan = (F_BLOCK *)heap->base;
+
+       while(scan)
+       {
+               if(scan->status == B_FREE)
+                       size += scan->size;
+               scan = next_block(heap,scan);
+       }
+
+       return size;
+}
+
+CELL heap_size(HEAP *heap)
+{
+       CELL start = heap->base;
+       F_BLOCK *scan = (F_BLOCK *)start;
+       while(next_block(heap,scan))
+               scan = next_block(heap,scan);
+       return (CELL)scan - (CELL)start;
+}
+
+void iterate_code_heap(CODE_HEAP_ITERATOR iter)
+{
+       F_BLOCK *scan = (F_BLOCK *)compiling.base;
+
+       while(scan)
+       {
+               if(scan->status != B_FREE)
+                       iterate_code_heap_step((F_COMPILED *)(scan + 1),iter);
+               scan = next_block(&compiling,scan);
+       }
+}
+
+void collect_literals_step(F_COMPILED *relocating, CELL code_start,
+       CELL reloc_start, CELL literal_start, CELL words_start, CELL words_end)
+{
+       CELL scan;
+
+       CELL literal_end = literal_start + relocating->literal_length;
+
+       for(scan = literal_start; scan < literal_end; scan += CELLS)
+               copy_handle((CELL*)scan);
+
+       if(!relocating->finalized)
+       {
+               for(scan = words_start; scan < words_end; scan += CELLS)
+                       copy_handle((CELL*)scan);
+       }
+}
+
+void collect_literals(void)
+{
+       iterate_code_heap(collect_literals_step);
+}
+
+void mark_sweep_step(F_COMPILED *compiled, CELL code_start,
+       CELL reloc_start, CELL literal_start, CELL words_start, CELL words_end)
+{
+       CELL scan;
+
+       if(compiled->finalized)
+       {
+               for(scan = words_start; scan < words_end; scan += CELLS)
+                       mark_and_sweep(get(scan));
+       }
+}
+
+void mark_and_sweep(CELL xt)
+{
+       F_BLOCK *block = xt_to_block(xt);
+
+       if(block->status == B_MARKED)
+               return;
+       else if(block->status == B_FREE)
+               critical_error("Marking a free block",(CELL)block);
+
+       block->status = B_MARKED;
+
+       F_COMPILED *compiled = xt_to_compiled(xt);
+       iterate_code_heap_step(compiled,collect_literals_step);
+       iterate_code_heap_step(compiled,mark_sweep_step);
+}
+
+void primitive_code_room(void)
+{
+       box_unsigned_cell(heap_free_space(&compiling));
+       box_unsigned_cell(compiling.limit - compiling.base);
+}
diff --git a/vm/code_gc.h b/vm/code_gc.h
new file mode 100644 (file)
index 0000000..46f414a
--- /dev/null
@@ -0,0 +1,82 @@
+typedef enum
+{
+       B_FREE,
+       B_ALLOCATED,
+       B_MARKED
+} F_BLOCK_STATUS;
+
+typedef struct _F_BLOCK
+{
+       F_BLOCK_STATUS status;
+       CELL size;
+       struct _F_BLOCK *next_free;
+} F_BLOCK;
+
+typedef struct {
+       CELL base;
+       CELL limit;
+       F_BLOCK *free_list;
+} HEAP;
+
+void new_heap(HEAP *heap, CELL size);
+void build_free_list(HEAP *heap, CELL size);
+CELL heap_allot(HEAP *heap, CELL size);
+void free_unmarked(HEAP *heap);
+CELL heap_free_space(HEAP *heap);
+CELL heap_size(HEAP *heap);
+
+INLINE F_BLOCK *next_block(HEAP *heap, F_BLOCK *block)
+{
+       CELL next = ((CELL)block + block->size);
+       if(next == heap->limit)
+               return NULL;
+       else
+               return (F_BLOCK *)next;
+}
+
+/* compiled code */
+HEAP compiling;
+
+/* The compiled code heap is structured into blocks. */
+typedef struct
+{
+       CELL code_length; /* # bytes */
+       CELL reloc_length; /* # bytes */
+       CELL literal_length; /* # bytes */
+       CELL words_length; /* # bytes */
+       CELL finalized; /* has finalize_code_block() been called on this yet? */
+} F_COMPILED;
+
+typedef void (*CODE_HEAP_ITERATOR)(F_COMPILED *compiled, CELL code_start,
+       CELL reloc_start, CELL literal_start, CELL words_start, CELL words_end);
+
+void init_code_heap(CELL size);
+
+void iterate_code_heap(CODE_HEAP_ITERATOR iter);
+
+void collect_literals(void);
+
+void mark_and_sweep(CELL xt);
+
+void primitive_code_room(void);
+
+INLINE void iterate_code_heap_step(F_COMPILED *compiled, CODE_HEAP_ITERATOR iter)
+{
+       CELL code_start = (CELL)(compiled + 1);
+       CELL reloc_start = code_start + compiled->code_length;
+       CELL literal_start = reloc_start + compiled->reloc_length;
+       CELL words_start = literal_start + compiled->literal_length;
+       CELL words_end = words_start + compiled->words_length;
+
+       iter(compiled,code_start,reloc_start,literal_start,words_start,words_end);
+}
+
+INLINE F_BLOCK *xt_to_block(CELL xt)
+{
+       return (F_BLOCK *)(xt - sizeof(F_BLOCK) - sizeof(F_COMPILED));
+}
+
+INLINE F_COMPILED *xt_to_compiled(CELL xt)
+{
+       return (F_COMPILED *)(xt - sizeof(F_COMPILED));
+}
index 0495612e3a191216fe78f33b299a4469602ac108..bd2343d7cd7e9f75644410dca7ae49f6d55f4a36 100644 (file)
@@ -1,32 +1,5 @@
 #include "factor.h"
 
-void init_compiler(CELL size)
-{
-       new_heap(&compiling,size);
-}
-
-INLINE void iterate_code_heap_step(F_COMPILED *compiled, CODE_HEAP_ITERATOR iter)
-{
-       CELL code_start = (CELL)(compiled + 1);
-       CELL reloc_start = code_start + compiled->code_length;
-       CELL literal_start = reloc_start + compiled->reloc_length;
-       CELL words_start = literal_start + compiled->literal_length;
-
-       iter(compiled,code_start,reloc_start,literal_start,words_start);
-}
-
-void iterate_code_heap(CODE_HEAP_ITERATOR iter)
-{
-       F_BLOCK *scan = (F_BLOCK *)compiling.base;
-
-       while(scan)
-       {
-               if(scan->status != B_FREE)
-                       iterate_code_heap_step((F_COMPILED *)(scan + 1),iter);
-               scan = next_block(&compiling,scan);
-       }
-}
-
 void undefined_symbol(void)
 {
        general_error(ERROR_UNDEFINED_SYMBOL,F,F,true);
@@ -139,7 +112,7 @@ void apply_relocation(F_REL *rel,
 }
 
 void relocate_code_block(F_COMPILED *relocating, CELL code_start,
-       CELL reloc_start, CELL literal_start, CELL words_start)
+       CELL reloc_start, CELL literal_start, CELL words_start, CELL words_end)
 {
        F_REL *rel = (F_REL *)reloc_start;
        F_REL *rel_end = (F_REL *)literal_start;
@@ -150,10 +123,8 @@ void relocate_code_block(F_COMPILED *relocating, CELL code_start,
 }
 
 void finalize_code_block(F_COMPILED *relocating, CELL code_start,
-       CELL reloc_start, CELL literal_start, CELL words_start)
+       CELL reloc_start, CELL literal_start, CELL words_start, CELL words_end)
 {
-       CELL words_end = words_start + relocating->words_length;
-
        CELL scan;
 
        for(scan = words_start; scan < words_end; scan += CELLS)
@@ -162,34 +133,11 @@ void finalize_code_block(F_COMPILED *relocating, CELL code_start,
        relocating->finalized = true;
 
        relocate_code_block(relocating,code_start,reloc_start,
-               literal_start,words_start);
+               literal_start,words_start,words_end);
 
        flush_icache(code_start,reloc_start - code_start);
 }
 
-void collect_literals_step(F_COMPILED *relocating, CELL code_start,
-       CELL reloc_start, CELL literal_start, CELL words_start)
-{
-       CELL scan;
-
-       CELL literal_end = literal_start + relocating->literal_length;
-       CELL words_end = words_start + relocating->words_length;
-
-       for(scan = literal_start; scan < literal_end; scan += CELLS)
-               copy_handle((CELL*)scan);
-
-       for(scan = words_start; scan < words_end; scan += CELLS)
-       {
-               if(!relocating->finalized)
-                       copy_handle((CELL*)scan);
-       }
-}
-
-void collect_literals(void)
-{
-       iterate_code_heap(collect_literals_step);
-}
-
 void deposit_integers(CELL here, F_VECTOR *vector, CELL format)
 {
        CELL count = untag_fixnum_fast(vector->top);
@@ -291,12 +239,6 @@ void primitive_finalize_compile(void)
        {
                F_ARRAY *pair = untag_array(get(AREF(array,i)));
                CELL xt = to_cell(get(AREF(pair,1)));
-               iterate_code_heap_step((F_COMPILED*)xt - 1,finalize_code_block);
+               iterate_code_heap_step(xt_to_compiled(xt),finalize_code_block);
        }
 }
-
-void primitive_code_room(void)
-{
-       box_unsigned_cell(heap_free_space(&compiling));
-       box_unsigned_cell(compiling.limit - compiling.base);
-}
index 5ad5f2afaa41701b7f5236905851ec6de8729c77..a717a7a3107d06b43b20b5a8567937f8b578bbf0 100644 (file)
@@ -1,21 +1,3 @@
-/* compiled code */
-HEAP compiling;
-
-/* The compiled code heap is structured into blocks. */
-typedef struct
-{
-       CELL code_length; /* # bytes */
-       CELL reloc_length; /* # bytes */
-       CELL literal_length; /* # bytes */
-       CELL words_length; /* # bytes */
-       CELL finalized; /* has finalize_code_block() been called on this yet? */
-} F_COMPILED;
-
-typedef void (*CODE_HEAP_ITERATOR)(F_COMPILED *compiled, CELL code_start,
-       CELL reloc_start, CELL literal_start, CELL words_start);
-
-void iterate_code_heap(CODE_HEAP_ITERATOR iter);
-
 typedef enum {
        /* arg is a primitive number */
        RT_PRIMITIVE,
@@ -57,9 +39,6 @@ typedef struct {
 } F_REL;
 
 void relocate_code_block(F_COMPILED *relocating, CELL code_start,
-       CELL reloc_start, CELL literal_start, CELL words_start);
-void collect_literals(void);
-void init_compiler(CELL size);
+       CELL reloc_start, CELL literal_start, CELL words_start, CELL words_end);
 void primitive_add_compiled_block(void);
 void primitive_finalize_compile(void);
-void primitive_code_room(void);
diff --git a/vm/data_gc.c b/vm/data_gc.c
new file mode 100644 (file)
index 0000000..316d7e8
--- /dev/null
@@ -0,0 +1,588 @@
+#include "factor.h"
+
+/* this function tests if a given faulting location is in a poison page. The
+page address is taken from area + round_up_to_page_size(area_size) + 
+ pagesize*offset */
+bool in_page(void *fault, void *i_area, CELL area_size, int offset)
+{
+       const int pagesize = getpagesize();
+       intptr_t area = (intptr_t) i_area;
+       area += pagesize * ((area_size + (pagesize - 1)) / pagesize);
+       area += offset * pagesize;
+
+       const int page = area / pagesize;
+       const int fault_page = (intptr_t)fault / pagesize;
+       return page == fault_page;
+}
+
+void *safe_malloc(size_t size)
+{
+       void *ptr = malloc(size);
+       if(ptr == 0)
+               fatal_error("malloc() failed", 0);
+       return ptr;
+}
+
+CELL object_size(CELL tagged)
+{
+       if(tagged == F || TAG(tagged) == FIXNUM_TYPE)
+               return 0;
+       else
+               return untagged_object_size(UNTAG(tagged));
+}
+
+CELL untagged_object_size(CELL pointer)
+{
+       return align8(unaligned_object_size(pointer));
+}
+
+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:
+       case BYTE_ARRAY_TYPE:
+       case QUOTATION_TYPE:
+               return array_size(array_capacity((F_ARRAY*)(pointer)));
+       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:
+               return sizeof(F_RATIO);
+       case FLOAT_TYPE:
+               return sizeof(F_FLOAT);
+       case COMPLEX_TYPE:
+               return sizeof(F_COMPLEX);
+       case DLL_TYPE:
+               return sizeof(DLL);
+       case ALIEN_TYPE:
+               return sizeof(ALIEN);
+       case WRAPPER_TYPE:
+               return sizeof(F_WRAPPER);
+       default:
+               critical_error("Cannot determine untagged_object_size",pointer);
+               return -1; /* can't happen */
+       }
+}
+
+void primitive_size(void)
+{
+       drepl(tag_fixnum(object_size(dpeek())));
+}
+
+/* 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. */
+CELL binary_payload_start(CELL pointer)
+{
+       switch(untag_header(get(pointer)))
+       {
+       /* these objects do not refer to other objects at all */
+       case STRING_TYPE:
+       case FLOAT_TYPE:
+       case BYTE_ARRAY_TYPE:
+       case BIGNUM_TYPE:
+               return 0;
+       /* these objects have some binary data at the end */
+       case WORD_TYPE:
+               return sizeof(F_WORD) - CELLS;
+       case ALIEN_TYPE:
+       case DLL_TYPE:
+               return CELLS * 2;
+       /* everything else consists entirely of pointers */
+       default:
+               return unaligned_object_size(pointer);
+       }
+}
+
+void primitive_data_room(void)
+{
+       F_ARRAY *a = array(ARRAY_TYPE,gen_count,F);
+       int gen;
+       box_unsigned_cell(cards_end - cards);
+       box_unsigned_cell(prior.limit - prior.base);
+       for(gen = 0; gen < gen_count; gen++)
+       {
+               ZONE *z = &generations[gen];
+               put(AREF(a,gen),make_array_2(tag_cell(z->limit - z->here),
+                       tag_cell(z->limit - z->base)));
+       }
+       dpush(tag_object(a));
+}
+
+/* Disables GC and activates next-object ( -- obj ) primitive */
+void primitive_begin_scan(void)
+{
+       garbage_collection(TENURED,false);
+       heap_scan_ptr = tenured.base;
+       heap_scan = true;
+}
+
+/* Push object at heap scan cursor and advance; pushes f when done */
+void primitive_next_object(void)
+{
+       CELL value = get(heap_scan_ptr);
+       CELL obj = heap_scan_ptr;
+       CELL type;
+
+       if(!heap_scan)
+               general_error(ERROR_HEAP_SCAN,F,F,true);
+
+       if(heap_scan_ptr >= tenured.here)
+       {
+               dpush(F);
+               return;
+       }
+       
+       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));
+}
+
+/* Re-enables GC */
+void primitive_end_scan(void)
+{
+       heap_scan = false;
+}
+
+/* scan all the objects in the card */
+INLINE void collect_card(CARD *ptr, CELL here)
+{
+       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(c == 0xff)
+                       critical_error("bad card",(CELL)ptr);
+               else
+                       return;
+       }
+
+       while(card_scan < card_end && card_scan < here)
+               card_scan = collect_next(card_scan);
+       
+       cards_scanned++;
+}
+
+INLINE void collect_gen_cards(CELL gen)
+{
+       CARD *ptr = ADDR_TO_CARD(generations[gen].base);
+       CELL here = generations[gen].here;
+       CARD *last_card = ADDR_TO_CARD(here);
+       
+       if(generations[gen].here == generations[gen].limit)
+               last_card--;
+       
+       for(; ptr <= last_card; ptr++)
+       {
+               if(card_marked(*ptr))
+                       collect_card(ptr,here);
+       }
+}
+
+void unmark_cards(CELL from, CELL to)
+{
+       CARD *ptr = ADDR_TO_CARD(generations[from].base);
+       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);
+}
+
+void clear_cards(CELL from, CELL to)
+{
+       /* NOTE: reverse order due to heap layout. */
+       CARD *last_card = ADDR_TO_CARD(generations[from].limit);
+       CARD *ptr = ADDR_TO_CARD(generations[to].base);
+       for(; ptr < last_card; ptr++)
+               clear_card(ptr);
+}
+
+/* scan cards in all generations older than the one being collected */
+void collect_cards(CELL gen)
+{
+       int i;
+       for(i = gen + 1; i < gen_count; i++)
+               collect_gen_cards(i);
+}
+
+/* Generational copying garbage collector */
+
+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 - (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
+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 */
+void init_data_heap(CELL gens, CELL young_size, CELL aging_size)
+{
+       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(ZONE) * gen_count);
+
+       data_heap_start = (CELL)(alloc_bounded_block(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);
+
+       heap_scan = false;
+       gc_time = 0;
+       minor_collections = 0;
+       cards_scanned = 0;
+}
+
+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;
+}
+
+void collect_stack(BOUNDED_BLOCK *region, CELL top)
+{
+       CELL bottom = region->start;
+       CELL ptr;
+
+       for(ptr = bottom; ptr <= top; ptr += CELLS)
+               copy_handle((CELL*)ptr);
+}
+
+void collect_callstack(BOUNDED_BLOCK *region, CELL top)
+{
+       CELL bottom = region->start;
+       CELL ptr;
+
+       for(ptr = bottom; ptr <= top; ptr += CELLS * 3)
+               collect_callframe_triple((CELL*)ptr,
+                       (CELL*)ptr + 1, (CELL*)ptr + 2);
+}
+
+void collect_roots(void)
+{
+       int i;
+       STACKS *stacks;
+
+       copy_handle(&T);
+       copy_handle(&bignum_zero);
+       copy_handle(&bignum_pos_one);
+       copy_handle(&bignum_neg_one);
+       collect_callframe_triple(&callframe,&callframe_scan,&callframe_end);
+
+       save_stacks();
+       stacks = stack_chain;
+
+       while(stacks)
+       {
+               collect_stack(stacks->data_region,stacks->data);
+               collect_stack(stacks->retain_region,stacks->retain);
+               
+               collect_callstack(stacks->call_region,stacks->call);
+
+               if(stacks->next != NULL)
+               {
+                       collect_callframe_triple(&stacks->callframe,
+                               &stacks->callframe_scan,&stacks->callframe_end);
+               }
+
+               copy_handle(&stacks->catch_save);
+
+               stacks = stacks->next;
+       }
+
+       for(i = 0; i < USER_ENV; i++)
+               copy_handle(&userenv[i]);
+}
+
+/* Given a pointer to oldspace, copy it to newspace. */
+INLINE void *copy_untagged_object(void *pointer, CELL size)
+{
+       void *newpointer;
+       if(newspace->here + size >= newspace->limit)
+               longjmp(gc_jmp,1);
+       newpointer = allot_zone(newspace,size);
+       memcpy(newpointer,pointer,size);
+       return newpointer;
+}
+
+INLINE CELL copy_object_impl(CELL pointer)
+{
+       CELL newpointer = (CELL)copy_untagged_object((void*)UNTAG(pointer),
+               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;
+       }
+}
+
+/*
+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.
+*/
+CELL copy_object(CELL pointer)
+{
+       CELL tag;
+       CELL header;
+
+       if(pointer == F)
+               return F;
+
+       tag = TAG(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);
+}
+
+INLINE void collect_object(CELL scan)
+{
+       CELL payload_start = binary_payload_start(scan);
+       CELL end = scan + payload_start;
+
+       scan += CELLS;
+
+       while(scan < end)
+       {
+               copy_handle((CELL*)scan);
+               scan += CELLS;
+       }
+}
+
+CELL collect_next(CELL scan)
+{
+       CELL size = untagged_object_size(scan);
+       collect_object(scan);
+       return scan + size;
+}
+
+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);
+}
+
+void begin_gc(CELL gen)
+{
+       collecting_gen = gen;
+       collecting_gen_start = generations[gen].base;
+
+       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;
+               newspace = &generations[gen];
+               clear_cards(TENURED,TENURED);
+       }
+       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];
+       }
+}
+
+void end_gc(CELL gen)
+{
+       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,"*** Data GC (%ld minor, %ld cards)\n",
+                       minor_collections,cards_scanned);
+               minor_collections = 0;
+               cards_scanned = 0;
+       }
+       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++;
+       }
+}
+
+/* collect gen and all younger generations */
+void garbage_collection(CELL gen, bool code_gc)
+{
+       s64 start = current_millis();
+       CELL scan;
+
+       if(heap_scan)
+               critical_error("GC disabled during heap scan",gen);
+
+       /* 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++;
+       }
+
+       begin_gc(gen);
+
+       /* initialize chase pointer */
+       scan = newspace->here;
+
+       /* collect objects referenced from stacks and environment */
+       collect_roots();
+       
+       /* collect objects referenced from older generations */
+       collect_cards(gen);
+
+       /* collect literal objects referenced from compiled code */
+       collect_literals();
+       
+       while(scan < newspace->here)
+               scan = collect_next(scan);
+
+       end_gc(gen);
+
+       gc_time += (current_millis() - start);
+}
+
+void primitive_gc(void)
+{
+       bool code_gc = unbox_boolean();
+       CELL gen = to_fixnum(dpop());
+       if(gen <= NURSERY || code_gc)
+               gen = NURSERY;
+       else if(gen >= TENURED)
+               gen = TENURED;
+       garbage_collection(gen,code_gc);
+}
+
+/* WARNING: only call this from a context where all local variables
+are also reachable via the GC roots. */
+void maybe_gc(CELL size)
+{
+       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,false);
+       }
+}
+
+void simple_gc(void)
+{
+       maybe_gc(0);
+}
+
+void primitive_gc_time(void)
+{
+       simple_gc();
+       dpush(tag_bignum(s48_long_long_to_bignum(gc_time)));
+}
diff --git a/vm/data_gc.h b/vm/data_gc.h
new file mode 100644 (file)
index 0000000..c4d8717
--- /dev/null
@@ -0,0 +1,215 @@
+bool in_page(void *fault, void *i_area, CELL area_size, int offset);
+
+void *safe_malloc(size_t size);
+
+typedef struct {
+    CELL start;
+    CELL size;
+} BOUNDED_BLOCK;
+
+/* set up guard pages to check for under/overflow.
+size must be a multiple of the page size */
+BOUNDED_BLOCK *alloc_bounded_block(CELL size);
+void dealloc_bounded_block(BOUNDED_BLOCK *block);
+
+CELL untagged_object_size(CELL pointer);
+CELL unaligned_object_size(CELL pointer);
+CELL object_size(CELL pointer);
+CELL binary_payload_start(CELL pointer);
+void primitive_data_room(void);
+void primitive_size(void);
+void primitive_begin_scan(void);
+void primitive_next_object(void);
+void primitive_end_scan(void);
+
+CELL data_heap_start;
+CELL data_heap_end;
+
+/* card marking write barrier. a card is a byte storing a mark flag,
+and the offset (in cells) of the first object in the card.
+
+the mark flag is set by the write barrier when an object in the
+card has a slot written to.
+
+the offset of the first object is set by the allocator.
+*/
+#define CARD_MARK_MASK 0x80
+#define CARD_BASE_MASK 0x7f
+typedef u8 CARD;
+
+CARD *cards;
+CARD *cards_end;
+
+/* A card is 16 bytes (128 bits), 5 address bits per card.
+it is important that 7 bits is sufficient to represent every
+offset within the card */
+#define CARD_SIZE 128
+#define CARD_BITS 7
+#define ADDR_CARD_MASK (CARD_SIZE-1)
+
+INLINE CARD card_marked(CARD c)
+{
+       return c & CARD_MARK_MASK;
+}
+
+INLINE void unmark_card(CARD *c)
+{
+       *c &= CARD_BASE_MASK;
+}
+
+INLINE void clear_card(CARD *c)
+{
+       *c = CARD_BASE_MASK; /* invalid value */
+}
+
+INLINE u8 card_base(CARD c)
+{
+       return c & CARD_BASE_MASK;
+}
+
+#define ADDR_TO_CARD(a) (CARD*)(((CELL)a >> CARD_BITS) + cards_offset)
+#define CARD_TO_ADDR(c) (CELL*)(((CELL)c - cards_offset)<<CARD_BITS)
+
+/* this is an inefficient write barrier. compiled definitions use a more
+efficient one hand-coded in assembly. the write barrier must be called
+any time we are potentially storing a pointer from an older generation
+to a younger one */
+INLINE void write_barrier(CELL address)
+{
+       CARD *c = ADDR_TO_CARD(address);
+       *c |= CARD_MARK_MASK;
+}
+
+/* we need to remember the first object allocated in the card */
+INLINE void allot_barrier(CELL address)
+{
+       CARD *ptr = ADDR_TO_CARD(address);
+       CARD c = *ptr;
+       CELL b = card_base(c);
+       CELL a = (address & ADDR_CARD_MASK);
+       *ptr = (card_marked(c) | ((b < a) ? b : a));
+}
+
+void unmark_cards(CELL from, CELL to);
+void clear_cards(CELL from, CELL to);
+void collect_cards(CELL gen);
+
+/* generational copying GC divides memory into zones */
+typedef struct {
+       /* start of zone */
+       CELL base;
+       /* allocation pointer */
+       CELL here;
+       /* only for nursery: when it gets this full, call GC */
+       CELL alarm;
+       /* end of zone */
+       CELL limit;
+} ZONE;
+
+/* total number of generations. */
+CELL gen_count;
+
+/* the 0th generation is where new objects are allocated. */
+#define NURSERY 0
+/* the oldest generation */
+#define TENURED (gen_count-1)
+
+DLLEXPORT ZONE *generations;
+
+/* used during garbage collection only */
+ZONE *newspace;
+
+#define tenured generations[TENURED]
+#define nursery generations[NURSERY]
+
+/* spare semi-space; rotates with tenured. */
+ZONE prior;
+
+INLINE bool in_zone(ZONE* z, CELL pointer)
+{
+       return pointer >= z->base && pointer < z->limit;
+}
+
+CELL init_zone(ZONE *z, CELL size, CELL base);
+
+void init_data_heap(CELL gen_count, CELL young_size, CELL aging_size);
+
+/* statistics */
+s64 gc_time;
+CELL minor_collections;
+CELL cards_scanned;
+
+/* only meaningful during a GC */
+CELL collecting_gen;
+CELL collecting_gen_start;
+
+/* test if the pointer is in generation being collected, or a younger one.
+init_data_heap() arranges things so that the older generations are first,
+so we have to check that the pointer occurs after the beginning of
+the requested generation. */
+#define COLLECTING_GEN(ptr) (collecting_gen_start <= ptr)
+
+INLINE bool should_copy(CELL untagged)
+{
+       if(collecting_gen == TENURED)
+               return !in_zone(newspace,untagged);
+       else
+               return(in_zone(&prior,untagged) || COLLECTING_GEN(untagged));
+}
+
+CELL copy_object(CELL pointer);
+#define COPY_OBJECT(lvalue) if(should_copy(lvalue)) lvalue = copy_object(lvalue)
+
+INLINE void copy_handle(CELL *handle)
+{
+       COPY_OBJECT(*handle);
+}
+
+/* in case a generation fills up in the middle of a gc, we jump back
+up to try collecting the next generation. */
+jmp_buf gc_jmp;
+
+/* A heap walk allows useful things to be done, like finding all
+references to an object for debugging purposes. */
+CELL heap_scan_ptr;
+
+/* GC is off during heap walking */
+bool heap_scan;
+
+INLINE void *allot_zone(ZONE *z, CELL a)
+{
+       CELL h = z->here;
+       z->here = h + align8(a);
+       if(z->here > z->limit)
+       {
+               fprintf(stderr,"Nursery space exhausted\n");
+               factorbug();
+       }
+
+       allot_barrier(h);
+       return (void*)h;
+}
+
+INLINE void *allot(CELL a)
+{
+       return allot_zone(&nursery,a);
+}
+
+/*
+ * It is up to the caller to fill in the object's fields in a meaningful
+ * fashion!
+ */
+INLINE void* allot_object(CELL type, CELL length)
+{
+       CELL* object = allot(length);
+       *object = tag_header(type);
+       return object;
+}
+
+void update_cards_offset(void);
+CELL collect_next(CELL scan);
+void garbage_collection(CELL gen, bool code_gc);
+void primitive_gc(void);
+void maybe_gc(CELL size);
+DLLEXPORT void simple_gc(void);
+void primitive_gc_time(void);
index 6dbeee7857cf31b1c29b3a5e993205b757683377..01532b68c6aded86a2be018b5979355703d84c7e 100644 (file)
@@ -223,12 +223,6 @@ void factorbug(void)
                }
                else if(strcmp(cmd,"g") == 0)
                        dump_generations();
-               else if(strcmp(cmd,"c") == 0)
-               {
-                       CELL gen;
-                       scanf("%lu",&gen);
-                       garbage_collection(gen);
-               }
                else if(strcmp(cmd,"card") == 0)
                {
                        CELL addr;
index 7dc85a0977d59ad4504b4def8a5dfb163cdaa657..1eaff445953134ba304e8ed10d36e9fff631531c 100644 (file)
@@ -5,8 +5,8 @@ void init_factor(const char* image,
        CELL gen_count, CELL young_size, CELL aging_size, CELL code_size)
 {
        init_ffi();
-       init_arena(gen_count,young_size,aging_size);
-       init_compiler(code_size);
+       init_data_heap(gen_count,young_size,aging_size);
+       init_code_heap(code_size);
        init_stacks(ds_size,rs_size,cs_size);
        /* callframe must be valid in case load_image() does GC */
        callframe = F;
index c188f4583fdbf271e1a75a05dad09dbe1119af4b..3e4c167511446365cfcbda3997bd150c496b42c5 100644 (file)
 #include "platform.h"
 #include "debug.h"
 #include "run.h"
-#include "memory.h"
 #include "bignumint.h"
 #include "bignum.h"
+#include "data_gc.h"
 #include "math.h"
 #include "types.h"
 #include "io.h"
-#include "heap.h"
+#include "code_gc.h"
 #include "compiler.h"
 #include "image.h"
 #include "primitives.h"
diff --git a/vm/heap.c b/vm/heap.c
deleted file mode 100644 (file)
index d12cc74..0000000
--- a/vm/heap.c
+++ /dev/null
@@ -1,158 +0,0 @@
-#include "factor.h"
-
-void new_heap(HEAP *heap, CELL size)
-{
-       heap->base = (CELL)(alloc_bounded_block(size)->start);
-       if(heap->base == 0)
-               fatal_error("Cannot allocate code heap",size);
-       heap->limit = heap->base + size;
-       heap->free_list = NULL;
-}
-
-INLINE void update_free_list(HEAP *heap, F_BLOCK *prev, F_BLOCK *next_free)
-{
-       if(prev)
-               prev->next_free = next_free;
-       else
-               heap->free_list = next_free;
-}
-
-/* called after reading the code heap from the image file. we must build the
-free list, and add a large free block from compiling.base + size to
-compiling.limit. */
-void build_free_list(HEAP *heap, CELL size)
-{
-       F_BLOCK *prev = NULL;
-       F_BLOCK *scan = (F_BLOCK *)heap->base;
-       F_BLOCK *end = (F_BLOCK *)(heap->base + size);
-
-       while(scan && scan < end)
-       {
-               if(scan->status == B_FREE)
-               {
-                       update_free_list(heap,prev,scan);
-                       prev = scan;
-               }
-
-               scan = next_block(heap,scan);
-       }
-
-       if((CELL)(end + 1) <= heap->limit)
-       {
-               end->status = B_FREE;
-               end->next_free = NULL;
-               end->size = heap->limit - (CELL)end;
-       }
-       else
-       {
-               end = NULL;
-
-               if(prev)
-                       prev->size = heap->limit - (CELL)prev;
-       }
-
-       update_free_list(heap,prev,end);
-}
-
-CELL heap_allot(HEAP *heap, CELL size)
-{
-       F_BLOCK *prev = NULL;
-       F_BLOCK *scan = heap->free_list;
-
-       while(scan)
-       {
-               CELL this_size = scan->size - sizeof(F_BLOCK);
-
-               if(this_size < size)
-               {
-                       prev = scan;
-                       scan = scan->next_free;
-                       continue;
-               }
-
-               /* we found a candidate block */
-               F_BLOCK *next_free;
-
-               if(this_size - size <= sizeof(F_BLOCK))
-               {
-                       /* too small to be split */
-                       next_free = scan->next_free;
-               }
-               else
-               {
-                       /* split the block in two */
-                       CELL new_size = size + sizeof(F_BLOCK);
-                       F_BLOCK *split = (F_BLOCK *)((CELL)scan + new_size);
-                       split->status = B_FREE;
-                       split->size = scan->size - new_size;
-                       split->next_free = scan->next_free;
-                       scan->size = new_size;
-                       next_free = split;
-               }
-
-               /* update the free list */
-               update_free_list(heap,prev,next_free);
-
-               /* this is our new block */
-               scan->status = B_ALLOCATED;
-
-               return (CELL)(scan + 1);
-       }
-
-       if(heap->base == 0)
-               critical_error("Code heap is full",size);
-
-       return 0; /* can't happen */
-}
-
-/* free blocks which are allocated and not marked */
-void free_unmarked(HEAP *heap)
-{
-       F_BLOCK *prev = NULL;
-       F_BLOCK *scan = (F_BLOCK *)heap->base;
-
-       while(scan)
-       {
-               if(scan->status == B_ALLOCATED)
-               {
-                       /* merge blocks? */
-                       if(next_block(heap,prev) == scan)
-                               prev->size += scan->size;
-                       else
-                       {
-                               scan->status = B_FREE;
-                               update_free_list(heap,prev,scan);
-                               prev = scan;
-                       }
-               }
-
-               scan = next_block(heap,scan);
-       }
-
-       if(prev)
-               prev->next_free = NULL;
-}
-
-CELL heap_free_space(HEAP *heap)
-{
-       CELL size = 0;
-       F_BLOCK *scan = (F_BLOCK *)heap->base;
-
-       while(scan)
-       {
-               if(scan->status == B_FREE)
-                       size += scan->size;
-               scan = next_block(heap,scan);
-       }
-
-       return size;
-}
-
-CELL heap_size(HEAP *heap)
-{
-       CELL start = heap->base;
-       F_BLOCK *scan = (F_BLOCK *)start;
-       while(next_block(heap,scan))
-               scan = next_block(heap,scan);
-       return (CELL)scan - (CELL)start;
-}
diff --git a/vm/heap.h b/vm/heap.h
deleted file mode 100644 (file)
index cabea8d..0000000
--- a/vm/heap.h
+++ /dev/null
@@ -1,35 +0,0 @@
-typedef enum
-{
-       B_FREE,
-       B_ALLOCATED,
-       B_MARKED
-} F_BLOCK_STATUS;
-
-typedef struct _F_BLOCK
-{
-       F_BLOCK_STATUS status;
-       CELL size;
-       struct _F_BLOCK *next_free;
-} F_BLOCK;
-
-typedef struct {
-       CELL base;
-       CELL limit;
-       F_BLOCK *free_list;
-} HEAP;
-
-void new_heap(HEAP *heap, CELL size);
-void build_free_list(HEAP *heap, CELL size);
-CELL heap_allot(HEAP *heap, CELL size);
-void free_unmarked(HEAP *heap);
-CELL heap_free_space(HEAP *heap);
-CELL heap_size(HEAP *heap);
-
-INLINE F_BLOCK *next_block(HEAP *heap, F_BLOCK *block)
-{
-       CELL next = ((CELL)block + block->size);
-       if(next == heap->limit)
-               return NULL;
-       else
-               return (F_BLOCK *)next;
-}
index 0518d8bd84dd423a70c10dd977baa999581b6e2c..73d064b5ab35a200a5a2be387b9a85ec1f2cfd95 100644 (file)
@@ -116,7 +116,7 @@ void primitive_save_image(void)
 {
        F_STRING* filename;
        /* do a full GC to push everything into tenured space */
-       garbage_collection(TENURED);
+       garbage_collection(TENURED,false);
        filename = untag_string(dpop());
        save_image(to_char_string(filename,true));
 }
@@ -173,12 +173,11 @@ void relocate_data()
 }
 
 void fixup_code_block(F_COMPILED *relocating, CELL code_start,
-       CELL reloc_start, CELL literal_start, CELL words_start)
+       CELL reloc_start, CELL literal_start, CELL words_start, CELL words_end)
 {
        /* relocate literal table data */
        CELL scan;
        CELL literal_end = literal_start + relocating->literal_length;
-       CELL words_end = words_start + relocating->words_length;
 
        for(scan = literal_start; scan < literal_end; scan += CELLS)
                data_fixup((CELL*)scan);
@@ -192,7 +191,7 @@ void fixup_code_block(F_COMPILED *relocating, CELL code_start,
        }
 
        relocate_code_block(relocating,code_start,reloc_start,
-               literal_start,words_start);
+               literal_start,words_start,words_end);
 }
 
 void relocate_code()
diff --git a/vm/memory.c b/vm/memory.c
deleted file mode 100644 (file)
index 129546c..0000000
+++ /dev/null
@@ -1,642 +0,0 @@
-#include "factor.h"
-
-/* this function tests if a given faulting location is in a poison page. The
-page address is taken from area + round_up_to_page_size(area_size) + 
- pagesize*offset */
-bool in_page(void *fault, void *i_area, CELL area_size, int offset)
-{
-       const int pagesize = getpagesize();
-       intptr_t area = (intptr_t) i_area;
-       area += pagesize * ((area_size + (pagesize - 1)) / pagesize);
-       area += offset * pagesize;
-
-       const int page = area / pagesize;
-       const int fault_page = (intptr_t)fault / pagesize;
-       return page == fault_page;
-}
-
-void *safe_malloc(size_t size)
-{
-       void *ptr = malloc(size);
-       if(ptr == 0)
-               fatal_error("malloc() failed", 0);
-       return ptr;
-}
-
-CELL object_size(CELL tagged)
-{
-       if(tagged == F || TAG(tagged) == FIXNUM_TYPE)
-               return 0;
-       else
-               return untagged_object_size(UNTAG(tagged));
-}
-
-CELL untagged_object_size(CELL pointer)
-{
-       return align8(unaligned_object_size(pointer));
-}
-
-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:
-       case BYTE_ARRAY_TYPE:
-       case QUOTATION_TYPE:
-               return array_size(array_capacity((F_ARRAY*)(pointer)));
-       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:
-               return sizeof(F_RATIO);
-       case FLOAT_TYPE:
-               return sizeof(F_FLOAT);
-       case COMPLEX_TYPE:
-               return sizeof(F_COMPLEX);
-       case DLL_TYPE:
-               return sizeof(DLL);
-       case ALIEN_TYPE:
-               return sizeof(ALIEN);
-       case WRAPPER_TYPE:
-               return sizeof(F_WRAPPER);
-       default:
-               critical_error("Cannot determine untagged_object_size",pointer);
-               return -1; /* can't happen */
-       }
-}
-
-/* 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. */
-CELL binary_payload_start(CELL pointer)
-{
-       switch(untag_header(get(pointer)))
-       {
-       /* these objects do not refer to other objects at all */
-       case STRING_TYPE:
-       case FLOAT_TYPE:
-       case BYTE_ARRAY_TYPE:
-       case BIGNUM_TYPE:
-               return 0;
-       /* these objects have some binary data at the end */
-       case WORD_TYPE:
-               return sizeof(F_WORD) - CELLS;
-       case ALIEN_TYPE:
-       case DLL_TYPE:
-               return CELLS * 2;
-       /* everything else consists entirely of pointers */
-       default:
-               return unaligned_object_size(pointer);
-       }
-}
-
-void primitive_type(void)
-{
-       drepl(tag_fixnum(type_of(dpeek())));
-}
-
-void primitive_tag(void)
-{
-       drepl(tag_fixnum(TAG(dpeek())));
-}
-
-void primitive_slot(void)
-{
-       F_FIXNUM slot = untag_fixnum_fast(dpop());
-       CELL obj = UNTAG(dpop());
-       dpush(get(SLOT(obj,slot)));
-}
-
-void primitive_set_slot(void)
-{
-       F_FIXNUM slot = untag_fixnum_fast(dpop());
-       CELL obj = UNTAG(dpop());
-       CELL value = dpop();
-       put(SLOT(obj,slot),value);
-       write_barrier(obj);
-}
-
-void primitive_integer_slot(void)
-{
-       F_FIXNUM slot = untag_fixnum_fast(dpop());
-       CELL obj = UNTAG(dpop());
-       dpush(tag_cell(get(SLOT(obj,slot))));
-}
-
-void primitive_set_integer_slot(void)
-{
-       F_FIXNUM slot = untag_fixnum_fast(dpop());
-       CELL obj = UNTAG(dpop());
-       F_FIXNUM value = to_cell(dpop());
-       put(SLOT(obj,slot),value);
-}
-
-void primitive_size(void)
-{
-       drepl(tag_fixnum(object_size(dpeek())));
-}
-
-CELL clone(CELL obj)
-{
-       CELL size = object_size(obj);
-       CELL tag = TAG(obj);
-       void *new_obj = allot(size);
-       return RETAG(memcpy(new_obj,(void*)UNTAG(obj),size),tag);
-}
-
-void primitive_clone(void)
-{
-       maybe_gc(0);
-       drepl(clone(dpeek()));
-}
-
-void primitive_data_room(void)
-{
-       F_ARRAY *a = array(ARRAY_TYPE,gen_count,F);
-       int gen;
-       box_unsigned_cell(cards_end - cards);
-       box_unsigned_cell(prior.limit - prior.base);
-       for(gen = 0; gen < gen_count; gen++)
-       {
-               ZONE *z = &generations[gen];
-               put(AREF(a,gen),make_array_2(tag_cell(z->limit - z->here),
-                       tag_cell(z->limit - z->base)));
-       }
-       dpush(tag_object(a));
-}
-
-/* Disables GC and activates next-object ( -- obj ) primitive */
-void primitive_begin_scan(void)
-{
-       garbage_collection(TENURED);
-       heap_scan_ptr = tenured.base;
-       heap_scan = true;
-}
-
-/* Push object at heap scan cursor and advance; pushes f when done */
-void primitive_next_object(void)
-{
-       CELL value = get(heap_scan_ptr);
-       CELL obj = heap_scan_ptr;
-       CELL type;
-
-       if(!heap_scan)
-               general_error(ERROR_HEAP_SCAN,F,F,true);
-
-       if(heap_scan_ptr >= tenured.here)
-       {
-               dpush(F);
-               return;
-       }
-       
-       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));
-}
-
-/* Re-enables GC */
-void primitive_end_scan(void)
-{
-       heap_scan = false;
-}
-
-/* scan all the objects in the card */
-INLINE void collect_card(CARD *ptr, CELL here)
-{
-       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(c == 0xff)
-                       critical_error("bad card",(CELL)ptr);
-               else
-                       return;
-       }
-
-       while(card_scan < card_end && card_scan < here)
-               card_scan = collect_next(card_scan);
-       
-       cards_scanned++;
-}
-
-INLINE void collect_gen_cards(CELL gen)
-{
-       CARD *ptr = ADDR_TO_CARD(generations[gen].base);
-       CELL here = generations[gen].here;
-       CARD *last_card = ADDR_TO_CARD(here);
-       
-       if(generations[gen].here == generations[gen].limit)
-               last_card--;
-       
-       for(; ptr <= last_card; ptr++)
-       {
-               if(card_marked(*ptr))
-                       collect_card(ptr,here);
-       }
-}
-
-void unmark_cards(CELL from, CELL to)
-{
-       CARD *ptr = ADDR_TO_CARD(generations[from].base);
-       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);
-}
-
-void clear_cards(CELL from, CELL to)
-{
-       /* NOTE: reverse order due to heap layout. */
-       CARD *last_card = ADDR_TO_CARD(generations[from].limit);
-       CARD *ptr = ADDR_TO_CARD(generations[to].base);
-       for(; ptr < last_card; ptr++)
-               clear_card(ptr);
-}
-
-/* scan cards in all generations older than the one being collected */
-void collect_cards(CELL gen)
-{
-       int i;
-       for(i = gen + 1; i < gen_count; i++)
-               collect_gen_cards(i);
-}
-
-/* Generational copying garbage collector */
-
-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 - (data_heap_start >> CARD_BITS);
-}
-
-/* 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 */
-void init_arena(CELL gens, CELL young_size, CELL aging_size)
-{
-       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(ZONE) * gen_count);
-
-       data_heap_start = (CELL)(alloc_bounded_block(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);
-
-       heap_scan = false;
-       gc_time = 0;
-       minor_collections = 0;
-       cards_scanned = 0;
-}
-
-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;
-}
-
-void collect_stack(BOUNDED_BLOCK *region, CELL top)
-{
-       CELL bottom = region->start;
-       CELL ptr;
-
-       for(ptr = bottom; ptr <= top; ptr += CELLS)
-               copy_handle((CELL*)ptr);
-}
-
-void collect_callstack(BOUNDED_BLOCK *region, CELL top)
-{
-       CELL bottom = region->start;
-       CELL ptr;
-
-       for(ptr = bottom; ptr <= top; ptr += CELLS * 3)
-               collect_callframe_triple((CELL*)ptr,
-                       (CELL*)ptr + 1, (CELL*)ptr + 2);
-}
-
-void collect_roots(void)
-{
-       int i;
-       STACKS *stacks;
-
-       copy_handle(&T);
-       copy_handle(&bignum_zero);
-       copy_handle(&bignum_pos_one);
-       copy_handle(&bignum_neg_one);
-       collect_callframe_triple(&callframe,&callframe_scan,&callframe_end);
-
-       save_stacks();
-       stacks = stack_chain;
-
-       while(stacks)
-       {
-               collect_stack(stacks->data_region,stacks->data);
-               collect_stack(stacks->retain_region,stacks->retain);
-               
-               collect_callstack(stacks->call_region,stacks->call);
-
-               if(stacks->next != NULL)
-               {
-                       collect_callframe_triple(&stacks->callframe,
-                               &stacks->callframe_scan,&stacks->callframe_end);
-               }
-
-               copy_handle(&stacks->catch_save);
-
-               stacks = stacks->next;
-       }
-
-       for(i = 0; i < USER_ENV; i++)
-               copy_handle(&userenv[i]);
-}
-
-/* Given a pointer to oldspace, copy it to newspace. */
-INLINE void *copy_untagged_object(void *pointer, CELL size)
-{
-       void *newpointer;
-       if(newspace->here + size >= newspace->limit)
-               longjmp(gc_jmp,1);
-       newpointer = allot_zone(newspace,size);
-       memcpy(newpointer,pointer,size);
-       return newpointer;
-}
-
-INLINE CELL copy_object_impl(CELL pointer)
-{
-       CELL newpointer = (CELL)copy_untagged_object((void*)UNTAG(pointer),
-               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;
-       }
-}
-
-/*
-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.
-*/
-CELL copy_object(CELL pointer)
-{
-       CELL tag;
-       CELL header;
-
-       if(pointer == F)
-               return F;
-
-       tag = TAG(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);
-}
-
-INLINE void collect_object(CELL scan)
-{
-       CELL payload_start = binary_payload_start(scan);
-       CELL end = scan + payload_start;
-
-       scan += CELLS;
-
-       while(scan < end)
-       {
-               copy_handle((CELL*)scan);
-               scan += CELLS;
-       }
-}
-
-CELL collect_next(CELL scan)
-{
-       CELL size = untagged_object_size(scan);
-       collect_object(scan);
-       return scan + size;
-}
-
-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);
-}
-
-void begin_gc(CELL gen)
-{
-       collecting_gen = gen;
-       collecting_gen_start = generations[gen].base;
-
-       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;
-               newspace = &generations[gen];
-               clear_cards(TENURED,TENURED);
-       }
-       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];
-       }
-}
-
-void end_gc(CELL gen)
-{
-       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;
-       }
-       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++;
-       }
-}
-
-/* collect gen and all younger generations */
-void garbage_collection(CELL gen)
-{
-       s64 start = current_millis();
-       CELL scan;
-
-       if(heap_scan)
-               critical_error("GC disabled during heap scan",gen);
-
-       /* 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++;
-       }
-
-       begin_gc(gen);
-
-       /* initialize chase pointer */
-       scan = newspace->here;
-
-       /* collect objects referenced from stacks and environment */
-       collect_roots();
-       
-       /* collect objects referenced from older generations */
-       collect_cards(gen);
-
-       /* collect literal objects referenced from compiled code */
-       collect_literals();
-       
-       while(scan < newspace->here)
-               scan = collect_next(scan);
-
-       end_gc(gen);
-
-       gc_time += (current_millis() - start);
-}
-
-void primitive_gc(void)
-{
-       CELL gen = to_fixnum(dpop());
-       if(gen <= NURSERY)
-               gen = NURSERY;
-       else if(gen >= TENURED)
-               gen = TENURED;
-       garbage_collection(gen);
-}
-
-/* WARNING: only call this from a context where all local variables
-are also reachable via the GC roots. */
-void maybe_gc(CELL size)
-{
-       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);
-       }
-}
-
-void simple_gc(void)
-{
-       maybe_gc(0);
-}
-
-void primitive_gc_time(void)
-{
-       simple_gc();
-       dpush(tag_bignum(s48_long_long_to_bignum(gc_time)));
-}
diff --git a/vm/memory.h b/vm/memory.h
deleted file mode 100644 (file)
index 0554e28..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-bool in_page(void *fault, void *i_area, CELL area_size, int offset);
-
-void *safe_malloc(size_t size);
-
-typedef struct {
-    CELL start;
-    CELL size;
-} BOUNDED_BLOCK;
-
-/* set up guard pages to check for under/overflow.
-size must be a multiple of the page size */
-BOUNDED_BLOCK *alloc_bounded_block(CELL size);
-void dealloc_bounded_block(BOUNDED_BLOCK *block);
-
-/* macros for reading/writing memory, useful when working around
-C's type system */
-INLINE CELL get(CELL where)
-{
-       return *((CELL*)where);
-}
-
-INLINE void put(CELL where, CELL what)
-{
-       *((CELL*)where) = what;
-}
-
-INLINE u16 cget(CELL where)
-{
-       return *((u16*)where);
-}
-
-INLINE void cput(CELL where, u16 what)
-{
-       *((u16*)where) = what;
-}
-
-INLINE CELL align8(CELL a)
-{
-       return (a + 7) & ~7;
-}
-
-/* Canonical T object. It's just a word */
-CELL T;
-
-#define SLOT(obj,slot) ((obj) + (slot) * CELLS)
-
-INLINE CELL tag_header(CELL cell)
-{
-       return RETAG(cell << TAG_BITS,OBJECT_TYPE);
-}
-
-INLINE CELL untag_header(CELL cell)
-{
-       /* if((cell & TAG_MASK) != OBJECT_TYPE)
-               critical_error("Corrupt object header",cell); */
-
-       return cell >> TAG_BITS;
-}
-
-INLINE CELL tag_object(void* cell)
-{
-       return RETAG(cell,OBJECT_TYPE);
-}
-
-INLINE CELL object_type(CELL tagged)
-{
-       return untag_header(get(UNTAG(tagged)));
-}
-
-INLINE CELL type_of(CELL tagged)
-{
-       if(tagged == F)
-               return F_TYPE;
-       else if(TAG(tagged) == FIXNUM_TYPE)
-               return FIXNUM_TYPE;
-       else
-               return object_type(tagged);
-}
-
-INLINE void type_check(CELL type, CELL tagged)
-{
-       if(type_of(tagged) != type)
-               type_error(type,tagged);
-}
-
-CELL untagged_object_size(CELL pointer);
-CELL unaligned_object_size(CELL pointer);
-CELL object_size(CELL pointer);
-CELL binary_payload_start(CELL pointer);
-void primitive_data_room(void);
-void primitive_type(void);
-void primitive_tag(void);
-void primitive_slot(void);
-void primitive_set_slot(void);
-void primitive_integer_slot(void);
-void primitive_set_integer_slot(void);
-void primitive_size(void);
-CELL clone(CELL obj);
-void primitive_clone(void);
-void primitive_begin_scan(void);
-void primitive_next_object(void);
-void primitive_end_scan(void);
-
-CELL data_heap_start;
-CELL data_heap_end;
-
-/* card marking write barrier. a card is a byte storing a mark flag,
-and the offset (in cells) of the first object in the card.
-
-the mark flag is set by the write barrier when an object in the
-card has a slot written to.
-
-the offset of the first object is set by the allocator.
-*/
-#define CARD_MARK_MASK 0x80
-#define CARD_BASE_MASK 0x7f
-typedef u8 CARD;
-
-CARD *cards;
-CARD *cards_end;
-
-/* A card is 16 bytes (128 bits), 5 address bits per card.
-it is important that 7 bits is sufficient to represent every
-offset within the card */
-#define CARD_SIZE 128
-#define CARD_BITS 7
-#define ADDR_CARD_MASK (CARD_SIZE-1)
-
-INLINE CARD card_marked(CARD c)
-{
-       return c & CARD_MARK_MASK;
-}
-
-INLINE void unmark_card(CARD *c)
-{
-       *c &= CARD_BASE_MASK;
-}
-
-INLINE void clear_card(CARD *c)
-{
-       *c = CARD_BASE_MASK; /* invalid value */
-}
-
-INLINE u8 card_base(CARD c)
-{
-       return c & CARD_BASE_MASK;
-}
-
-#define ADDR_TO_CARD(a) (CARD*)(((CELL)a >> CARD_BITS) + cards_offset)
-#define CARD_TO_ADDR(c) (CELL*)(((CELL)c - cards_offset)<<CARD_BITS)
-
-/* this is an inefficient write barrier. compiled definitions use a more
-efficient one hand-coded in assembly. the write barrier must be called
-any time we are potentially storing a pointer from an older generation
-to a younger one */
-INLINE void write_barrier(CELL address)
-{
-       CARD *c = ADDR_TO_CARD(address);
-       *c |= CARD_MARK_MASK;
-}
-
-/* we need to remember the first object allocated in the card */
-INLINE void allot_barrier(CELL address)
-{
-       CARD *ptr = ADDR_TO_CARD(address);
-       CARD c = *ptr;
-       CELL b = card_base(c);
-       CELL a = (address & ADDR_CARD_MASK);
-       *ptr = (card_marked(c) | ((b < a) ? b : a));
-}
-
-void unmark_cards(CELL from, CELL to);
-void clear_cards(CELL from, CELL to);
-void collect_cards(CELL gen);
-
-/* generational copying GC divides memory into zones */
-typedef struct {
-       /* start of zone */
-       CELL base;
-       /* allocation pointer */
-       CELL here;
-       /* only for nursery: when it gets this full, call GC */
-       CELL alarm;
-       /* end of zone */
-       CELL limit;
-} ZONE;
-
-/* total number of generations. */
-CELL gen_count;
-
-/* the 0th generation is where new objects are allocated. */
-#define NURSERY 0
-/* the oldest generation */
-#define TENURED (gen_count-1)
-
-DLLEXPORT ZONE *generations;
-
-/* used during garbage collection only */
-ZONE *newspace;
-
-#define tenured generations[TENURED]
-#define nursery generations[NURSERY]
-
-/* spare semi-space; rotates with tenured. */
-ZONE prior;
-
-INLINE bool in_zone(ZONE* z, CELL pointer)
-{
-       return pointer >= z->base && pointer < z->limit;
-}
-
-CELL init_zone(ZONE *z, CELL size, CELL base);
-
-void init_arena(CELL gen_count, CELL young_size, CELL aging_size);
-
-/* statistics */
-s64 gc_time;
-CELL minor_collections;
-CELL cards_scanned;
-
-/* only meaningful during a GC */
-CELL collecting_gen;
-CELL collecting_gen_start;
-
-/* test if the pointer is in generation being collected, or a younger one.
-init_arena() arranges things so that the older generations are first,
-so we have to check that the pointer occurs after the beginning of
-the requested generation. */
-#define COLLECTING_GEN(ptr) (collecting_gen_start <= ptr)
-
-INLINE bool should_copy(CELL untagged)
-{
-       if(collecting_gen == TENURED)
-               return !in_zone(newspace,untagged);
-       else
-               return(in_zone(&prior,untagged) || COLLECTING_GEN(untagged));
-}
-
-CELL copy_object(CELL pointer);
-#define COPY_OBJECT(lvalue) if(should_copy(lvalue)) lvalue = copy_object(lvalue)
-
-INLINE void copy_handle(CELL *handle)
-{
-       COPY_OBJECT(*handle);
-}
-
-/* in case a generation fills up in the middle of a gc, we jump back
-up to try collecting the next generation. */
-jmp_buf gc_jmp;
-
-/* A heap walk allows useful things to be done, like finding all
-references to an object for debugging purposes. */
-CELL heap_scan_ptr;
-
-/* GC is off during heap walking */
-bool heap_scan;
-
-INLINE void *allot_zone(ZONE *z, CELL a)
-{
-       CELL h = z->here;
-       z->here = h + align8(a);
-       if(z->here > z->limit)
-       {
-               fprintf(stderr,"Nursery space exhausted\n");
-               factorbug();
-       }
-
-       allot_barrier(h);
-       return (void*)h;
-}
-
-INLINE void *allot(CELL a)
-{
-       return allot_zone(&nursery,a);
-}
-
-/*
- * It is up to the caller to fill in the object's fields in a meaningful
- * fashion!
- */
-INLINE void* allot_object(CELL type, CELL length)
-{
-       CELL* object = allot(length);
-       *object = tag_header(type);
-       return object;
-}
-
-void update_cards_offset(void);
-CELL collect_next(CELL scan);
-void garbage_collection(CELL gen);
-void primitive_gc(void);
-void maybe_gc(CELL size);
-DLLEXPORT void simple_gc(void);
-void primitive_gc_time(void);
index 1a2204f32d76ed711a6f6435f8497b7a55dcb7c9..fde495312e335f14792d0ec73dc04c31a8005e97 100644 (file)
--- a/vm/run.c
+++ b/vm/run.c
@@ -191,6 +191,61 @@ void primitive_millis(void)
        dpush(tag_bignum(s48_long_long_to_bignum(current_millis())));
 }
 
+void primitive_type(void)
+{
+       drepl(tag_fixnum(type_of(dpeek())));
+}
+
+void primitive_tag(void)
+{
+       drepl(tag_fixnum(TAG(dpeek())));
+}
+
+void primitive_slot(void)
+{
+       F_FIXNUM slot = untag_fixnum_fast(dpop());
+       CELL obj = UNTAG(dpop());
+       dpush(get(SLOT(obj,slot)));
+}
+
+void primitive_set_slot(void)
+{
+       F_FIXNUM slot = untag_fixnum_fast(dpop());
+       CELL obj = UNTAG(dpop());
+       CELL value = dpop();
+       put(SLOT(obj,slot),value);
+       write_barrier(obj);
+}
+
+void primitive_integer_slot(void)
+{
+       F_FIXNUM slot = untag_fixnum_fast(dpop());
+       CELL obj = UNTAG(dpop());
+       dpush(tag_cell(get(SLOT(obj,slot))));
+}
+
+void primitive_set_integer_slot(void)
+{
+       F_FIXNUM slot = untag_fixnum_fast(dpop());
+       CELL obj = UNTAG(dpop());
+       F_FIXNUM value = to_cell(dpop());
+       put(SLOT(obj,slot),value);
+}
+
+CELL clone(CELL obj)
+{
+       CELL size = object_size(obj);
+       CELL tag = TAG(obj);
+       void *new_obj = allot(size);
+       return RETAG(memcpy(new_obj,(void*)UNTAG(obj),size),tag);
+}
+
+void primitive_clone(void)
+{
+       maybe_gc(0);
+       drepl(clone(dpeek()));
+}
+
 void fatal_error(char* msg, CELL tagged)
 {
        fprintf(stderr,"Fatal error: %s %lx\n",msg,tagged);
index 2255dbe1e110c79664e270f4435efc3a2f10a576..b9fdcc710fc8b33cc193ce7849d72dae4e21a515 100644 (file)
--- a/vm/run.h
+++ b/vm/run.h
@@ -33,6 +33,71 @@ CELL callframe_end;
 /* TAGGED user environment data; see getenv/setenv prims */
 DLLEXPORT CELL userenv[USER_ENV];
 
+/* macros for reading/writing memory, useful when working around
+C's type system */
+INLINE CELL get(CELL where)
+{
+       return *((CELL*)where);
+}
+
+INLINE void put(CELL where, CELL what)
+{
+       *((CELL*)where) = what;
+}
+
+INLINE u16 cget(CELL where)
+{
+       return *((u16*)where);
+}
+
+INLINE void cput(CELL where, u16 what)
+{
+       *((u16*)where) = what;
+}
+
+INLINE CELL align8(CELL a)
+{
+       return (a + 7) & ~7;
+}
+
+/* Canonical T object. It's just a word */
+CELL T;
+
+#define SLOT(obj,slot) ((obj) + (slot) * CELLS)
+
+INLINE CELL tag_header(CELL cell)
+{
+       return RETAG(cell << TAG_BITS,OBJECT_TYPE);
+}
+
+INLINE CELL untag_header(CELL cell)
+{
+       /* if((cell & TAG_MASK) != OBJECT_TYPE)
+               critical_error("Corrupt object header",cell); */
+
+       return cell >> TAG_BITS;
+}
+
+INLINE CELL tag_object(void* cell)
+{
+       return RETAG(cell,OBJECT_TYPE);
+}
+
+INLINE CELL object_type(CELL tagged)
+{
+       return untag_header(get(UNTAG(tagged)));
+}
+
+INLINE CELL type_of(CELL tagged)
+{
+       if(tagged == F)
+               return F_TYPE;
+       else if(TAG(tagged) == FIXNUM_TYPE)
+               return FIXNUM_TYPE;
+       else
+               return object_type(tagged);
+}
+
 void call(CELL quot);
 
 void handle_error();
@@ -53,6 +118,14 @@ void primitive_exit(void);
 void primitive_os_env(void);
 void primitive_eq(void);
 void primitive_millis(void);
+void primitive_type(void);
+void primitive_tag(void);
+void primitive_slot(void);
+void primitive_set_slot(void);
+void primitive_integer_slot(void);
+void primitive_set_integer_slot(void);
+CELL clone(CELL obj);
+void primitive_clone(void);
 
 /* Runtime errors */
 typedef enum
@@ -97,3 +170,9 @@ void signal_error(int signal);
 void type_error(CELL type, CELL tagged);
 void primitive_throw(void);
 void primitive_die(void);
+
+INLINE void type_check(CELL type, CELL tagged)
+{
+       if(type_of(tagged) != type)
+               type_error(type,tagged);
+}