~swisschili/bluejay

ref: fa496ccc893eb5cbc4a07825cc1b6694503e461e bluejay/src/lisp/gc.c -rw-r--r-- 4.0 KiB
fa496cccswissChili Fix bug in _do_gc with broken segmentation, update C functions to declare segments 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#include "gc.h"
#include "lisp.h"
#include "plat/plat.h"
#include <stdarg.h>
#include <string.h>

value_t *gc_base;
THREAD_LOCAL static unsigned int gc_mark;

struct gc_skipped_segment gc_segments[64] = { 0 };
int gc_current_segment = 0;

void __attribute__((noinline)) gc_set_base_here()
{
	// Move the current stack top address to gc_base. This works nicely because
	// it means that when another (presumably lisp) function is called right
	// after this, the stack top for it will be the same as for this function.
	asm("movl %%esp, %0" : "=g"(gc_base));
}

void _mark(value_t value, unsigned int *marked)
{
	if (heapp(value))
	{
		void *pointer = (void *)(value & ~HEAP_MASK);
		struct alloc *alloc = pointer - sizeof(struct alloc);

		// Only recursively mark if this hasn't been marked yet. i.e. prevent
		// marking circular references twice
		if (alloc->mark != gc_mark)
		{
			++*marked;

			alloc->mark = gc_mark;

			printf("[ GC ] val =");
			printval(alloc_to_value(alloc), 2);

			switch (alloc->type_tag)
			{
			case CONS_TAG: {
				struct cons_alloc *cons = (struct cons_alloc *)alloc;

				_mark(cons->cons.car, marked);
				_mark(cons->cons.cdr, marked);
				break;
			}
			case CLOSURE_TAG: {
				struct closure_alloc *closure = (void *)alloc;

				for (int i = 0; i < closure->closure.num_captured; i++)
				{
					_mark(closure->closure.data[i], marked);
				}

				break;
			}
			}
		}
	}
}

value_t alloc_to_value(struct alloc *a)
{
	void *val = ((void *)a) + sizeof(struct alloc);
	return (unsigned int)val | a->type_tag;
}

unsigned int _sweep()
{
	unsigned int swept = 0;

	for (struct alloc *a = first_a; a;)
	{
		if (pool_alive(a->pool) || a->mark == gc_mark)
		{
			// Marked or in living pool
			a = a->next;
		}
		else
		{
			fprintf(stderr, "_sweeping:\n");
			printval(alloc_to_value(a), 2);

			// Free and remove from allocation list
			struct alloc *p = a->prev, *n = a->next;

			free_aligned(a);
			swept++;

			a = n;

			if (p)
				p->next = n;

			if (n)
				n->prev = p;
		}
	}

	fprintf(stderr, "current pool = %d, swept %d\n", current_pool, swept);

	return swept;
}

unsigned int _do_gc(unsigned int esp, unsigned int ebp)
{
	value_t *esp_p = (value_t *)esp,
		*ebp_p = (value_t *)ebp;
	
	unsigned int num_marked = 0;

	gc_mark++;

	for (int i = gc_current_segment; i >= 0; i--)
	{
		value_t *base = gc_segments[i].top;

		// For every stack frame until the base of the stack
		while (esp_p && esp_p < base)
		{
			// Walk up the stack until we reach either the frame pointer or the base
			// of the stack. Basically walk to the top of this function's stack
			// frame.
			for (; esp_p < ebp_p && esp_p < base; esp_p++)
			{
				_mark(*esp_p, &num_marked);
			}

			// Set the frame pointer to the frame pointer on the stack
			ebp_p = (value_t *)*esp_p;

			// Step up two stack slots, one for the frame pointer and one for the
			// return address.
			esp_p += 2;
		}

		// Mark the values marked by the skipped segment
		for (int j = 0; j < gc_segments[i].num_marked; j++)
		{
			_mark(gc_segments[i].mark[j], &num_marked);
		}
		
		esp_p = gc_segments[i].bottom;
	}

	return _sweep();
}

void free_all()
{
	for (struct alloc *a = first_a; a;)
	{
		struct alloc *next = a->next;
		free_aligned(a);
		a = next;
//		fprintf(stderr, "a = %p\n", a);
	}
}

void gc_skip(void *last_arg, ...)
{
	va_list list;
	va_start(list, last_arg);

	struct gc_skipped_segment *seg = &gc_segments[++gc_current_segment];
	memset(seg, 0, sizeof(struct gc_skipped_segment));

	seg->bottom = last_arg + sizeof(value_t);

	for (int i = 0; i < 8; i++)
	{
		value_t v = va_arg(list, value_t);
		if (nilp(v))
			break;
		seg->mark[i] = v;
		seg->num_marked++;
	}

	va_end(list);
}

void gc_resume()
{
	gc_resumen(0);
}

void gc_resumen(int n)
{
	void *esp;
	asm("movl %%esp, %0" : "=g"(esp));

	gc_segments[gc_current_segment].top = esp - sizeof(value_t) * n;;
}

void gc_endskip()
{
	gc_current_segment--;
}

void gc_pushmark(value_t val)
{
	gc_segments[gc_current_segment].mark[gc_segments[gc_current_segment].num_marked++] = val;
}

void gc_popmark()
{
	gc_segments[gc_current_segment].num_marked--;
}