mirror of
https://codeberg.org/andyscott/marCsweep.git
synced 2024-11-09 13:50:51 -05:00
Compare commits
2 commits
c73dfc2c00
...
26ef0bc3ad
Author | SHA1 | Date | |
---|---|---|---|
26ef0bc3ad | |||
0747e78b15 |
7 changed files with 220 additions and 214 deletions
27
Makefile
27
Makefile
|
@ -1,22 +1,23 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS =
|
CFLAGS = -std=gnu11
|
||||||
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
|
|
||||||
REEXE = $(REDIR)/$(EXE)
|
REDIR = bin
|
||||||
REOBJS = $(addprefix $(REDIR)/, $(OBJS))
|
REEXE := $(REDIR)/$(EXE)
|
||||||
RECFLAGS = -O3 -Wall -Wextra -Wpedantic -Werror
|
REOBJS := $(addprefix $(REDIR)/, $(OBJS))
|
||||||
|
RECFLAGS = -O3 -Wall -Wextra -Wpedantic -Werror
|
||||||
|
|
||||||
.PHONY: all clean debug prep release
|
.PHONY: all clean debug prep release
|
||||||
|
|
||||||
|
@ -43,3 +44,5 @@ prep:
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(DBDIR) $(REDIR)
|
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
|
// 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
|
||||||
|
|
36
include/vm.h
36
include/vm.h
|
@ -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);
|
||||||
|
|
51
src/gc.c
51
src/gc.c
|
@ -4,44 +4,45 @@
|
||||||
|
|
||||||
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 : STACK_MAX;
|
vm->refMax = vm->refCount * 2 <= STACK_MAX ? vm->refCount * 2 :
|
||||||
|
STACK_MAX;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,45 +5,47 @@
|
||||||
|
|
||||||
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;
|
{
|
||||||
void *request;
|
struct gcHeader *block;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
149
src/main.c
149
src/main.c
|
@ -7,108 +7,107 @@
|
||||||
|
|
||||||
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(
|
assert(vm->refCount == 0 &&
|
||||||
vm->refCount == 0
|
"test_unreachable: FAILED: 2 GARBAGE_INT should have been freed\n");
|
||||||
&& "test_unreachable: FAILED: 2 GARBAGE_INT should have been freed\n");
|
printf("test_unreachable: PASS\n");
|
||||||
printf("test_unreachable: PASS\n");
|
deinitVM(vm);
|
||||||
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
101
src/vm.c
|
@ -6,84 +6,85 @@
|
||||||
|
|
||||||
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, "ERROR: push(): refusing to overflow the stack!\n");
|
fprintf(stderr,
|
||||||
return;
|
"ERROR: push(): refusing to overflow the stack!\n");
|
||||||
}
|
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue