mirror of
https://codeberg.org/andyscott/marCsweep.git
synced 2024-12-22 17:53:10 -05:00
Compare commits
3 commits
620f779274
...
c73dfc2c00
Author | SHA1 | Date | |
---|---|---|---|
c73dfc2c00 | |||
73e2716100 | |||
02d017af64 |
14 changed files with 301 additions and 274 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,7 +1,9 @@
|
||||||
*
|
*
|
||||||
!.gitignore
|
!.gitignore
|
||||||
!*.c
|
|
||||||
!*.h
|
|
||||||
!*.md
|
!*.md
|
||||||
!LICENSE
|
!LICENSE
|
||||||
!Makefile
|
!Makefile
|
||||||
|
!/src
|
||||||
|
!/src/*.c
|
||||||
|
!/include
|
||||||
|
!/include/*.h
|
||||||
|
|
36
Makefile
36
Makefile
|
@ -1,8 +1,11 @@
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -O3 -Wall -Wextra -Wpedantic
|
CFLAGS =
|
||||||
|
CPPFLAGS = -Iinclude -MMD -MP
|
||||||
|
|
||||||
SRCS = main.c vm.c gc.c galloc.c
|
SRCDIR = src
|
||||||
OBJS = $(SRCS:.c=.o)
|
|
||||||
|
SRCS = $(wildcard $(SRCDIR)/*.c)
|
||||||
|
OBJS = $(patsubst $(SRCDIR)/%.c,%.o,$(SRCS))
|
||||||
EXE = gc
|
EXE = gc
|
||||||
|
|
||||||
DBDIR = debug
|
DBDIR = debug
|
||||||
|
@ -10,38 +13,33 @@ DBEXE = $(DBDIR)/$(EXE)
|
||||||
DBOBJS = $(addprefix $(DBDIR)/, $(OBJS))
|
DBOBJS = $(addprefix $(DBDIR)/, $(OBJS))
|
||||||
DBCFLAGS = -g -O0 -DDEBUG
|
DBCFLAGS = -g -O0 -DDEBUG
|
||||||
|
|
||||||
REDIR = build
|
REDIR = bin
|
||||||
REEXE = $(REDIR)/$(EXE)
|
REEXE = $(REDIR)/$(EXE)
|
||||||
REOBJS = $(addprefix $(REDIR)/, $(OBJS))
|
REOBJS = $(addprefix $(REDIR)/, $(OBJS))
|
||||||
|
RECFLAGS = -O3 -Wall -Wextra -Wpedantic -Werror
|
||||||
|
|
||||||
.PHONY: all clean debug release prep
|
.PHONY: all clean debug prep release
|
||||||
|
|
||||||
all: debug release gc
|
all: debug release
|
||||||
|
|
||||||
gc: $(REEXE)
|
|
||||||
@cp $< $@
|
|
||||||
|
|
||||||
debug: prep $(DBEXE)
|
debug: prep $(DBEXE)
|
||||||
|
|
||||||
$(DBEXE): $(DBOBJS)
|
$(DBEXE): $(DBOBJS)
|
||||||
$(CC) $(CFLAGS) $(DBCFLAGS) -DTEST -o $@ $^
|
$(CC) $(CFLAGS) $(DBCFLAGS) -o $@ $^
|
||||||
|
|
||||||
$(DBDIR)/%.o: %.c
|
$(DBDIR)/%.o: $(SRCDIR)/%.c
|
||||||
$(CC) -c $(CFLAGS) $(DBCFLAGS) -o $@ $<
|
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DBCFLAGS) -o $@ $<
|
||||||
|
|
||||||
release: prep $(REEXE)
|
release: prep $(REEXE)
|
||||||
|
|
||||||
$(REEXE): $(REOBJS)
|
$(REEXE): $(REOBJS)
|
||||||
$(CC) $(CFLAGS) -o $@ $^
|
$(CC) $(CFLAGS) $(RECFLAGS) -o $@ $^
|
||||||
|
|
||||||
$(REDIR)/%.o: %.c
|
$(REDIR)/%.o: $(SRCDIR)/%.c
|
||||||
$(CC) -c $(CFLAGS) -o $@ $<
|
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(RECFLAGS) -o $@ $<
|
||||||
|
|
||||||
prep:
|
prep:
|
||||||
@mkdir -p $(DBDIR) $(REDIR)
|
@mkdir -p $(DBDIR) $(REDIR)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf gc $(DBDIR) $(REDIR)
|
rm -rf $(DBDIR) $(REDIR)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
28
README.md
28
README.md
|
@ -1,38 +1,44 @@
|
||||||
# marCsweep
|
# marCsweep
|
||||||
|
|
||||||
A mark-and-sweep garbage collector with a simple virtual machine to perform
|
A mark-and-sweep garbage collector written in C.
|
||||||
allocations.
|
|
||||||
|
Includes a simple virtual machine and a custom `malloc` implementation
|
||||||
|
for creating, tracing, and freeing garbage.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
Compiling the project should be relatively easy, even if you aren't familiar
|
Compiling the project should be relatively easy, even if you aren't familiar
|
||||||
with `make`.
|
with `make`.
|
||||||
|
|
||||||
Dependencies: `git`, `make`, and `gcc`. Compiling with `clang` is also possible,
|
Dependencies: `git`, `make`, and `gcc`.
|
||||||
simply replace "gcc" with "clang" in the first line of the Makefile.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone https://codeberg.org/andyscott/marCsweep.git
|
git clone https://codeberg.org/andyscott/marCsweep.git
|
||||||
|
|
||||||
cd marCsweep
|
cd marCsweep
|
||||||
|
|
||||||
make release
|
make release # or debug
|
||||||
```
|
```
|
||||||
|
|
||||||
That's it! The Makefile will automatically create the build directory and place
|
That's it! Make will create the `bin` directory and place the finished
|
||||||
the compiled executable there.
|
executable there.
|
||||||
|
|
||||||
|
Compiling with `clang` is also supported, just replace "gcc" with "clang" in the
|
||||||
|
first line of the `Makefile`. Also note that if you don't specify a target for
|
||||||
|
make (i.e. "release" or "debug") both will be built. Debug builds are placed in
|
||||||
|
the `debug` directory.
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
Once compiled, the program can be run with the following:
|
Once compiled, the program can be run with the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd build # or debug
|
cd bin # or debug
|
||||||
|
|
||||||
./gc
|
./gc
|
||||||
```
|
```
|
||||||
|
|
||||||
By default the program will output the results of the test cases:
|
The results of the test cases will be output to your terminal:
|
||||||
|
|
||||||
> test_int_alloc: PASS
|
> test_int_alloc: PASS
|
||||||
test_pair_alloc: PASS
|
test_pair_alloc: PASS
|
||||||
|
@ -50,3 +56,7 @@ This is a personal project that I am writing in my free time to learn more about
|
||||||
garbage collection. Thank you to [Robert
|
garbage collection. Thank you to [Robert
|
||||||
Nystrom](https://journal.stuffwithstuff.com/2013/12/08/babys-first-garbage-collector/)
|
Nystrom](https://journal.stuffwithstuff.com/2013/12/08/babys-first-garbage-collector/)
|
||||||
for the idea.
|
for the idea.
|
||||||
|
|
||||||
|
Sadly, it was this author's idea to extend the project by writing a custom
|
||||||
|
`malloc`. Perhaps I should have been lazy and used the preexisting definitions,
|
||||||
|
but where's the fun in that?
|
||||||
|
|
44
gc.c
44
gc.c
|
@ -1,44 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "gc.h"
|
|
||||||
#include "vm.h"
|
|
||||||
|
|
||||||
void mark(struct garbageObject *obj) {
|
|
||||||
if (obj->mark)
|
|
||||||
return;
|
|
||||||
|
|
||||||
++obj->mark;
|
|
||||||
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sweep(struct virtualMachine *vm) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void collect(struct virtualMachine *vm) {
|
|
||||||
markAll(vm);
|
|
||||||
sweep(vm);
|
|
||||||
vm->refMax = vm->refCount * 2 <= STACK_MAX ? vm->refCount * 2 : STACK_MAX;
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef GALLOC_H
|
#ifndef GC_ALLOC_H
|
||||||
#define GALLOC_H
|
#define GC_ALLOC_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
@ -23,4 +23,4 @@ void *galloc(size_t size);
|
||||||
// Frees memory allocated with galloc()
|
// Frees memory allocated with galloc()
|
||||||
void gfree(void *ptr);
|
void gfree(void *ptr);
|
||||||
|
|
||||||
#endif /* GALLOC_H */
|
#endif /* GC_ALLOC_H */
|
107
main.c
107
main.c
|
@ -1,107 +0,0 @@
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "gc.h"
|
|
||||||
#include "test_gc.h"
|
|
||||||
#include "vm.h"
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_auto_gc(void) {
|
|
||||||
struct virtualMachine *vm = initVM();
|
|
||||||
|
|
||||||
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 < 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
|
|
||||||
test_int_alloc();
|
|
||||||
test_pair_alloc();
|
|
||||||
test_obj_count();
|
|
||||||
test_nested_pair();
|
|
||||||
test_unreachable();
|
|
||||||
test_auto_gc();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
47
src/gc.c
Normal file
47
src/gc.c
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "gc.h"
|
||||||
|
|
||||||
|
void mark(struct garbageObject *obj)
|
||||||
|
{
|
||||||
|
if (obj->mark)
|
||||||
|
return;
|
||||||
|
|
||||||
|
++obj->mark;
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sweep(struct virtualMachine *vm)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void collect(struct virtualMachine *vm)
|
||||||
|
{
|
||||||
|
markAll(vm);
|
||||||
|
sweep(vm);
|
||||||
|
vm->refMax = vm->refCount * 2 <= STACK_MAX ? vm->refCount * 2 : STACK_MAX;
|
||||||
|
}
|
114
src/main.c
Normal file
114
src/main.c
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "gc.h"
|
||||||
|
#include "test_gc.h"
|
||||||
|
#include "vm.h"
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_auto_gc(void)
|
||||||
|
{
|
||||||
|
struct virtualMachine *vm = initVM();
|
||||||
|
|
||||||
|
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 < 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
test_int_alloc();
|
||||||
|
test_pair_alloc();
|
||||||
|
test_obj_count();
|
||||||
|
test_nested_pair();
|
||||||
|
test_unreachable();
|
||||||
|
test_auto_gc();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
89
src/vm.c
Normal file
89
src/vm.c
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "gc.h"
|
||||||
|
#include "vm.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinitVM(struct virtualMachine *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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct garbageObject *pop(struct virtualMachine *vm)
|
||||||
|
{
|
||||||
|
if (vm->stackSize < 0) {
|
||||||
|
fprintf(stderr, "ERROR: pop(): Stack size cannot be negative!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm->stack[--vm->stackSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct garbageObject *initGarbage(struct virtualMachine *vm,
|
||||||
|
enum garbageData type)
|
||||||
|
{
|
||||||
|
if (vm->refCount >= vm->refMax) {
|
||||||
|
collect(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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?");
|
||||||
|
}
|
||||||
|
|
||||||
|
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?");
|
||||||
|
}
|
||||||
|
|
||||||
|
obj->tail = pop(vm);
|
||||||
|
obj->head = pop(vm);
|
||||||
|
|
||||||
|
push(vm, obj);
|
||||||
|
return obj;
|
||||||
|
}
|
82
vm.c
82
vm.c
|
@ -1,82 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "gc.h"
|
|
||||||
#include "vm.h"
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void deinitVM(struct virtualMachine *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;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct garbageObject *pop(struct virtualMachine *vm) {
|
|
||||||
if (vm->stackSize < 0) {
|
|
||||||
fprintf(stderr, "ERROR: pop(): Stack size cannot be negative!");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return vm->stack[--vm->stackSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
struct garbageObject *initGarbage(struct virtualMachine *vm,
|
|
||||||
enum garbageData type) {
|
|
||||||
|
|
||||||
if (vm->refCount >= vm->refMax) {
|
|
||||||
collect(vm);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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?");
|
|
||||||
}
|
|
||||||
|
|
||||||
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?");
|
|
||||||
}
|
|
||||||
|
|
||||||
obj->tail = pop(vm);
|
|
||||||
obj->head = pop(vm);
|
|
||||||
|
|
||||||
push(vm, obj);
|
|
||||||
return obj;
|
|
||||||
}
|
|
Loading…
Reference in a new issue