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 | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 7 | |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 8 | void __attribute__((noinline)) gc_set_base_here() |
| 9 | { |
| 10 | // Move the current stack top address to gc_base. This works nicely because |
| 11 | // it means that when another (presumably lisp) function is called right |
| 12 | // after this, the stack top for it will be the same as for this function. |
| 13 | asm("movl %%esp, %0" : "=g"(gc_base)); |
| 14 | } |
| 15 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 16 | void _mark(value_t value, unsigned int *marked) |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 17 | { |
| 18 | if (heapp(value)) |
| 19 | { |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 20 | void *pointer = (void *)(value & ~HEAP_MASK); |
| 21 | struct alloc *alloc = pointer - sizeof(struct alloc); |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 22 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 23 | fprintf(stderr, "[ GC ] Marking 0x%p\n", pointer); |
| 24 | |
| 25 | // Only recursively mark if this hasn't been marked yet. i.e. prevent |
| 26 | // marking circular references twice |
| 27 | if (alloc->mark != gc_mark) |
| 28 | { |
| 29 | ++*marked; |
| 30 | |
| 31 | alloc->mark = gc_mark; |
| 32 | |
| 33 | // printf("[ GC ] val ="); |
| 34 | // printval(alloc_to_value(alloc), 2); |
| 35 | |
| 36 | switch (alloc->type_tag) |
| 37 | { |
| 38 | case CONS_TAG: { |
| 39 | struct cons_alloc *cons = (struct cons_alloc *)alloc; |
| 40 | _mark(cons->cons.car, marked); |
| 41 | _mark(cons->cons.cdr, marked); |
| 42 | break; |
| 43 | } |
| 44 | } |
| 45 | } |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 46 | } |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 47 | } |
| 48 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 49 | value_t alloc_to_value(struct alloc *a) |
| 50 | { |
| 51 | void *val = ((void *)a) + sizeof(struct alloc); |
| 52 | return (unsigned int)val | a->type_tag; |
| 53 | } |
| 54 | |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 55 | void _sweep() |
| 56 | { |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 57 | for (struct alloc *a = first_a; a; a = a->next) |
| 58 | { |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 59 | if (pool_alive(a->pool) || a->mark == gc_mark) |
| 60 | { |
| 61 | // Marked or in living pool |
| 62 | } |
| 63 | else |
| 64 | { |
| 65 | printf("Freeing:\n"); |
| 66 | printval(alloc_to_value(a), 2); |
| 67 | |
| 68 | // Free and remove from allocation list |
| 69 | struct alloc *p = a->prev, *n = a->next; |
| 70 | free_aligned(a); |
| 71 | |
| 72 | a = n; |
| 73 | |
| 74 | if (p) |
| 75 | p->next = n; |
| 76 | |
| 77 | if (n) |
| 78 | n->prev = p; |
| 79 | } |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 80 | } |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | void _do_gc(unsigned int esp, unsigned int ebp) |
| 84 | { |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 85 | value_t *esp_p = (value_t *)esp, |
| 86 | *ebp_p = (value_t *)ebp; |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 87 | |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 88 | unsigned int num_marked = 0; |
| 89 | |
| 90 | gc_mark++; |
| 91 | |
| 92 | fprintf(stderr, "[ GC ] %d (esp 0x%p, ebp 0x%p)\n", gc_mark, esp_p, ebp_p); |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 93 | |
| 94 | // For every stack frame until the base of the stack |
| 95 | while (esp_p < gc_base) |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 96 | { |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 97 | // Walk up the stack until we reach either the frame pointer or the base |
| 98 | // of the stack. Basically walk to the top of this function's stack |
| 99 | // frame. |
| 100 | for (; esp_p < ebp_p && esp_p < gc_base; esp_p++) |
| 101 | { |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 102 | _mark(*esp_p, &num_marked); |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 103 | } |
| 104 | |
| 105 | // Set the frame pointer to the frame pointer on the stack |
| 106 | ebp_p = (value_t *)*esp_p; |
| 107 | |
| 108 | // Step up two stack slots, one for the frame pointer and one for the |
| 109 | // return address. |
| 110 | esp_p += 2; |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 111 | } |
swissChili | 9e57da4 | 2021-06-15 22:22:46 -0700 | [diff] [blame] | 112 | |
| 113 | fprintf(stderr, "Marked %d\n", num_marked); |
swissChili | e9fec8b | 2021-06-22 13:59:33 -0700 | [diff] [blame] | 114 | |
| 115 | _sweep(); |
swissChili | 6d6525e | 2021-06-15 21:20:53 -0700 | [diff] [blame] | 116 | } |
swissChili | b8fd471 | 2021-06-23 15:32:04 -0700 | [diff] [blame] | 117 | |
| 118 | void free_all() |
| 119 | { |
| 120 | for (struct alloc *a = first_a; a;) |
| 121 | { |
| 122 | struct alloc *next = a->next; |
| 123 | free_aligned(a); |
| 124 | a = next; |
| 125 | } |
| 126 | } |