mirror of
https://codeberg.org/andyscott/marCsweep.git
synced 2024-11-09 13:50:51 -05:00
Compare commits
No commits in common. "26ef0bc3ad3a5bef00f2d2d36eb2e543dea4bf70" and "c73dfc2c0060991de10addf27c79b5e0dfd44b41" have entirely different histories.
26ef0bc3ad
...
c73dfc2c00
7 changed files with 214 additions and 220 deletions
27
Makefile
27
Makefile
|
@ -1,23 +1,22 @@
|
|||
CC = gcc
|
||||
CFLAGS = -std=gnu11
|
||||
CFLAGS =
|
||||
CPPFLAGS = -Iinclude -MMD -MP
|
||||
|
||||
SRCDIR = src
|
||||
|
||||
SRCS := $(wildcard $(SRCDIR)/*.c)
|
||||
OBJS := $(patsubst $(SRCDIR)/%.c,%.o,$(SRCS))
|
||||
EXE = gc
|
||||
SRCS = $(wildcard $(SRCDIR)/*.c)
|
||||
OBJS = $(patsubst $(SRCDIR)/%.c,%.o,$(SRCS))
|
||||
EXE = gc
|
||||
|
||||
DBDIR = debug
|
||||
DBEXE := $(DBDIR)/$(EXE)
|
||||
DBOBJS := $(addprefix $(DBDIR)/, $(OBJS))
|
||||
DBCFLAGS = -g -O0 -DDEBUG
|
||||
DBDIR = debug
|
||||
DBEXE = $(DBDIR)/$(EXE)
|
||||
DBOBJS = $(addprefix $(DBDIR)/, $(OBJS))
|
||||
DBCFLAGS = -g -O0 -DDEBUG
|
||||
|
||||
|
||||
REDIR = bin
|
||||
REEXE := $(REDIR)/$(EXE)
|
||||
REOBJS := $(addprefix $(REDIR)/, $(OBJS))
|
||||
RECFLAGS = -O3 -Wall -Wextra -Wpedantic -Werror
|
||||
REDIR = bin
|
||||
REEXE = $(REDIR)/$(EXE)
|
||||
REOBJS = $(addprefix $(REDIR)/, $(OBJS))
|
||||
RECFLAGS = -O3 -Wall -Wextra -Wpedantic -Werror
|
||||
|
||||
.PHONY: all clean debug prep release
|
||||
|
||||
|
@ -44,5 +43,3 @@ prep:
|
|||
|
||||
clean:
|
||||
rm -rf $(DBDIR) $(REDIR)
|
||||
|
||||
-include $(OBJ:.o=.d)
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
// Metadata for allocated memory - Headers are stored in a linked list to keep
|
||||
// track of alloc's and free's
|
||||
struct gcHeader {
|
||||
size_t size;
|
||||
int free;
|
||||
struct gcHeader *next;
|
||||
size_t size;
|
||||
int free;
|
||||
struct gcHeader *next;
|
||||
};
|
||||
|
||||
// Iterates over the galloc linked list attempting to find free space
|
||||
|
|
36
include/vm.h
36
include/vm.h
|
@ -6,38 +6,38 @@
|
|||
|
||||
// Primitive types for garbage collection
|
||||
enum garbageData {
|
||||
GARBAGE_INT,
|
||||
GARBAGE_PAIR,
|
||||
GARBAGE_INT,
|
||||
GARBAGE_PAIR,
|
||||
};
|
||||
|
||||
// Holds type and value data for the virtual machine
|
||||
struct garbageObject {
|
||||
enum garbageData type;
|
||||
struct garbageObject *next;
|
||||
unsigned char mark;
|
||||
enum garbageData type;
|
||||
struct garbageObject *next;
|
||||
unsigned char mark;
|
||||
|
||||
union {
|
||||
int value;
|
||||
union {
|
||||
int value;
|
||||
|
||||
struct {
|
||||
struct garbageObject *head;
|
||||
struct garbageObject *tail;
|
||||
};
|
||||
};
|
||||
struct {
|
||||
struct garbageObject *head;
|
||||
struct garbageObject *tail;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// Virtual machine hold the stack to trace references
|
||||
struct virtualMachine {
|
||||
int stackSize;
|
||||
unsigned refCount;
|
||||
unsigned refMax;
|
||||
struct garbageObject *head;
|
||||
struct garbageObject *stack[STACK_MAX];
|
||||
int stackSize;
|
||||
unsigned refCount;
|
||||
unsigned refMax;
|
||||
struct garbageObject *head;
|
||||
struct garbageObject *stack[STACK_MAX];
|
||||
};
|
||||
|
||||
// Create a new garbageObject
|
||||
struct garbageObject *initGarbage(struct virtualMachine *vm,
|
||||
enum garbageData type);
|
||||
enum garbageData type);
|
||||
|
||||
// Create a new VM
|
||||
struct virtualMachine *initVM(void);
|
||||
|
|
51
src/gc.c
51
src/gc.c
|
@ -4,45 +4,44 @@
|
|||
|
||||
void mark(struct garbageObject *obj)
|
||||
{
|
||||
if (obj->mark)
|
||||
return;
|
||||
if (obj->mark)
|
||||
return;
|
||||
|
||||
++obj->mark;
|
||||
++obj->mark;
|
||||
|
||||
if (obj->type == GARBAGE_PAIR) {
|
||||
mark(obj->head);
|
||||
mark(obj->tail);
|
||||
}
|
||||
if (obj->type == GARBAGE_PAIR) {
|
||||
mark(obj->head);
|
||||
mark(obj->tail);
|
||||
}
|
||||
}
|
||||
|
||||
void markAll(struct virtualMachine *vm)
|
||||
{
|
||||
for (int i = 0; i < vm->stackSize; ++i) {
|
||||
mark(vm->stack[i]);
|
||||
}
|
||||
for (int i = 0; i < vm->stackSize; ++i) {
|
||||
mark(vm->stack[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void sweep(struct virtualMachine *vm)
|
||||
{
|
||||
struct garbageObject **obj = &vm->head;
|
||||
struct garbageObject **obj = &vm->head;
|
||||
|
||||
while (*obj) {
|
||||
if (!(*obj)->mark) {
|
||||
struct garbageObject *unreachable = *obj;
|
||||
*obj = unreachable->next;
|
||||
free(unreachable);
|
||||
vm->refCount--;
|
||||
} else {
|
||||
(*obj)->mark = 0;
|
||||
obj = &(*obj)->next;
|
||||
}
|
||||
}
|
||||
while (*obj) {
|
||||
if (!(*obj)->mark) {
|
||||
struct garbageObject *unreachable = *obj;
|
||||
*obj = unreachable->next;
|
||||
free(unreachable);
|
||||
vm->refCount--;
|
||||
} else {
|
||||
(*obj)->mark = 0;
|
||||
obj = &(*obj)->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collect(struct virtualMachine *vm)
|
||||
{
|
||||
markAll(vm);
|
||||
sweep(vm);
|
||||
vm->refMax = vm->refCount * 2 <= STACK_MAX ? vm->refCount * 2 :
|
||||
STACK_MAX;
|
||||
markAll(vm);
|
||||
sweep(vm);
|
||||
vm->refMax = vm->refCount * 2 <= STACK_MAX ? vm->refCount * 2 : STACK_MAX;
|
||||
}
|
||||
|
|
|
@ -5,47 +5,45 @@
|
|||
|
||||
static void *allocHead = NULL;
|
||||
|
||||
struct gcHeader *findFree(struct gcHeader **prev, size_t size)
|
||||
{
|
||||
struct gcHeader *curr = allocHead;
|
||||
struct gcHeader *findFree(struct gcHeader **prev, size_t size) {
|
||||
struct gcHeader *curr = allocHead;
|
||||
|
||||
while (curr && !(curr->free && curr->size >= size)) {
|
||||
*prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
while (curr && !(curr->free && curr->size >= size)) {
|
||||
*prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
return curr;
|
||||
return curr;
|
||||
}
|
||||
|
||||
struct gcHeader *requestMem(struct gcHeader *prev, size_t size)
|
||||
{
|
||||
struct gcHeader *block;
|
||||
void *request;
|
||||
struct gcHeader *requestMem(struct gcHeader *prev, size_t size) {
|
||||
struct gcHeader *block;
|
||||
void *request;
|
||||
|
||||
block = sbrk(0);
|
||||
if (block == (void *)-1) {
|
||||
perror("sbrk block");
|
||||
return NULL;
|
||||
}
|
||||
block = sbrk(0);
|
||||
if (block == (void *)-1) {
|
||||
perror("sbrk block");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
request = sbrk(size + sizeof(struct gcHeader));
|
||||
if (request == (void *)-1) {
|
||||
perror("sbrk request");
|
||||
return NULL;
|
||||
}
|
||||
request = sbrk(size + sizeof(struct gcHeader));
|
||||
if (request == (void *)-1) {
|
||||
perror("sbrk request");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((void *)block != request) {
|
||||
fprintf(stderr, "gcHeader: block != request");
|
||||
return NULL;
|
||||
}
|
||||
if ((void *)block != request) {
|
||||
fprintf(stderr, "gcHeader: block != request");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
prev->next = block;
|
||||
}
|
||||
if (prev) {
|
||||
prev->next = block;
|
||||
}
|
||||
|
||||
block->size = size;
|
||||
block->next = NULL;
|
||||
block->free = 0;
|
||||
block->size = size;
|
||||
block->next = NULL;
|
||||
block->free = 0;
|
||||
|
||||
return block;
|
||||
return block;
|
||||
}
|
||||
|
|
149
src/main.c
149
src/main.c
|
@ -7,107 +7,108 @@
|
|||
|
||||
void test_int_alloc(void)
|
||||
{
|
||||
struct virtualMachine *vm = initVM();
|
||||
pushInt(vm, 100);
|
||||
pushInt(vm, 1000);
|
||||
pushInt(vm, 10000);
|
||||
pushInt(vm, -100000);
|
||||
assert(vm->refCount == 4 &&
|
||||
"test_int_alloc: GARBAGE_INT allocation failure\n");
|
||||
printf("test_int_alloc: PASS\n");
|
||||
deinitVM(vm);
|
||||
struct virtualMachine *vm = initVM();
|
||||
pushInt(vm, 100);
|
||||
pushInt(vm, 1000);
|
||||
pushInt(vm, 10000);
|
||||
pushInt(vm, -100000);
|
||||
assert(vm->refCount == 4
|
||||
&& "test_int_alloc: GARBAGE_INT allocation failure\n");
|
||||
printf("test_int_alloc: PASS\n");
|
||||
deinitVM(vm);
|
||||
}
|
||||
|
||||
void test_pair_alloc(void)
|
||||
{
|
||||
struct virtualMachine *vm = initVM();
|
||||
pushInt(vm, 100);
|
||||
pushInt(vm, 1000);
|
||||
pushInt(vm, 10000);
|
||||
pushInt(vm, -100000);
|
||||
pushPair(vm);
|
||||
pushPair(vm);
|
||||
assert(vm->refCount == 6 &&
|
||||
"test_pair_alloc: FAILED: GARBAGE_PAIR allocation failure\n");
|
||||
printf("test_pair_alloc: PASS\n");
|
||||
deinitVM(vm);
|
||||
struct virtualMachine *vm = initVM();
|
||||
pushInt(vm, 100);
|
||||
pushInt(vm, 1000);
|
||||
pushInt(vm, 10000);
|
||||
pushInt(vm, -100000);
|
||||
pushPair(vm);
|
||||
pushPair(vm);
|
||||
assert(vm->refCount == 6
|
||||
&& "test_pair_alloc: FAILED: GARBAGE_PAIR allocation failure\n");
|
||||
printf("test_pair_alloc: PASS\n");
|
||||
deinitVM(vm);
|
||||
}
|
||||
|
||||
void test_obj_count(void)
|
||||
{
|
||||
struct virtualMachine *vm = initVM();
|
||||
pushInt(vm, 100);
|
||||
pushInt(vm, 1000);
|
||||
pushInt(vm, 10000);
|
||||
pushInt(vm, -100000);
|
||||
collect(vm);
|
||||
assert(vm->refCount == 4 &&
|
||||
"test_obj_count: FAILED: GC occurred when it shouldn't have\n");
|
||||
printf("test_obj_count: PASS\n");
|
||||
deinitVM(vm);
|
||||
struct virtualMachine *vm = initVM();
|
||||
pushInt(vm, 100);
|
||||
pushInt(vm, 1000);
|
||||
pushInt(vm, 10000);
|
||||
pushInt(vm, -100000);
|
||||
collect(vm);
|
||||
assert(vm->refCount == 4
|
||||
&& "test_obj_count: FAILED: GC occurred when it shouldn't have\n");
|
||||
printf("test_obj_count: PASS\n");
|
||||
deinitVM(vm);
|
||||
}
|
||||
|
||||
void test_nested_pair(void)
|
||||
{
|
||||
struct virtualMachine *vm = initVM();
|
||||
pushInt(vm, 100);
|
||||
pushInt(vm, 1000);
|
||||
pushPair(vm);
|
||||
pushInt(vm, 10000);
|
||||
pushInt(vm, -100000);
|
||||
pushPair(vm);
|
||||
pushPair(vm);
|
||||
collect(vm);
|
||||
assert(vm->refCount == 7 &&
|
||||
"test_nested_pair: FAILED: GARBAGE_PAIR allocation failure\n");
|
||||
printf("test_pair_alloc: PASS\n");
|
||||
deinitVM(vm);
|
||||
struct virtualMachine *vm = initVM();
|
||||
pushInt(vm, 100);
|
||||
pushInt(vm, 1000);
|
||||
pushPair(vm);
|
||||
pushInt(vm, 10000);
|
||||
pushInt(vm, -100000);
|
||||
pushPair(vm);
|
||||
pushPair(vm);
|
||||
collect(vm);
|
||||
assert(vm->refCount == 7
|
||||
&& "test_nested_pair: FAILED: GARBAGE_PAIR allocation failure\n");
|
||||
printf("test_pair_alloc: PASS\n");
|
||||
deinitVM(vm);
|
||||
}
|
||||
|
||||
void test_unreachable(void)
|
||||
{
|
||||
struct virtualMachine *vm = initVM();
|
||||
pushInt(vm, 100);
|
||||
pushInt(vm, 1000);
|
||||
pop(vm);
|
||||
pop(vm);
|
||||
collect(vm);
|
||||
assert(vm->refCount == 0 &&
|
||||
"test_unreachable: FAILED: 2 GARBAGE_INT should have been freed\n");
|
||||
printf("test_unreachable: PASS\n");
|
||||
deinitVM(vm);
|
||||
struct virtualMachine *vm = initVM();
|
||||
pushInt(vm, 100);
|
||||
pushInt(vm, 1000);
|
||||
pop(vm);
|
||||
pop(vm);
|
||||
collect(vm);
|
||||
assert(
|
||||
vm->refCount == 0
|
||||
&& "test_unreachable: FAILED: 2 GARBAGE_INT should have been freed\n");
|
||||
printf("test_unreachable: PASS\n");
|
||||
deinitVM(vm);
|
||||
}
|
||||
|
||||
void test_auto_gc(void)
|
||||
{
|
||||
struct virtualMachine *vm = initVM();
|
||||
struct virtualMachine *vm = initVM();
|
||||
|
||||
for (size_t i = 0; i < 505; ++i) {
|
||||
pushInt(vm, 1);
|
||||
}
|
||||
for (size_t i = 0; i < 505; ++i) {
|
||||
pushInt(vm, 1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
pop(vm);
|
||||
}
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
pop(vm);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 50; ++i) {
|
||||
pushInt(vm, 2);
|
||||
}
|
||||
for (size_t i = 0; i < 50; ++i) {
|
||||
pushInt(vm, 2);
|
||||
}
|
||||
|
||||
assert(vm->refCount == 550 &&
|
||||
"test_auto_gc: FAILED: 5 references should have been freed\n");
|
||||
printf("test_auto_gc: PASS\n");
|
||||
deinitVM(vm);
|
||||
assert(vm->refCount == 550
|
||||
&& "test_auto_gc: FAILED: 5 references should have been freed\n");
|
||||
printf("test_auto_gc: PASS\n");
|
||||
deinitVM(vm);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_int_alloc();
|
||||
test_pair_alloc();
|
||||
test_obj_count();
|
||||
test_nested_pair();
|
||||
test_unreachable();
|
||||
test_auto_gc();
|
||||
test_int_alloc();
|
||||
test_pair_alloc();
|
||||
test_obj_count();
|
||||
test_nested_pair();
|
||||
test_unreachable();
|
||||
test_auto_gc();
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
101
src/vm.c
101
src/vm.c
|
@ -6,85 +6,84 @@
|
|||
|
||||
struct virtualMachine *initVM(void)
|
||||
{
|
||||
struct virtualMachine *vm = malloc(sizeof(struct virtualMachine));
|
||||
vm->stackSize = 0;
|
||||
vm->refCount = 0;
|
||||
vm->refMax = GC_THRESHOLD;
|
||||
vm->head = NULL;
|
||||
return vm;
|
||||
struct virtualMachine *vm = malloc(sizeof(struct virtualMachine));
|
||||
vm->stackSize = 0;
|
||||
vm->refCount = 0;
|
||||
vm->refMax = GC_THRESHOLD;
|
||||
vm->head = NULL;
|
||||
return vm;
|
||||
}
|
||||
|
||||
void deinitVM(struct virtualMachine *vm)
|
||||
{
|
||||
vm->stackSize = 0;
|
||||
collect(vm);
|
||||
free(vm);
|
||||
vm->stackSize = 0;
|
||||
collect(vm);
|
||||
free(vm);
|
||||
}
|
||||
|
||||
void push(struct virtualMachine *vm, struct garbageObject *value)
|
||||
{
|
||||
if (vm->stackSize >= STACK_MAX) {
|
||||
fprintf(stderr,
|
||||
"ERROR: push(): refusing to overflow the stack!\n");
|
||||
return;
|
||||
}
|
||||
vm->stack[vm->stackSize++] = value;
|
||||
if (vm->stackSize >= STACK_MAX) {
|
||||
fprintf(stderr, "ERROR: push(): refusing to overflow the stack!\n");
|
||||
return;
|
||||
}
|
||||
vm->stack[vm->stackSize++] = value;
|
||||
}
|
||||
|
||||
struct garbageObject *pop(struct virtualMachine *vm)
|
||||
{
|
||||
if (vm->stackSize < 0) {
|
||||
fprintf(stderr, "ERROR: pop(): Stack size cannot be negative!");
|
||||
return NULL;
|
||||
}
|
||||
if (vm->stackSize < 0) {
|
||||
fprintf(stderr, "ERROR: pop(): Stack size cannot be negative!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return vm->stack[--vm->stackSize];
|
||||
return vm->stack[--vm->stackSize];
|
||||
}
|
||||
|
||||
struct garbageObject *initGarbage(struct virtualMachine *vm,
|
||||
enum garbageData type)
|
||||
enum garbageData type)
|
||||
{
|
||||
if (vm->refCount >= vm->refMax) {
|
||||
collect(vm);
|
||||
}
|
||||
if (vm->refCount >= vm->refMax) {
|
||||
collect(vm);
|
||||
}
|
||||
|
||||
struct garbageObject *obj = malloc(sizeof(struct garbageObject));
|
||||
if (obj == NULL) {
|
||||
fprintf(stderr, "initGarbage(): Allocation failure!");
|
||||
return obj;
|
||||
}
|
||||
struct garbageObject *obj = malloc(sizeof(struct garbageObject));
|
||||
if (obj == NULL) {
|
||||
fprintf(stderr, "initGarbage(): Allocation failure!");
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj->type = type;
|
||||
obj->mark = 0;
|
||||
obj->next = vm->head;
|
||||
vm->head = obj;
|
||||
vm->refCount++;
|
||||
return obj;
|
||||
obj->type = type;
|
||||
obj->mark = 0;
|
||||
obj->next = vm->head;
|
||||
vm->head = obj;
|
||||
vm->refCount++;
|
||||
return obj;
|
||||
}
|
||||
|
||||
void pushInt(struct virtualMachine *vm, int value)
|
||||
{
|
||||
struct garbageObject *obj = initGarbage(vm, GARBAGE_INT);
|
||||
if (obj == NULL) {
|
||||
fprintf(stderr,
|
||||
"pushInt(): Unable to create GARBAGE_INT - out of memory?");
|
||||
}
|
||||
struct garbageObject *obj = initGarbage(vm, GARBAGE_INT);
|
||||
if (obj == NULL) {
|
||||
fprintf(stderr,
|
||||
"pushInt(): Unable to create GARBAGE_INT - out of memory?");
|
||||
}
|
||||
|
||||
obj->value = value;
|
||||
push(vm, obj);
|
||||
obj->value = value;
|
||||
push(vm, obj);
|
||||
}
|
||||
|
||||
struct garbageObject *pushPair(struct virtualMachine *vm)
|
||||
{
|
||||
struct garbageObject *obj = initGarbage(vm, GARBAGE_PAIR);
|
||||
if (obj == NULL) {
|
||||
fprintf(stderr,
|
||||
"pushPair(): Unable to create GARBAGE_PAIR - out of memory?");
|
||||
}
|
||||
struct garbageObject *obj = initGarbage(vm, GARBAGE_PAIR);
|
||||
if (obj == NULL) {
|
||||
fprintf(stderr,
|
||||
"pushPair(): Unable to create GARBAGE_PAIR - out of memory?");
|
||||
}
|
||||
|
||||
obj->tail = pop(vm);
|
||||
obj->head = pop(vm);
|
||||
obj->tail = pop(vm);
|
||||
obj->head = pop(vm);
|
||||
|
||||
push(vm, obj);
|
||||
return obj;
|
||||
push(vm, obj);
|
||||
return obj;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue