wasm: String refactor. Release macro memory
authorJoel Martin <github@martintribe.org>
Sun, 11 Nov 2018 21:30:42 +0000 (15:30 -0600)
committerJoel Martin <github@martintribe.org>
Sat, 8 Dec 2018 23:02:12 +0000 (17:02 -0600)
Also split platform out.

21 files changed:
wasm/Makefile
wasm/core.wam
wasm/debug.wam
wasm/mem.wam
wasm/platform_libc.wam [new file with mode: 0644]
wasm/platform_os.wam [new file with mode: 0644]
wasm/printer.wam
wasm/printf.wam [moved from wasm/util.wam with 57% similarity]
wasm/step0_repl.wam
wasm/step1_read_print.wam
wasm/step2_eval.wam
wasm/step3_env.wam
wasm/step4_if_fn_do.wam
wasm/step5_tco.wam
wasm/step6_file.wam
wasm/step7_quote.wam
wasm/step8_macros.wam
wasm/step9_try.wam
wasm/stepA_mal.wam
wasm/string.wam
wasm/types.wam

index e44953e..c4bb854 100644 (file)
@@ -1,4 +1,6 @@
-STEP0_DEPS = string.wam util.wam
+MODE ?= libc
+
+STEP0_DEPS = platform_$(MODE).wam string.wam printf.wam
 STEP1_DEPS = $(STEP0_DEPS) types.wam mem.wam debug.wam reader.wam printer.wam
 STEP3_DEPS = $(STEP1_DEPS) env.wam
 STEP4_DEPS = $(STEP3_DEPS) core.wam
index ed73dc8..1709018 100644 (file)
@@ -86,8 +86,8 @@
     (if i32 (i32.eq (i32.load8_u $str) (CHR "\x7f"))
       (then ($INC_REF ($MEM_VAL1_ptr $args)))
       (else
-        (drop ($sprintf_1 (get_global $util_buf) "\x7f%s" $str))
-        ($STRING (get_global $STRING_T) (get_global $util_buf))))
+        (drop ($sprintf_1 (get_global $printf_buf) "\x7f%s" $str))
+        ($STRING (get_global $STRING_T) (get_global $printf_buf))))
   )
 
   (func $keyword_Q (param $args i32) (result i32)
 
   (func $core_readline (param $args i32) (result i32)
     (local $line i32 $mv i32)
-    (set_local $line ($readline ($to_String ($MEM_VAL1_ptr $args))))
-    (if (i32.eqz $line) (return ($INC_REF (get_global $NIL))))
+    (set_local $line (STATIC_ARRAY 201))
+    (if (i32.eqz ($readline ($to_String ($MEM_VAL1_ptr $args)) $line))
+      (return ($INC_REF (get_global $NIL))))
     (set_local $mv ($STRING (get_global $STRING_T) $line))
-    ($free $line)
     $mv
   )
 
     ($read_str ($to_String ($MEM_VAL1_ptr $args))))
 
   (func $slurp (param $args i32) (result i32)
-    (local $content i32 $mv i32)
-    (set_local $content ($read_file ($to_String ($MEM_VAL1_ptr $args))))
-    (if (i32.le_s $content 0)
+    (local $mv i32 $size i32)
+    (set_local $mv ($STRING_INIT (get_global $STRING_T)))
+    (set_local $size ($read_file ($to_String ($MEM_VAL1_ptr $args))
+                                 ($to_String $mv)))
+    (if (i32.eqz $size)
       (then
         ($THROW_STR_1 "failed to read file '%s'" ($to_String ($MEM_VAL1_ptr $args)))
         (return ($INC_REF (get_global $NIL)))))
-    (set_local $mv ($STRING (get_global $STRING_T) $content))
-    ($free $content)
+    (set_local $mv ($STRING_FINALIZE $mv $size))
     $mv
   )
 
index 8ec023d..b998b96 100644 (file)
@@ -2,6 +2,7 @@
 
   (func $checkpoint_user_memory
     (set_global $mem_user_start (get_global $mem_unused_start))
+    (set_global $string_mem_user_start (get_global $string_mem_next))
   )
 
   (func $CHECK_FREE_LIST (result i32)
     (i32.add $size $idx)
   )
 
+  (func $PR_STRINGS (param $start i32)
+    (local $ms i32 $idx i32)
+    ($printf_2 "String - showing %d -> %d:\n"
+               $start (i32.sub_s (get_global $string_mem_next)
+                                 (get_global $string_mem)))
+    (if (i32.le_s (i32.sub_s (get_global $string_mem_next)
+                             (get_global $string_mem))
+                  $start)
+      (then ($print " ---\n"))
+      (else
+        (set_local $ms (get_global $string_mem))
+        (block $done
+          (loop $loop
+            (if (i32.ge_u $ms (get_global $string_mem_next))
+              (br $done))
+            (set_local $idx (i32.sub_u $ms (get_global $string_mem)))
+            (if (i32.ge_s $idx $start)
+              ($printf_4 "%4d: refs %2d, size %2d >> '%s'\n"
+                         $idx
+                         (i32.load16_u $ms)
+                         (i32.load16_u (i32.add $ms 2))
+                         (i32.add $ms 4)))
+
+            (set_local $ms (i32.add $ms (i32.load16_u (i32.add $ms 2))))
+            (br $loop)
+          )
+        )))
+  )
+
   (func $PR_MEMORY (param $start i32 $end i32)
-    (local $idx i32)
+    (local $string_start i32 $idx i32)
     (if (i32.lt_s $start 0)
-      (set_local $start (get_global $mem_user_start)))
+      (then
+        (set_local $start (get_global $mem_user_start))
+        (set_local $string_start (i32.sub_s (get_global $string_mem_user_start)
+                                            (get_global $string_mem)))))
     (if (i32.lt_s $end 0)
       (set_local $end (get_global $mem_unused_start)))
     ;;; printf("Values - (mem) showing %d -> %d", start, end)
             (br $loopvals)
           )
         )))
+    ($PR_STRINGS $string_start)
     ($PR_MEMORY_SUMMARY_SMALL)
   )
 
index e67824b..8a416ee 100644 (file)
@@ -10,9 +10,9 @@
   (global $mem_free_list          (mut i32) 0)
   (global $mem_user_start         (mut i32) 0)
 
-;;  (global $string_mem             (mut i32) 0)
-;;  (global $string_mem_next        (mut i32) 0)
-;;  (global $string_mem_user_start  (mut i32) 0)
+  (global $string_mem             (mut i32) 0)
+  (global $string_mem_next        (mut i32) 0)
+  (global $string_mem_user_start  (mut i32) 0)
 
   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   ;; General type storage/pointer functions
 
 ;;    ($print ">>> init_memory\n")
 
-    ($init_sprintf_mem)
+    ($init_printf_mem)
 
     ;; error_str string buffer
     (set_global $error_str (STATIC_ARRAY 100))
     (set_global $mem_unused_start 0)
     (set_global $mem_free_list 0)
 
-;;    (set_global $string_mem (i32.add (get_global $heap_start)
-;;                                     (get_global $MEM_SIZE)))
-;;    (set_global $string_mem_next (get_global $string_mem))
+    (set_global $string_mem (i32.add (get_global $heap_start)
+                                     (get_global $MEM_SIZE)))
+    (set_global $string_mem_next (get_global $string_mem))
+
+    (set_global $mem_user_start (get_global $mem_unused_start))
+    (set_global $string_mem_user_start (get_global $string_mem_next))
 
     ;; Empty values
     (set_global $NIL
             ;; ALLOC_UNUSED
             ;;; if (res + size > MEM_SIZE)
             (if (i32.gt_u (i32.add $res $size) (get_global $MEM_SIZE))
-              (then
-                ;; Out of memory, exit
-                ($print "Out of mal memory!\n")
-                ($exit 1)))
+              ;; Out of memory, exit
+              ($fatal 7 "Out of mal memory!\n"))
             ;;; if (mem_unused_start += size)
             (set_global $mem_unused_start
                         (i32.add (get_global $mem_unused_start) $size))
     ;;; printf(">>> RELEASE idx: %d, type: %d, size: %d\n", idx, type, size)
 
     (if (i32.eq 0 $mv)
-      (then
-        ($print "RELEASE of NULL!\n")
-        ($exit 1)))
+      ($fatal 7 "RELEASE of NULL!\n"))
 
     (if (i32.eq (get_global $FREE_T) $type)
       (then
         ($printf_2 "RELEASE of already free mv: 0x%x, idx: 0x%x\n" $mv $idx)
-        ($exit 1)))
+        ($fatal 1 "")))
     (if (i32.lt_u ($MalVal_refcnt_type $idx) 15)
       (then
         ($printf_2 "RELEASE of unowned mv: 0x%x, idx: 0x%x\n" $mv $idx)
-        ($exit 1)))
+        ($fatal 1 "")))
 
     ;; decrease reference count by one
     (i32.store ($MalVal_ptr $idx)
         (if (i32.lt_u ($MalVal_refcnt_type $idx) 32)
           (then
             ($printf_2 "RELEASE of unowned mv: 0x%x, idx: 0x%x\n" $mv $idx)
-            ($exit 1)))
+            ($fatal 1 "")))
         (return)))
 
     ;; our reference count is not 0, so don't release
       (br $done))
       ;; string, kw, symbol
       ;; release string, then FREE reference
