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 | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 4 | |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 5 | value_t *gc_base; |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 6 | THREAD_LOCAL static unsigned int gc_mark; |
swissChili | a890aed | 2022-07-30 17:13:07 -0700 | [diff] [blame] | 7 | THREAD_LOCAL static unsigned long gc_runs = 0; |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 8 | |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 9 | void __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 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 17 | void _mark(value_t value, unsigned int *marked) |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 18 | { |
| 19 | if (heapp(value)) |
| 20 | { |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 21 | void *pointer = (void *)(value & ~HEAP_MASK); |
| 22 | struct alloc *alloc = pointer - sizeof(struct alloc); |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 23 | |
swissChili | a890aed | 2022-07-30 17:13:07 -0700 | [diff] [blame] | 24 | 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 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 33 | // 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 | } |
swissChili | ddc9754 | 2021-07-04 11:47:42 -0700 | [diff] [blame] | 52 | 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 | } |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 62 | } |
| 63 | } |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 64 | } |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 65 | } |
| 66 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 67 | value_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 | |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 73 | void _sweep() |
| 74 | { |
swissChili | 5db22a0 | 2021-06-30 22:04:33 -0700 | [diff] [blame] | 75 | for (struct alloc *a = first_a; a;) |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 76 | { |
swissChili | a890aed | 2022-07-30 17:13:07 -0700 | [diff] [blame] | 77 | // 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 | |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 80 | if (pool_alive(a->pool) || a->mark == gc_mark) |
| 81 | { |
| 82 | // Marked or in living pool |
swissChili | 5db22a0 | 2021-06-30 22:04:33 -0700 | [diff] [blame] | 83 | a = a->next; |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 84 | } |
| 85 | else |
| 86 | { |
swissChili | a890aed | 2022-07-30 17:13:07 -0700 | [diff] [blame] | 87 | fprintf(stderr, "[ GC ] freeing: "); |
| 88 | printval(alloc_to_value(a), 1); |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 89 | // 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 | } |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 101 | } |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 102 | } |
| 103 | |
swissChili | a890aed | 2022-07-30 17:13:07 -0700 | [diff] [blame] | 104 | struct 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 | |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 118 | void _do_gc(unsigned int esp, unsigned int ebp) |
| 119 | { |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 120 | value_t *esp_p = (value_t *)esp, |
| 121 | *ebp_p = (value_t *)ebp; |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 122 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 123 | unsigned int num_marked = 0; |
| 124 | |
| 125 | gc_mark++; |
swissChili | a890aed | 2022-07-30 17:13:07 -0700 | [diff] [blame] | 126 | gc_runs++; |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 127 | |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 128 | // For every stack frame until the base of the stack |
| 129 | while (esp_p < gc_base) |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 130 | { |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 131 | // 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 | { |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 136 | _mark(*esp_p, &num_marked); |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 137 | } |
| 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; |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 145 | } |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 146 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 147 | _sweep(); |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 148 | } |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 149 | |
| 150 | void 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; |
swissChili | 53e7cd1 | 2021-08-02 21:55:53 -0700 | [diff] [blame] | 157 | // fprintf(stderr, "a = %p\n", a); |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 158 | } |
| 159 | } |