blob: 06d8a07b84791bbdd234e52117e1dfeb5440fa65 [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 {
swissChilia890aed2022-07-30 17:13:07 -070087 fprintf(stderr, "[ GC ] freeing: ");
88 printval(alloc_to_value(a), 1);
swissChilib8fd4712021-06-23 15:32:04 -070089 // Free and remove from allocation list
90 struct alloc *p = a->prev, *n = a->next;
91 free_aligned(a);
92
93 a = n;
94
95 if (p)
96 p->next = n;
97
98 if (n)
99 n->prev = p;
100 }
swissChilie9fec8b2021-06-22 13:59:33 -0700101 }
swissChili6d6525e2021-06-15 21:20:53 -0700102}
103
swissChilia890aed2022-07-30 17:13:07 -0700104struct gc_stats gc_get_stats()
105{
106 struct gc_stats stats = {0};
107
108 stats.gc_runs = gc_runs;
109
110 for (struct alloc *a = first_a; a; a = a->next)
111 {
112 stats.total_allocs++;
113 }
114
115 return stats;
116}
117
swissChili6d6525e2021-06-15 21:20:53 -0700118void _do_gc(unsigned int esp, unsigned int ebp)
119{
swissChili9e57da42021-06-15 22:22:46 -0700120 value_t *esp_p = (value_t *)esp,
121 *ebp_p = (value_t *)ebp;
swissChili6d6525e2021-06-15 21:20:53 -0700122
swissChilie9fec8b2021-06-22 13:59:33 -0700123 unsigned int num_marked = 0;
124
125 gc_mark++;
swissChilia890aed2022-07-30 17:13:07 -0700126 gc_runs++;
swissChilie9fec8b2021-06-22 13:59:33 -0700127
swissChili9e57da42021-06-15 22:22:46 -0700128 // For every stack frame until the base of the stack
129 while (esp_p < gc_base)
swissChili6d6525e2021-06-15 21:20:53 -0700130 {
swissChili9e57da42021-06-15 22:22:46 -0700131 // Walk up the stack until we reach either the frame pointer or the base
132 // of the stack. Basically walk to the top of this function's stack
133 // frame.
134 for (; esp_p < ebp_p && esp_p < gc_base; esp_p++)
135 {
swissChilie9fec8b2021-06-22 13:59:33 -0700136 _mark(*esp_p, &num_marked);
swissChili9e57da42021-06-15 22:22:46 -0700137 }
138
139 // Set the frame pointer to the frame pointer on the stack
140 ebp_p = (value_t *)*esp_p;
141
142 // Step up two stack slots, one for the frame pointer and one for the
143 // return address.
144 esp_p += 2;
swissChili6d6525e2021-06-15 21:20:53 -0700145 }
swissChili9e57da42021-06-15 22:22:46 -0700146
swissChilie9fec8b2021-06-22 13:59:33 -0700147 _sweep();
swissChili6d6525e2021-06-15 21:20:53 -0700148}
swissChilib8fd4712021-06-23 15:32:04 -0700149
150void free_all()
151{
152 for (struct alloc *a = first_a; a;)
153 {
154 struct alloc *next = a->next;
155 free_aligned(a);
156 a = next;
swissChili53e7cd12021-08-02 21:55:53 -0700157// fprintf(stderr, "a = %p\n", a);
swissChilib8fd4712021-06-23 15:32:04 -0700158 }
159}