#include "gc.h" #include "runtime_common.h" #include #include #include #include #ifdef DEBUG_VERSION // function from runtime that maps string to int value extern int LtagHash (char *s); extern void *Bsexp (int n, ...); extern void *Barray (int bn, ...); extern void *Bstring (void *); extern void *Bclosure (int bn, void *entry, ...); extern size_t __gc_stack_top, __gc_stack_bottom; void test_correct_structure_sizes (void) { // something like induction base assert((array_size(0) == get_header_size(ARRAY))); assert((string_size(0) == get_header_size(STRING) + 1)); // +1 is because of '\0' assert((sexp_size(0) == get_header_size(SEXP) + MEMBER_SIZE)); assert((closure_size(0) == get_header_size(CLOSURE))); // just check correctness for some small sizes for (int k = 1; k < 20; ++k) { assert((array_size(k) == get_header_size(ARRAY) + MEMBER_SIZE * k)); assert((string_size(k) == get_header_size(STRING) + k + 1)); assert((sexp_size(k) == get_header_size(SEXP) + MEMBER_SIZE * (k + 1))); assert((closure_size(k) == get_header_size(CLOSURE) + MEMBER_SIZE * k)); } } void no_gc_tests (void) { test_correct_structure_sizes(); } // unfortunately there is no generic function pointer that can hold pointer to function with arbitrary signature extern size_t call_runtime_function (void *virt_stack_pointer, void *function_pointer, size_t num_args, ...); # include "virt_stack.h" virt_stack *init_test () { __init(); virt_stack *st = vstack_create(); vstack_init(st); __gc_stack_bottom = (size_t)vstack_top(st); return st; } void cleanup_test (virt_stack *st) { vstack_destruct(st); __shutdown(); } void force_gc_cycle (virt_stack *st) { __gc_stack_top = (size_t)vstack_top(st) - 4; gc_alloc(0); __gc_stack_top = 0; } void test_simple_string_alloc (void) { virt_stack *st = init_test(); for (int i = 0; i < 5; ++i) { vstack_push(st, BOX(i)); } vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "abc")); const int N = 10; int ids[N]; size_t alive = objects_snapshot(ids, N); assert((alive == 1)); cleanup_test(st); } void test_simple_array_alloc (void) { virt_stack *st = init_test(); // allocate array [ BOX(1) ] and push it onto the stack vstack_push(st, call_runtime_function(vstack_top(st) - 4, Barray, 2, BOX(1), BOX(1))); const int N = 10; int ids[N]; size_t alive = objects_snapshot(ids, N); assert((alive == 1)); cleanup_test(st); } void test_simple_sexp_alloc (void) { virt_stack *st = init_test(); // allocate sexp with one boxed field and push it onto the stack // calling runtime function Bsexp(BOX(2), BOX(1), LtagHash("test")) vstack_push( st, call_runtime_function(vstack_top(st) - 4, Bsexp, 3, BOX(2), BOX(1), LtagHash("test"))); const int N = 10; int ids[N]; size_t alive = objects_snapshot(ids, N); assert((alive == 1)); cleanup_test(st); } void test_simple_closure_alloc (void) { virt_stack *st = init_test(); // allocate closure with boxed captured value and push it onto the stack vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bclosure, 3, BOX(1), NULL, BOX(1))); const int N = 10; int ids[N]; size_t alive = objects_snapshot(ids, N); assert((alive == 1)); cleanup_test(st); } void test_single_object_allocation_with_collection_virtual_stack (void) { virt_stack *st = init_test(); vstack_push(st, call_runtime_function( vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); const int N = 10; int ids[N]; size_t alive = objects_snapshot(ids, N); assert((alive == 1)); cleanup_test(st); } void test_garbage_is_reclaimed (void) { virt_stack *st = init_test(); call_runtime_function(vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); force_gc_cycle(st); const int N = 10; int ids[N]; size_t alive = objects_snapshot(ids, N); assert((alive == 0)); cleanup_test(st); } void test_alive_are_not_reclaimed (void) { virt_stack *st = init_test(); vstack_push(st, call_runtime_function( vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); force_gc_cycle(st); const int N = 10; int ids[N]; size_t alive = objects_snapshot(ids, N); assert((alive == 1)); cleanup_test(st); } void test_small_tree_compaction (void) { virt_stack *st = init_test(); // this one will increase heap size call_runtime_function(vstack_top(st) - 4, Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaa"); vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "left-s")); vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bstring, 1, "right-s")); vstack_push(st, call_runtime_function(vstack_top(st) - 4, Bsexp, 4, BOX(3), vstack_kth_from_start(st, 0), vstack_kth_from_start(st, 1), LtagHash("tree"))); force_gc_cycle(st); const int SZ = 10; int ids[SZ]; size_t alive = objects_snapshot(ids, SZ); assert((alive == 3)); // check that order is indeed preserved for (int i = 0; i < alive - 1; ++i) { assert((ids[i] < ids[i + 1])); } cleanup_test(st); } extern size_t cur_id; size_t generate_random_obj_forest (virt_stack *st, int cnt, int seed) { srand(seed); int cur_sz = 0; size_t alive = 0; while (cnt) { --cnt; if (cur_sz == 0) { vstack_push(st, BOX(1)); ++cur_sz; continue; } size_t pos[2] = {rand() % vstack_size(st), rand() % vstack_size(st)}; size_t field[2]; for (int t = 0; t < 2; ++t) { field[t] = vstack_kth_from_start(st, pos[t]); } size_t obj; if (rand() % 2) { obj = call_runtime_function( vstack_top(st) - 4, Bsexp, 4, BOX(3), field[0], field[1], LtagHash("test")); } else { obj = BOX(1); } // whether object is stored on stack if (rand() % 2 != 0) { vstack_push(st, obj); if ((obj & 1) == 0) { ++alive; } } ++cur_sz; } force_gc_cycle(st); return alive; } void run_stress_test_random_obj_forest (int seed) { virt_stack *st = init_test(); const int SZ = 100000; size_t expectedAlive = generate_random_obj_forest(st, SZ, seed); int ids[SZ]; size_t alive = objects_snapshot(ids, SZ); assert(alive == expectedAlive); // check that order is indeed preserved for (int i = 0; i < alive - 1; ++i) { assert((ids[i] < ids[i + 1])); } cleanup_test(st); } #endif #include int main (int argc, char **argv) { #ifdef DEBUG_VERSION no_gc_tests(); test_simple_string_alloc(); test_simple_array_alloc(); test_simple_sexp_alloc(); test_simple_closure_alloc(); test_single_object_allocation_with_collection_virtual_stack(); test_garbage_is_reclaimed(); test_alive_are_not_reclaimed(); test_small_tree_compaction(); time_t start, end; double diff; time(&start); // stress test for (int s = 0; s < 100; ++s) { run_stress_test_random_obj_forest(s); } time(&end); diff = difftime(end, start); printf("Stress tests took %.2lf seconds to complete\n", diff); #endif }