Compare commits

..

No commits in common. "26ef0bc3ad3a5bef00f2d2d36eb2e543dea4bf70" and "c73dfc2c0060991de10addf27c79b5e0dfd44b41" have entirely different histories.

7 changed files with 214 additions and 220 deletions

View file

@ -1,23 +1,22 @@
CC = gcc CC = gcc
CFLAGS = -std=gnu11 CFLAGS =
CPPFLAGS = -Iinclude -MMD -MP CPPFLAGS = -Iinclude -MMD -MP
SRCDIR = src SRCDIR = src
SRCS := $(wildcard $(SRCDIR)/*.c) SRCS = $(wildcard $(SRCDIR)/*.c)
OBJS := $(patsubst $(SRCDIR)/%.c,%.o,$(SRCS)) OBJS = $(patsubst $(SRCDIR)/%.c,%.o,$(SRCS))
EXE = gc EXE = gc
DBDIR = debug DBDIR = debug
DBEXE := $(DBDIR)/$(EXE) DBEXE = $(DBDIR)/$(EXE)
DBOBJS := $(addprefix $(DBDIR)/, $(OBJS)) DBOBJS = $(addprefix $(DBDIR)/, $(OBJS))
DBCFLAGS = -g -O0 -DDEBUG DBCFLAGS = -g -O0 -DDEBUG
REDIR = bin
REDIR = bin REEXE = $(REDIR)/$(EXE)
REEXE := $(REDIR)/$(EXE) REOBJS = $(addprefix $(REDIR)/, $(OBJS))
REOBJS := $(addprefix $(REDIR)/, $(OBJS)) RECFLAGS = -O3 -Wall -Wextra -Wpedantic -Werror
RECFLAGS = -O3 -Wall -Wextra -Wpedantic -Werror
.PHONY: all clean debug prep release .PHONY: all clean debug prep release
@ -44,5 +43,3 @@ prep:
clean: clean:
rm -rf $(DBDIR) $(REDIR) rm -rf $(DBDIR) $(REDIR)
-include $(OBJ:.o=.d)

View file

@ -6,9 +6,9 @@
// Metadata for allocated memory - Headers are stored in a linked list to keep // Metadata for allocated memory - Headers are stored in a linked list to keep
// track of alloc's and free's // track of alloc's and free's
struct gcHeader { struct gcHeader {
size_t size; size_t size;
int free; int free;
struct gcHeader *next; struct gcHeader *next;
}; };
// Iterates over the galloc linked list attempting to find free space // Iterates over the galloc linked list attempting to find free space

View file

@ -6,38 +6,38 @@
// Primitive types for garbage collection // Primitive types for garbage collection
enum garbageData { enum garbageData {
GARBAGE_INT, GARBAGE_INT,
GARBAGE_PAIR, GARBAGE_PAIR,
}; };
// Holds type and value data for the virtual machine // Holds type and value data for the virtual machine
struct garbageObject { struct garbageObject {
enum garbageData type; enum garbageData type;
struct garbageObject *next; struct garbageObject *next;
unsigned char mark; unsigned char mark;
union { union {
int value; int value;
struct { struct {
struct garbageObject *head; struct garbageObject *head;
struct garbageObject *tail; struct garbageObject *tail;
}; };
}; };
}; };
// Virtual machine hold the stack to trace references // Virtual machine hold the stack to trace references
struct virtualMachine { struct virtualMachine {
int stackSize; int stackSize;
unsigned refCount; unsigned refCount;
unsigned refMax; unsigned refMax;
struct garbageObject *head; struct garbageObject *head;
struct garbageObject *stack[STACK_MAX]; struct garbageObject *stack[STACK_MAX];
}; };
// Create a new garbageObject // Create a new garbageObject
struct garbageObject *initGarbage(struct virtualMachine *vm, struct garbageObject *initGarbage(struct virtualMachine *vm,
enum garbageData type); enum garbageData type);
// Create a new VM // Create a new VM
struct virtualMachine *initVM(void); struct virtualMachine *initVM(void);

View file

@ -4,45 +4,44 @@
void mark(struct garbageObject *obj) void mark(struct garbageObject *obj)
{ {
if (obj->mark) if (obj->mark)
return; return;
++obj->mark; ++obj->mark;
if (obj->type == GARBAGE_PAIR) { if (obj->type == GARBAGE_PAIR) {
mark(obj->head); mark(obj->head);
mark(obj->tail); mark(obj->tail);
} }
} }
void markAll(struct virtualMachine *vm) void markAll(struct virtualMachine *vm)
{ {
for (int i = 0; i < vm->stackSize; ++i) { for (int i = 0; i < vm->stackSize; ++i) {
mark(vm->stack[i]); mark(vm->stack[i]);
} }
} }
void sweep(struct virtualMachine *vm) void sweep(struct virtualMachine *vm)
{ {
struct garbageObject **obj = &vm->head; struct garbageObject **obj = &vm->head;
while (*obj) { while (*obj) {
if (!(*obj)->mark) { if (!(*obj)->mark) {
struct garbageObject *unreachable = *obj; struct garbageObject *unreachable = *obj;
*obj = unreachable->next; *obj = unreachable->next;
free(unreachable); free(unreachable);
vm->refCount--; vm->refCount--;
} else { } else {
(*obj)->mark = 0; (*obj)->mark = 0;
obj = &(*obj)->next; obj = &(*obj)->next;
} }
} }
} }
void collect(struct virtualMachine *vm) void collect(struct virtualMachine *vm)
{ {
markAll(vm); markAll(vm);
sweep(vm); sweep(vm);
vm->refMax = vm->refCount * 2 <= STACK_MAX ? vm->refCount * 2 : vm->refMax = vm->refCount * 2 <= STACK_MAX ? vm->refCount * 2 : STACK_MAX;
STACK_MAX;
} }

View file

@ -5,47 +5,45 @@
static void *allocHead = NULL; static void *allocHead = NULL;
struct gcHeader *findFree(struct gcHeader **prev, size_t size) struct gcHeader *findFree(struct gcHeader **prev, size_t size) {
{ struct gcHeader *curr = allocHead;
struct gcHeader *curr = allocHead;
while (curr && !(curr->free && curr->size >= size)) { while (curr && !(curr->free && curr->size >= size)) {
*prev = curr; *prev = curr;
curr = curr->next; curr = curr->next;
} }
return curr; return curr;
} }
struct gcHeader *requestMem(struct gcHeader *prev, size_t size) struct gcHeader *requestMem(struct gcHeader *prev, size_t size) {
{ struct gcHeader *block;
struct gcHeader *block; void *request;
void *request;
block = sbrk(0); block = sbrk(0);
if (block == (void *)-1) { if (block == (void *)-1) {
perror("sbrk block"); perror("sbrk block");
return NULL; return NULL;
} }
request = sbrk(size + sizeof(struct gcHeader)); request = sbrk(size + sizeof(struct gcHeader));
if (request == (void *)-1) { if (request == (void *)-1) {
perror("sbrk request"); perror("sbrk request");
return NULL; return NULL;
} }
if ((void *)block != request) { if ((void *)block != request) {
fprintf(stderr, "gcHeader: block != request"); fprintf(stderr, "gcHeader: block != request");
return NULL; return NULL;
} }
if (prev) { if (prev) {
prev->next = block; prev->next = block;
} }
block->size = size; block->size = size;
block->next = NULL; block->next = NULL;
block->free = 0; block->free = 0;
return block; return block;
} }

View file

@ -7,107 +7,108 @@
void test_int_alloc(void) void test_int_alloc(void)
{ {
struct virtualMachine *vm = initVM(); struct virtualMachine *vm = initVM();
pushInt(vm, 100); pushInt(vm, 100);
pushInt(vm, 1000); pushInt(vm, 1000);
pushInt(vm, 10000); pushInt(vm, 10000);
pushInt(vm, -100000); pushInt(vm, -100000);
assert(vm->refCount == 4 && assert(vm->refCount == 4
"test_int_alloc: GARBAGE_INT allocation failure\n"); && "test_int_alloc: GARBAGE_INT allocation failure\n");
printf("test_int_alloc: PASS\n"); printf("test_int_alloc: PASS\n");
deinitVM(vm); deinitVM(vm);
} }
void test_pair_alloc(void) void test_pair_alloc(void)
{ {
struct virtualMachine *vm = initVM(); struct virtualMachine *vm = initVM();
pushInt(vm, 100); pushInt(vm, 100);
pushInt(vm, 1000); pushInt(vm, 1000);
pushInt(vm, 10000); pushInt(vm, 10000);
pushInt(vm, -100000); pushInt(vm, -100000);
pushPair(vm); pushPair(vm);
pushPair(vm); pushPair(vm);
assert(vm->refCount == 6 && assert(vm->refCount == 6
"test_pair_alloc: FAILED: GARBAGE_PAIR allocation failure\n"); && "test_pair_alloc: FAILED: GARBAGE_PAIR allocation failure\n");
printf("test_pair_alloc: PASS\n"); printf("test_pair_alloc: PASS\n");
deinitVM(vm); deinitVM(vm);
} }
void test_obj_count(void) void test_obj_count(void)
{ {
struct virtualMachine *vm = initVM(); struct virtualMachine *vm = initVM();
pushInt(vm, 100); pushInt(vm, 100);
pushInt(vm, 1000); pushInt(vm, 1000);
pushInt(vm, 10000); pushInt(vm, 10000);
pushInt(vm, -100000); pushInt(vm, -100000);
collect(vm); collect(vm);
assert(vm->refCount == 4 && assert(vm->refCount == 4
"test_obj_count: FAILED: GC occurred when it shouldn't have\n"); && "test_obj_count: FAILED: GC occurred when it shouldn't have\n");
printf("test_obj_count: PASS\n"); printf("test_obj_count: PASS\n");
deinitVM(vm); deinitVM(vm);
} }
void test_nested_pair(void) void test_nested_pair(void)
{ {
struct virtualMachine *vm = initVM(); struct virtualMachine *vm = initVM();
pushInt(vm, 100); pushInt(vm, 100);
pushInt(vm, 1000); pushInt(vm, 1000);
pushPair(vm); pushPair(vm);
pushInt(vm, 10000); pushInt(vm, 10000);
pushInt(vm, -100000); pushInt(vm, -100000);
pushPair(vm); pushPair(vm);
pushPair(vm); pushPair(vm);
collect(vm); collect(vm);
assert(vm->refCount == 7 && assert(vm->refCount == 7
"test_nested_pair: FAILED: GARBAGE_PAIR allocation failure\n"); && "test_nested_pair: FAILED: GARBAGE_PAIR allocation failure\n");
printf("test_pair_alloc: PASS\n"); printf("test_pair_alloc: PASS\n");
deinitVM(vm); deinitVM(vm);
} }
void test_unreachable(void) void test_unreachable(void)
{ {
struct virtualMachine *vm = initVM(); struct virtualMachine *vm = initVM();
pushInt(vm, 100); pushInt(vm, 100);
pushInt(vm, 1000); pushInt(vm, 1000);
pop(vm); pop(vm);
pop(vm); pop(vm);
collect(vm); collect(vm);
assert(vm->refCount == 0 && assert(
"test_unreachable: FAILED: 2 GARBAGE_INT should have been freed\n"); vm->refCount == 0
printf("test_unreachable: PASS\n"); && "test_unreachable: FAILED: 2 GARBAGE_INT should have been freed\n");
deinitVM(vm); printf("test_unreachable: PASS\n");
deinitVM(vm);
} }
void test_auto_gc(void) void test_auto_gc(void)
{ {
struct virtualMachine *vm = initVM(); struct virtualMachine *vm = initVM();
for (size_t i = 0; i < 505; ++i) { for (size_t i = 0; i < 505; ++i) {
pushInt(vm, 1); pushInt(vm, 1);
} }
for (size_t i = 0; i < 5; ++i) { for (size_t i = 0; i < 5; ++i) {
pop(vm); pop(vm);
} }
for (size_t i = 0; i < 50; ++i) { for (size_t i = 0; i < 50; ++i) {
pushInt(vm, 2); pushInt(vm, 2);
} }
assert(vm->refCount == 550 && assert(vm->refCount == 550
"test_auto_gc: FAILED: 5 references should have been freed\n"); && "test_auto_gc: FAILED: 5 references should have been freed\n");
printf("test_auto_gc: PASS\n"); printf("test_auto_gc: PASS\n");
deinitVM(vm); deinitVM(vm);
} }
int main(void) int main(void)
{ {
test_int_alloc(); test_int_alloc();
test_pair_alloc(); test_pair_alloc();
test_obj_count(); test_obj_count();
test_nested_pair(); test_nested_pair();
test_unreachable(); test_unreachable();
test_auto_gc(); test_auto_gc();
return 0; return 0;
} }

101
src/vm.c
View file

@ -6,85 +6,84 @@
struct virtualMachine *initVM(void) struct virtualMachine *initVM(void)
{ {
struct virtualMachine *vm = malloc(sizeof(struct virtualMachine)); struct virtualMachine *vm = malloc(sizeof(struct virtualMachine));
vm->stackSize = 0; vm->stackSize = 0;
vm->refCount = 0; vm->refCount = 0;
vm->refMax = GC_THRESHOLD; vm->refMax = GC_THRESHOLD;
vm->head = NULL; vm->head = NULL;
return vm; return vm;
} }
void deinitVM(struct virtualMachine *vm) void deinitVM(struct virtualMachine *vm)
{ {
vm->stackSize = 0; vm->stackSize = 0;
collect(vm); collect(vm);
free(vm); free(vm);
} }
void push(struct virtualMachine *vm, struct garbageObject *value) void push(struct virtualMachine *vm, struct garbageObject *value)
{ {
if (vm->stackSize >= STACK_MAX) { if (vm->stackSize >= STACK_MAX) {
fprintf(stderr, fprintf(stderr, "ERROR: push(): refusing to overflow the stack!\n");
"ERROR: push(): refusing to overflow the stack!\n"); return;
return; }
} vm->stack[vm->stackSize++] = value;
vm->stack[vm->stackSize++] = value;
} }
struct garbageObject *pop(struct virtualMachine *vm) struct garbageObject *pop(struct virtualMachine *vm)
{ {
if (vm->stackSize < 0) { if (vm->stackSize < 0) {
fprintf(stderr, "ERROR: pop(): Stack size cannot be negative!"); fprintf(stderr, "ERROR: pop(): Stack size cannot be negative!");
return NULL; return NULL;
} }
return vm->stack[--vm->stackSize]; return vm->stack[--vm->stackSize];
} }
struct garbageObject *initGarbage(struct virtualMachine *vm, struct garbageObject *initGarbage(struct virtualMachine *vm,
enum garbageData type) enum garbageData type)
{ {
if (vm->refCount >= vm->refMax) { if (vm->refCount >= vm->refMax) {
collect(vm); collect(vm);
} }
struct garbageObject *obj = malloc(sizeof(struct garbageObject)); struct garbageObject *obj = malloc(sizeof(struct garbageObject));
if (obj == NULL) { if (obj == NULL) {
fprintf(stderr, "initGarbage(): Allocation failure!"); fprintf(stderr, "initGarbage(): Allocation failure!");
return obj; return obj;
} }
obj->type = type; obj->type = type;
obj->mark = 0; obj->mark = 0;
obj->next = vm->head; obj->next = vm->head;
vm->head = obj; vm->head = obj;
vm->refCount++; vm->refCount++;
return obj; return obj;
} }
void pushInt(struct virtualMachine *vm, int value) void pushInt(struct virtualMachine *vm, int value)
{ {
struct garbageObject *obj = initGarbage(vm, GARBAGE_INT); struct garbageObject *obj = initGarbage(vm, GARBAGE_INT);
if (obj == NULL) { if (obj == NULL) {
fprintf(stderr, fprintf(stderr,
"pushInt(): Unable to create GARBAGE_INT - out of memory?"); "pushInt(): Unable to create GARBAGE_INT - out of memory?");
} }
obj->value = value; obj->value = value;
push(vm, obj); push(vm, obj);
} }
struct garbageObject *pushPair(struct virtualMachine *vm) struct garbageObject *pushPair(struct virtualMachine *vm)
{ {
struct garbageObject *obj = initGarbage(vm, GARBAGE_PAIR); struct garbageObject *obj = initGarbage(vm, GARBAGE_PAIR);
if (obj == NULL) { if (obj == NULL) {
fprintf(stderr, fprintf(stderr,
"pushPair(): Unable to create GARBAGE_PAIR - out of memory?"); "pushPair(): Unable to create GARBAGE_PAIR - out of memory?");
} }
obj->tail = pop(vm); obj->tail = pop(vm);
obj->head = pop(vm); obj->head = pop(vm);
push(vm, obj); push(vm, obj);
return obj; return obj;
} }