wasm: add package.json to pull in wamp.
[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)
32 (i32.add (get_global $mem)
0c62f14e 33 (i32.mul (i32.load (i32.add $mv 4)) 4)))
33309c6a
JM
34 (func $MEM_VAL1_ptr (param $mv i32) (result i32)
35 (i32.add (get_global $mem)
0c62f14e 36 (i32.mul (i32.load (i32.add $mv 8)) 4)))
33309c6a
JM
37 (func $MEM_VAL2_ptr (param $mv i32) (result i32)
38 (i32.add (get_global $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
0c62f14e 45 (i32.div_u (i32.sub $mv (get_global $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
0c62f14e 53 (i32.add (get_global $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)
77bf4e61 103 (if (result i32) (i32.eq $type (get_global $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
JM
121 ;; error_str string buffer
122 (set_global $error_str (STATIC_ARRAY 100))
123 ;; reader token string buffer
124 (set_global $token_buf (STATIC_ARRAY 256))
125 ;; printer string buffer
126 (set_global $printer_buf (STATIC_ARRAY 4096))
33309c6a
JM
127
128 (set_local $heap_size (i32.add (get_global $MEM_SIZE)
129 (get_global $STRING_MEM_SIZE)))
130 (set_global $heap_start (i32.add (get_global $memoryBase)
131 (get_global $S_STRING_END)))
132 (set_global $heap_end (i32.add (get_global $heap_start)
133 $heap_size))
134
135 (set_global $mem (get_global $heap_start))
136 (set_global $mem_unused_start 0)
137 (set_global $mem_free_list 0)
138
50eea9ad
JM
139 (set_global $string_mem (i32.add (get_global $heap_start)
140 (get_global $MEM_SIZE)))
141 (set_global $string_mem_next (get_global $string_mem))
142
143 (set_global $mem_user_start (get_global $mem_unused_start))
144 (set_global $string_mem_user_start (get_global $string_mem_next))
33309c6a
JM
145
146 ;; Empty values
147 (set_global $NIL
148 ($ALLOC_SCALAR (get_global $NIL_T) 0))
149 (set_global $FALSE
150 ($ALLOC_SCALAR (get_global $BOOLEAN_T) 0))
151 (set_global $TRUE
152 ($ALLOC_SCALAR (get_global $BOOLEAN_T) 1))
153 (set_global $EMPTY_LIST
154 ($ALLOC (get_global $LIST_T)
155 (get_global $NIL) (get_global $NIL) (get_global $NIL)))
156 (set_global $EMPTY_VECTOR
157 ($ALLOC (get_global $VECTOR_T)
158 (get_global $NIL) (get_global $NIL) (get_global $NIL)))
159 (set_global $EMPTY_HASHMAP
160 ($ALLOC (get_global $HASHMAP_T)
161 (get_global $NIL) (get_global $NIL) (get_global $NIL)))
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)
349faa83
JM
172 (LET $prev (get_global $mem_free_list)
173 $res (get_global $mem_free_list)
174 $size ($MalType_size $type))
33309c6a
JM
175
176 (block $loop_done
177 (loop $loop
178 ;; res == mem_unused_start
179 (if (i32.eq $res (get_global $mem_unused_start))
180 (then
181 ;; ALLOC_UNUSED
182 ;;; if (res + size > MEM_SIZE)
183 (if (i32.gt_u (i32.add $res $size) (get_global $MEM_SIZE))
50eea9ad
JM
184 ;; Out of memory, exit
185 ($fatal 7 "Out of mal memory!\n"))
33309c6a
JM
186 ;;; if (mem_unused_start += size)
187 (set_global $mem_unused_start
188 (i32.add (get_global $mem_unused_start) $size))
189 ;;; if (prev == res)
190 (if (i32.eq $prev $res)
191 (then
192 (set_global $mem_free_list (get_global $mem_unused_start)))
193 (else
194 ;;; mem[prev].val[0] = mem_unused_start
195 (i32.store
196 ($MalVal_val_ptr $prev 0)
197 (get_global $mem_unused_start))))
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)
205 (if (i32.eq $res (get_global $mem_free_list))
206 ;; set free pointer (mem_free_list) to next free
207 ;;; mem_free_list = mem[res].val[0];
208 (set_global $mem_free_list ($MalVal_val $res 0)))
209 ;; if (res != mem_free_list)
210 (if (i32.ne $res (get_global $mem_free_list))
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
216 (set_local $prev $res)
217 ;;; res = mem[res].val[0]
218 (set_local $res ($MalVal_val $res 0))
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
3ea09886 274 (set_local $idx ($IDX $mv))
33309c6a
JM
275 ;;; type = mv->refcnt_type & 31
276 (set_local $type (i32.and (i32.load $mv) 0x1f)) ;; 0x1f == 31
277 ;;; size = MalType_size(type)
278 (set_local $size ($MalType_size $type))
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
JM
285
286 (if (i32.eq (get_global $FREE_T) $type)
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
300 (if (i32.le_u $mv (get_global $EMPTY_HASHMAP))
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
50eea9ad 319 ($RELEASE_STRING (i32.add (get_global $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
0c62f14e 365 (i32.store $mv (i32.add (i32.mul $size 32) (get_global $FREE_T)))
33309c6a
JM
366 (i32.store ($MalVal_val_ptr $idx 0) (get_global $mem_free_list))
367 (set_global $mem_free_list $idx)
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)
349faa83 374 (LET $ms (get_global $string_mem))
50eea9ad
JM
375 (block $done
376 (loop $loop
377 (br_if $done (i32.ge_s $ms (get_global $string_mem_next)))
378 (if (i32.eqz ($strcmp $str (i32.add $ms 4)))
379 (return $ms))
33309c6a 380
50eea9ad
JM
381 (set_local $ms (i32.add $ms (i32.load16_u (i32.add $ms 2))))
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
397 (set_local $ms ($FIND_STRING $str))
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
405 (set_local $ms (get_global $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
411 (set_global $string_mem_next
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"
0c62f14e 425 (i32.sub $ms (get_global $string_mem)) $ms)
50eea9ad 426 ($fatal 1 "")))
33309c6a 427
50eea9ad
JM
428 ;;; size = ms->size
429 (set_local $size (i32.load16_u (i32.add $ms 2)))
430 ;;; *next = (void *)ms + size
431 (set_local $next (i32.add $ms $size))
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
438 (if (i32.gt_s (get_global $string_mem_next) $next)
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).
0c62f14e 443 ($memmove $ms $next (i32.sub (get_global $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
0c62f14e 448 (set_local $ms_idx (i32.sub $ms (get_global $string_mem)))
50eea9ad
JM
449 (set_local $idx ($IDX (get_global $EMPTY_HASHMAP)))
450 (loop $loop
451 (set_local $mv ($MalVal_ptr $idx))
452 (set_local $type ($TYPE $mv))
453 (if (AND (i32.gt_s ($VAL0 $mv) $ms_idx)
454 (OR (i32.eq $type (get_global $STRING_T))
455 (i32.eq $type (get_global $SYMBOL_T))))
0c62f14e 456 (i32.store ($VAL0_ptr $mv) (i32.sub ($VAL0 $mv) $size)))
50eea9ad
JM
457 (set_local $idx (i32.add $idx ($MalVal_size $mv)))
458
459 (br_if $loop (i32.lt_s $idx (get_global $mem_unused_start)))
460 )))
461
462 (set_global $string_mem_next
0c62f14e 463 (i32.sub (get_global $string_mem_next) $size))))
33309c6a
JM
464 )
465)