-      ($RELEASE_STRING $mv)
+      ($RELEASE_STRING (i32.add (get_global $string_mem) ($VAL0 $mv)))
       (br $done))
       ;; list, vector
       (if (i32.ne ($MalVal_val $idx 0) 0)
     (if (i32.eq $size 4) (i32.store ($MalVal_val_ptr $idx 2) 0))
   )
 
-  ;; Allocate a string as follows:
-  ;; refcnt (i32 set to 1), string data, NULL byte
-  (func $STRING_DUPE (param $str i32) (result i32)
-    (local $len i32 $cur i32 $new i32 $idx i32)
-
-    ;; Calculate length of string needed
-    (set_local $len ($strlen $str))
+  ;; find string in string memory or 0 if not found
+  (func $FIND_STRING (param $str i32) (result i32)
+    (local $ms i32)
+    (set_local $ms (get_global $string_mem))
+    (block $done
+      (loop $loop
+        (br_if $done (i32.ge_s $ms (get_global $string_mem_next)))
+        (if (i32.eqz ($strcmp $str (i32.add $ms 4)))
+          (return $ms))
 
-    ;; leading i32 refcnt + trailing NULL
-    (set_local $new ($malloc (i32.add 5 $len)))
+        (set_local $ms (i32.add $ms (i32.load16_u (i32.add $ms 2))))
+        (br $loop)
+      )
+    )
+    0
+  )
 
-    ;; set initial refcnt to 1
-    (i32.store $new 1)
-    ;; skip refcnt
-    (set_local $cur (i32.add $new 4))
-    ;; Set NULL terminator
-    (i32.store8_u (i32.add $cur $len) 0)
+  ;; str is a NULL terminated string
+  ;; size is number of characters in the string not including the
+  ;; trailing NULL
+  (func $ALLOC_STRING (param $str i32 $size i32 $intern i32) (result i32)
+    (local $ms i32)
 
-    ;; Copy the characters
-    ($memmove $cur $str $len)
-    $new
+    ;; search for matching string in string_mem
+    (if $intern
+      (then
+        (set_local $ms ($FIND_STRING $str))
+        (if $ms
+          (then
+            ;;; ms->refcnt += 1
+            (i32.store16_u $ms (i32.add (i32.load16_u $ms) 1))
+            (return $ms)))))
+
+    ;; no existing matching string so create a new one
+    (set_local $ms (get_global $string_mem_next))
+    (i32.store16_u $ms 1)
+    ;;; ms->size = sizeof(MalString)+size+1
+    (i32.store16_u offset=2 $ms (i32.add (i32.add 4 $size) 1))
+    ($memmove (i32.add $ms 4) $str (i32.add $size 1))
+    ;;; string_mem_next = (void *)ms + ms->size
+    (set_global $string_mem_next
+                ;;(i32.add $ms (i32.load16_u (i32.add $ms 2))))
+                (i32.add $ms (i32.load16_u offset=2 $ms)))
+
+;;($printf_2 "ALLOC_STRING 6 ms 0x%x, refs: %d\n" $ms (i32.load16_u $ms))
+    $ms
   )
 
-  ;; Duplicate regular character array string into a Mal string and
-  ;; return the MalVal pointer
-  (func $STRING (param $type i32 $str i32) (result i32)
-    ($ALLOC_SCALAR $type ($STRING_DUPE $str))
-  )
+  (func $RELEASE_STRING (param $ms i32)
+    (local $size i32 $next i32 $ms_idx i32 $idx i32 $type i32 $mv i32)
 
-  (func $RELEASE_STRING (param $mv i32)
-    (local $str i32)
-    (set_local $str ($MalVal_val ($IDX $mv) 0))
+    (if (i32.le_s (i32.load16_u $ms) 0)
+      (then
+        ($printf_2 "Release of already free string: %d (0x%x)\n"
+                   (i32.sub_s $ms (get_global $string_mem)) $ms)
+        ($fatal 1 "")))
 
-    ;; DEBUG
-;;    ($printf_1 "RELEASE_STRING - calling free on: %d" $str)
+    ;;; size = ms->size
+    (set_local $size (i32.load16_u (i32.add $ms 2)))
+    ;;; *next = (void *)ms + size
+    (set_local $next (i32.add $ms $size))
+
+    ;;; ms->refcnt -= 1
+    (i32.store16_u $ms (i32.sub_u (i32.load16_u $ms) 1))
 
-    ($free $str)
+    (if (i32.eqz (i32.load16_u $ms))
+      (then
+        (if (i32.gt_s (get_global $string_mem_next) $next)
+          (then
+            ;; If no more references to this string then free it up by
+            ;; shifting up every string afterwards to fill the gap
+            ;; (splice).
+            ($memmove $ms $next (i32.sub_s (get_global $string_mem_next)
+                                           $next))
+
+            ;; Scan the mem values for string types after the freed
+            ;; string and shift their indexes by size
+            (set_local $ms_idx (i32.sub_s $ms (get_global $string_mem)))
+            (set_local $idx ($IDX (get_global $EMPTY_HASHMAP)))
+            (loop $loop
+              (set_local $mv ($MalVal_ptr $idx))
+              (set_local $type ($TYPE $mv))
+              (if (AND (i32.gt_s ($VAL0 $mv) $ms_idx)
+                       (OR (i32.eq $type (get_global $STRING_T))
+                           (i32.eq $type (get_global $SYMBOL_T))))
+                (i32.store ($VAL0_ptr $mv) (i32.sub_s ($VAL0 $mv) $size)))
+              (set_local $idx (i32.add $idx ($MalVal_size $mv)))
+
+              (br_if $loop (i32.lt_s $idx (get_global $mem_unused_start)))
+            )))
+
+        (set_global $string_mem_next
+                    (i32.sub_s (get_global $string_mem_next) $size))))
   )
 )
