commit e0a2599d8debf2d6871eb121bc4af8345fbfa62c Author: Elf M. Sternberg Date: Sat Dec 10 17:00:58 2016 -0800 Initial check-in. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9d39f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +*# +.#* +*~ +*.orig +npm-debug.log +package.yml +node_modules/* +tmp/ +bin/_mocha +bin/mocha +bin/escodegen +bin/esgenerate +test-reports.xml +LisperatorLanguage +src/test.js +src/test.coffee +notes/ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..802a300 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +project("Collector") + +list(APPEND CMAKE_C_FLAGS "-std=c99 ${CMAKE_C_FLAGS} -g -ftest-coverage -fprofile-arcs") + +add_executable(collector + src/collector.c) + diff --git a/src/collector.c b/src/collector.c new file mode 100644 index 0000000..4daa110 --- /dev/null +++ b/src/collector.c @@ -0,0 +1,238 @@ +#include +#include + +#define MAX_STACK 256 +#define MAX_BARRIER 8 + +typedef enum { + OBJ_INT, + OBJ_PAIR +} ObjectType; + +typedef struct sObject { + unsigned char marked; + struct sObject* next; + ObjectType type; + union { + int value; + struct { + struct sObject* head; + struct sObject* tail; + }; + }; +} Object; + +typedef struct { + Object* stack[MAX_STACK]; + Object* root; + int stackSize; + int numObjects; + int maxObjects; +} VM; + +void assert(int condition, const char* message) { + if (!condition) { + printf("%s\n", message); + exit(1); + } +} + +VM* newVM() { + VM* vm = malloc(sizeof(VM)); + vm->stackSize = 0; + vm->numObjects = 0; + vm->maxObjects = MAX_BARRIER; + return vm; +} + +void push(VM* vm, Object* value) { + assert(vm->stackSize < MAX_STACK, "Stack overflow!"); + vm->stack[vm->stackSize++] = value; +} + +Object* pop(VM* vm) { + assert(vm->stackSize > 0, "Stack underflow!"); + return vm->stack[--(vm->stackSize)]; +} + +void mark(Object* object) { + if (object->marked) { + return; + } + + object->marked = 1; + if (object->type == OBJ_PAIR) { + mark(object->head); + mark(object->tail); + } +} + +void markAll(VM* vm) { + for(int i = 0; i < vm->stackSize; i++) { + mark(vm->stack[i]); + } +} + +void sweep(VM* vm) { + Object** object = &vm->root; + while (*object) { + if (!(*object)->marked) { + Object* unreached = *object; + *object = unreached->next; + vm->numObjects--; + free(unreached); + } else { + (*object)->marked = 0; + object = &(*object)->next; + } + } +} + +void gc(VM* vm) { + int numObjects = vm->numObjects; + markAll(vm); + sweep(vm); + vm->maxObjects = vm->numObjects * 2; + printf("Collected %d objects, %d remaining.\n", numObjects - vm->numObjects, + vm->numObjects); +} + +Object *newObject(VM* vm, ObjectType type) { + if (vm->numObjects == vm->maxObjects) { + gc(vm); + } + + Object* object = malloc(sizeof(Object)); + object->marked = 0; + object->type = type; + object->next = vm->root; + vm->root = object; + vm->numObjects++; + return object; +} + +Object* pushInt(VM* vm, int value) { + Object* object = newObject(vm, OBJ_INT); + object->value = value; + push(vm, object); + return object; +} + +Object* pushPair(VM* vm) { + Object* object = newObject(vm, OBJ_PAIR); + object->head = pop(vm); + object->tail = pop(vm); + push(vm, object); + return object; +} + +void freeVM(VM *vm) { + vm->stackSize = 0; + gc(vm); + free(vm); +} + +void objectPrint(Object* object) { + switch(object->type) { + case OBJ_INT: + printf("%d\n", object->value); + break; + + case OBJ_PAIR: + printf("("); + objectPrint(object->head); + printf(", "); + objectPrint(object->tail); + printf(")"); + break; + } +} + +void test1() { + printf("Test 1: Objects on stack are preserved.\n"); + VM* vm = newVM(); + pushInt(vm, 1); + pushInt(vm, 2); + + gc(vm); + assert(vm->numObjects == 2, "Should have preserved objects."); + freeVM(vm); +} + +void test2() { + printf("Test 2: Unreached objects are collected.\n"); + VM* vm = newVM(); + pushInt(vm, 1); + pushInt(vm, 2); + pop(vm); + pop(vm); + + gc(vm); + assert(vm->numObjects == 0, "Should have collected objects."); + freeVM(vm); +} + +void test3() { + printf("Test 3: Reach nested objects.\n"); + VM* vm = newVM(); + pushInt(vm, 1); + pushInt(vm, 2); + pushPair(vm); + pushInt(vm, 3); + pushInt(vm, 4); + pushPair(vm); + pushPair(vm); + + gc(vm); + assert(vm->numObjects == 7, "Should have reached objects."); + freeVM(vm); +} + +void test4() { + printf("Test 4: Handle cycles.\n"); + VM* vm = newVM(); + pushInt(vm, 1); + pushInt(vm, 2); + Object* a = pushPair(vm); + pushInt(vm, 3); + pushInt(vm, 4); + Object* b = pushPair(vm); + + /* Set up a cycle, and also make 2 and 4 unreachable and collectible. */ + a->tail = b; + b->tail = a; + + gc(vm); + assert(vm->numObjects == 4, "Should have collected objects."); + freeVM(vm); +} + +void perfTest() { + printf("Performance Test.\n"); + VM* vm = newVM(); + + for (int i = 0; i < 1000; i++) { + for (int j = 0; j < 20; j++) { + pushInt(vm, i); + } + + for (int k = 0; k < 20; k++) { + pop(vm); + } + } + freeVM(vm); +} + +int main(int argc, const char * argv[]) { + test1(); + test2(); + test3(); + test4(); + perfTest(); + + return 0; +} + + + +