From 2c46407bd837518e1ff12d5c400e3c548a639423 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Sun, 8 Dec 2013 11:52:24 -0800 Subject: [PATCH] Initial commit. --- COPYRIGHT | 30 +++++++ main.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 278 insertions(+) create mode 100644 COPYRIGHT create mode 100644 main.c diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..d7ee7e5 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,30 @@ +This uses the MIT License: + +Copyright (c) 2013 Robert Nystrom + +Permission is hereby granted, free of charge, to +any person obtaining a copy of this software and +associated documentation files (the "Software"), +to deal in the Software without restriction, +including without limitation the rights to use, +copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is +furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission +notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT +WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/main.c b/main.c new file mode 100644 index 0000000..68efeea --- /dev/null +++ b/main.c @@ -0,0 +1,248 @@ +#include +#include + +#define STACK_MAX 256 + +typedef enum { + OBJ_INT, + OBJ_PAIR +} ObjectType; + +typedef struct sObject { + ObjectType type; + unsigned char marked; + + /* The next object in the linked list of heap allocated objects. */ + struct sObject* next; + + union { + /* OBJ_INT */ + int value; + + /* OBJ_PAIR */ + struct { + struct sObject* head; + struct sObject* tail; + }; + }; +} Object; + +typedef struct { + Object* stack[STACK_MAX]; + int stackSize; + + /* The first object in the linked list of all objects on the heap. */ + Object* firstObject; + + /* The total number of currently allocated objects. */ + int numObjects; + + /* The number of objects required to trigger a GC. */ + 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->firstObject = NULL; + vm->numObjects = 0; + vm->maxObjects = 8; + return vm; +} + +void push(VM* vm, Object* value) { + assert(vm->stackSize < STACK_MAX, "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 already marked, we're done. Check this first to avoid recursing + on cycles in the object graph. */ + 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->firstObject; + while (*object) { + if (!(*object)->marked) { + /* This object wasn't reached, so remove it from the list and free it. */ + Object* unreached = *object; + + *object = unreached->next; + free(unreached); + + vm->numObjects--; + } else { + /* This object was reached, so unmark it (for the next GC) and move on to + the next. */ + (*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->type = type; + object->next = vm->firstObject; + vm->firstObject = object; + object->marked = 0; + + vm->numObjects++; + + return object; +} + +void pushInt(VM* vm, int intValue) { + Object* object = newObject(vm, OBJ_INT); + object->value = intValue; + + push(vm, object); +} + +Object* pushPair(VM* vm) { + Object* object = newObject(vm, OBJ_PAIR); + object->tail = pop(vm); + object->head = pop(vm); + + push(vm, object); + return object; +} + +void objectPrint(Object* object) { + switch (object->type) { + case OBJ_INT: + printf("%d", 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."); +} + +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."); +} + +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."); +} + +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); + + a->tail = b; + b->tail = a; + + gc(vm); + assert(vm->numObjects == 4, "Should have collected objects."); +} + +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); + } + } +} + +int main(int argc, const char * argv[]) { + test1(); + test2(); + test3(); + test4(); + + perfTest(); + + return 0; +}