blob: 140bc3fdd1201f4a6f170941b01f8ad6a76d9584 [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"
swissChili6d6525e2021-06-15 21:20:53 -07004
swissChili9e57da42021-06-15 22:22:46 -07005value_t *gc_base;
swissChilie9fec8b2021-06-22 13:59:33 -07006THREAD_LOCAL static unsigned int gc_mark;
swissChilia890aed2022-07-30 17:13:07 -07007THREAD_LOCAL static unsigned long gc_runs = 0;
swissChili6d6525e2021-06-15 21:20:53 -07008
swissChili9e57da42021-06-15 22:22:46 -07009void __attribute__((noinline)) gc_set_base_here()
10{
11 // Move the current stack top address to gc_base. This works nicely because
12 // it means that when another (presumably lisp) function is called right
13 // after this, the stack top for it will be the same as for this function.
14 asm("movl %%esp, %0" : "=g"(gc_base));
15}
16
swissChilie9fec8b2021-06-22 13:59:33 -070017void _mark(value_t value, unsigned int *marked)
swissChili9e57da42021-06-15 22:22:46 -070018{
19 if (heapp(value))
20 {
swissChilie9fec8b2021-06-22 13:59:33 -070021 void *pointer = (void *)(value & ~HEAP_MASK);
22 struct alloc *alloc = pointer - sizeof(struct alloc);
swissChili9e57da42021-06-15 22:22:46 -070023
swissChilia890aed2022-07-30 17:13:07 -070024 if (!pointer)
25 {
26 // TODO: Where are these coming from? Maybe this is a C
27 // stack variable that we are interpreting as beign in
28 // Lisp stack space on accident?
29 fprintf(stderr, "lisp:gc:warning: value %x is a null pointer\n", value);
30 return;
31 }
32
swissChilie9fec8b2021-06-22 13:59:33 -070033 // Only recursively mark if this hasn't been marked yet. i.e. prevent
34 // marking circular references twice
35 if (alloc->mark != gc_mark)
36 {
37 ++*marked;
38
39 alloc->mark = gc_mark;
40
41 // printf("[ GC ] val =");
42 // printval(alloc_to_value(alloc), 2);
43
44 switch (alloc->type_tag)
45 {
46 case CONS_TAG: {
47 struct cons_alloc *cons = (struct cons_alloc *)alloc;
48 _mark(cons->cons.car, marked);
49 _mark(cons->cons.cdr, marked);
50 break;
51 }
swissChiliddc97542021-07-04 11:47:42 -070052 case CLOSURE_TAG: {
53 struct closure_alloc *closure = (void *)alloc;
54
55 for (int i = 0; i < closure->closure.num_captured; i++)
56 {
57 _mark(closure->closure.data[i], marked);
58 }
59
60 break;
61 }
swissChilie9fec8b2021-06-22 13:59:33 -070062 }
63 }
swissChili9e57da42021-06-15 22:22:46 -070064 }
swissChili6d6525e2021-06-15 21:20:53 -070065}
66
swissChilie9fec8b2021-06-22 13:59:33 -070067value_t alloc_to_value(struct alloc *a)
68{
69 void *val = ((void *)a) + sizeof(struct alloc);
70 return (unsigned int)val | a->type_tag;
71}
72
swissChili6d6525e2021-06-15 21:20:53 -070073void _sweep()
74{
swissChili5db22a02021-06-30 22:04:33 -070075 for (struct alloc *a = first_a; a;)
swissChilie9fec8b2021-06-22 13:59:33 -070076 {
swissChilia890aed2022-07-30 17:13:07 -070077 // fprintf(stderr, "[ GC ] %s %p pool? %d\n", (a->mark != gc_mark) ? "Unmarked" : "Marked", a, pool_alive(a->pool));
78 // printval(alloc_to_value(a), 2);
79
swissChilib8fd4712021-06-23 15:32:04 -070080 if (pool_alive(a->pool) || a->mark == gc_mark)
81 {
82 // Marked or in living pool
swissChili5db22a02021-06-30 22:04:33 -070083 a = a->next;
swissChilib8fd4712021-06-23 15:32:04 -070084 }
85 else
86 {
swissChilic0acce42022-07-31 13:38:17 -070087 fprintf(stderr, "[ GC ] freeing: %p\n", a);
swissChilib8fd4712021-06-23 15:32:04 -070088 // Free and remove from allocation list
89 struct alloc *p = a->prev, *n = a->next;
swissChilic0acce42022-07-31 13:38:17 -070090 del_alloc(a);
swissChilib8fd4712021-06-23 15:32:04 -070091
92 a = n;
93
swissChilic0acce42022-07-31 13:38:17 -070094 if (a == first_a)
95 {
96 first_a = n;
97 }
98
99 if (a == last_a)
100 {
101 last_a = p;
102 }
103
swissChilib8fd4712021-06-23 15:32:04 -0700104 if (p)
105 p->next = n;
106
107 if (n)
108 n->prev = p;
109 }
swissChilie9fec8b2021-06-22 13:59:33 -0700110 }
swissChili6d6525e2021-06-15 21:20:53 -0700111}
112
swissChilia890aed2022-07-30 17:13:07 -0700113struct gc_stats gc_get_stats()
114{
115 struct gc_stats stats = {0};
116
117 stats.gc_runs = gc_runs;
118
119 for (struct alloc *a = first_a; a; a = a->next)
120 {
121 stats.total_allocs++;
122 }
123
124 return stats;
125}
126
swissChili6d6525e2021-06-15 21:20:53 -0700127void _do_gc(unsigned int esp, unsigned int ebp)
128{
swissChili9e57da42021-06-15 22:22:46 -0700129 value_t *esp_p = (value_t *)esp,
130 *ebp_p = (value_t *)ebp;
swissChili6d6525e2021-06-15 21:20:53 -0700131
swissChilie9fec8b2021-06-22 13:59:33 -0700132 unsigned int num_marked = 0;
133
134 gc_mark++;
swissChilia890aed2022-07-30 17:13:07 -0700135 gc_runs++;
swissChilie9fec8b2021-06-22 13:59:33 -0700136
swissChili9e57da42021-06-15 22:22:46 -0700137 // For every stack frame until the base of the stack
138 while (esp_p < gc_base)
swissChili6d6525e2021-06-15 21:20:53 -0700139 {
swissChili9e57da42021-06-15 22:22:46 -0700140 // Walk up the stack until we reach either the frame pointer or the base
141 // of the stack. Basically walk to the top of this function's stack
142 // frame.
143 for (; esp_p < ebp_p && esp_p < gc_base; esp_p++)
144 {
swissChilie9fec8b2021-06-22 13:59:33 -0700145 _mark(*esp_p, &num_marked);
swissChili9e57da42021-06-15 22:22:46 -0700146 }
147
148 // Set the frame pointer to the frame pointer on the stack
149 ebp_p = (value_t *)*esp_p;
150
151 // Step up two stack slots, one for the frame pointer and one for the
152 // return address.
153 esp_p += 2;
swissChili6d6525e2021-06-15 21:20:53 -0700154 }
swissChili9e57da42021-06-15 22:22:46 -0700155
swissChilie9fec8b2021-06-22 13:59:33 -0700156 _sweep();
swissChili6d6525e2021-06-15 21:20:53 -0700157}
swissChilib8fd4712021-06-23 15:32:04 -0700158
159void free_all()
160{
161 for (struct alloc *a = first_a; a;)
162 {
163 struct alloc *next = a->next;
swissChilic0acce42022-07-31 13:38:17 -0700164 del_alloc(a);
swissChilib8fd4712021-06-23 15:32:04 -0700165 a = next;
swissChili53e7cd12021-08-02 21:55:53 -0700166// fprintf(stderr, "a = %p\n", a);
swissChilib8fd4712021-06-23 15:32:04 -0700167 }
168}