diff --git a/wasm/platform_libc.wam b/wasm/platform_libc.wam
new file mode 100644 (file)
index 0000000..c561d64
--- /dev/null
@@ -0,0 +1,98 @@
+(module $platform_libc
+
+  (import "env" "exit" (func $lib_exit (param i32)))
+
+  (import "env" "stdout" (global $lib_stdout i32))
+  (import "env" "fputs" (func $lib_fputs (param i32 i32) (result i32)))
+
+  (import "env" "free" (func $lib_free (param i32)))
+  (import "env" "readline" (func $lib_readline (param i32) (result i32)))
+  (import "env" "add_history" (func $lib_add_history (param i32)))
+
+  ;; read_file defintions / FFI information
+  (global $STAT_SIZE i32 88)
+  (global $STAT_ST_SIZE_OFFSET i32 44)
+  (global $STAT_VER_LINUX i32 3)
+  (global $O_RDONLY i32 0)
+  (import "env" "open" (func $lib_open (param i32 i32 i32) (result i32)))
+  (import "env" "read" (func $lib_read (param i32 i32 i32) (result i32)))
+  (import "env" "__fxstat" (func $lib___fxstat (param i32 i32 i32) (result i32)))
+  (global $TIMEVAL_SIZE i32 8)
+  (global $TV_SEC_OFFSET i32 0)
+  (global $TV_USEC_OFFSET i32 4)
+  (import "env" "gettimeofday" (func $lib_gettimeofday (param i32 i32) (result i32)))
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+  (func $fatal (param $code i32 $msg i32)
+    ($print $msg)
+    ($lib_exit $code)
+  )
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+  (func $print (param $addr i32)
+    (drop ($lib_fputs $addr (get_global $lib_stdout))))
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+  (func $readline (param $prompt i32 $buf i32) (result i32)
+    (local $line i32 $len i32)
+    (set_local $len 0)
+
+    (set_local $line ($lib_readline $prompt))
+    (if $line
+      (then
+        ($lib_add_history $line)
+        (set_local $len ($strlen $line))
+        ($memmove $buf $line $len)
+        ($lib_free $line)))
+    (i32.store8_u (i32.add $buf $len) (CHR "\x00"))
+    (return (if i32 $line 1 0))
+  )
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+  ;; Returns malloc'd string. Must be free by caller
+  (func $read_file (param $path i32 $buf i32) (result i32)
+    (local $fst i32 $fd i32 $st_size i32 $sz i32)
+    (set_local $fst (STATIC_ARRAY 100)) ;; at least STAT_SIZE
+
+    (set_local $fd ($lib_open $path (get_global $O_RDONLY) 0))
+    (if (i32.lt_s $fd 0)
+      (then
+        ($printf_1 "ERROR: slurp failed to open '%s'\n" $path)
+        (return 0)))
+    (if (i32.lt_s ($lib___fxstat (get_global $STAT_VER_LINUX) $fd $fst) 0)
+      (then
+        ($printf_1 "ERROR: slurp failed to stat '%s'\n" $path)
+        (return 0)))
+    (set_local $st_size (i32.load
+                          (i32.add $fst (get_global $STAT_ST_SIZE_OFFSET))))
+    (set_local $sz ($lib_read $fd $buf $st_size))
+    (if (i32.ne $sz $st_size)
+      (then
+        ($printf_1 "ERROR: slurp failed to stat '%s'\n" $path)
+        (return 0)))
+    ;; Add null to string
+    (i32.store8_u (i32.add $buf $st_size) 0)
+    (i32.add 1 $st_size)
+  )
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+  (func $get_time_ms (result i32)
+    (local $tv i32 $secs i32 $usecs i32 $msecs i32)
+    (set_local $tv (STATIC_ARRAY 10)) ;; at least TIMEVAL_SIZE
+    (drop ($lib_gettimeofday $tv 0))
+    (set_local $secs (i32.load (i32.add $tv (get_global $TV_SEC_OFFSET))))
+    ;; subtract 30 years to make sure secs is positive and can be
+    ;; multiplied by 1000
+    (set_local $secs (i32.sub_s $secs 0x38640900))
+    (set_local $usecs (i32.load (i32.add $tv (get_global $TV_USEC_OFFSET))))
+    (set_local $msecs (i32.add (i32.mul_u $secs 1000)
+                               (i32.div_u $usecs 1000)))
+    $msecs
+  )
+)
diff --git a/wasm/platform_os.wam b/wasm/platform_os.wam
new file mode 100644 (file)
index 0000000..f7e0ec0
--- /dev/null
@@ -0,0 +1,54 @@
+(module $platform_os
+
+  (import "env" "exit" (func $lib_exit (param i32)))
+
+  (import "env" "stdout" (global $lib_stdout i32))
+  (import "env" "fputs" (func $lib_fputs (param i32 i32) (result i32)))
+
+  (import "env" "readline" (func $lib_readline (param i32 i32 i32) (result i32)))
+  (import "env" "add_history" (func $lib_add_history (param i32)))
+
+  (import "env" "read_file" (func $lib_read_file (param i32 i32) (result i32)))
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+  (func $fatal (param $code i32 $msg i32)
+    ($print $msg)
+    ($lib_exit $code)
+  )
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+  (func $print (param $addr i32)
+    (drop ($lib_fputs $addr (get_global $lib_stdout))))
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+  (func $readline (param $prompt i32 $buf i32) (result i32)
+    (local $res i32)
+
+    ;; TODO: don't hardcode count to 200
+    (set_local $res ($lib_readline $prompt $buf 200))
+    (if $res
+      ($lib_add_history $buf))
+    $res
+  )
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+  (func $read_file (param $path i32 $buf i32) (result i32)
+    (local $size i32)
+    (set_local $size ($lib_read_file $path $buf))
+    ;; Add null to string
+    (i32.store8_u (i32.add $buf $size) 0)
+    (i32.add $size 1)
+  )
+
+  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+  (func $get_time_ms (result i32)
+    0
+  )
+
+)
index 95afdc4..34cba90 100644 (file)
 
   (func $pr_str_internal (param $seq i32) (param $mv i32)
         (param $print_readably i32) (param $sep i32) (result i32)
-    (local $res i32)
-    (set_local $res (get_global $printer_buf))
-    (i32.store8_u $res 0)
+    (local $res i32 $res_str i32)
+    (set_local $res ($STRING_INIT (get_global $STRING_T)))
+    (set_local $res_str ($to_String $res))
 
     (if $seq
       (then
         (block $done
           (loop $loop
-            (if (i32.eqz ($VAL0 $mv)) (br $done))
-            (set_local $res ($pr_str_val $res ($MEM_VAL1_ptr $mv) $print_readably))
+            (br_if $done (i32.eqz ($VAL0 $mv)))
+            (set_local $res_str ($pr_str_val $res_str ($MEM_VAL1_ptr $mv) $print_readably))
             (set_local $mv ($MEM_VAL0_ptr $mv))
             (if (i32.ne ($VAL0 $mv) 0)
-              (set_local $res ($sprintf_1 $res "%s" $sep)))
+              (set_local $res_str ($sprintf_1 $res_str "%s" $sep)))
             (br $loop)
           )
         ))
       (else
-        (set_local $res ($pr_str_val $res $mv $print_readably))))
+        (set_local $res_str ($pr_str_val $res_str $mv $print_readably))))
 
