swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 1 | #include "gc.h" |
| 2 | #include "lisp.h" |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 3 | #include "plat/plat.h" |
swissChili | 8056031 | 2022-07-31 21:05:47 -0700 | [diff] [blame] | 4 | #include <stdlib.h> |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 5 | |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 6 | value_t *gc_base; |
swissChili | 8056031 | 2022-07-31 21:05:47 -0700 | [diff] [blame] | 7 | struct gc_segment *gc_last_segment = NULL; |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 8 | THREAD_LOCAL static unsigned int gc_mark; |
swissChili | a890aed | 2022-07-30 17:13:07 -0700 | [diff] [blame] | 9 | THREAD_LOCAL static unsigned long gc_runs = 0; |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 10 | |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 11 | void __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 | |
swissChili | 8056031 | 2022-07-31 21:05:47 -0700 | [diff] [blame] | 19 | void 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 | |
| 43 | void 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 | |
| 50 | void 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 | |
| 58 | void gc_set_retained(int index, value_t retained) |
| 59 | { |
| 60 | gc_last_segment->retained[index] = retained; |
| 61 | } |
| 62 | |
| 63 | void gc_set_retained(int index, value_t retained); |
| 64 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 65 | void _mark(value_t value, unsigned int *marked) |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 66 | { |
| 67 | if (heapp(value)) |
| 68 | { |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 69 | void *pointer = (void *)(value & ~HEAP_MASK); |
| 70 | struct alloc *alloc = pointer - sizeof(struct alloc); |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 71 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 72 | // 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 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 80 | 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 | } |
swissChili | ddc9754 | 2021-07-04 11:47:42 -0700 | [diff] [blame] | 88 | 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 | } |
swissChili | 8b5ec7a | 2022-08-05 22:26:17 -0700 | [diff] [blame] | 98 | 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 | } |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 108 | } |
| 109 | } |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 110 | } |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 111 | } |
| 112 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 113 | value_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 | |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 119 | void _sweep() |
| 120 | { |
swissChili | 5db22a0 | 2021-06-30 22:04:33 -0700 | [diff] [blame] | 121 | for (struct alloc *a = first_a; a;) |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 122 | { |
swissChili | a890aed | 2022-07-30 17:13:07 -0700 | [diff] [blame] | 123 | // 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 | |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 126 | if (pool_alive(a->pool) || a->mark == gc_mark) |
| 127 | { |
| 128 | // Marked or in living pool |
swissChili | 5db22a0 | 2021-06-30 22:04:33 -0700 | [diff] [blame] | 129 | a = a->next; |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 130 | } |
| 131 | else |
| 132 | { |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 133 | // Free and remove from allocation list |
| 134 | struct alloc *p = a->prev, *n = a->next; |
swissChili | c0acce4 | 2022-07-31 13:38:17 -0700 | [diff] [blame] | 135 | del_alloc(a); |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 136 | |
| 137 | a = n; |
| 138 | |
swissChili | c0acce4 | 2022-07-31 13:38:17 -0700 | [diff] [blame] | 139 | if (a == first_a) |
| 140 | { |
| 141 | first_a = n; |
| 142 | } |
| 143 | |
| 144 | if (a == last_a) |
| 145 | { |
| 146 | last_a = p; |
| 147 | } |
| 148 | |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 149 | if (p) |
| 150 | p->next = n; |
| 151 | |
| 152 | if (n) |
| 153 | n->prev = p; |
| 154 | } |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 155 | } |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 156 | } |
| 157 | |
swissChili | a890aed | 2022-07-30 17:13:07 -0700 | [diff] [blame] | 158 | struct 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 | |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 172 | void _do_gc(unsigned int esp, unsigned int ebp) |
| 173 | { |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 174 | value_t *esp_p = (value_t *)esp, |
| 175 | *ebp_p = (value_t *)ebp; |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 176 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 177 | unsigned int num_marked = 0; |
| 178 | |
| 179 | gc_mark++; |
swissChili | a890aed | 2022-07-30 17:13:07 -0700 | [diff] [blame] | 180 | gc_runs++; |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 181 | |
swissChili | 8056031 | 2022-07-31 21:05:47 -0700 | [diff] [blame] | 182 | for (struct gc_segment *seg = gc_last_segment; seg && seg->seg_start; seg = seg->prev) |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 183 | { |
swissChili | 8056031 | 2022-07-31 21:05:47 -0700 | [diff] [blame] | 184 | // For every stack frame until the base of the stack |
| 185 | while (esp_p < (value_t *)seg->seg_end) |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 186 | { |
swissChili | 8056031 | 2022-07-31 21:05:47 -0700 | [diff] [blame] | 187 | // 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; |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 201 | } |
| 202 | |
swissChili | 8056031 | 2022-07-31 21:05:47 -0700 | [diff] [blame] | 203 | // skip above ret pointer |
| 204 | value_t *args = seg->seg_end + 4; |
| 205 | for (int i = 0; i < seg->nargs; i++) |
| 206 | { |
swissChili | 8056031 | 2022-07-31 21:05:47 -0700 | [diff] [blame] | 207 | // mark arguments |
| 208 | _mark(args[i], &num_marked); |
| 209 | } |
| 210 | |
| 211 | for (int i = 0; i < seg->nretained; i++) |
| 212 | { |
swissChili | 8056031 | 2022-07-31 21:05:47 -0700 | [diff] [blame] | 213 | _mark(seg->retained[i], &num_marked); |
| 214 | } |
| 215 | |
| 216 | esp_p = seg->seg_start; |
| 217 | ebp_p = seg->old_ebp; |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 218 | } |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 219 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 220 | _sweep(); |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 221 | } |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 222 | |
| 223 | void free_all() |
| 224 | { |
| 225 | for (struct alloc *a = first_a; a;) |
| 226 | { |
| 227 | struct alloc *next = a->next; |
swissChili | c0acce4 | 2022-07-31 13:38:17 -0700 | [diff] [blame] | 228 | del_alloc(a); |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 229 | a = next; |
swissChili | 53e7cd1 | 2021-08-02 21:55:53 -0700 | [diff] [blame] | 230 | // fprintf(stderr, "a = %p\n", a); |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 231 | } |
| 232 | } |