diff --git a/src/base.h b/src/base.h index 3d83446..cd7a136 100644 --- a/src/base.h +++ b/src/base.h @@ -134,7 +134,7 @@ static ALWAYS_INLINE bool OBJECT_STATIC_P(LispVal *val) { } static inline void MARK_OBJECT_ADDED(LispVal *val, LispVal *into) { - if ((!OBJECT_GC_SET_P(into, GC_WHITE) || OBJECT_STATIC_P(into)) + if (OBJECTP(val) && OBJECTP(into) && OBJECT_GC_SET_P(into, GC_BLACK) && OBJECT_GC_SET_P(val, GC_WHITE)) { gc_move_to_set(val, GC_GREY); } diff --git a/src/gc.c b/src/gc.c index 3d2adc1..865c7bf 100644 --- a/src/gc.c +++ b/src/gc.c @@ -8,127 +8,6 @@ #include -void free_object_process_stack(ObjectProcessStack *restrict stack) { - for (size_t i = 0; i < stack->num_blocks; ++i) { - lisp_free(stack->blocks[i]); - } - lisp_free(stack->blocks); -} - -// Ensure STACK can hold at least NEW_ENTS more entries -static void ensure_object_process_stack_size(ObjectProcessStack *restrict stack, - size_t new_ents) { - size_t total_spaces = stack->num_blocks * OBJECT_PROCESS_STACK_BLOCK_SIZE; - size_t ents_left = total_spaces - stack->num_objs; - if (ents_left < new_ents) { - // grow the stack - size_t need_to_alloc = - (new_ents - ents_left) / OBJECT_PROCESS_STACK_BLOCK_SIZE; - if ((new_ents - ents_left) % OBJECT_PROCESS_STACK_BLOCK_SIZE != 0) { - ++need_to_alloc; - } - stack->blocks = lisp_realloc(stack->blocks, - sizeof(struct ObjectProcessStackBlock *) - * (stack->num_blocks + need_to_alloc)); - for (size_t i = 0; i < need_to_alloc; ++i) { - stack->blocks[i + stack->num_blocks] = - lisp_malloc(sizeof(struct ObjectProcessStackBlock)); - } - stack->num_blocks += need_to_alloc; - } -} - -// this does NOT check if there is actually space to push the object, so make -// sure to expand the stack with the above function before pushing -static ALWAYS_INLINE void -add_to_object_process_stack(ObjectProcessStack *restrict stack, void *obj) { - if (OBJECTP(obj)) { - size_t block_idx = stack->num_objs / OBJECT_PROCESS_STACK_BLOCK_SIZE; - size_t small_idx = stack->num_objs % OBJECT_PROCESS_STACK_BLOCK_SIZE; - stack->blocks[block_idx]->objs[small_idx] = obj; - ++stack->num_objs; - } -} - -void object_process_stack_push_object(ObjectProcessStack *restrict stack, - void *obj) { - if (OBJECTP(obj)) { - ensure_object_process_stack_size(stack, 1); - add_to_object_process_stack(stack, obj); - } -} - -void object_process_stack_push_held_objects(ObjectProcessStack *restrict stack, - void *obj) { - if (!OBJECTP(obj)) { - return; - } - switch (((LispObject *) obj)->type) { - case TYPE_CONS: - ensure_object_process_stack_size(stack, 2); - add_to_object_process_stack(stack, XCAR(obj)); - add_to_object_process_stack(stack, XCDR(obj)); - break; - case TYPE_SYMBOL: { - LispSymbol *sym = obj; - ensure_object_process_stack_size(stack, 4); - add_to_object_process_stack(stack, sym->name); - add_to_object_process_stack(stack, sym->value); - add_to_object_process_stack(stack, sym->function); - add_to_object_process_stack(stack, sym->plist); - break; - } - case TYPE_VECTOR: { - LispVector *vec = obj; - for (size_t i = 0; i < vec->length; ++i) { - if (OBJECTP(vec->data[i])) { - ensure_object_process_stack_size(stack, 1); - } - add_to_object_process_stack(stack, vec->data[i]); - } - break; - } - case TYPE_FUNCTION: { - LispFunction *fobj = obj; - ensure_object_process_stack_size(stack, 6); - add_to_object_process_stack(stack, fobj->name); - add_to_object_process_stack(stack, fobj->docstr); - add_to_object_process_stack(stack, fobj->args.req); - add_to_object_process_stack(stack, fobj->args.opt); - add_to_object_process_stack(stack, fobj->args.kw); - add_to_object_process_stack(stack, fobj->args.rest); - // TODO when other function types are added, record their held - // references - break; - } - case TYPE_HASH_TABLE: { - HT_FOREACH_INDEX(obj, i) { - if (OBJECTP(HASH_KEY(obj, i)) || OBJECTP(HASH_VALUE(obj, i))) { - ensure_object_process_stack_size(stack, 2); - } - add_to_object_process_stack(stack, HASH_KEY(obj, i)); - add_to_object_process_stack(stack, HASH_VALUE(obj, i)); - } - break; - } - case TYPE_STRING: - // holds no references break; - break; - case TYPE_FIXNUM: - case TYPE_FLOAT: - default: - abort(); - } -} - -void *object_process_stack_pop(ObjectProcessStack *restrict stack) { - assert(stack->num_objs > 0); - --stack->num_objs; - size_t block_idx = stack->num_objs / OBJECT_PROCESS_STACK_BLOCK_SIZE; - size_t small_idx = stack->num_objs % OBJECT_PROCESS_STACK_BLOCK_SIZE; - return stack->blocks[block_idx]->objs[small_idx]; -} - bool lisp_doing_gc; struct timespec total_gc_time; size_t total_gc_count; @@ -279,60 +158,96 @@ static void free_object(LispVal *val) { lisp_release_object(val); } -static void mark_object_recurse(LispGCStats *restrict stats, LispVal *val) { +static inline void make_grey_if_while(LispVal *val) { + if (OBJECTP(val) && OBJECT_GC_SET_P(val, GC_WHITE)) { + gc_move_to_set(val, GC_GREY); + } +} + +static void mark_object(LispVal *val) { if (!OBJECTP(val) || OBJECT_GC_SET_P(val, GC_BLACK)) { return; } - ObjectProcessStack stack; - init_object_process_stack(&stack); - object_process_stack_push_object(&stack, val); - while (!OBJECT_PROCESS_STACK_EMPTY_P(&stack)) { - LispVal *cur = object_process_stack_pop(&stack); - if (!OBJECT_GC_SET_P(cur, GC_BLACK)) { - if (!OBJECT_STATIC_P(val)) { - ++stats->non_statics_kept; - } - gc_move_to_set(cur, GC_BLACK); - object_process_stack_push_held_objects(&stack, cur); + switch (((LispObject *) val)->type) { + case TYPE_CONS: + make_grey_if_while(((LispCons *) val)->car); + make_grey_if_while(((LispCons *) val)->cdr); + break; + case TYPE_SYMBOL: { + LispSymbol *sym = val; + make_grey_if_while(sym->name); + make_grey_if_while(sym->value); + make_grey_if_while(sym->function); + make_grey_if_while(sym->plist); + break; + } + case TYPE_VECTOR: { + LispVector *vec = val; + for (size_t i = 0; i < vec->length; ++i) { + make_grey_if_while(vec->data[i]); } + break; } - free_object_process_stack(&stack); + case TYPE_HASH_TABLE: { + HT_FOREACH_INDEX(val, i) { + make_grey_if_while(HASH_KEY(val, i)); + make_grey_if_while(HASH_VALUE(val, i)); + } + break; + } + case TYPE_FUNCTION: { + LispFunction *fobj = val; + make_grey_if_while(fobj->name); + make_grey_if_while(fobj->docstr); + make_grey_if_while(fobj->args.req); + make_grey_if_while(fobj->args.opt); + make_grey_if_while(fobj->args.kw); + make_grey_if_while(fobj->args.rest); + break; + } + case TYPE_STRING: + // no held refs + break; + case TYPE_FIXNUM: + case TYPE_FLOAT: + default: + abort(); + } + gc_move_to_set(val, GC_BLACK); } -static void mark_statics(LispGCStats *restrict stats) { +static void mark_statics(void) { for (struct GCObjectList *node = static_objects; node; node = node->next) { - mark_object_recurse(stats, node->obj); + mark_object(node->obj); } } -static void mark_stack_local_refs(LispGCStats *restrict stats, - struct LocalReferences *restrict refs) { +static void mark_stack_local_refs(struct LocalReferences *restrict refs) { size_t full_blocks = refs->num_refs / LOCAL_REFERENCES_BLOCK_LENGTH; size_t last_block_len = refs->num_refs % LOCAL_REFERENCES_BLOCK_LENGTH; for (size_t i = 0; i < full_blocks; ++i) { for (size_t j = 0; j < LOCAL_REFERENCES_BLOCK_LENGTH; ++j) { - mark_object_recurse(stats, refs->blocks[i]->refs[j]); + mark_object(refs->blocks[i]->refs[j]); } } for (size_t i = 0; i < last_block_len; ++i) { - mark_object_recurse(stats, refs->blocks[full_blocks]->refs[i]); + mark_object(refs->blocks[full_blocks]->refs[i]); } } -static void mark_stack_frame(LispGCStats *restrict stats, - struct StackFrame *frame) { - mark_object_recurse(stats, frame->name); - mark_object_recurse(stats, frame->args); - mark_object_recurse(stats, frame->fobj); - mark_object_recurse(stats, frame->lexenv); - mark_stack_local_refs(stats, &frame->local_refs); +static void mark_stack_frame(struct StackFrame *frame) { + mark_object(frame->name); + mark_object(frame->args); + mark_object(frame->fobj); + mark_object(frame->lexenv); + mark_stack_local_refs(&frame->local_refs); } -static void mark_and_compact_the_stack(LispGCStats *restrict stats) { - mark_object_recurse(stats, the_stack.nogc_retval); +static void mark_and_compact_the_stack(void) { + mark_object(the_stack.nogc_retval); size_t i; for (i = 0; i < the_stack.depth; ++i) { - mark_stack_frame(stats, &the_stack.frames[i]); + mark_stack_frame(&the_stack.frames[i]); } for (; i < the_stack.first_clear_local_refs; ++i) { compact_stack_frame(&the_stack.frames[i]); @@ -340,9 +255,14 @@ static void mark_and_compact_the_stack(LispGCStats *restrict stats) { the_stack.first_clear_local_refs = the_stack.depth; } -static void gc_sweep_objects(LispGCStats *restrict stats) { +static void mark_grey_objects(void) { + while (grey_objects) { + mark_object(grey_objects->obj); + } +} + +static void gc_sweep_objects(void) { while (white_objects) { - ++stats->objects_cleaned; free_object(white_objects->obj); } } @@ -365,34 +285,25 @@ static void swap_white_black_sets(void) { GC_BLACK = tmp_id; } -void lisp_gc_now(LispGCStats *restrict stats) { +void lisp_gc_now(struct timespec *restrict time_took) { lisp_doing_gc = true; - LispGCStats backup_stats; - if (!stats) { - stats = &backup_stats; - } - stats->objects_cleaned = 0; - stats->non_statics_kept = 0; struct timespec start_time; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_time); - mark_statics(stats); - mark_object_recurse(stats, obarray); - mark_and_compact_the_stack(stats); - gc_sweep_objects(stats); + mark_statics(); + mark_object(obarray); + mark_and_compact_the_stack(); + mark_grey_objects(); + gc_sweep_objects(); maybe_free_some_object_list_nodes(); swap_white_black_sets(); struct timespec end_time; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_time); - sub_timespecs(&end_time, &start_time, &stats->ellapsed_time); - add_timespecs(&stats->ellapsed_time, &total_gc_time, &total_gc_time); + struct timespec backup_time_took; + if (!time_took) { + time_took = &backup_time_took; + } + sub_timespecs(&end_time, &start_time, time_took); + add_timespecs(time_took, &total_gc_time, &total_gc_time); ++total_gc_count; lisp_doing_gc = false; } - -void debug_print_gc_stats(FILE *stream, const LispGCStats *stats) { - fprintf(stream, "Non-Statics Kept: %zu\n", stats->non_statics_kept); - fprintf(stream, "Objects Cleaned: %zu\n", stats->objects_cleaned); - double time = stats->ellapsed_time.tv_sec * 1000 - + (stats->ellapsed_time.tv_nsec / 1000000.0); - fprintf(stream, "Time Ellapsed (ms): %f\n", time); -} diff --git a/src/gc.h b/src/gc.h index 2be5c16..fe81348 100644 --- a/src/gc.h +++ b/src/gc.h @@ -1,51 +1,16 @@ #ifndef INCLUDED_GC_H #define INCLUDED_GC_H -#include "memory.h" - +#include #include +#include #include #include -#define OBJECT_PROCESS_STACK_BLOCK_SIZE 64 -struct ObjectProcessStackBlock { - void *objs[OBJECT_PROCESS_STACK_BLOCK_SIZE]; -}; -typedef struct { - size_t num_blocks; - size_t num_objs; - struct ObjectProcessStackBlock **blocks; -} ObjectProcessStack; - -static ALWAYS_INLINE bool -OBJECT_PROCESS_STACK_EMPTY_P(ObjectProcessStack *restrict stack) { - return !stack->num_objs; -} - -static ALWAYS_INLINE void -init_object_process_stack(ObjectProcessStack *restrict stack) { - stack->num_blocks = 0; - stack->num_objs = 0; - stack->blocks = NULL; -} - -void free_object_process_stack(ObjectProcessStack *restrict stack); -void object_process_stack_push_object(ObjectProcessStack *restrict stack, - void *obj); -void object_process_stack_push_held_objects(ObjectProcessStack *restrict stack, - void *obj); -void *object_process_stack_pop(ObjectProcessStack *restrict stack); - extern bool lisp_doing_gc; extern struct timespec total_gc_time; extern size_t total_gc_count; -typedef struct { - size_t non_statics_kept; - size_t objects_cleaned; - struct timespec ellapsed_time; -} LispGCStats; - typedef uint8_t ObjectGCSet; extern ObjectGCSet GC_BLACK; @@ -66,10 +31,6 @@ void lisp_gc_register_object(void *val); void lisp_gc_register_static_object(void *val); void gc_move_to_set(void *val, ObjectGCSet new_set); -// note that the argument is restrict! -void lisp_gc_now(LispGCStats *restrict stats); - -// Debug -void debug_print_gc_stats(FILE *stream, const LispGCStats *stats); +void lisp_gc_now(struct timespec *restrict time_took); #endif diff --git a/src/main.c b/src/main.c index 8ece9d7..7565900 100644 --- a/src/main.c +++ b/src/main.c @@ -4,33 +4,26 @@ #include -/* DEFUN(cool_func, "cool-func", (LispVal * a, LispVal *b), "(a &optional b)", - */ -/* "") { */ -/* printf("A: "); */ -/* debug_obj_info(stdout, a); */ -/* printf("B: "); */ -/* debug_obj_info(stdout, b); */ -/* return Qnil; */ -/* } */ +DEFUN(cool_func, "cool-func", (LispVal * a, LispVal *b), "(a &optional b)", + "") { + printf("A: "); + debug_obj_info(stdout, a); + printf("B: "); + debug_obj_info(stdout, b); + return Qnil; +} -/* int main(int argc, const cha-r **argv) { */ -/* LispGCStats gc_stats; */ -/* lisp_init(); */ -/* REGISTER_GLOBAL_FUNCTION(cool_func); */ -/* push_stack_frame(Qnil, Qnil, Qnil); */ -/* ReadStream s; */ -/* const char BUF[] = "(cool-func 1 (cons 1 2))"; */ -/* read_stream_init(&s, BUF, sizeof(BUF) - 1); */ -/* LispVal *l = read(&s); */ -/* Feval(l); */ -/* lisp_gc_now(&gc_stats); */ -/* debug_print_gc_stats(stdout, &gc_stats); */ -/* pop_stack_frame(); */ -/* lisp_shutdown(); */ -/* return 0; */ -/* } */ - -int main(int argc, const char **argvc) { +int main(int argc, const char **argv) { + lisp_init(); + REGISTER_GLOBAL_FUNCTION(cool_func); + push_stack_frame(Qnil, Qnil, Qnil); + ReadStream s; + const char BUF[] = "(cool-func 1 (cons 1 2))"; + read_stream_init(&s, BUF, sizeof(BUF) - 1); + LispVal *l = read(&s); + Feval(l); + lisp_gc_now(NULL); + pop_stack_frame(); + lisp_shutdown(); return 0; } diff --git a/src/stack.c b/src/stack.c index 1fe0652..2a31b3f 100644 --- a/src/stack.c +++ b/src/stack.c @@ -1,6 +1,8 @@ #include "stack.h" +#include "function.h" #include "hashtable.h" +#include "list.h" #include "memory.h" #include @@ -82,26 +84,86 @@ void add_local_reference_no_recurse(LispVal *obj) { } } +static LispVal *next_local_reference(size_t *restrict i) { + if (*i >= LISP_STACK_TOP()->local_refs.num_refs) { + return NULL; + } + size_t block_idx = *i / LOCAL_REFERENCES_BLOCK_LENGTH; + size_t small_idx = *i % LOCAL_REFERENCES_BLOCK_LENGTH; + LispVal *obj = + LISP_STACK_TOP()->local_refs.blocks[block_idx]->refs[small_idx]; + ++*i; + return obj; +} + +static inline void add_local_ref_if_not_seen_no_recurse(LispVal *seen_objs, + LispVal *obj) { + if (NILP(Fgethash(seen_objs, obj, Qnil))) { + add_local_reference_no_recurse(obj); + Fputhash(seen_objs, obj, Qt); + } +} + +static inline void add_local_refs_for_object_sub_vals(LispVal *seen_objs, + LispVal *val) { + switch (((LispObject *) val)->type) { + case TYPE_CONS: + add_local_ref_if_not_seen_no_recurse(seen_objs, + ((LispCons *) val)->car); + add_local_ref_if_not_seen_no_recurse(seen_objs, + ((LispCons *) val)->cdr); + break; + case TYPE_SYMBOL: { + LispSymbol *sym = val; + add_local_ref_if_not_seen_no_recurse(seen_objs, sym->name); + add_local_ref_if_not_seen_no_recurse(seen_objs, sym->value); + add_local_ref_if_not_seen_no_recurse(seen_objs, sym->function); + add_local_ref_if_not_seen_no_recurse(seen_objs, sym->plist); + break; + } + case TYPE_VECTOR: { + LispVector *vec = val; + for (size_t i = 0; i < vec->length; ++i) { + add_local_ref_if_not_seen_no_recurse(seen_objs, vec->data[i]); + } + break; + } + case TYPE_HASH_TABLE: { + HT_FOREACH_INDEX(val, i) { + add_local_ref_if_not_seen_no_recurse(seen_objs, HASH_KEY(val, i)); + add_local_ref_if_not_seen_no_recurse(seen_objs, HASH_VALUE(val, i)); + } + break; + } + case TYPE_FUNCTION: { + LispFunction *fobj = val; + add_local_ref_if_not_seen_no_recurse(seen_objs, fobj->name); + add_local_ref_if_not_seen_no_recurse(seen_objs, fobj->docstr); + add_local_ref_if_not_seen_no_recurse(seen_objs, fobj->args.req); + add_local_ref_if_not_seen_no_recurse(seen_objs, fobj->args.opt); + add_local_ref_if_not_seen_no_recurse(seen_objs, fobj->args.kw); + add_local_ref_if_not_seen_no_recurse(seen_objs, fobj->args.rest); + break; + } + case TYPE_STRING: + // ho held refs + break; + case TYPE_FIXNUM: + case TYPE_FLOAT: + default: + abort(); + } +} + void add_local_reference(LispVal *obj) { add_local_reference_no_recurse(obj); LispVal *seen_objs = make_hash_table_no_gc(Qnil, Qnil); - ObjectProcessStack stack; - init_object_process_stack(&stack); - object_process_stack_push_held_objects(&stack, obj); - while (!OBJECT_PROCESS_STACK_EMPTY_P(&stack)) { - LispVal *top = object_process_stack_pop(&stack); - assert(OBJECTP(top)); - if (!OBJECT_HAS_LOCAL_REFERENCE_P(obj)) { - store_local_reference_in_frame(LISP_STACK_TOP(), top); - SET_OBJECT_HAS_LOCAL_REFERENCE(obj, true); - } - if (NILP(Fgethash(seen_objs, obj, Qnil))) { - object_process_stack_push_held_objects(&stack, top); - Fputhash(seen_objs, obj, Qt); - } + Fputhash(seen_objs, obj, Qt); + size_t i = LISP_STACK_TOP()->local_refs.num_refs - 1; + LispVal *cur; + while ((cur = next_local_reference(&i))) { + add_local_refs_for_object_sub_vals(seen_objs, cur); } - free_object_process_stack(&stack); - release_hash_table_no_gc(seen_objs); } void compact_stack_frame(struct StackFrame *restrict frame) {