Skip to content

Commit dbe70f3

Browse files
Hare: Add simple garbage collector
1 parent 91f7934 commit dbe70f3

File tree

6 files changed

+295
-35
lines changed

6 files changed

+295
-35
lines changed

impls/hare/mal/core.ha

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,6 @@ fn first(args: []MalType) (MalType | error) = {
715715
return ls[0];
716716
};
717717

718-
719718
fn rest(args: []MalType) (MalType | error) = {
720719

721720
if(len(args) == 0)
@@ -754,7 +753,6 @@ fn throw(args: []MalType) (MalType | error) ={
754753
return ("error", args[0]): malerror;
755754
};
756755

757-
758756
fn map(args: []MalType) (MalType | error) = {
759757

760758
if(len(args) < 2)

impls/hare/mal/env.ha

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ export type env = struct {
44
};
55

66
export fn env_init(outer: nullable * env = null) *env ={
7-
return alloc(env {
7+
const new = alloc(env {
88
outer = outer,
99
data = hm_init(),
1010
});
11+
12+
append(gc.memory.envs, new);
13+
return new;
1114
};
1215

1316
export fn env_bind(

impls/hare/mal/gc.ha

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
// Some inspirations taken from https://git.sr.ht/~jummit/rekkyo
2+
3+
type memory = struct {
4+
envs: []*env,
5+
hashs: []hashmap,
6+
symbols: []symbol,
7+
funcs: []function,
8+
lists: []list,
9+
vecs: []vector,
10+
strings: []string,
11+
atoms: []atom,
12+
intrinsics: []intrinsic,
13+
};
14+
15+
type garbage_collector = struct {
16+
marked: memory,
17+
memory: memory,
18+
};
19+
20+
let gc = garbage_collector {
21+
marked = memory {
22+
funcs = [],
23+
...
24+
},
25+
memory = memory {
26+
funcs = [],
27+
...
28+
},
29+
};
30+
31+
fn finish_memory(memory: memory) void = {
32+
free(memory.envs);
33+
free(memory.hashs);
34+
free(memory.symbols);
35+
free(memory.funcs);
36+
free(memory.lists);
37+
free(memory.vecs);
38+
free(memory.strings);
39+
free(memory.atoms);
40+
free(memory.intrinsics);
41+
};
42+
43+
fn mark_hash(map: *nullable *hm_map) void = {
44+
45+
const map = match(*map){
46+
case null =>
47+
return void;
48+
case let map: *hm_map =>
49+
yield map;
50+
};
51+
52+
mark(map.key);
53+
mark(map.val);
54+
55+
for(let child .. map.child){
56+
mark_hash(&child);
57+
};
58+
};
59+
60+
fn mark_env(envi: *env) void = {
61+
62+
for(let e .. gc.marked.envs){
63+
if(e == envi) return void;
64+
};
65+
66+
append(gc.marked.envs, envi);
67+
append(gc.marked.hashs, envi.data);
68+
mark_hash(envi.data.map);
69+
70+
match(envi.outer){
71+
case null => void;
72+
case let e: *env =>
73+
mark_env(e);
74+
};
75+
};
76+
77+
fn mark_col(col: []MalType) void = {
78+
for(let v .. col) {
79+
mark(v);
80+
};
81+
};
82+
83+
fn mark (val: MalType) void = {
84+
85+
match(val){
86+
case let v: vector =>
87+
for(let x .. gc.marked.vecs){
88+
if(x == v) return void;
89+
};
90+
append(gc.marked.vecs, v);
91+
mark_col(v.data);
92+
mark(v.meta);
93+
case let l: list =>
94+
for(let x .. gc.marked.lists){
95+
if(x == l) return void;
96+
};
97+
append(gc.marked.lists, l);
98+
mark_col(l.data);
99+
mark(l.meta);
100+
case let f: function =>
101+
for(let x .. gc.marked.funcs){
102+
if(x == f) return void;
103+
};
104+
append(gc.marked.funcs, f);
105+
mark(f.meta);
106+
mark(f.body);
107+
mark_col(f.args);
108+
mark_env(f.envi);
109+
case let i: intrinsic =>
110+
for(let x .. gc.marked.intrinsics){
111+
if(x == i) return void;
112+
};
113+
append(gc.marked.intrinsics, i);
114+
mark(i.meta);
115+
case let m: macro =>
116+
let m = m:function;
117+
for(let x .. gc.marked.funcs){
118+
if(x == m) return void;
119+
};
120+
append(gc.marked.funcs, m);
121+
mark(m.meta);
122+
mark(m.body);
123+
mark_col(m.args);
124+
mark_env(m.envi);
125+
case let h: hashmap =>
126+
for(let x .. gc.marked.hashs){
127+
if(x == h) return void;
128+
};
129+
append(gc.marked.hashs, h);
130+
mark_hash(h.map);
131+
mark(h.meta);
132+
case let s: symbol =>
133+
for(let x .. gc.marked.symbols){
134+
if(x == s) return void;
135+
};
136+
append(gc.marked.symbols, s);
137+
case let s: string =>
138+
for(let x .. gc.marked.strings){
139+
if(x == s) return void;
140+
};
141+
append(gc.marked.strings, s);
142+
mark(s.meta);
143+
case let a: atom =>
144+
for(let x .. gc.marked.atoms){
145+
if(x == a) return void;
146+
};
147+
append(gc.marked.atoms, a);
148+
mark(*a);
149+
case => void;
150+
};
151+
};
152+
153+
fn sweep() void ={
154+
155+
// since we're only ever comparing pointers and never dereferencing here
156+
// it should be ok to not call delete(gc.memory.<t>[i-1]) on the after
157+
// freeing the value.
158+
//
159+
// This means doing anything with gc.memory after calling this function
160+
// is extremely unsafe.
161+
162+
for :sweep (let i: size = len(gc.memory.symbols); i > 0; i -= 1) {
163+
for(let x .. gc.marked.symbols){
164+
if(x == gc.memory.symbols[i-1]) continue :sweep;
165+
};
166+
free(gc.memory.symbols[i-1]);
167+
};
168+
for :sweep (let i: size = len(gc.memory.atoms); i > 0; i -= 1) {
169+
for(let x .. gc.marked.atoms){
170+
if(x == gc.memory.atoms[i-1]) continue :sweep;
171+
};
172+
free(gc.memory.atoms[i-1]);
173+
};
174+
for :sweep (let i: size = len(gc.memory.strings); i > 0; i -= 1) {
175+
for(let x .. gc.marked.strings){
176+
if(x == gc.memory.strings[i-1]) continue :sweep;
177+
};
178+
free_string(gc.memory.strings[i-1]);
179+
};
180+
for :sweep (let i: size = len(gc.memory.hashs); i > 0; i -= 1) {
181+
for(let x .. gc.marked.hashs){
182+
if(x == gc.memory.hashs[i-1]) continue :sweep;
183+
};
184+
hm_free(gc.memory.hashs[i-1]);
185+
};
186+
for :sweep (let i: size = len(gc.memory.envs); i > 0; i -= 1) {
187+
for(let x .. gc.marked.envs){
188+
if(x == gc.memory.envs[i-1]) continue :sweep;
189+
};
190+
free(gc.memory.envs[i-1]); //.data is collected as a hashmap
191+
};
192+
for :sweep (let i: size = len(gc.memory.vecs); i > 0; i -= 1) {
193+
for(let x .. gc.marked.vecs){
194+
if(x == gc.memory.vecs[i-1]) continue :sweep;
195+
};
196+
free_vec(gc.memory.vecs[i-1]);
197+
};
198+
for :sweep (let i: size = len(gc.memory.lists); i > 0; i -= 1) {
199+
for(let x .. gc.marked.lists){
200+
if(x == gc.memory.lists[i-1]) continue :sweep;
201+
};
202+
free_list(gc.memory.lists[i-1]);
203+
};
204+
for :sweep (let i: size = len(gc.memory.funcs); i > 0; i -= 1) {
205+
for(let x .. gc.marked.funcs){
206+
if(x == gc.memory.funcs[i-1]) continue :sweep;
207+
};
208+
free_func(gc.memory.funcs[i-1]);
209+
};
210+
for :sweep (let i: size = len(gc.memory.intrinsics); i > 0; i -= 1) {
211+
for(let x .. gc.marked.intrinsics){
212+
if(x == gc.memory.intrinsics[i-1]) continue :sweep;
213+
};
214+
free(gc.memory.intrinsics[i-1]);
215+
};
216+
};
217+
218+
// it doesn't make sense to call this with anything but the global repl_env.
219+
220+
export fn run_gc(envi: *env) void = {
221+
222+
mark_env(envi);
223+
sweep();
224+
finish_memory(gc.memory);
225+
226+
gc = garbage_collector {
227+
marked = memory {
228+
funcs = [],
229+
...
230+
},
231+
memory = gc.marked,
232+
};
233+
};
234+

impls/hare/mal/hashmap.ha

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,14 @@ export fn hm_init() hashmap = {
7777

7878
const new_map = alloc(null: nullable *hm_map);
7979

80-
return alloc(struct {
80+
const new = alloc(struct {
8181
meta: MalType = nil,
8282
count: size = 0,
8383
map: *nullable *hm_map = new_map,
8484
});
85+
86+
append(gc.memory.hashs, new);
87+
return new;
8588
};
8689

8790
fn hm_new_node(key: (symbol | string), val: MalType) *hm_map = {
@@ -136,24 +139,20 @@ export fn hm_get(
136139
};
137140
};
138141

139-
fn hm_traverse(hm: hashmap, act: *fn(*hm_map, size) void) void = {
140-
141-
hm_traverse_rec(hm.map, hm.count, act);
142+
fn hm_free(hm: hashmap) void = {
143+
hm_free_rec(hm.map);
144+
free(hm);
142145
};
143146

144-
fn hm_traverse_rec(
145-
map: *nullable *hm_map,
146-
count: size ,
147-
act: *fn(*hm_map,size) void
148-
) void = {
147+
fn hm_free_rec(map: *nullable *hm_map,) void = {
149148

150149
match(*map){
151150
case null =>
152151
return void;
153152
case let map: *hm_map =>
154153
for(let child .. map.child){
155-
hm_traverse_rec(&child, count - 1, act);
154+
hm_free_rec(&child);
156155
};
157-
act(map, count);
156+
free(map);
158157
};
159158
};

0 commit comments

Comments
 (0)