Skip to content
This repository was archived by the owner on May 24, 2022. It is now read-only.

Commit acc604b

Browse files
committed
Add GC to VM
1 parent 1e9e01f commit acc604b

12 files changed

+268
-5
lines changed

includes/common.h

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
//#define DEBUG_PRINT_CODE
99
//#define DEBUG_TRACE_EXECUTION
10+
//#define DEBUG_STRESS_GC
11+
//#define DEBUG_LOG_GC
1012

1113
#define UINT8_COUNT (UINT8_MAX + 1)
1214

includes/compiler.h

+1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
#include "object.h"
66

77
ObjectFunction* compile(const char *source, const char *filename, int debugLeevl);
8+
void markCompilerRoots();
89

910
#endif

includes/mem.h

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "common.h"
55
#include "object.h"
6+
#include "compiler.h"
67

78
#define GROW_ARRAY_SIZE(maxSize) ((maxSize) < 8 ? 8 : (maxSize)*2)
89
#define GROW_ARRAY(t, pointer, oldSize, newSize) \
@@ -14,6 +15,9 @@
1415
#define FREE(t, pointer) reallocate(pointer, sizeof(t), 0)
1516

1617
void *reallocate(void *pointer, size_t oldSize, size_t newSize);
18+
void markValue(Value value);
19+
void markObject(Object* object);
20+
void collectGarbage();
1721
void freeObjects();
1822

1923
#endif

includes/object.h

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct Object
3131
{
3232
object_t t;
3333
struct Object *next;
34+
bool isMarked;
3435
};
3536

3637
typedef struct

includes/table.h

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ void freeTable(Table *table);
2222
bool tableGet(Table* table, ObjectString* k, Value* v);
2323
bool tableSet(Table *table, ObjectString *k, Value v);
2424
void tableAddAll(Table* from, Table* to);
25+
void tableRemoveWhite(Table* table);
26+
void markTable(Table* table);
2527
TableItem *findTableItem(TableItem *entries, int maxSize, ObjectString *k);
2628
ObjectString* tableFindString(Table* table, const char* chars, int length, uint32_t hash);
2729
bool tableDelete(Table* table, ObjectString* k);

includes/vm.h

+9-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
typedef struct
1212
{
13-
ObjectClosure* closure;
13+
ObjectClosure *closure;
1414
uint8_t *ip;
1515
Value *slots;
1616
} CallFrame;
@@ -23,8 +23,15 @@ typedef struct
2323
Value *stackTop;
2424
Table globals;
2525
Table strings;
26-
ObjectUpvalue* openUpvalues;
26+
ObjectUpvalue *openUpvalues;
27+
28+
size_t bytesAllocated;
29+
size_t nextGC;
30+
2731
Object *objects;
32+
int grayCount;
33+
int grayCapacity;
34+
Object **grayStack;
2835
} VM;
2936

3037
typedef enum

src/chunk.c

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "chunk.h"
22
#include "mem.h"
3+
#include "vm.h"
34

