diff --git a/Makefile b/Makefile index f0abc72e..2d285843 100644 --- a/Makefile +++ b/Makefile @@ -45,10 +45,11 @@ libcyclone.so.1: runtime.c include/cyclone/runtime.h gcc -g -c -fPIC runtime.c -o runtime.o gcc -shared -Wl,-soname,libcyclone.so.1 -o libcyclone.so.1.0.1 runtime.o -libcyclone.a: runtime.c include/cyclone/runtime.h include/cyclone/types.h dispatch.c +libcyclone.a: runtime.c include/cyclone/runtime.h include/cyclone/types.h gc.c dispatch.c $(CC) -g -c -Iinclude dispatch.c -o dispatch.o + $(CC) -g -c -Iinclude gc.c -o gc.o $(CC) -g -c -Iinclude -DCYC_INSTALL_DIR=\"$(PREFIX)\" -DCYC_INSTALL_LIB=\"$(LIBDIR)\" -DCYC_INSTALL_INC=\"$(INCDIR)\" -DCYC_INSTALL_SLD=\"$(DATADIR)\" runtime.c -o runtime.o - $(AR) rcs libcyclone.a runtime.o dispatch.o + $(AR) rcs libcyclone.a runtime.o gc.o dispatch.o # Instructions from: http://www.adp-gmbh.ch/cpp/gcc/create_lib.html # Note compiler will have to link to this, eg: #Linking against static library diff --git a/gc.c b/gc.c new file mode 100644 index 00000000..14f84d3a --- /dev/null +++ b/gc.c @@ -0,0 +1,323 @@ +/* TODO: a basic mark-sweep GC + As of now, the GC code is based off the implementation from chibi scheme + + Goals of this project: + - write algorithms + - add test cases + - integrate with types + - integrate with cyclone + - extend to tri-color marking an on-the-fly collection + - etc... + */ + +#include "cyclone/types.h" + +gc_heap *gc_heap_create(size_t size, size_t max_size, size_t chunk_size) +{ + gc_free_list *free, *next; + gc_heap *h; + // TODO: mmap? + h = malloc(size); + if (!h) return NULL; + h->size = size; + h->chunk_size = chunk_size; + h->max_size = max_size; +printf("DEBUG h->data addr: %p\n", &(h->data)); + h->data = (char *) gc_heap_align(sizeof(h->data) + (uint)&(h->data)); +printf("DEBUG h->data addr: %p\n", h->data); + h->next = NULL; + free = h->free_list = (gc_free_list *)h->data; + next = (gc_free_list *)(((char *) free) + gc_heap_align(gc_free_chunk_size)); + free->size = 0; // First one is just a dummy record + free->next = next; + next->size = size - gc_heap_align(gc_free_chunk_size); + next->next = NULL; + return h; +} + +int gc_grow_heap(gc_heap *h, size_t size, size_t chunk_size) +{ + size_t cur_size, new_size; + gc_heap *h_last = gc_heap_last(h); + cur_size = h_last->size; + new_size = gc_heap_align(((cur_size > size) ? cur_size : size) * 2); + h->next = gc_heap_create(new_size, h->max_size, chunk_size); + return (h->next != NULL); +} + +void *gc_try_alloc(gc_heap *h, size_t size) +{ + gc_free_list *f1, *f2, *f3; + for (; h; h = h->next) { // All heaps + // TODO: chunk size (ignoring for now) + + for (f1 = h->free_list, f2 = f1->next; f2; f1 = f2, f2 = f2->next) { // all free in this heap + if (f2->size > size) { // Big enough for request + // TODO: take whole chunk or divide up f2 (using f3)? + if (f2->size >= (size + gc_heap_align(1) /* min obj size */)) { + f3 = (gc_free_list *) (((char *)f2) + size); + f3->size = f2->size - size; + f3->next = f2->next; + f1->next = f3; + } else { /* Take the whole chunk */ + f1->next = f2->next; + } + // zero-out the header + memset((object)f2, 0, sizeof(gc_header_type)); + return f2; + } + } + } + return NULL; +} + +void *gc_alloc(gc_heap *h, size_t size) +{ + void *result = NULL; + size_t max_freed, sum_freed, total_size; + // TODO: check return value, if null (could not alloc) then + // run a collection and check how much free space there is. if less + // the allowed ratio, try growing heap. + // then try realloc. if cannot alloc now, then throw out of memory error + size = gc_heap_align(size); + //return gc_try_alloc(h, size); + result = gc_try_alloc(h, size); + if (!result) { + // TODO: may want to consider not doing this now, and implementing gc_collect as + // part of the runtime, since we would have all of the roots, stack args, + // etc available there. +// max_freed = gc_collect(h); TODO: this does not work yet! +max_freed = 0; + + total_size = gc_heap_total_size(h); + if (((max_freed < size) || + ((total_size > sum_freed) && + (total_size - sum_freed) > (total_size * 0.75))) // Grow ratio + && ((!h->max_size) || (total_size < h->max_size))) { + gc_grow_heap(h, size, 0); + } + result = gc_try_alloc(h, size); + if (!result) { + fprintf(stderr, "out of memory error allocating %d bytes\n", size); + exit(1); // TODO: throw error??? + } + } +#if GC_DEBUG_PRINTFS + fprintf(stdout, "alloc %p\n", result); +#endif + return result; +} + +size_t gc_allocated_bytes(object obj) +{ + tag_type t; + if (is_value_type(obj)) + return gc_heap_align(1); + t = type_of(obj); + if (t == cons_tag) return gc_heap_align(sizeof(cons_type)); + if (t == integer_tag) return gc_heap_align(sizeof(integer_type)); + +#if GC_DEBUG_PRINTFS + fprintf(stderr, "cannot get size of object %ld\n", t); +#endif + return 0; +} + +gc_heap *gc_heap_last(gc_heap *h) +{ + while (h->next) + h = h->next; + return h; +} + +size_t gc_heap_total_size(gc_heap *h) +{ + size_t total_size = 0; + while(h) { + total_size += h->size; + h = h->next; + } + return total_size; +} + +void gc_mark(gc_heap *h, object obj) +{ + if (!obj || is_marked(obj)) + return; + +#if GC_DEBUG_PRINTFS + fprintf(stdout, "gc_mark %p\n", obj); +#endif + ((list)obj)->hdr.mark = 1; + // TODO: mark heap saves (??) + // could this be a write barrier? + + // Mark objects this one references + if (type_of(obj) == cons_tag) { + gc_mark(h, car(obj)); + gc_mark(h, cdr(obj)); + } + // TODO: will be more work in here the "real" implementation +} + +size_t gc_sweep(gc_heap *h, size_t *sum_freed_ptr) +{ + // TODO: scan entire heap, freeing objects that have not been marked + size_t freed, max_freed=0, sum_freed=0, size; + object p, end; + gc_free_list *q, *r, *s; + for (; h; h = h->next) { // All heaps + p = gc_heap_first_block(h); + q = h->free_list; + end = gc_heap_end(h); + while (p < end) { + // find preceding/succeeding free list pointers for p + for (r = q->next; r && ((char *)r < (char *)p); q=r, r=r->next); + + if ((char *)r == (char *)p) { // this is a free block, skip it + p = (object) (((char *)p) + r->size); + continue; + } + size = gc_heap_align(gc_allocated_bytes(p)); + +#if GC_DEBUG_PRINTFS + // DEBUG + if (!is_object_type(p)) + fprintf(stderr, "sweep: invalid object at %p", p); + if ((char *)q + q->size > (char *)p) + fprintf(stderr, "bad size at %p < %p + %u", p, q, q->size); + if (r && ((char *)p) + size > (char *)r) + fprintf(stderr, "sweep: bad size at %p + %d > %p", p, size, r); + // END DEBUG +#endif + + if (!is_marked(p)) { +#if GC_DEBUG_PRINTFS + fprintf(stdout, "sweep: object is not marked %p\n", p); +#endif + // free p + sum_freed += size; + if (((((char *)q) + q->size) == (char *)p) && (q != h->free_list)) { + /* merge q with p */ + if (r && r->size && ((((char *)p)+size) == (char *)r)) { + // ... and with r + q->next = r->next; + freed = q->size + size + r->size; + p = (object) (((char *)p) + size + r->size); + } else { + freed = q->size + size; + p = (object) (((char *)p) + size); + } + q->size = freed; + } else { + s = (gc_free_list *)p; + if (r && r->size && ((((char *)p) + size) == (char *)r)) { + // merge p with r + s->size = size + r->size; + s->next = r->next; + q->next = s; + freed = size + r->size; + } else { + s->size = size; + s->next = r; + q->next = s; + freed = size; + } + p = (object) (((char *)p) + freed); + } + if (freed > max_freed) + max_freed = freed; + } else { +#if GC_DEBUG_PRINTFS + fprintf(stdout, "sweep: object is marked %p\n", p); +#endif + ((list)p)->hdr.mark = 0; + p = (object)(((char *)p) + size); + } + } + } + if (sum_freed_ptr) *sum_freed_ptr = sum_freed; + return max_freed; +} + +void gc_collect(gc_heap *h, size_t *sum_freed) +{ + printf("(heap: %p size: %d)\n", h, (unsigned int)gc_heap_total_size(h)); + // TODO: mark globals + // TODO: gc_mark(h, h); + // conservative mark? + // weak refs? + // finalize? + gc_sweep(h, sum_freed); + // debug print free stats + // return value from sweep?? +} + +// void gc_init() +// { +// } +// END heap definitions + +/* tri-color GC stuff, we will care about this later... +int colorWhite = 0; +int colorGray = 1; +int colorBlack = 2; +int colorBlue = 3; + +typedef enum {STATUS_ASYNC, STATUS_SYNC1, STATUS_SYNC2} status_type; + +// DLG globals +static void *swept; +static int dirty; +static void *scanned; + +// TODO: mutator actions +// TODO: collector +// TODO: extentions +// TODO: proofs, etc +// TODO: revist design using content from kolodner +*/ + +// int main(int argc, char **argv) { +// int i; +// size_t freed = 0, max_freed = 0; +// gc_heap *h = gc_heap_create(8 * 1024 * 1024, 0, 0); +// void *obj1 = gc_alloc(h, sizeof(cons_type)); +// void *obj2 = gc_alloc(h, sizeof(cons_type)); +// void *objI = gc_alloc(h, sizeof(integer_type)); +// +// for (i = 0; i < 1000000; i++) { +// gc_alloc(h, sizeof(integer_type)); +// gc_alloc(h, sizeof(integer_type)); +// } +// +// // Build up an object graph to test collection... +// ((integer_type *)objI)->hdr.mark = 0; +// ((integer_type *)objI)->tag = integer_tag; +// ((integer_type *)objI)->value = 42; +// +// ((list)obj2)->hdr.mark = 0; +// ((list)obj2)->tag = cons_tag; +// ((list)obj2)->cons_car = objI; +// ((list)obj2)->cons_cdr = NULL; +// +// ((list)obj1)->hdr.mark = 0; +// ((list)obj1)->tag = cons_tag; +// ((list)obj1)->cons_car = obj2; +// ((list)obj1)->cons_cdr = NULL; +// +// printf("(heap: %p size: %d)", h, (unsigned int)gc_heap_total_size(h)); +// gc_mark(h, obj1); +// max_freed = gc_sweep(h, &freed); +// printf("done, freed = %d, max_freed = %d\n", freed, max_freed); +// for (i = 0; i < 10; i++) { +// gc_alloc(h, sizeof(integer_type)); +// gc_alloc(h, sizeof(integer_type)); +// } +// printf("(heap: %p size: %d)", h, (unsigned int)gc_heap_total_size(h)); +// gc_mark(h, obj1); +// max_freed = gc_sweep(h, &freed); +// printf("done, freed = %d, max_freed = %d\n", freed, max_freed); +// +// return 0; +// }