blob: e3fc231da836151bc929355787104f4a361b27fa [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 }
swissChili8b5ec7a2022-08-05 22:26:17 -070098 case CLASS_TAG: {
99 struct class_alloc *class = (void *)alloc;
100
101 for (int i = 0; i < class->class.num_members; i++)
102 {
103 _mark(class->class.members[i], marked);
104 }
105
106 break;
107 }
swissChilie9fec8b2021-06-22 13:59:33 -0700108 }
109 }
swissChili9e57da42021-06-15 22:22:46 -0700110 }
swissChili6d6525e2021-06-15 21:20:53 -0700111}
112
swissChilie9fec8b2021-06-22 13:59:33 -0700113value_t alloc_to_value(struct alloc *a)
114{
115 void *val = ((void *)a) + sizeof(struct alloc);
116 return (unsigned int)val | a->type_tag;
117}
118
swissChili6d6525e2021-06-15 21:20:53 -0700119void _sweep()
120{
swissChili5db22a02021-06-30 22:04:33 -0700121 for (struct alloc *a = first_a; a;)
swissChilie9fec8b2021-06-22 13:59:33 -0700122 {
swissChilia890aed2022-07-30 17:13:07 -0700123 // fprintf(stderr, "[ GC ] %s %p pool? %d\n", (a->mark != gc_mark) ? "Unmarked" : "Marked", a, pool_alive(a->pool));
124 // printval(alloc_to_value(a), 2);
125
swissChilib8fd4712021-06-23 15:32:04 -0700126 if (pool_alive(a->pool) || a->mark == gc_mark)
127 {
128 // Marked or in living pool
swissChili5db22a02021-06-30 22:04:33 -0700129 a = a->next;
swissChilib8fd4712021-06-23 15:32:04 -0700130 }
131 else
132 {
swissChilib8fd4712021-06-23 15:32:04 -0700133 // Free and remove from allocation list
134 struct alloc *p = a->prev, *n = a->next;
swissChilic0acce42022-07-31 13:38:17 -0700135 del_alloc(a);
swissChilib8fd4712021-06-23 15:32:04 -0700136
137 a = n;
138
swissChilic0acce42022-07-31 13:38:17 -0700139 if (a == first_a)
140 {
141 first_a = n;
142 }
143
144 if (a == last_a)
145 {
146 last_a = p;
147 }
148
swissChilib8fd4712021-06-23 15:32:04 -0700149 if (p)
150 p->next = n;
151
152 if (n)
153 n->prev = p;
154 }
swissChilie9fec8b2021-06-22 13:59:33 -0700155 }
swissChili6d6525e2021-06-15 21:20:53 -0700156}
157
swissChilia890aed2022-07-30 17:13:07 -0700158struct gc_stats gc_get_stats()
159{
160 struct gc_stats stats = {0};
161
162 stats.gc_runs = gc_runs;
163
164 for (struct alloc *a = first_a; a; a = a->next)
165 {
166 stats.total_allocs++;
167 }
168
169 return stats;
170}
171
swissChili6d6525e2021-06-15 21:20:53 -0700172void _do_gc(unsigned int esp, unsigned int ebp)
173{
swissChili9e57da42021-06-15 22:22:46 -0700174 value_t *esp_p = (value_t *)esp,
175 *ebp_p = (value_t *)ebp;
swissChili6d6525e2021-06-15 21:20:53 -0700176
swissChilie9fec8b2021-06-22 13:59:33 -0700177 unsigned int num_marked = 0;
178
179 gc_mark++;
swissChilia890aed2022-07-30 17:13:07 -0700180 gc_runs++;
swissChilie9fec8b2021-06-22 13:59:33 -0700181
swissChili80560312022-07-31 21:05:47 -0700182 for (struct gc_segment *seg = gc_last_segment; seg && seg->seg_start; seg = seg->prev)
swissChili6d6525e2021-06-15 21:20:53 -0700183 {
swissChili80560312022-07-31 21:05:47 -0700184 // For every stack frame until the base of the stack
185 while (esp_p < (value_t *)seg->seg_end)
swissChili9e57da42021-06-15 22:22:46 -0700186 {
swissChili80560312022-07-31 21:05:47 -0700187 // Walk up the stack until we reach either the frame pointer or the base
188 // of the stack. Basically walk to the top of this function's stack
189 // frame.
190 for (; esp_p < ebp_p && esp_p < gc_base; esp_p++)
191 {
192 _mark(*esp_p, &num_marked);
193 }
194
195 // Set the frame pointer to the frame pointer on the stack
196 ebp_p = (value_t *)*esp_p;
197
198 // Step up two stack slots, one for the frame pointer and one for the
199 // return address.
200 esp_p += 2;
swissChili9e57da42021-06-15 22:22:46 -0700201 }
202
swissChili80560312022-07-31 21:05:47 -0700203 // skip above ret pointer
204 value_t *args = seg->seg_end + 4;
205 for (int i = 0; i < seg->nargs; i++)
206 {
swissChili80560312022-07-31 21:05:47 -0700207 // mark arguments
208 _mark(args[i], &num_marked);
209 }
210
211 for (int i = 0; i < seg->nretained; i++)
212 {
swissChili80560312022-07-31 21:05:47 -0700213 _mark(seg->retained[i], &num_marked);
214 }
215
216 esp_p = seg->seg_start;
217 ebp_p = seg->old_ebp;
swissChili6d6525e2021-06-15 21:20:53 -0700218 }
swissChili9e57da42021-06-15 22:22:46 -0700219
swissChilie9fec8b2021-06-22 13:59:33 -0700220 _sweep();
swissChili6d6525e2021-06-15 21:20:53 -0700221}
swissChilib8fd4712021-06-23 15:32:04 -0700222
223void free_all()
224{
225 for (struct alloc *a = first_a; a;)
226 {
227 struct alloc *next = a->next;
swissChilic0acce42022-07-31 13:38:17 -0700228 del_alloc(a);
swissChilib8fd4712021-06-23 15:32:04 -0700229 a = next;
swissChili53e7cd12021-08-02 21:55:53 -0700230// fprintf(stderr, "a = %p\n", a);
swissChilib8fd4712021-06-23 15:32:04 -0700231 }
232}