1 #include "system/stacktrace.h"
11 #define GC_INITIAL_CAPACITY 256
22 static intptr_t value_of_expr(struct Expr expr)
24 if (expr.type == EXPR_CONS) {
25 return (intptr_t) expr.cons;
26 } else if (expr.type == EXPR_ATOM) {
27 return (intptr_t) expr.atom;
33 static int compare_exprs(const void *a, const void *b)
38 const intptr_t ptr_a = value_of_expr(*(const struct Expr *)a);
39 const intptr_t ptr_b = value_of_expr(*(const struct Expr *)b);
40 const intptr_t d = ptr_b - ptr_a;
55 Gc *gc = PUSH_LT(lt, calloc(1, sizeof(Gc)), free);
61 gc->exprs = PUSH_LT(lt, calloc(GC_INITIAL_CAPACITY, sizeof(struct Expr)), free);
62 if (gc->exprs == NULL) {
66 gc->visited = PUSH_LT(lt, calloc(GC_INITIAL_CAPACITY, sizeof(int)), free);
67 if (gc->visited == NULL) {
72 gc->capacity = GC_INITIAL_CAPACITY;
77 void destroy_gc(Gc *gc)
81 for (size_t i = 0; i < gc->size; ++i) {
82 destroy_expr(gc->exprs[i]);
88 int gc_add_expr(Gc *gc, struct Expr expr)
92 if (gc->size >= gc->capacity) {
93 const size_t new_capacity = gc->capacity * 2;
94 struct Expr *const new_exprs = realloc(
96 sizeof(struct Expr) * new_capacity);
98 if (new_exprs == NULL) {
102 int *const new_visited = realloc(
104 sizeof(int) * new_capacity);
106 if (new_visited == NULL) {
110 gc->capacity = new_capacity;
111 gc->exprs = REPLACE_LT(gc->lt, gc->exprs, new_exprs);
112 gc->visited = REPLACE_LT(gc->lt, gc->visited, new_visited);
115 gc->exprs[gc->size++] = expr;
120 static long int gc_find_expr(Gc *gc, struct Expr expr)
125 struct Expr *result =
126 (struct Expr *) bsearch(&expr, gc->exprs, gc->size,
127 sizeof(struct Expr), compare_exprs);
129 if (result == NULL) {
133 return (long int) (result - gc->exprs);
136 static void gc_traverse_expr(Gc *gc, struct Expr root)
139 trace_assert(root.type != EXPR_VOID);
140 const long int root_index = gc_find_expr(gc, root);
141 if (root_index < 0) {
142 fprintf(stderr, "GC tried to collect something that was not registered\n");
143 print_expr_as_sexpr(stderr, root);
144 fprintf(stderr, "\n");
145 trace_assert(root_index >= 0);
148 if (gc->visited[root_index]) {
152 gc->visited[root_index] = 1;
155 gc_traverse_expr(gc, root.cons->car);
156 gc_traverse_expr(gc, root.cons->cdr);
157 } else if (root.type == EXPR_ATOM
158 && root.atom->type == ATOM_LAMBDA) {
159 gc_traverse_expr(gc, root.atom->lambda.args_list);
160 gc_traverse_expr(gc, root.atom->lambda.body);
161 gc_traverse_expr(gc, root.atom->lambda.envir);
165 void gc_collect(Gc *gc, struct Expr root)
170 /* Sort gc->exprs O(nlogn) */
171 qsort(gc->exprs, gc->size, sizeof(struct Expr), compare_exprs);
173 /* Defragment O(n) */
174 while(gc->size > 0 && gc->exprs[gc->size - 1].type == EXPR_VOID) {
178 /* Initialize visited array O(n) */
179 memset(gc->visited, 0, sizeof(int) * gc->size);
181 /* Traverse root O(nlogn) */
182 gc_traverse_expr(gc, root);
184 /* Dealloc unvisted O(n) */
185 for (size_t i = 0; i < gc->size; ++i) {
186 if (!gc->visited[i]) {
187 destroy_expr(gc->exprs[i]);
188 gc->exprs[i] = void_expr();
193 void gc_inspect(const Gc *gc)
195 for (size_t i = 0; i < gc->size; ++i) {
196 if (gc->exprs[i].type == EXPR_VOID) {