-    ($STRING (get_global $STRING_T) (get_global $printer_buf))
+    (set_local $res ($STRING_FINALIZE $res (i32.sub_s $res_str ($to_String $res))))
+
+    $res
   )
 
   (func $pr_str (param $mv i32 $print_readably i32) (result i32)
similarity index 57%
rename from wasm/util.wam
rename to wasm/printf.wam
index f023810..68ca24c 100644 (file)
@@ -1,74 +1,49 @@
-(module $util
-  (import "env" "malloc" (func $malloc (param i32) (result i32)))
-  (import "env" "free" (func $free (param i32)))
-  (import "env" "exit" (func $exit (param i32)))
+(module $printf
 
-  (import "env" "stdout" (global $stdout i32))
-  (import "env" "fputs" (func $fputs (param i32 i32) (result i32)))
-  ;;(import "env" "readline" (func $readline (param i32) (result i32)))
-  (import "libedit.so" "readline" (func $readline (param i32) (result i32)))
-  ;;(import "libreadline.so" "readline" (func $readline (param i32) (result i32)))
+  (global $printf_buf (mut i32) 0)
 
-  (global $util_buf (mut i32) 0)
-
-  ;; read_file defintions / FFI information
-  (global $STAT_SIZE i32 88)
-  (global $STAT_ST_SIZE_OFFSET i32 44)
-  (global $STAT_VER_LINUX i32 3)
-  (global $O_RDONLY i32 0)
-  (import "env" "open" (func $open (param i32 i32 i32) (result i32)))
-  (import "env" "read" (func $read (param i32 i32 i32) (result i32)))
-  (import "env" "__fxstat" (func $__fxstat (param i32 i32 i32) (result i32)))
-  (global $TIMEVAL_SIZE i32 8)
-  (global $TV_SEC_OFFSET i32 0)
-  (global $TV_USEC_OFFSET i32 4)
-  (import "env" "gettimeofday" (func $gettimeofday (param i32 i32) (result i32)))
-
-  (func $init_sprintf_mem
+  (func $init_printf_mem
     ;; sprintf static buffer
-    (set_global $util_buf (STATIC_ARRAY 256))
-    )
+    (set_global $printf_buf (STATIC_ARRAY 256))
+  )
 
   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-  (func $print (param $addr i32)
-    (drop ($fputs $addr (get_global $stdout))))
-
   (func $printf_1 (param $fmt i32) (param $v0 i32)
-    (drop ($sprintf_6 (get_global $util_buf) $fmt $v0 0 0 0 0 0))
-    ($print (get_global $util_buf))
+    (drop ($sprintf_6 (get_global $printf_buf) $fmt $v0 0 0 0 0 0))
+    ($print (get_global $printf_buf))
   )
 
   (func $printf_2 (param $fmt i32 $v0 i32 $v1 i32)
-    (drop ($sprintf_6 (get_global $util_buf) $fmt $v0 $v1 0 0 0 0))
-    ($print (get_global $util_buf))
+    (drop ($sprintf_6 (get_global $printf_buf) $fmt $v0 $v1 0 0 0 0))
+    ($print (get_global $printf_buf))
   )
 
   (func $printf_3 (param $fmt i32)
         (param $v0 i32) (param $v1 i32) (param $v2 i32)
-    (drop ($sprintf_6 (get_global $util_buf) $fmt $v0 $v1 $v2 0 0 0))
-    ($print (get_global $util_buf))
+    (drop ($sprintf_6 (get_global $printf_buf) $fmt $v0 $v1 $v2 0 0 0))
+    ($print (get_global $printf_buf))
   )
 
   (func $printf_4 (param $fmt i32)
         (param $v0 i32) (param $v1 i32) (param $v2 i32)
         (param $v3 i32)
-    (drop ($sprintf_6 (get_global $util_buf) $fmt $v0 $v1 $v2 $v3 0 0))
-    ($print (get_global $util_buf))
+    (drop ($sprintf_6 (get_global $printf_buf) $fmt $v0 $v1 $v2 $v3 0 0))
+    ($print (get_global $printf_buf))
   )
 
   (func $printf_5 (param $fmt i32)
         (param $v0 i32) (param $v1 i32) (param $v2 i32)
         (param $v3 i32) (param $v4 i32)
-    (drop ($sprintf_6 (get_global $util_buf) $fmt $v0 $v1 $v2 $v3 $v4 0))
-    ($print (get_global $util_buf))
+    (drop ($sprintf_6 (get_global $printf_buf) $fmt $v0 $v1 $v2 $v3 $v4 0))
+    ($print (get_global $printf_buf))
   )
 
   (func $printf_6 (param $fmt i32)
         (param $v0 i32) (param $v1 i32) (param $v2 i32)
         (param $v3 i32) (param $v4 i32) (param $v5 i32)
-    (drop ($sprintf_6 (get_global $util_buf) $fmt $v0 $v1 $v2 $v3 $v4 $v5))
-    ($print (get_global $util_buf))
+    (drop ($sprintf_6 (get_global $printf_buf) $fmt $v0 $v1 $v2 $v3 $v4 $v5))
+    ($print (get_global $printf_buf))
   )
 
   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     ;; Calculate smallest to most significant digit
     (loop $loop
       (set_local $digit (i32.rem_u $val $radix))
-      (i32.store8_u $pbuf (if i32 (i32.lt_s $digit 10)
+      (i32.store8_u $pbuf (if i32 (i32.lt_u $digit 10)
                             (i32.add (CHR "0") $digit)
-                            (i32.sub_s (i32.add (CHR "A") $digit) 10)))
+                            (i32.sub_u (i32.add (CHR "A") $digit) 10)))
       (set_local $pbuf (i32.add $pbuf 1))
-      (set_local $val (i32.div_s $val $radix))
-      (if (i32.gt_s $val 0) (br $loop))
+      (set_local $val (i32.div_u $val $radix))
+      (if (i32.gt_u $val 0) (br $loop))
     )
 
-    (set_local $i (i32.sub_s $pbuf $buf))
+    (set_local $i (i32.sub_u $pbuf $buf))
     (block $done
       (loop $loop
-        (if (i32.ge_s $i $pad_cnt) (br $done))
+        (if (i32.ge_u $i $pad_cnt) (br $done))
         (i32.store8_u $pbuf $pad_char)
         (set_local $pbuf (i32.add $pbuf 1))
         (set_local $i (i32.add $i 1))
     (i32.store8_u $pbuf (CHR "\x00"))
 
     ;; now reverse it
-    (set_local $len (i32.sub_s $pbuf $buf))
+    (set_local $len (i32.sub_u $pbuf $buf))
     (set_local $i 0)
     (block $done
       (loop $loop
-        (if (i32.ge_s $i (i32.div_s $len 2))
+        (if (i32.ge_u $i (i32.div_u $len 2))
           (br $done))
 
         (set_local $j (i32.load8_u (i32.add $buf $i)))
-        (set_local $k (i32.add $buf (i32.sub_s (i32.sub_s $len $i) 1)))
+        (set_local $k (i32.add $buf (i32.sub_u (i32.sub_u $len $i) 1)))
         (i32.store8_u (i32.add $buf $i) (i32.load8_u $k))
         (i32.store8_u $k $j)
         (set_local $i (i32.add $i 1))
             (br $loop)) ;; don't increase vidx
         (else
           ($printf_1 "Illegal format character: '%c'\n" $ch)
-          ($exit 3)))))))))))
+          ($fatal 3 "")))))))))))
 
         (set_local $vidx (i32.add 1 $vidx))
         (br $loop)
     $pstr
   )
 
-  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-  ;; Returns malloc'd string. Must be free by caller
-  (func $read_file (param $path i32) (result i32)
-    (local $fst i32 $fd i32 $str i32 $st_size i32 $sz i32)
-    (set_local $str 0)
-    (set_local $fst ($malloc (get_global $STAT_SIZE)))
-    (if (i32.le_s $fst 0)
-      (then
-        ($printf_1 "ERROR: malloc of %d bytes failed\n"
-                   (get_global $STAT_SIZE))
-        (return 0)))
-
-    (block $free_fst
-      (set_local $fd ($open $path (get_global $O_RDONLY) 0))
-      (if (i32.lt_s $fd 0)
-        (then
-          ($printf_1 "ERROR: slurp failed to open '%s'\n" $path)
-          (br $free_fst)))
-      (if (i32.lt_s ($__fxstat (get_global $STAT_VER_LINUX) $fd $fst) 0)
-        (then
-          ($printf_1 "ERROR: slurp failed to stat '%s'\n" $path)
-          (br $free_fst)))
-      (set_local $st_size (i32.load
-                            (i32.add $fst (get_global $STAT_ST_SIZE_OFFSET))))
-      (set_local $str ($malloc (i32.add 1 $st_size)))
-      (if (i32.le_s $str 0)
-        (then
-          ($printf_1 "ERROR: malloc of %d bytes failed\n" $st_size)
-          (br $free_fst)))
-      (set_local $sz ($read $fd $str $st_size))
-      (if (i32.ne $sz $st_size)
-        (then
-          ($free $str)
-          (set_local $str 0)
-          ($printf_1 "ERROR: slurp failed to stat '%s'\n" $path)
-          (br $free_fst)))
-      ;; Add null to string
-      (i32.store8_u (i32.add $str $st_size) 0)
-    )
-    ($free $fst)
-    $str
-  )
-
-  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
-  (func $get_time_ms (result i32)
-    (local $tv i32 $secs i32 $usecs i32 $msecs i32)
-    (set_local $tv ($malloc (get_global $TIMEVAL_SIZE)))
-    (drop ($gettimeofday $tv 0))
-    (set_local $secs (i32.load (i32.add $tv (get_global $TV_SEC_OFFSET))))
-    ;; subtract 30 years to make sure secs is positive and can be
-    ;; multiplied by 1000
-    (set_local $secs (i32.sub_s $secs 0x38640900))
-    (set_local $usecs (i32.load (i32.add $tv (get_global $TV_USEC_OFFSET))))
-    (set_local $msecs (i32.add (i32.mul_u $secs 1000)
-                               (i32.div_u $usecs 1000)))
-    ($free $tv)
-    $msecs
-  )
 )
index a1b4a07..167c773 100644 (file)
   (func $main (result i32)
     ;; Constant location/value definitions
     (local $line i32)
+    (set_local $line (STATIC_ARRAY 201))
+
+    ;; DEBUG
+    ($printf_1 "memoryBase: 0x%x\n" (get_global $memoryBase))
 
     ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
+        ;;($printf_1 "here1 %d\n", 7);
         ($printf_1 "%s\n" ($rep $line))
-        ($free $line)
-        (br $repl_loop)))
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     0
@@ -38,6 +44,6 @@
 
 
   (export "_main" (func $main))
-  (export "__post_instantiate" (func $init_sprintf_mem))
+  (export "__post_instantiate" (func $init_printf_mem))
 )
 
index a9e017b..d9ef2b8 100644 (file)
     (local $mv1 i32 $mv2 i32 $ms i32)
     (block $rep_done
       (set_local $mv1 ($READ $line))
-      (if (get_global $error_type) (br $rep_done))
+      (br_if $rep_done (get_global $error_type))
 
       (set_local $mv2 ($EVAL $mv1 $env))
-      (if (get_global $error_type) (br $rep_done))
+      (br_if $rep_done (get_global $error_type))
 
 ;;      ($PR_MEMORY -1 -1)
       (set_local $ms ($PRINT $mv2))
+
     )
 
     ;; release memory from MAL_READ
