blob: 826cd02ebe6984a01d1347c5270708ea1b6321e9 [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
swissChilie9fec8b2021-06-22 13:59:33 -070072 // Only recursively mark if this hasn't been marked yet. i.e. prevent
73 // marking circular references twice
74 if (alloc->mark != gc_mark)
75 {
76 ++*marked;
77
78 alloc->mark = gc_mark;
79
swissChilie9fec8b2021-06-22 13:59:33 -070080 switch (alloc->type_tag)
81 {
82 case CONS_TAG: {
83 struct cons_alloc *cons = (struct cons_alloc *)alloc;
84 _mark(cons->cons.car, marked);
85 _mark(cons->cons.cdr, marked);
86 break;
87 }
swissChiliddc97542021-07-04 11:47:42 -070088 case CLOSURE_TAG: {
89 struct closure_alloc *closure = (void *)alloc;
90
91 for (int i = 0; i < closure->closure.num_captured; i++)
92 {
93 _mark(closure->closure.data[i], marked);
94 }
95
96 break;
97 }
swissChilie9fec8b2021-06-22 13:59:33 -070098 }
99 }
swissChili9e57da42021-06-15 22:22:46 -0700100 }
swissChili6d6525e2021-06-15 21:20:53 -0700101}
102
swissChilie9fec8b2021-06-22 13:59:33 -0700103value_t alloc_to_value(struct alloc *a)
104{
105 void *val = ((void *)a) + sizeof(struct alloc);
106 return (unsigned int)val | a->type_tag;
107}
108
swissChili6d6525e2021-06-15 21:20:53 -0700109void _sweep()
110{
swissChili5db22a02021-06-30 22:04:33 -0700111 for (struct alloc *a = first_a; a;)
swissChilie9fec8b2021-06-22 13:59:33 -0700112 {
swissChilia890aed2022-07-30 17:13:07 -0700113 // fprintf(stderr, "[ GC ] %s %p pool? %d\n", (a->mark != gc_mark) ? "Unmarked" : "Marked", a, pool_alive(a->pool));
114 // printval(alloc_to_value(a), 2);
115
swissChilib8fd4712021-06-23 15:32:04 -0700116 if (pool_alive(a->pool) || a->mark == gc_mark)
117 {
118 // Marked or in living pool
swissChili5db22a02021-06-30 22:04:33 -0700119 a = a->next;
swissChilib8fd4712021-06-23 15:32:04 -0700120 }
121 else
122 {
swissChilib8fd4712021-06-23 15:32:04 -0700123 // Free and remove from allocation list
124 struct alloc *p = a->prev, *n = a->next;
swissChilic0acce42022-07-31 13:38:17 -0700125 del_alloc(a);
swissChilib8fd4712021-06-23 15:32:04 -0700126
127 a = n;
128
swissChilic0acce42022-07-31 13:38:17 -0700129 if (a == first_a)
130 {
131 first_a = n;
132 }
133
134 if (a == last_a)
135 {
136 last_a = p;
137 }
138
swissChilib8fd4712021-06-23 15:32:04 -0700139 if (p)
140 p->next = n;
141
142 if (n)
143 n->prev = p;
144 }
swissChilie9fec8b2021-06-22 13:59:33 -0700145 }
swissChili6d6525e2021-06-15 21:20:53 -0700146}
147
swissChilia890aed2022-07-30 17:13:07 -0700148struct gc_stats gc_get_stats()
149{
150 struct gc_stats stats = {0};
151
152 stats.gc_runs = gc_runs;
153
154 for (struct alloc *a = first_a; a; a = a->next)
155 {
156 stats.total_allocs++;
157 }
158
159 return stats;
160}
161
swissChili6d6525e2021-06-15 21:20:53 -0700162void _do_gc(unsigned int esp, unsigned int ebp)
163{
swissChili9e57da42021-06-15 22:22:46 -0700164 value_t *esp_p = (value_t *)esp,
165 *ebp_p = (value_t *)ebp;
swissChili6d6525e2021-06-15 21:20:53 -0700166
swissChilie9fec8b2021-06-22 13:59:33 -0700167 unsigned int num_marked = 0;
168
169 gc_mark++;
swissChilia890aed2022-07-30 17:13:07 -0700170 gc_runs++;
swissChilie9fec8b2021-06-22 13:59:33 -0700171
swissChili80560312022-07-31 21:05:47 -0700172 for (struct gc_segment *seg = gc_last_segment; seg && seg->seg_start; seg = seg->prev)
swissChili6d6525e2021-06-15 21:20:53 -0700173 {
swissChili80560312022-07-31 21:05:47 -0700174 // For every stack frame until the base of the stack
175 while (esp_p < (value_t *)seg->seg_end)
swissChili9e57da42021-06-15 22:22:46 -0700176 {
swissChili80560312022-07-31 21:05:47 -0700177 // Walk up the stack until we reach either the frame pointer or the base
178 // of the stack. Basically walk to the top of this function's stack
179 // frame.
180 for (; esp_p < ebp_p && esp_p < gc_base; esp_p++)
181 {
182 _mark(*esp_p, &num_marked);
183 }
184
185 // Set the frame pointer to the frame pointer on the stack
186 ebp_p = (value_t *)*esp_p;
187
188 // Step up two stack slots, one for the frame pointer and one for the
189 // return address.
190 esp_p += 2;
swissChili9e57da42021-06-15 22:22:46 -0700191 }
192
swissChili80560312022-07-31 21:05:47 -0700193 // skip above ret pointer
194 value_t *args = seg->seg_end + 4;
195 for (int i = 0; i < seg->nargs; i++)
196 {
swissChili80560312022-07-31 21:05:47 -0700197 // mark arguments
198 _mark(args[i], &num_marked);
199 }
200
201 for (int i = 0; i < seg->nretained; i++)
202 {
swissChili80560312022-07-31 21:05:47 -0700203 _mark(seg->retained[i], &num_marked);
204 }
205
206 esp_p = seg->seg_start;
207 ebp_p = seg->old_ebp;
swissChili6d6525e2021-06-15 21:20:53 -0700208 }
swissChili9e57da42021-06-15 22:22:46 -0700209
swissChilie9fec8b2021-06-22 13:59:33 -0700210 _sweep();
swissChili6d6525e2021-06-15 21:20:53 -0700211}
swissChilib8fd4712021-06-23 15:32:04 -0700212
213void free_all()
214{
215 for (struct alloc *a = first_a; a;)
216 {
217 struct alloc *next = a->next;
swissChilic0acce42022-07-31 13:38:17 -0700218 del_alloc(a);
swissChilib8fd4712021-06-23 15:32:04 -0700219 a = next;
swissChili53e7cd12021-08-02 21:55:53 -0700220// fprintf(stderr, "a = %p\n", a);
swissChilib8fd4712021-06-23 15:32:04 -0700221 }
222}