45
void initChunk(Chunk *chunk)
56
{
@@ -51,7 +52,9 @@ void freeChunk(Chunk *chunk)
5152

5253
int addConstant(Chunk *chunk, Value value)
5354
{
55+
push(value);
5456
writeValueArr(&chunk->constants, value);
57+
pop();
5558
return chunk->constants.size - 1;
5659
}
5760

src/compiler.c

+10
Original file line numberDiff line numberDiff line change
@@ -1187,4 +1187,14 @@ ObjectFunction *compile(const char *source, const char *filename, int debugLevel
11871187

11881188
ObjectFunction *function = endCompiler(debugLevel);
11891189
return parser.hadError ? NULL : function;
1190+
}
1191+
1192+
void markCompilerRoots()
1193+
{
1194+
Compiler *compiler = current;
1195+
while (compiler != NULL)
1196+
{
1197+
markObject((Object *)compiler->function);
1198+
compiler = compiler->enclosing;
1199+
}
11901200
}

src/mem.c

+180
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,27 @@
22
#include "mem.h"
33
#include "vm.h"
44

5+
#ifdef DEBUG_LOG_GC
6+
#include <stdio.h>
7+
#include "debug.h"
8+
#endif
9+
10+
#define GC_HEAP_GROW_FACTOR 2
11+
512
void *reallocate(void *pointer, size_t oldSize, size_t newSize)
613
{
14+
vm.bytesAllocated += newSize - oldSize;
15+
if (newSize > oldSize)
16+
{
17+
#ifdef DEBUG_STRESS_GC
18+
collectGarbage();
19+
#endif
20+
}
21+
if (vm.bytesAllocated > vm.nextGC)
22+
{
23+
collectGarbage();
24+
}
25+
726
if (newSize == 0)
827
{
928
free(pointer);
@@ -15,8 +34,86 @@ void *reallocate(void *pointer, size_t oldSize, size_t newSize)
1534
return result;
1635
}
1736

37+
void markObject(Object *object)
38+
{
39+
if (object == NULL)
40+
return;
41+
if (object->isMarked)
42+
return;
43+
#ifdef DEBUG_LOG_GC
44+
printf("%p mark ", (void *)object);
45+
printValue(OBJ_VAL(object));
46+
printf("\n");
47+
#endif
48+
object->isMarked = true;
49+
if (vm.grayCapacity < vm.grayCount + 1)
50+
{
51+
vm.grayCapacity = GROW_ARRAY_SIZE(vm.grayCapacity);
52+
vm.grayStack = realloc(vm.grayStack,
53+
sizeof(Object *) * vm.grayCapacity);
54+
55+
if (vm.grayStack == NULL)
56+
exit(1);
57+
}
58+
59+
vm.grayStack[vm.grayCount++] = object;
60+
}
61+
62+
void markValue(Value value)
63+
{
64+
if (!IS_OBJ(value))
65+
return;
66+
markObject(AS_OBJ(value));
67+
}
68+
69+
static void markArray(ValueArr *array)
70+
{
71+
for (int i = 0; i < array->maxSize; i++)
72+
{
73+
markValue(array->values[i]);
74+
}
75+
}
76+
77+
static void blackenObject(Object *object)
78+
{
79+
#ifdef DEBUG_LOG_GC
80+
printf("%p blacken ", (void *)object);
81+
printValue(OBJ_VAL(object));
82+
printf("\n");
83+
#endif
84+
switch (object->t)
85+
{
86+
case OBJECT_CLOSURE:
87+
{
88+
ObjectClosure *closure = (ObjectClosure *)object;
89+
markObject((Object *)closure->function);
90+
for (int i = 0; i < closure->upvalueCount; i++)
91+
{
92+
markObject((Object *)closure->upvalues[i]);
93+
}
94+
break;
95+
}
96+
case OBJECT_FUNCTION:
97+
{
98+
ObjectFunction *function = (ObjectFunction *)object;
99+
markObject((Object *)function->name);
100+
markArray(&function->chunk.constants);
101+
break;
102+
}
103+
case OBJECT_UPVALUE:
104+
markValue(((ObjectUpvalue *)object)->closed);
105+
break;
106+
case OBJECT_NATIVE:
107+
case OBJECT_STRING:
108+
break;
109+
}
110+
}
111+
18112
static void freeObject(Object *object)
19113
{
114+
#ifdef DEBUG_LOG_GC
115+
printf("%p free type %d\n", (void *)object, object->t);
116+
#endif
20117
switch (object->t)
21118
{
22119
case OBJECT_STRING:
@@ -49,6 +146,88 @@ static void freeObject(Object *object)
49146
}
50147
}
51148

149+
static void markRoots()
150+
{
151+
for (Value *slot = vm.stack; slot < vm.stackTop; slot++)
152+
{
153+
markValue(*slot);
154+
}
155+
for (int i = 0; i < vm.frameCount; i++)
156+
{
157+
markObject((Object *)vm.frames[i].closure);
158+
}
159+
for (ObjectUpvalue *upvalue = vm.openUpvalues;
160+
upvalue != NULL;
161+
upvalue = upvalue->next)
162+
{
163+
markObject((Object *)upvalue);
164+
}
165+
166+
markTable(&vm.globals);
167+
markCompilerRoots();
168+
}
169+
170+
static void traceReferences()
171+
{
172+
while (vm.grayCount > 0)
173+
{
174+
Object *object = vm.grayStack[--vm.grayCount];
175+
blackenObject(object);
176+
}
177+
}
178+
179+
static void sweep()
180+
{
181+
Object *previous = NULL;
182+
Object *object = vm.objects;
183+
while (object != NULL)
184+
{
185+
if (object->isMarked)
186+
{
187+
object->isMarked = false;
188+
previous = object;
189+
object = object->next;
190+
}
191+
else
192+
{
193+
Object *unreached = object;
194+
object = object->next;
195+
if (previous != NULL)
196+
{
197+
previous->next = object;
198+
}
199+
else
200+
{
201+
vm.objects = object;
202+
}
203+
204+
freeObject(unreached);
205+
}
206+
}
207+
}
208+
209+
void collectGarbage()
210+
{
211+
#ifdef DEBUG_LOG_GC
212+
printf("-- gc begin\n");
213+
size_t before = vm.bytesAllocated;
214+
#endif
215+
216+
markRoots();
217+
traceReferences();
218+
tableRemoveWhite(&vm.strings);
219+
sweep();
220+
221+
vm.nextGC = vm.bytesAllocated * GC_HEAP_GROW_FACTOR;
222+
223+
#ifdef DEBUG_LOG_GC
224+
printf("-- gc end\n");
225+
printf(" collected %ld bytes (from %ld to %ld) next at %ld\n",
226+
before - vm.bytesAllocated, before, vm.bytesAllocated,
227+
vm.nextGC);
228+
#endif
229+
}
230+
52231
void freeObjects()
53232
{
54233
Object *object = vm.objects;
@@ -58,4 +237,5 @@ void freeObjects()
58237
freeObject(object);
59238
object = next;
60239
}
240+
free(vm.grayStack);
61241
}

src/object.c

+7
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@ static Object *allocateObject(size_t size, object_t t)
1414
{
1515
Object *object = (Object *)reallocate(NULL, 0, size);
1616
object->t = t;
17+
object->isMarked = false;
1718

1819
object->next = vm.objects;
1920
vm.objects = object;
21+
#ifdef DEBUG_LOG_GC
22+
printf("%p allocate %ld for %d\n", (void *)object, size, t);
23+
#endif
24+
2025
return object;
2126
}
2227

@@ -37,7 +42,9 @@ static ObjectString *allocateString(char *chars, int length, uint32_t hash)
3742
string->length = length;
3843
string->chars = chars;
3944
string->hash = hash;
45+
push(OBJ_VAL(string));
4046
tableSet(&vm.strings, string, NULL_VAL);
47+
pop();
4148
return string;
4249
}
4350

src/table.c

+22
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,28 @@ bool tableSet(Table *table, ObjectString *k, Value v)
111111
return isNew;
112112
}
113113

114+
void markTable(Table *table)
115+
{
116+
for (int i = 0; i < table->maxSize; i++)
117+
{
118+
TableItem *entry = &table->items[i];
119+
markObject((Object *)entry->k);
120+
markValue(entry->v);
121+
}
122+
}
123+
124+
void tableRemoveWhite(Table *table)
125+
{
126+
for (int i = 0; i < table->maxSize; i++)
127+
{
128+
TableItem *entry = &table->items[i];
129+
if (entry->k != NULL && !entry->k->object.isMarked)
130+
{
131+
tableDelete(table, entry->k);
132+
}
133+
}
134+
}
135+
114136
bool tableDelete(Table *table, ObjectString *k)
115137
{
116138
if (table->size == 0)

0 commit comments

Comments
 (0)