@@ -36,6 +37,7 @@
 
   (func $main (result i32)
     (local $line i32 $res i32)
+    (set_local $line (STATIC_ARRAY 201))
 
     ;; DEBUG
     ($printf_1 "memoryBase: 0x%x\n" (get_global $memoryBase))
     ($printf_1 "heap_end: 0x%x\n" (get_global $heap_end))
     ($printf_1 "mem: 0x%x\n" (get_global $mem))
 ;;    ($printf_1 "string_mem: %d\n" (get_global $string_mem))
-    ;;($PR_MEMORY -1 -1)
 
-    ;; Start
+;;    ($PR_MEMORY_RAW
+;;      (get_global $mem) (i32.add (get_global $mem)
+;;                                 (i32.mul_u (get_global $mem_unused_start) 4)))
+
+    ($PR_MEMORY -1 -1)
+
+    ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
-        (if (i32.eq (i32.load8_u $line) 0)
-          (then
-            ($free $line)
-            (br $repl_loop)))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
         (set_local $res ($REP $line 0))
         (if (get_global $error_type)
           (then
           (else
             ($printf_1 "%s\n" ($to_String $res))))
         ($RELEASE $res)
-;;        ($PR_MEMORY -1 -1)
-        ($free $line)
-        (br $repl_loop)))
+        ;;($PR_MEMORY_SUMMARY_SMALL)
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     ;;($PR_MEMORY -1 -1)
index c885285..656dca3 100644 (file)
 
     (if (get_global $error_type) (return 0))
 
-    (if (i32.ne $type (get_global $LIST_T))
-      (return ($EVAL_AST $ast $env)))
+    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
+
+    (if (i32.ne $type (get_global $LIST_T)) (return ($EVAL_AST $ast $env)))
 
     ;; APPLY_LIST
     (if ($EMPTY_Q $ast) (return ($INC_REF $ast)))
 
   (func $main (result i32)
     (local $line i32 $res i32 $repl_env i32)
+    (set_local $line (STATIC_ARRAY 201))
 
     ;; DEBUG
     ($printf_1 "memoryBase: 0x%x\n" (get_global $memoryBase))
     ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
-        (if (i32.eq (i32.load8_u $line) 0)
-          (then
-            ($free $line)
-            (br $repl_loop)))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
         (set_local $res ($REP $line $repl_env))
         (if (get_global $error_type)
           (then
           (else
             ($printf_1 "%s\n" ($to_String $res))))
         ($RELEASE $res)
-;;        ($PR_MEMORY -1 -1)
-        ($free $line)
-        (br $repl_loop)))
+        ;;($PR_MEMORY_SUMMARY_SMALL)
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     ;;($PR_MEMORY -1 -1)
index 83de79e..f089c98 100644 (file)
 
   (func $EVAL (param $ast i32 $env i32) (result i32)
     (local $res i32)
-    (local $type i32 $ftype i32 $f_args i32 $f i32 $args i32)
+    (local $ftype i32 $f_args i32 $f i32 $args i32)
     (local $a0 i32 $a0sym i32 $a1 i32 $a2 i32)
     (local $let_env i32)
 
     (set_local $f_args 0)
     (set_local $f 0)
     (set_local $args 0)
-    (set_local $type ($TYPE $ast))
-
-    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
 
     (if (get_global $error_type) (return 0))
 
-    (if (i32.ne $type (get_global $LIST_T)) (return ($EVAL_AST $ast $env)))
+    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
+
+    (if (i32.ne ($TYPE $ast) (get_global $LIST_T))
+      (return ($EVAL_AST $ast $env)))
 
     ;; APPLY_LIST
     (if ($EMPTY_Q $ast) (return ($INC_REF $ast)))
         (then
           (set_local $res (call_indirect (type $fnT) $args ($VAL0 $f))))
         (else
-          ($THROW_STR_1 "apply of non-function type: %d\n" $type)
+          ($THROW_STR_1 "apply of non-function type: %d\n" $ftype)
           (set_local $res 0)))
 
       ($RELEASE $f_args)))))
 
   (func $main (result i32)
     (local $line i32 $res i32 $repl_env i32)
+    (set_local $line (STATIC_ARRAY 201))
 
     ;; DEBUG
     ($printf_1 "memoryBase: 0x%x\n" (get_global $memoryBase))
     ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
-        (if (i32.eq (i32.load8_u $line) 0)
-          (then
-            ($free $line)
-            (br $repl_loop)))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
         (set_local $res ($REP $line $repl_env))
         (if (get_global $error_type)
           (then
           (else
             ($printf_1 "%s\n" ($to_String $res))))
         ($RELEASE $res)
-;;        ($PR_MEMORY -1 -1)
-        ($free $line)
-        (br $repl_loop)))
+        ;;($PR_MEMORY_SUMMARY_SMALL)
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     ;;($PR_MEMORY -1 -1)
index f877541..273ccf7 100644 (file)
@@ -94,7 +94,7 @@
 
   (func $EVAL (param $ast i32 $env i32) (result i32)
     (local $res i32 $el i32)
-    (local $type i32 $ftype i32 $f_args i32 $f i32 $args i32)
+    (local $ftype i32 $f_args i32 $f i32 $args i32)
     (local $a0 i32 $a0sym i32 $a1 i32 $a2 i32 $a3 i32)
     (local $let_env i32 $fn_env i32 $a i32)
 
     (set_local $f 0)
     (set_local $args 0)
 
-    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
+    (if (get_global $error_type) (return 0))
 
-    (if (get_global $error_type)
-      (return 0))
+    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
 
     (if (i32.ne ($TYPE $ast) (get_global $LIST_T))
       (return ($EVAL_AST $ast $env)))
           ($RELEASE $a))
       (else
         ;; create new environment using env and params stored in function
-        ($THROW_STR_1 "apply of non-function type: %d\n" $type)
+        ($THROW_STR_1 "apply of non-function type: %d\n" $ftype)
         (set_local $res 0)
         ($RELEASE $f_args)))))))))))))))
 
 
   (func $main (result i32)
     (local $line i32 $res i32 $repl_env i32)
+    (set_local $line (STATIC_ARRAY 201))
 
     ;; DEBUG
     ($printf_1 "memoryBase: 0x%x\n" (get_global $memoryBase))
     ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
-        (if (i32.eq (i32.load8_u $line) 0)
-          (then
-            ($free $line)
-            (br $repl_loop)))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
         (set_local $res ($REP $line $repl_env))
         (if (get_global $error_type)
           (then
           (else
             ($printf_1 "%s\n" ($to_String $res))))
         ($RELEASE $res)
-;;        ($PR_MEMORY -1 -1)
-        ($free $line)
-        (br $repl_loop)))
+        ;;($PR_MEMORY_SUMMARY_SMALL)
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     ;;($PR_MEMORY -1 -1)
index e9db72e..2aca251 100644 (file)
     (set_local $f 0)
     (set_local $args 0)
 
-    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
-
     (if (get_global $error_type)
       (then
         (set_local $res 0)
         (br $EVAL_return)))
 
