blob: a6d88ac7a7b207ddbaf69aa4c693adb2d1e10e7e [file] [log] [blame]
swissChili6d6525e2021-06-15 21:20:53 -07001#include "gc.h"
2#include "lisp.h"
swissChilie9fec8b2021-06-22 13:59:33 -07003#include "plat/plat.h"
swissChili80560312022-07-31 21:05:47 -07004#include <stdlib.h>
swissChili6d6525e2021-06-15 21:20:53 -07005
swissChili9e57da42021-06-15 22:22:46 -07006value_t *gc_base;
swissChili80560312022-07-31 21:05:47 -07007struct gc_segment *gc_last_segment = NULL;
swissChilie9fec8b2021-06-22 13:59:33 -07008THREAD_LOCAL static unsigned int gc_mark;
swissChilia890aed2022-07-30 17:13:07 -07009THREAD_LOCAL static unsigned long gc_runs = 0;
swissChili6d6525e2021-06-15 21:20:53 -070010
swissChili9e57da42021-06-15 22:22:46 -070011void __attribute__((noinline)) gc_set_base_here()
12{
13 // Move the current stack top address to gc_base. This works nicely because
14 // it means that when another (presumably lisp) function is called right
15 // after this, the stack top for it will be the same as for this function.
16 asm("movl %%esp, %0" : "=g"(gc_base));
17}
18
swissChili80560312022-07-31 21:05:47 -070019void gc_push_segment(void *last_arg_addr, int nretained)
20{
21 // base will be the address below (at higher memory than) the ret
22 // pointer when lisp func is called.
23 struct gc_segment *seg = malloc(sizeof(struct gc_segment) + sizeof(value_t) * nretained);
24
25 if (last_arg_addr)
26 seg->seg_start = last_arg_addr + 4;
27 else
28 seg->seg_start = NULL;
29
30 seg->nretained = nretained;
31 seg->prev = gc_last_segment;
32 gc_last_segment = seg;
33
34 // remember, stack looks like this:
35 // ret
36 // old ebp <- current ebp points here
37 // ...
38 void **ebp;
39 asm("movl %%ebp, %0" : "=g"(ebp));
40 seg->old_ebp = *ebp; // could do this in one mov but whatever
41}
42
43void gc_pop_segment()
44{
45 struct gc_segment *prev = gc_last_segment->prev;
46 free(gc_last_segment);
47 gc_last_segment = prev;
48}
49
50void gc_prepare_call_(void *esp, int nargs)
51{
52 gc_last_segment->nargs = nargs;
53 // esp points to its position BEFORE the lisp function is
54 // called. So seg_end is that + return pointer + arguments.
55 gc_last_segment->seg_end = esp + 4 + sizeof(value_t) * nargs;
56}
57
58void gc_set_retained(int index, value_t retained)
59{
60 gc_last_segment->retained[index] = retained;
61}
62
63void gc_set_retained(int index, value_t retained);
64
swissChilie9fec8b2021-06-22 13:59:33 -070065void _mark(value_t value, unsigned int *marked)
swissChili9e57da42021-06-15 22:22:46 -070066{
67 if (heapp(value))
68 {
swissChilie9fec8b2021-06-22 13:59:33 -070069 void *pointer = (void *)(value & ~HEAP_MASK);
70 struct alloc *alloc = pointer - sizeof(struct alloc);
swissChili9e57da42021-06-15 22:22:46 -070071
swissChilia890aed2022-07-30 17:13:07 -070072 if (!pointer)
73 {
74 // TODO: Where are these coming from? Maybe this is a C
75 // stack variable that we are interpreting as beign in
76 // Lisp stack space on accident?
77 fprintf(stderr, "lisp:gc:warning: value %x is a null pointer\n", value);
78 return;
79 }
80
swissChilie9fec8b2021-06-22 13:59:33 -070081 // Only recursively mark if this hasn't been marked yet. i.e. prevent
82 // marking circular references twice
83 if (alloc->mark != gc_mark)
84 {
85 ++*marked;
86
87 alloc->mark = gc_mark;
88
89 // printf("[ GC ] val =");
90 // printval(alloc_to_value(alloc), 2);
91
92 switch (alloc->type_tag)
93 {
94 case CONS_TAG: {
95 struct cons_alloc *cons = (struct cons_alloc *)alloc;
96 _mark(cons->cons.car, marked);
97 _mark(cons->cons.cdr, marked);
98 break;
99 }
swissChiliddc97542021-07-04 11:47:42 -0700100 case CLOSURE_TAG: {
101 struct closure_alloc *closure = (void *)alloc;
102
103 for (int i = 0; i < closure->closure.num_captured; i++)
104 {
105 _mark(closure->closure.data[i], marked);
106 }
107
108 break;
109 }
swissChilie9fec8b2021-06-22 13:59:33 -0700110 }
111 }
swissChili9e57da42021-06-15 22:22:46 -0700112 }
swissChili6d6525e2021-06-15 21:20:53 -0700113}
114
swissChilie9fec8b2021-06-22 13:59:33 -0700115value_t alloc_to_value(struct alloc *a)
116{
117 void *val = ((void *)a) + sizeof(struct alloc);
118 return (unsigned int)val | a->type_tag;
119}
120
swissChili6d6525e2021-06-15 21:20:53 -0700121void _sweep()
122{
swissChili5db22a02021-06-30 22:04:33 -0700123 for (struct alloc *a = first_a; a;)
swissChilie9fec8b2021-06-22 13:59:33 -0700124 {
swissChilia890aed2022-07-30 17:13:07 -0700125 // fprintf(stderr, "[ GC ] %s %p pool? %d\n", (a->mark != gc_mark) ? "Unmarked" : "Marked", a, pool_alive(a->pool));
126 // printval(alloc_to_value(a), 2);
127
swissChilib8fd4712021-06-23 15:32:04 -0700128 if (pool_alive(a->pool) || a->mark == gc_mark)
129 {
130 // Marked or in living pool
swissChili5db22a02021-06-30 22:04:33 -0700131 a = a->next;
swissChilib8fd4712021-06-23 15:32:04 -0700132 }
133 else
134 {
swissChilic0acce42022-07-31 13:38:17 -0700135 fprintf(stderr, "[ GC ] freeing: %p\n", a);
swissChilib8fd4712021-06-23 15:32:04 -0700136 // Free and remove from allocation list
137 struct alloc *p = a->prev, *n = a->next;
swissChilic0acce42022-07-31 13:38:17 -0700138 del_alloc(a);
swissChilib8fd4712021-06-23 15:32:04 -0700139
140 a = n;
141
swissChilic0acce42022-07-31 13:38:17 -0700142 if (a == first_a)
143 {
144 first_a = n;
145 }
146
147 if (a == last_a)
148 {
149 last_a = p;
150 }
151
swissChilib8fd4712021-06-23 15:32:04 -0700152 if (p)
153 p->next = n;
154
155 if (n)
156 n->prev = p;
157 }
swissChilie9fec8b2021-06-22 13:59:33 -0700158 }
swissChili6d6525e2021-06-15 21:20:53 -0700159}
160
swissChilia890aed2022-07-30 17:13:07 -0700161struct gc_stats gc_get_stats()
162{
163 struct gc_stats stats = {0};
164
165 stats.gc_runs = gc_runs;
166
167 for (struct alloc *a = first_a; a; a = a->next)
168 {
169 stats.total_allocs++;
170 }
171
172 return stats;
173}
174
swissChili6d6525e2021-06-15 21:20:53 -0700175void _do_gc(unsigned int esp, unsigned int ebp)
176{
swissChili9e57da42021-06-15 22:22:46 -0700177 value_t *esp_p = (value_t *)esp,
178 *ebp_p = (value_t *)ebp;
swissChili6d6525e2021-06-15 21:20:53 -0700179
swissChilie9fec8b2021-06-22 13:59:33 -0700180 unsigned int num_marked = 0;
181
182 gc_mark++;
swissChilia890aed2022-07-30 17:13:07 -0700183 gc_runs++;
swissChilie9fec8b2021-06-22 13:59:33 -0700184
swissChili80560312022-07-31 21:05:47 -0700185 for (struct gc_segment *seg = gc_last_segment; seg && seg->seg_start; seg = seg->prev)
swissChili6d6525e2021-06-15 21:20:53 -0700186 {
swissChili80560312022-07-31 21:05:47 -0700187 // For every stack frame until the base of the stack
188 while (esp_p < (value_t *)seg->seg_end)
swissChili9e57da42021-06-15 22:22:46 -0700189 {
swissChili80560312022-07-31 21:05:47 -0700190 // Walk up the stack until we reach either the frame pointer or the base
191 // of the stack. Basically walk to the top of this function's stack
192 // frame.
193 for (; esp_p < ebp_p && esp_p < gc_base; esp_p++)
194 {
195 _mark(*esp_p, &num_marked);
196 }
197
198 // Set the frame pointer to the frame pointer on the stack
199 ebp_p = (value_t *)*esp_p;
200
201 // Step up two stack slots, one for the frame pointer and one for the
202 // return address.
203 esp_p += 2;
swissChili9e57da42021-06-15 22:22:46 -0700204 }
205
swissChili80560312022-07-31 21:05:47 -0700206 // skip above ret pointer
207 value_t *args = seg->seg_end + 4;
208 for (int i = 0; i < seg->nargs; i++)
209 {
210 fprintf(stderr, "Marking arg %d\n", i);
swissChili9e57da42021-06-15 22:22:46 -0700211
swissChili80560312022-07-31 21:05:47 -0700212 // mark arguments
213 _mark(args[i], &num_marked);
214 }
215
216 for (int i = 0; i < seg->nretained; i++)
217 {
218 fprintf(stderr, "Marking retained %d\n", i);
219 printval(seg->retained[i], 0);
220
221 _mark(seg->retained[i], &num_marked);
222 }
223
224 esp_p = seg->seg_start;
225 ebp_p = seg->old_ebp;
swissChili6d6525e2021-06-15 21:20:53 -0700226 }
swissChili9e57da42021-06-15 22:22:46 -0700227
swissChilie9fec8b2021-06-22 13:59:33 -0700228 _sweep();
swissChili6d6525e2021-06-15 21:20:53 -0700229}
swissChilib8fd4712021-06-23 15:32:04 -0700230
231void free_all()
232{
233 for (struct alloc *a = first_a; a;)
234 {
235 struct alloc *next = a->next;
swissChilic0acce42022-07-31 13:38:17 -0700236 del_alloc(a);
swissChilib8fd4712021-06-23 15:32:04 -0700237 a = next;
swissChili53e7cd12021-08-02 21:55:53 -0700238// fprintf(stderr, "a = %p\n", a);
swissChilib8fd4712021-06-23 15:32:04 -0700239 }
240}