DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / wasm / mem.wam
CommitLineData
33309c6a
JM
1(module $mem
2 (global $MEM_SIZE i32 1048576)
3 (global $STRING_MEM_SIZE i32 1048576)
4
5 (global $heap_start (mut i32) 0)
6 (global $heap_end (mut i32) 0)
7
8 (global $mem (mut i32) 0)
9 (global $mem_unused_start (mut i32) 0)
10 (global $mem_free_list (mut i32) 0)
11 (global $mem_user_start (mut i32) 0)
12
50eea9ad
JM
13 (global $string_mem (mut i32) 0)
14 (global $string_mem_next (mut i32) 0)
15 (global $string_mem_user_start (mut i32) 0)
33309c6a
JM
16
17 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
18 ;; General type storage/pointer functions
19
20 (func $VAL0_ptr (param $mv i32) (result i32)
21 (i32.add $mv 4))
22 (func $VAL1_ptr (param $mv i32) (result i32)
23 (i32.add $mv 8))
24
25 (func $VAL0 (param $mv i32) (result i32)
26 (i32.load (i32.add $mv 4)))
27 (func $VAL1 (param $mv i32) (result i32)
28 (i32.load (i32.add $mv 8)))
29
30
31 (func $MEM_VAL0_ptr (param $mv i32) (result i32)
0a19c2f1 32 (i32.add (global.get $mem)
0c62f14e 33 (i32.mul (i32.load (i32.add $mv 4)) 4)))
33309c6a 34 (func $MEM_VAL1_ptr (param $mv i32) (result i32)
0a19c2f1 35 (i32.add (global.get $mem)
0c62f14e 36 (i32.mul (i32.load (i32.add $mv 8)) 4)))
33309c6a 37 (func $MEM_VAL2_ptr (param $mv i32) (result i32)
0a19c2f1 38 (i32.add (global.get $mem)
0c62f14e 39 (i32.mul (i32.load (i32.add $mv 12)) 4)))
3ea09886
JM
40
41 ;; Returns the memory index mem of mv
42 ;; Will usually be used with a load or store by the caller
43 (func $IDX (param $mv i32) (result i32)
44 ;; MalVal memory 64 bit (2 * i32) aligned
0a19c2f1 45 (i32.div_u (i32.sub $mv (global.get $mem)) 4))
33309c6a
JM
46
47 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
48
49 ;; Returns the address of 'mem[mv_idx]'
50 (func $MalVal_ptr (param $mv_idx i32) (result i32)
51 ;; MalVal memory 64 bit (2 * i32) aligned
52 ;;; mem[mv_idx].refcnt_type
0a19c2f1 53 (i32.add (global.get $mem) (i32.mul $mv_idx 4)))
33309c6a
JM
54
55 ;; Returns the address of 'mem[mv_idx].refcnt_type'
56 (func $MalVal_refcnt_type (param $mv_idx i32) (result i32)
57 (i32.load ($MalVal_ptr $mv_idx)))
58
59 (func $TYPE (param $mv i32) (result i32)
60 ;;; type = mv->refcnt_type & 31
61 (i32.and (i32.load $mv) 0x1f)) ;; 0x1f == 31
62
3ea09886
JM
63 (func $SET_TYPE (param $mv i32 $type i32)
64 ;;; type = mv->refcnt_type & 31
65 ;;; mv->refcnt_type += - (mv->refcnt_type & 31) + type
66 (i32.store $mv (i32.or
67 (i32.and $type 0x1f) ;; 0x1f == 31
68 (i32.and (i32.load $mv) 0xffffffe1)))
69 )
70
71
33309c6a
JM
72 (func $REFS (param $mv i32) (result i32)
73 ;;; type = mv->refcnt_type & 31
74 (i32.shr_u (i32.load $mv) 5)) ;; / 32
75
76 ;; Returns the address of 'mem[mv_idx].val[val]'
77 ;; Will usually be used with a load or store by the caller
3ea09886 78 (func $MalVal_val_ptr (param $mv_idx i32 $val i32) (result i32)
33309c6a 79 (i32.add (i32.add ($MalVal_ptr $mv_idx) 4)
0c62f14e 80 (i32.mul $val 4)))
33309c6a
JM
81
82 ;; Returns the value of 'mem[mv_idx].val[val]'
3ea09886 83 (func $MalVal_val (param $mv_idx i32 $val i32) (result i32)
33309c6a
JM
84 (i32.load ($MalVal_val_ptr $mv_idx $val)))
85
86 (func $MalType_size (param $type i32) (result i32)
87 ;;; if (type <= 5 || type == 9 || type == 12)
77bf4e61 88 (if (result i32) (OR (i32.le_u $type 5)
3ea09886
JM
89 (i32.eq $type 9)
90 (i32.eq $type 12))
33309c6a
JM
91 (then 2)
92 (else
93 ;;; else if (type == 8 || type == 10 || type == 11)
77bf4e61 94 (if (result i32) (OR (i32.eq $type 8)
3ea09886
JM
95 (i32.eq $type 10)
96 (i32.eq $type 11))
33309c6a
JM
97 (then 4)
98 (else 3)))))
99
100 (func $MalVal_size (param $mv i32) (result i32)
349faa83 101 (LET $type ($TYPE $mv))
33309c6a 102 ;; if (type == FREE_T)
0a19c2f1 103 (if (result i32) (i32.eq $type (global.get $FREE_T))
33309c6a
JM
104 (then
105 ;;; return (mv->refcnt_type & 0xffe0)>>5
106 (i32.shr_u (i32.and (i32.load $mv) 0xffe0) 5)) ;;; / 32
107 (else
108 ;;; return MalType_size(type)
109 ($MalType_size $type))))
110
111 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
112 ;; init_memory
113
114 (func $init_memory
349faa83 115 (LET $heap_size 0)
33309c6a
JM
116
117;; ($print ">>> init_memory\n")
118
50eea9ad 119 ($init_printf_mem)
33309c6a 120
3ea09886 121 ;; error_str string buffer
0a19c2f1 122 (global.set $error_str (STATIC_ARRAY 100))
3ea09886 123 ;; reader token string buffer
0a19c2f1 124 (global.set $token_buf (STATIC_ARRAY 256))
3ea09886 125 ;; printer string buffer
0a19c2f1 126 (global.set $printer_buf (STATIC_ARRAY 4096))
33309c6a 127
0a19c2f1
JM
128 (local.set $heap_size (i32.add (global.get $MEM_SIZE)
129 (global.get $STRING_MEM_SIZE)))
130 (global.set $heap_start (i32.add (global.get $memoryBase)
131 (global.get $S_STRING_END)))
132 (global.set $heap_end (i32.add (global.get $heap_start)
33309c6a
JM
133 $heap_size))
134
0a19c2f1
JM
135 (global.set $mem (global.get $heap_start))
136 (global.set $mem_unused_start 0)
137 (global.set $mem_free_list 0)
33309c6a 138
0a19c2f1
JM
139 (global.set $string_mem (i32.add (global.get $heap_start)
140 (global.get $MEM_SIZE)))
141 (global.set $string_mem_next (global.get $string_mem))
50eea9ad 142
0a19c2f1
JM
143 (global.set $mem_user_start (global.get $mem_unused_start))
144 (global.set $string_mem_user_start (global.get $string_mem_next))
33309c6a
JM
145
146 ;; Empty values
0a19c2f1
JM
147 (global.set $NIL
148 ($ALLOC_SCALAR (global.get $NIL_T) 0))
149 (global.set $FALSE
150 ($ALLOC_SCALAR (global.get $BOOLEAN_T) 0))
151 (global.set $TRUE
152 ($ALLOC_SCALAR (global.get $BOOLEAN_T) 1))
153 (global.set $EMPTY_LIST
154 ($ALLOC (global.get $LIST_T)
155 (global.get $NIL) (global.get $NIL) (global.get $NIL)))
156 (global.set $EMPTY_VECTOR
157 ($ALLOC (global.get $VECTOR_T)
158 (global.get $NIL) (global.get $NIL) (global.get $NIL)))
159 (global.set $EMPTY_HASHMAP
160 ($ALLOC (global.get $HASHMAP_T)
161 (global.get $NIL) (global.get $NIL) (global.get $NIL)))
33309c6a
JM
162
163;; ($print "<<< init_memory\n")
164
165 )
166
167 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
168 ;; memory management
169
3ea09886
JM
170 (func $ALLOC_INTERNAL (param $type i32
171 $val1 i32 $val2 i32 $val3 i32) (result i32)
0a19c2f1
JM
172 (LET $prev (global.get $mem_free_list)
173 $res (global.get $mem_free_list)
349faa83 174 $size ($MalType_size $type))
33309c6a
JM
175
176 (block $loop_done
177 (loop $loop
178 ;; res == mem_unused_start
0a19c2f1 179 (if (i32.eq $res (global.get $mem_unused_start))
33309c6a
JM
180 (then
181 ;; ALLOC_UNUSED
182 ;;; if (res + size > MEM_SIZE)
0a19c2f1 183 (if (i32.gt_u (i32.add $res $size) (global.get $MEM_SIZE))
50eea9ad
JM
184 ;; Out of memory, exit
185 ($fatal 7 "Out of mal memory!\n"))
33309c6a 186 ;;; if (mem_unused_start += size)
0a19c2f1
JM
187 (global.set $mem_unused_start
188 (i32.add (global.get $mem_unused_start) $size))
33309c6a
JM
189 ;;; if (prev == res)
190 (if (i32.eq $prev $res)
191 (then
0a19c2f1 192 (global.set $mem_free_list (global.get $mem_unused_start)))
33309c6a
JM
193 (else
194 ;;; mem[prev].val[0] = mem_unused_start
195 (i32.store
196 ($MalVal_val_ptr $prev 0)
0a19c2f1 197 (global.get $mem_unused_start))))
33309c6a
JM
198 (br $loop_done)))
199 ;; if (MalVal_size(mem+res) == size)
200 (if (i32.eq ($MalVal_size ($MalVal_ptr $res))
201 $size)
202 (then
203 ;; ALLOC_MIDDLE
204 ;;; if (res == mem_free_list)
0a19c2f1 205 (if (i32.eq $res (global.get $mem_free_list))
33309c6a
JM
206 ;; set free pointer (mem_free_list) to next free
207 ;;; mem_free_list = mem[res].val[0];
0a19c2f1 208 (global.set $mem_free_list ($MalVal_val $res 0)))
33309c6a 209 ;; if (res != mem_free_list)
0a19c2f1 210 (if (i32.ne $res (global.get $mem_free_list))
33309c6a
JM
211 ;; set previous free to next free
212 ;;; mem[prev].val[0] = mem[res].val[0]
213 (i32.store ($MalVal_val_ptr $prev 0) ($MalVal_val $res 0)))
214 (br $loop_done)))
215 ;;; prev = res
0a19c2f1 216 (local.set $prev $res)
33309c6a 217 ;;; res = mem[res].val[0]
0a19c2f1 218 (local.set $res ($MalVal_val $res 0))
33309c6a
JM
219 (br $loop)
220 )
221 )
222 ;; ALLOC_DONE
223 ;;; mem[res].refcnt_type = type + 32
224 (i32.store ($MalVal_ptr $res) (i32.add $type 32))
225 ;; set val to default val1
226 ;;; mem[res].val[0] = val1
227 (i32.store ($MalVal_val_ptr $res 0) $val1)
228 ;;; if (type > 5 && type != 9)
3ea09886
JM
229 (if (AND (i32.gt_u $type 5)
230 (i32.ne $type 9))
33309c6a
JM
231 (then
232 ;; inc refcnt of referenced value
233 ;;; mem[val1].refcnt_type += 32
234 (i32.store ($MalVal_ptr $val1)
235 (i32.add ($MalVal_refcnt_type $val1) 32))))
236 ;;; if (size > 2)
237 (if (i32.gt_u $size 2)
238 (then
239 ;; inc refcnt of referenced value
240 ;;; mem[val2].refcnt_type += 32
241 (i32.store ($MalVal_ptr $val2)
242 (i32.add ($MalVal_refcnt_type $val2) 32))
243 ;;; mem[res].val[1] = val2
244 (i32.store ($MalVal_val_ptr $res 1) $val2)))
245 ;;; if (size > 3)
246 (if (i32.gt_u $size 3)
247 (then
248 ;; inc refcnt of referenced value
249 ;;; mem[val3].refcnt_type += 32
250 (i32.store ($MalVal_ptr $val3)
251 (i32.add ($MalVal_refcnt_type $val3) 32))
252 ;;; mem[res].val[2] = val3
253 (i32.store ($MalVal_val_ptr $res 2) $val3)))
254
255 ;;; return mem + res
256 ($MalVal_ptr $res)
257 )
258
3ea09886 259 (func $ALLOC_SCALAR (param $type i32 $val1 i32) (result i32)
33309c6a
JM
260 ($ALLOC_INTERNAL $type $val1 0 0)
261 )
262
3ea09886
JM
263 (func $ALLOC (param $type i32 $val1 i32 $val2 i32 $val3 i32) (result i32)
264 ($ALLOC_INTERNAL $type ($IDX $val1) ($IDX $val2) ($IDX $val3))
33309c6a
JM
265 )
266
267 (func $RELEASE (param $mv i32)
349faa83 268 (LET $idx 0 $type 0 $size 0)
33309c6a
JM
269
270 ;; Ignore NULLs
271 ;;; if (mv == NULL) { return; }
272 (if (i32.eqz $mv) (return))
273 ;;; idx = mv - mem
0a19c2f1 274 (local.set $idx ($IDX $mv))
33309c6a 275 ;;; type = mv->refcnt_type & 31
0a19c2f1 276 (local.set $type (i32.and (i32.load $mv) 0x1f)) ;; 0x1f == 31
33309c6a 277 ;;; size = MalType_size(type)
0a19c2f1 278 (local.set $size ($MalType_size $type))
33309c6a
JM
279
280 ;; DEBUG
281 ;;; printf(">>> RELEASE idx: %d, type: %d, size: %d\n", idx, type, size)
282
283 (if (i32.eq 0 $mv)
50eea9ad 284 ($fatal 7 "RELEASE of NULL!\n"))
33309c6a 285
0a19c2f1 286 (if (i32.eq (global.get $FREE_T) $type)
33309c6a
JM
287 (then
288 ($printf_2 "RELEASE of already free mv: 0x%x, idx: 0x%x\n" $mv $idx)
50eea9ad 289 ($fatal 1 "")))
33309c6a
JM
290 (if (i32.lt_u ($MalVal_refcnt_type $idx) 15)
291 (then
292 ($printf_2 "RELEASE of unowned mv: 0x%x, idx: 0x%x\n" $mv $idx)
50eea9ad 293 ($fatal 1 "")))
33309c6a
JM
294
295 ;; decrease reference count by one
296 (i32.store ($MalVal_ptr $idx)
0c62f14e 297 (i32.sub ($MalVal_refcnt_type $idx) 32))
33309c6a
JM
298
299 ;; nil, false, true, empty sequences
0a19c2f1 300 (if (i32.le_u $mv (global.get $EMPTY_HASHMAP))
33309c6a
JM
301 (then
302 (if (i32.lt_u ($MalVal_refcnt_type $idx) 32)
303 (then
304 ($printf_2 "RELEASE of unowned mv: 0x%x, idx: 0x%x\n" $mv $idx)
50eea9ad 305 ($fatal 1 "")))
33309c6a
JM
306 (return)))
307
308 ;; our reference count is not 0, so don't release
309 (if (i32.ge_u ($MalVal_refcnt_type $idx) 32)
310 (return))
311
312 (block $done
3ea09886
JM
313 (block (block (block (block (block (block (block (block (block
314 (br_table 0 0 0 0 1 1 2 2 3 0 4 4 5 6 7 8 8 $type))
33309c6a
JM
315 ;; nil, boolean, integer, float
316 (br $done))
317 ;; string, kw, symbol
318 ;; release string, then FREE reference
0a19c2f1 319 ($RELEASE_STRING (i32.add (global.get $string_mem) ($VAL0 $mv)))
33309c6a
JM
320 (br $done))
321 ;; list, vector
322 (if (i32.ne ($MalVal_val $idx 0) 0)
323 (then
324 ;; release next element and value
325 ($RELEASE ($MEM_VAL0_ptr $mv))
326 ($RELEASE ($MEM_VAL1_ptr $mv))))
327 (br $done))
328 ;; hashmap
329 (if (i32.ne ($MalVal_val $idx 0) 0)
330 (then
331 ;; release next element, value, and key
332 ($RELEASE ($MEM_VAL0_ptr $mv))
333 ($RELEASE ($MEM_VAL2_ptr $mv))
334 ($RELEASE ($MEM_VAL1_ptr $mv))))
335 (br $done))
3ea09886
JM
336 ;; mal / macro function
337 ;; release ast, params, and environment
338 ($RELEASE ($MEM_VAL2_ptr $mv))
339 ($RELEASE ($MEM_VAL1_ptr $mv))
340 ($RELEASE ($MEM_VAL0_ptr $mv))
341 (br $done))
342 ;; atom
343 ;; release contained/referred value
344 ($RELEASE ($MEM_VAL0_ptr $mv))
345 (br $done))
33309c6a
JM
346 ;; env
347 ;; if outer is set then release outer
348 (if (i32.ne ($MalVal_val $idx 1) 0)
349 ($RELEASE ($MEM_VAL1_ptr $mv)))
3ea09886
JM
350 ;; release the env data (hashmap)
351 ($RELEASE ($MEM_VAL0_ptr $mv))
352 (br $done))
353 ;; metadata
354 ;; release object and metdata object
33309c6a 355 ($RELEASE ($MEM_VAL0_ptr $mv))
3ea09886 356 ($RELEASE ($MEM_VAL1_ptr $mv))
33309c6a
JM
357 (br $done))
358 ;; default/unknown
359 )
360
361 ;; FREE, free the current element
362
363 ;; set type(FREE/15) and size
364 ;;; mv->refcnt_type = size*32 + FREE_T
0a19c2f1
JM
365 (i32.store $mv (i32.add (i32.mul $size 32) (global.get $FREE_T)))
366 (i32.store ($MalVal_val_ptr $idx 0) (global.get $mem_free_list))
367 (global.set $mem_free_list $idx)
33309c6a
JM
368 (if (i32.ge_u $size 3) (i32.store ($MalVal_val_ptr $idx 1) 0))
369 (if (i32.eq $size 4) (i32.store ($MalVal_val_ptr $idx 2) 0))
370 )
371
50eea9ad
JM
372 ;; find string in string memory or 0 if not found
373 (func $FIND_STRING (param $str i32) (result i32)
0a19c2f1 374 (LET $ms (global.get $string_mem))
50eea9ad
JM
375 (block $done
376 (loop $loop
0a19c2f1 377 (br_if $done (i32.ge_s $ms (global.get $string_mem_next)))
50eea9ad
JM
378 (if (i32.eqz ($strcmp $str (i32.add $ms 4)))
379 (return $ms))
33309c6a 380
0a19c2f1 381 (local.set $ms (i32.add $ms (i32.load16_u (i32.add $ms 2))))
50eea9ad
JM
382 (br $loop)
383 )
384 )
385 0
386 )
33309c6a 387
50eea9ad
JM
388 ;; str is a NULL terminated string
389 ;; size is number of characters in the string not including the
390 ;; trailing NULL
391 (func $ALLOC_STRING (param $str i32 $size i32 $intern i32) (result i32)
349faa83 392 (LET $ms 0)
33309c6a 393
50eea9ad
JM
394 ;; search for matching string in string_mem
395 (if $intern
396 (then
0a19c2f1 397 (local.set $ms ($FIND_STRING $str))
50eea9ad
JM
398 (if $ms
399 (then
400 ;;; ms->refcnt += 1
0c62f14e 401 (i32.store16 $ms (i32.add (i32.load16_u $ms) 1))
50eea9ad
JM
402 (return $ms)))))
403
404 ;; no existing matching string so create a new one
0a19c2f1 405 (local.set $ms (global.get $string_mem_next))
0c62f14e 406 (i32.store16 $ms 1)
50eea9ad 407 ;;; ms->size = sizeof(MalString)+size+1
0c62f14e 408 (i32.store16 offset=2 $ms (i32.add (i32.add 4 $size) 1))
50eea9ad
JM
409 ($memmove (i32.add $ms 4) $str (i32.add $size 1))
410 ;;; string_mem_next = (void *)ms + ms->size
0a19c2f1 411 (global.set $string_mem_next
50eea9ad
JM
412 ;;(i32.add $ms (i32.load16_u (i32.add $ms 2))))
413 (i32.add $ms (i32.load16_u offset=2 $ms)))
414
415;;($printf_2 "ALLOC_STRING 6 ms 0x%x, refs: %d\n" $ms (i32.load16_u $ms))
416 $ms
33309c6a
JM
417 )
418
50eea9ad 419 (func $RELEASE_STRING (param $ms i32)
349faa83 420 (LET $size 0 $next 0 $ms_idx 0 $idx 0 $type 0 $mv 0)
33309c6a 421
50eea9ad
JM
422 (if (i32.le_s (i32.load16_u $ms) 0)
423 (then
424 ($printf_2 "Release of already free string: %d (0x%x)\n"
0a19c2f1 425 (i32.sub $ms (global.get $string_mem)) $ms)
50eea9ad 426 ($fatal 1 "")))
33309c6a 427
50eea9ad 428 ;;; size = ms->size
0a19c2f1 429 (local.set $size (i32.load16_u (i32.add $ms 2)))
50eea9ad 430 ;;; *next = (void *)ms + size
0a19c2f1 431 (local.set $next (i32.add $ms $size))
50eea9ad
JM
432
433 ;;; ms->refcnt -= 1
0c62f14e 434 (i32.store16 $ms (i32.sub (i32.load16_u $ms) 1))
33309c6a 435
50eea9ad
JM
436 (if (i32.eqz (i32.load16_u $ms))
437 (then
0a19c2f1 438 (if (i32.gt_s (global.get $string_mem_next) $next)
50eea9ad
JM
439 (then
440 ;; If no more references to this string then free it up by
441 ;; shifting up every string afterwards to fill the gap
442 ;; (splice).
0a19c2f1 443 ($memmove $ms $next (i32.sub (global.get $string_mem_next)
50eea9ad
JM
444 $next))
445
446 ;; Scan the mem values for string types after the freed
447 ;; string and shift their indexes by size
0a19c2f1
JM
448 (local.set $ms_idx (i32.sub $ms (global.get $string_mem)))
449 (local.set $idx ($IDX (global.get $EMPTY_HASHMAP)))
50eea9ad 450 (loop $loop
0a19c2f1
JM
451 (local.set $mv ($MalVal_ptr $idx))
452 (local.set $type ($TYPE $mv))
50eea9ad 453 (if (AND (i32.gt_s ($VAL0 $mv) $ms_idx)
0a19c2f1
JM
454 (OR (i32.eq $type (global.get $STRING_T))
455 (i32.eq $type (global.get $SYMBOL_T))))
0c62f14e 456 (i32.store ($VAL0_ptr $mv) (i32.sub ($VAL0 $mv) $size)))
0a19c2f1 457 (local.set $idx (i32.add $idx ($MalVal_size $mv)))
50eea9ad 458
0a19c2f1 459 (br_if $loop (i32.lt_s $idx (global.get $mem_unused_start)))
50eea9ad
JM
460 )))
461
0a19c2f1
JM
462 (global.set $string_mem_next
463 (i32.sub (global.get $string_mem_next) $size))))
33309c6a
JM
464 )
465)