+    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
+
     (if (i32.ne ($TYPE $ast) (get_global $LIST_T))
       (then
         (set_local $res ($EVAL_AST $ast $env 0))
         ;; EVAL the rest through second to last
         (set_local $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))
         (set_local $ast ($LAST $ast))
+        ($RELEASE $ast) ;; we already own it via ast
         ($RELEASE $el)
         (br $TCO_loop))
     (else (if (i32.eqz ($strcmp "if" $a0sym))
 
   (func $main (result i32)
     (local $line i32 $res i32 $repl_env i32)
+    (set_local $line (STATIC_ARRAY 201))
 
     ;; DEBUG
-    ($printf_1 "memoryBase: 0x%x\n" (get_global $memoryBase))
-    ($printf_1 "heap_start: 0x%x\n" (get_global $heap_start))
-    ($printf_1 "heap_end: 0x%x\n" (get_global $heap_end))
-    ($printf_1 "mem: 0x%x\n" (get_global $mem))
+;;    ($printf_1 "argc: 0x%x\n" $argc)
+;;    ($printf_1 "memoryBase: 0x%x\n" (get_global $memoryBase))
+;;    ($printf_1 "heap_start: 0x%x\n" (get_global $heap_start))
+;;    ($printf_1 "heap_end: 0x%x\n" (get_global $heap_end))
+;;    ($printf_1 "mem: 0x%x\n" (get_global $mem))
 ;;    ($printf_1 "string_mem: %d\n" (get_global $string_mem))
 
     (set_global $repl_env ($ENV_NEW (get_global $NIL)))
     ;; core.EXT: defined in wasm
     ($add_core_ns $repl_env)
 
+    ($checkpoint_user_memory)
+
     ;; core.mal: defined using the language itself
     ($RELEASE ($RE "(def! not (fn* (a) (if a false true)))" $repl_env))
 
     ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
-        (if (i32.eq (i32.load8_u $line) 0)
-          (then
-            ($free $line)
-            (br $repl_loop)))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
         (set_local $res ($REP $line $repl_env))
         (if (get_global $error_type)
           (then
           (else
             ($printf_1 "%s\n" ($to_String $res))))
         ($RELEASE $res)
-;;        ($PR_MEMORY -1 -1)
-        ($free $line)
-        (br $repl_loop)))
+        ;;($PR_MEMORY_SUMMARY_SMALL)
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     ;;($PR_MEMORY -1 -1)
index e58c424..33da6cf 100644 (file)
     (set_local $f 0)
     (set_local $args 0)
 
-    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
-
     (if (get_global $error_type)
       (then
         (set_local $res 0)
         (br $EVAL_return)))
 
+    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
+
     (if (i32.ne ($TYPE $ast) (get_global $LIST_T))
       (then
         (set_local $res ($EVAL_AST $ast $env 0))
         ;; EVAL the rest through second to last
         (set_local $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))
         (set_local $ast ($LAST $ast))
+        ($RELEASE $ast) ;; we already own it via ast
         ($RELEASE $el)
         (br $TCO_loop))
     (else (if (i32.eqz ($strcmp "if" $a0sym))
     (local $line i32 $res i32 $repl_env i32)
     ;; argument processing
     (local $i i32 $ret i32 $empty i32 $current i32 $tmp i32 $val2 i32)
+    (set_local $line (STATIC_ARRAY 201))
 
     ;; DEBUG
 ;;    ($printf_1 "argc: 0x%x\n" $argc)
     ($add_core_ns $repl_env)
     (drop ($ENV_SET_S $repl_env "eval" ($FUNCTION 0)))
 
+    ($checkpoint_user_memory)
+
     ;; core.mal: defined using the language itself
     ($RELEASE ($RE "(def! not (fn* (a) (if a false true)))" $repl_env))
     ($RELEASE ($RE "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" $repl_env))
     ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
-        (if (i32.eq (i32.load8_u $line) 0)
-          (then
-            ($free $line)
-            (br $repl_loop)))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
         (set_local $res ($REP $line $repl_env))
         (if (get_global $error_type)
           (then
           (else
             ($printf_1 "%s\n" ($to_String $res))))
         ($RELEASE $res)
-;;        ($PR_MEMORY -1 -1)
-        ($free $line)
-        (br $repl_loop)))
+        ;;($PR_MEMORY_SUMMARY_SMALL)
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     ;;($PR_MEMORY -1 -1)
index 16b4e85..9a718cc 100644 (file)
     (set_local $f 0)
     (set_local $args 0)
 
-    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
-
     (if (get_global $error_type)
       (then
         (set_local $res 0)
         (br $EVAL_return)))
 
+    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
+
     (if (i32.ne ($TYPE $ast) (get_global $LIST_T))
       (then
         (set_local $res ($EVAL_AST $ast $env 0))
         ;; EVAL the rest through second to last
         (set_local $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))
         (set_local $ast ($LAST $ast))
+        ($RELEASE $ast) ;; we already own it via ast
         ($RELEASE $el)
         (br $TCO_loop))
     (else (if (i32.eqz ($strcmp "quote" $a0sym))
     (local $line i32 $res i32 $repl_env i32)
     ;; argument processing
     (local $i i32 $ret i32 $empty i32 $current i32 $tmp i32 $val2 i32)
+    (set_local $line (STATIC_ARRAY 201))
 
     ;; DEBUG
 ;;    ($printf_1 "argc: 0x%x\n" $argc)
     ($add_core_ns $repl_env)
     (drop ($ENV_SET_S $repl_env "eval" ($FUNCTION 0)))
 
+    ($checkpoint_user_memory)
+
     ;; core.mal: defined using the language itself
     ($RELEASE ($RE "(def! not (fn* (a) (if a false true)))" $repl_env))
     ($RELEASE ($RE "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" $repl_env))
     ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
-        (if (i32.eq (i32.load8_u $line) 0)
-          (then
-            ($free $line)
-            (br $repl_loop)))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
         (set_local $res ($REP $line $repl_env))
         (if (get_global $error_type)
           (then
           (else
             ($printf_1 "%s\n" ($to_String $res))))
         ($RELEASE $res)
-;;        ($PR_MEMORY -1 -1)
-        ($free $line)
-        (br $repl_loop)))
+        ;;($PR_MEMORY_SUMMARY_SMALL)
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     ;;($PR_MEMORY -1 -1)
index 6fb6da1..389977b 100644 (file)
     $res
   )
 
-  (global $mac_ast_stack (mut i32) (i32.const 0))
-  (global $mac_ast_stack_top (mut i32) (i32.const -1))
+  (global $mac_stack (mut i32) (i32.const 0))
+  (global $mac_stack_top (mut i32) (i32.const -1))
 
   (func $MACROEXPAND (param $orig_ast i32 $env i32) (result i32)
     (local $ast i32 $mac i32 $mac_env i64)
-    (set_global $mac_ast_stack (STATIC_ARRAY 128))
+    (set_global $mac_stack (STATIC_ARRAY 1024)) ;; 256 * 4
     (set_local $ast $orig_ast)
     (set_local $mac 0)
     (block $done
         ;; to the pending release list.
         (if (i32.ne $ast $orig_ast)
           (then
-            (set_global $mac_ast_stack_top
-                        (i32.add (get_global $mac_ast_stack_top) 1))
+            (set_global $mac_stack_top
+                        (i32.add (get_global $mac_stack_top) 1))
+            (if (i32.ge_s (i32.mul_s (get_global $mac_stack_top) 4) 1024) ;; 256 * 4
+              ($fatal 7 "Exhausted mac_stack!\n"))
             (i32.store (i32.add
-                         (get_global $mac_ast_stack)
-                         (i32.mul_s (get_global $mac_ast_stack_top) 4))
+                         (get_global $mac_stack)
+                         (i32.mul_s (get_global $mac_stack_top) 4))
               $ast)))
         (if (get_global $error_type)
           (br $done))
 
-      (br $loop)
+        (br $loop)
       )
     )
     $ast
     (local $ast i32 $env i32 $prev_ast i32 $prev_env i32 $res i32 $el i32)
     (local $ftype i32 $f_args i32 $f i32 $args i32)
     (local $a0 i32 $a0sym i32 $a1 i32 $a2 i32)
+    (local $orig_mac_stack_top i32)
 
     (set_local $ast $orig_ast)
     (set_local $env $orig_env)
     (set_local $prev_ast 0)
     (set_local $prev_env 0)
     (set_local $res 0)
+    (set_local $orig_mac_stack_top (get_global $mac_stack_top))
 
     (block $EVAL_return
     (loop $TCO_loop
     (set_local $f 0)
     (set_local $args 0)
 
-    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
-
     (if (get_global $error_type)
       (then
         (set_local $res 0)
         (br $EVAL_return)))
 
+    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
+
     (if (i32.ne ($TYPE $ast) (get_global $LIST_T))
       (then
         (set_local $res ($EVAL_AST $ast $env 0))
         ;; EVAL the rest through second to last
         (set_local $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))
         (set_local $ast ($LAST $ast))
+        ($RELEASE $ast) ;; we already own it via ast
         ($RELEASE $el)
         (br $TCO_loop))
     (else (if (i32.eqz ($strcmp "quote" $a0sym))
     (if (i32.ne $env $orig_env) ($RELEASE $env))
     (if $prev_ast ($RELEASE $prev_ast))
 
+    ;; release memory from MACROEXPAND
+    ;; TODO: needs to happen here so self-hosting doesn't leak
+    (block $done
+      (loop $loop
+        (if (i32.le_s (get_global $mac_stack_top) $orig_mac_stack_top)
+          (br $done))
+;;($printf_1 "free orig_mac_stack_top: %d\n" $orig_mac_stack_top)
+;;($printf_1 "free mac_stack_top: %d\n" (get_global $mac_stack_top))
+        ($RELEASE (i32.load (i32.add
+                              (get_global $mac_stack)
+                              (i32.mul_s (get_global $mac_stack_top) 4))))
+        (set_global $mac_stack_top
+                    (i32.sub_s (get_global $mac_stack_top) 1))
+        (br $loop)
+      )
+    )
+
     $res
   )
 
 
     ;; release memory from MAL_READ
     ($RELEASE $mv1)
-    ;; release memory from MACROEXPAND
-    ;; TODO: needs to happen in EVAL
-    (block $done
-      (loop $loop
-        (if (i32.lt_s (get_global $mac_ast_stack_top) 0)
-          (br $done))
-        ($RELEASE (i32.load (i32.add
-                              (get_global $mac_ast_stack)
-                              (i32.mul_s (get_global $mac_ast_stack_top) 4))))
-        (set_global $mac_ast_stack_top
-                    (i32.sub_s (get_global $mac_ast_stack_top) 1))
-        (br $loop)
-      )
-    )
     $res
   )
 
     (local $line i32 $res i32 $repl_env i32)
     ;; argument processing
     (local $i i32 $ret i32 $empty i32 $current i32 $tmp i32 $val2 i32)
+    (set_local $line (STATIC_ARRAY 201))
 
     ;; DEBUG
 ;;    ($printf_1 "argc: 0x%x\n" $argc)
     ($add_core_ns $repl_env)
     (drop ($ENV_SET_S $repl_env "eval" ($FUNCTION 0)))
 
+    ($checkpoint_user_memory)
+
     ;; core.mal: defined using the language itself
     ($RELEASE ($RE "(def! not (fn* (a) (if a false true)))" $repl_env))
     ($RELEASE ($RE "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" $repl_env))
     ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
-        (if (i32.eq (i32.load8_u $line) 0)
-          (then
-            ($free $line)
-            (br $repl_loop)))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
         (set_local $res ($REP $line $repl_env))
         (if (get_global $error_type)
           (then
           (else
             ($printf_1 "%s\n" ($to_String $res))))
         ($RELEASE $res)
-;;        ($PR_MEMORY -1 -1)
-        ($free $line)
-        (br $repl_loop)))
+        ;;($PR_MEMORY_SUMMARY_SMALL)
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     ;;($PR_MEMORY -1 -1)
index 3ebeee1..67fd7fd 100644 (file)
@@ -1,4 +1,4 @@
-(module $step8_macros
+(module $step9_try
 
   (global $repl_env (mut i32) (i32.const 0))
 
     $res
   )
 
-  (global $mac_ast_stack (mut i32) (i32.const 0))
-  (global $mac_ast_stack_top (mut i32) (i32.const -1))
+  (global $mac_stack (mut i32) (i32.const 0))
+  (global $mac_stack_top (mut i32) (i32.const -1))
 
   (func $MACROEXPAND (param $orig_ast i32 $env i32) (result i32)
     (local $ast i32 $mac i32 $mac_env i64)
-    (set_global $mac_ast_stack (STATIC_ARRAY 128))
+    (set_global $mac_stack (STATIC_ARRAY 1024)) ;; 256 * 4
     (set_local $ast $orig_ast)
     (set_local $mac 0)
     (block $done
         ;; to the pending release list.
         (if (i32.ne $ast $orig_ast)
           (then
-            (set_global $mac_ast_stack_top
-                        (i32.add (get_global $mac_ast_stack_top) 1))
+            (set_global $mac_stack_top
+                        (i32.add (get_global $mac_stack_top) 1))
+            (if (i32.ge_s (i32.mul_s (get_global $mac_stack_top) 4) 1024) ;; 256 * 4
+              ($fatal 7 "Exhausted mac_stack!\n"))
             (i32.store (i32.add
-                         (get_global $mac_ast_stack)
-                         (i32.mul_s (get_global $mac_ast_stack_top) 4))
+                         (get_global $mac_stack)
+                         (i32.mul_s (get_global $mac_stack_top) 4))
               $ast)))
         (if (get_global $error_type)
           (br $done))
 
-      (br $loop)
+        (br $loop)
       )
     )
     $ast
     (local $ftype i32 $f_args i32 $f i32 $args i32)
     (local $a0 i32 $a0sym i32 $a1 i32 $a2 i32)
     (local $err i32)
+    (local $orig_mac_stack_top i32)
 
     (set_local $ast $orig_ast)
     (set_local $env $orig_env)
     (set_local $prev_ast 0)
     (set_local $prev_env 0)
     (set_local $res 0)
+    (set_local $orig_mac_stack_top (get_global $mac_stack_top))
 
     (block $EVAL_return
     (loop $TCO_loop
     (set_local $f 0)
     (set_local $args 0)
 
-    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
-
     (if (get_global $error_type)
       (then
         (set_local $res 0)
         (br $EVAL_return)))
 
+    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
+
     (if (i32.ne ($TYPE $ast) (get_global $LIST_T))
       (then
         (set_local $res ($EVAL_AST $ast $env 0))
         ;; EVAL the rest through second to last
         (set_local $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))
         (set_local $ast ($LAST $ast))
+        ($RELEASE $ast) ;; we already own it via ast
         ($RELEASE $el)
         (br $TCO_loop))
     (else (if (i32.eqz ($strcmp "quote" $a0sym))
         (if (i32.eqz (get_global $error_type))
           (br $EVAL_return))
         ;; if there is an error and res is set, we need to free it
-        ($printf_1 "res value: %d\n" $res)
         ($RELEASE $res)
         ;; if there is no catch block then return
         (if (i32.eqz ($VAL0 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))
     (if (i32.ne $env $orig_env) ($RELEASE $env))
     (if $prev_ast ($RELEASE $prev_ast))
 
+    ;; release memory from MACROEXPAND
+    ;; TODO: needs to happen here so self-hosting doesn't leak
+    (block $done
+      (loop $loop
+        (if (i32.le_s (get_global $mac_stack_top) $orig_mac_stack_top)
+          (br $done))
+;;($printf_1 "free orig_mac_stack_top: %d\n" $orig_mac_stack_top)
+;;($printf_1 "free mac_stack_top: %d\n" (get_global $mac_stack_top))
+        ($RELEASE (i32.load (i32.add
+                              (get_global $mac_stack)
+                              (i32.mul_s (get_global $mac_stack_top) 4))))
+        (set_global $mac_stack_top
+                    (i32.sub_s (get_global $mac_stack_top) 1))
+        (br $loop)
+      )
+    )
+
     $res
   )
 
 
     ;; release memory from MAL_READ
     ($RELEASE $mv1)
-    ;; release memory from MACROEXPAND
-    ;; TODO: needs to happen in EVAL
-    (block $done
-      (loop $loop
-        (if (i32.lt_s (get_global $mac_ast_stack_top) 0)
-          (br $done))
-        ($RELEASE (i32.load (i32.add
-                              (get_global $mac_ast_stack)
-                              (i32.mul_s (get_global $mac_ast_stack_top) 4))))
-        (set_global $mac_ast_stack_top
-                    (i32.sub_s (get_global $mac_ast_stack_top) 1))
-        (br $loop)
-      )
-    )
     $res
   )
 
     (local $line i32 $res i32 $repl_env i32)
     ;; argument processing
     (local $i i32 $ret i32 $empty i32 $current i32 $tmp i32 $val2 i32)
+    (set_local $line (STATIC_ARRAY 201))
 
     ;; DEBUG
 ;;    ($printf_1 "argc: 0x%x\n" $argc)
     ($add_core_ns $repl_env)
     (drop ($ENV_SET_S $repl_env "eval" ($FUNCTION 0)))
 
+    ($checkpoint_user_memory)
 
     ;; core.mal: defined using the language itself
     ($RELEASE ($RE "(def! not (fn* (a) (if a false true)))" $repl_env))
     ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
-        (if (i32.eq (i32.load8_u $line) 0)
-          (then
-            ($free $line)
-            (br $repl_loop)))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
         (set_local $res ($REP $line $repl_env))
         (if (get_global $error_type)
           (then
           (else
             ($printf_1 "%s\n" ($to_String $res))))
         ($RELEASE $res)
-;;        ($PR_MEMORY -1 -1)
-        ($free $line)
-        (br $repl_loop)))
+        ;;($PR_MEMORY_SUMMARY_SMALL)
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     ;;($PR_MEMORY -1 -1)
index 4d0788f..f196fab 100644 (file)
@@ -1,4 +1,4 @@
-(module $step8_macros
+(module $stepA_mal
 
   (global $repl_env (mut i32) (i32.const 0))
 
     $res
   )
 
-  (global $mac_ast_stack (mut i32) (i32.const 0))
-  (global $mac_ast_stack_top (mut i32) (i32.const -1))
+  (global $mac_stack (mut i32) (i32.const 0))
+  (global $mac_stack_top (mut i32) (i32.const -1))
 
   (func $MACROEXPAND (param $orig_ast i32 $env i32) (result i32)
     (local $ast i32 $mac i32 $mac_env i64)
-    (set_global $mac_ast_stack (STATIC_ARRAY 128))
+    (set_global $mac_stack (STATIC_ARRAY 1024)) ;; 256 * 4
     (set_local $ast $orig_ast)
     (set_local $mac 0)
     (block $done
         ;; to the pending release list.
         (if (i32.ne $ast $orig_ast)
           (then
-            (set_global $mac_ast_stack_top
-                        (i32.add (get_global $mac_ast_stack_top) 1))
+            (set_global $mac_stack_top
+                        (i32.add (get_global $mac_stack_top) 1))
+            (if (i32.ge_s (i32.mul_s (get_global $mac_stack_top) 4) 1024) ;; 256 * 4
+              ($fatal 7 "Exhausted mac_stack!\n"))
             (i32.store (i32.add
-                         (get_global $mac_ast_stack)
-                         (i32.mul_s (get_global $mac_ast_stack_top) 4))
+                         (get_global $mac_stack)
+                         (i32.mul_s (get_global $mac_stack_top) 4))
               $ast)))
         (if (get_global $error_type)
           (br $done))
 
-      (br $loop)
+        (br $loop)
       )
     )
     $ast
     (local $ftype i32 $f_args i32 $f i32 $args i32)
     (local $a0 i32 $a0sym i32 $a1 i32 $a2 i32)
     (local $err i32)
+    (local $orig_mac_stack_top i32)
 
     (set_local $ast $orig_ast)
     (set_local $env $orig_env)
     (set_local $prev_ast 0)
     (set_local $prev_env 0)
     (set_local $res 0)
+    (set_local $orig_mac_stack_top (get_global $mac_stack_top))
 
     (block $EVAL_return
     (loop $TCO_loop
     (set_local $f 0)
     (set_local $args 0)
 
-    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
-
     (if (get_global $error_type)
       (then
         (set_local $res 0)
         (br $EVAL_return)))
 
+    ;;($PR_VALUE ">>> EVAL ast: '%s'\n" $ast)
+
     (if (i32.ne ($TYPE $ast) (get_global $LIST_T))
       (then
         (set_local $res ($EVAL_AST $ast $env 0))
         ;; EVAL the rest through second to last
         (set_local $el ($EVAL_AST ($MEM_VAL0_ptr $ast) $env 1))
         (set_local $ast ($LAST $ast))
+        ($RELEASE $ast) ;; we already own it via ast
         ($RELEASE $el)
         (br $TCO_loop))
     (else (if (i32.eqz ($strcmp "quote" $a0sym))
         (if (i32.eqz (get_global $error_type))
           (br $EVAL_return))
         ;; if there is an error and res is set, we need to free it
-        ($printf_1 "res value: %d\n" $res)
         ($RELEASE $res)
         ;; if there is no catch block then return
         (if (i32.eqz ($VAL0 ($MEM_VAL0_ptr ($MEM_VAL0_ptr $ast))))
     (if (i32.ne $env $orig_env) ($RELEASE $env))
     (if $prev_ast ($RELEASE $prev_ast))
 
+    ;; release memory from MACROEXPAND
+    ;; TODO: needs to happen here so self-hosting doesn't leak
+    (block $done
+      (loop $loop
+        (if (i32.le_s (get_global $mac_stack_top) $orig_mac_stack_top)
+          (br $done))
+;;($printf_1 "free orig_mac_stack_top: %d\n" $orig_mac_stack_top)
+;;($printf_1 "free mac_stack_top: %d\n" (get_global $mac_stack_top))
+        ($RELEASE (i32.load (i32.add
+                              (get_global $mac_stack)
+                              (i32.mul_s (get_global $mac_stack_top) 4))))
+        (set_global $mac_stack_top
+                    (i32.sub_s (get_global $mac_stack_top) 1))
+        (br $loop)
+      )
+    )
+
     $res
   )
 
 
     ;; release memory from MAL_READ
     ($RELEASE $mv1)
-    ;; release memory from MACROEXPAND
-    ;; TODO: needs to happen in EVAL
-    (block $done
-      (loop $loop
-        (if (i32.lt_s (get_global $mac_ast_stack_top) 0)
-          (br $done))
-        ($RELEASE (i32.load (i32.add
-                              (get_global $mac_ast_stack)
-                              (i32.mul_s (get_global $mac_ast_stack_top) 4))))
-        (set_global $mac_ast_stack_top
-                    (i32.sub_s (get_global $mac_ast_stack_top) 1))
-        (br $loop)
-      )
-    )
     $res
   )
 
     (local $line i32 $res i32 $repl_env i32)
     ;; argument processing
     (local $i i32 $ret i32 $empty i32 $current i32 $tmp i32 $val2 i32)
+    (set_local $line (STATIC_ARRAY 201))
 
     ;; DEBUG
 ;;    ($printf_1 "argc: 0x%x\n" $argc)
     ;; Start REPL
     (block $repl_done
       (loop $repl_loop
-        (set_local $line ($readline "user> "))
-        (if (i32.eqz $line) (br $repl_done))
-        (if (i32.eq (i32.load8_u $line) 0)
-          (then
-            ($free $line)
-            (br $repl_loop)))
+        (br_if $repl_done (i32.eqz ($readline "user> " $line)))
+        (br_if $repl_loop (i32.eq (i32.load8_u $line) 0))
         (set_local $res ($REP $line $repl_env))
         (if (get_global $error_type)
           (then
           (else
             ($printf_1 "%s\n" ($to_String $res))))
         ($RELEASE $res)
-        ($free $line)
         ;;($PR_MEMORY_SUMMARY_SMALL)
-        (br $repl_loop)))
+        (br $repl_loop)
+      )
+    )
 
     ($print "\n")
     ;;($PR_MEMORY -1 -1)
index 4a68c0c..dd2ff3c 100644 (file)
             (set_local $needle_len ($strlen $needle))
             (set_local $replace_len ($strlen $replace))
             (if (i32.gt_u $replace_len $needle_len)
-              (then
-                ($print "REPLACE: invalid expanding in-place call\n")
-                ($exit 1)))
+              ($fatal 7 "REPLACE: invalid expanding in-place call\n"))
             (set_local $s (i32.add $s 1))
             (br $loop)
           )
index 8f14ad9..5574c3d 100644 (file)
   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   ;; string functions
 
+  (func $to_MalString (param $mv i32) (result i32)
+    ;; TODO: assert mv is a string/keyword/symbol
+    (i32.add (get_global $string_mem) ($VAL0 $mv))
+  )
+
   (func $to_String (param $mv i32) (result i32)
-    ;; skip string refcnt
-    (i32.add 4 ($VAL0 $mv))
+    ;; skip string refcnt and size
+    (i32.add 4 ($to_MalString $mv))
+  )
+
+  ;; Duplicate regular character array string into a Mal string and
+  ;; return the MalVal pointer
+  (func $STRING (param $type i32 $str i32) (result i32)
+    (local $ms i32)
+    ;; TODO: assert mv is a string/keyword/symbol
+    (set_local $ms ($ALLOC_STRING $str ($strlen $str) 1))
+    ($ALLOC_SCALAR $type (i32.sub_u $ms (get_global $string_mem)))
+  )
+
+  ;; Find first duplicate (internet) of mv. If one is found, free up
+  ;; mv and return the interned version. If no duplicate is found,
+  ;; return NULL.
+  (func $INTERN_STRING (param $mv i32) (result i32)
+    (local $res i32 $ms i32 $existing_ms i32 $tmp i32)
+    (set_local $res 0)
+    (set_local $ms ($to_MalString $mv))
+    (set_local $existing_ms ($FIND_STRING (i32.add $ms 4)))
+    (if (AND $existing_ms (i32.lt_s $existing_ms $ms))
+      (then
+        (set_local $tmp $mv)
+        (set_local $res ($ALLOC_SCALAR (get_global $STRING_T)
+                                       (i32.sub_s $existing_ms
+                                                  (get_global $string_mem))))
+        (i32.store16_u $existing_ms (i32.add (i32.load16_u $existing_ms) 1))
+        ($RELEASE $tmp)))
+    $res
+  )
+
+  (func $STRING_INIT (param $type i32) (result i32)
+    (local $ms i32)
+    (set_local $ms ($ALLOC_STRING "" 0 0))
+    ($ALLOC_SCALAR $type (i32.sub_s $ms (get_global $string_mem)))
+  )
+
+  (func $STRING_FINALIZE (param $mv i32 $size i32) (result i32)
+    (local $tmp i32 $ms i32)
+    ;; Check if the new string can be interned.
+    (set_local $tmp ($INTERN_STRING $mv))
+    (set_local $ms ($to_MalString $mv))
+    (if $tmp
+      (then
+        (set_local $mv $tmp))
+      (else
+        ;;; ms->size = sizeof(MalString) + size + 1
+        (i32.store16_u (i32.add $ms 2)
+                     (i32.add (i32.add 4 $size) 1))
+        ;;; string_mem_next = (void *)ms  + ms->size
+        (set_global $string_mem_next
+                    (i32.add $ms (i32.load16_u (i32.add $ms 2))))))
+    $mv
   )
 
   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;