about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xapps/mubin256204 -> 256251 bytes
-rw-r--r--apps/mu.subx263
2 files changed, 115 insertions, 148 deletions
diff --git a/apps/mu b/apps/mu
index fa14405a..7ad2a412 100755
--- a/apps/mu
+++ b/apps/mu
Binary files differdiff --git a/apps/mu.subx b/apps/mu.subx
index 61c0c390..85794961 100644
--- a/apps/mu.subx
+++ b/apps/mu.subx
@@ -355,6 +355,15 @@ Stmt-var-is-deref:  # boolean
 Stmt-var-size:  # (addr int)
   0x14/imm32
 
+# A live-var is a var augmented with information needed for tracking live
+# variables.
+Live-var-value:  # (handle var)
+  0/imm32
+Live-var-register-spilled:  # boolean; only used if value is in a register, and only during code-gen
+  8/imm32
+Live-var-size:  # (addr int)
+  0xc/imm32
+
 # Types are expressed as trees (s-expressions) of type-ids (ints).
 # However, there's no need for singletons, so we can assume (int) == int
 #   - if x->right == nil, x is an atom
@@ -3593,7 +3602,7 @@ parse-mu:  # in: (addr buffered-file)
     #       continue                                # end of line
     #     else if slice-equal?(word-slice, "fn")
     #       var new-function: (handle function) = allocate(function)
-    #       var vars: (stack (handle var) 256)
+    #       var vars: (stack live-var 256)
     #       populate-mu-function-header(line, new-function, vars)
     #       populate-mu-function-body(in, new-function, vars)
     #       assert(vars->top == 0)
@@ -3630,9 +3639,9 @@ parse-mu:  # in: (addr buffered-file)
     89/<- %edx 4/r32/esp
     # var curr-function/edi: (addr handle function)
     bf/copy-to-edi _Program-functions/imm32
-    # var vars/ebx: (stack (handle var) 256)
-    81 5/subop/subtract %esp 0x800/imm32
-    68/push 0x800/imm32/size
+    # var vars/ebx: (stack live-var 256)
+    81 5/subop/subtract %esp 0xc00/imm32
+    68/push 0xc00/imm32/size
     68/push 0/imm32/top
     89/<- %ebx 4/r32/esp
     {
@@ -3726,7 +3735,7 @@ $parse-mu:type:
     } # end line loop
 $parse-mu:end:
     # . reclaim locals
-    81 0/subop/add %esp 0xa1c/imm32
+    81 0/subop/add %esp 0xe1c/imm32
     # . restore registers
     5f/pop-to-edi
     5e/pop-to-esi
@@ -3775,7 +3784,7 @@ $parse-mu:error2:
 # ✓ fn foo x: int {
 # ✓ fn foo x: int {
 # ✓ fn foo x: int -> y/eax: int {
-populate-mu-function-header:  # first-line: (addr stream byte), out: (addr function), vars: (addr stack (handle var))
+populate-mu-function-header:  # first-line: (addr stream byte), out: (addr function), vars: (addr stack live-var)
     # pseudocode:
     #   var name: slice
     #   next-mu-token(first-line, name)
@@ -3792,7 +3801,7 @@ populate-mu-function-header:  # first-line: (addr stream byte), out: (addr funct
     #     assert(v->register == null)
     #     # v->block-depth is implicitly 0
     #     out->inouts = append(v, out->inouts)
-    #     push(vars, v)
+    #     push(vars, {v, false})
     #   ## outputs
     #   while true
     #     ## name
@@ -3868,9 +3877,10 @@ $populate-mu-function-header:check-for-inout:
       # out->inouts = append(v, out->inouts)
       8d/copy-address *(edi+8) 0/r32/eax  # Function-inouts
       (append-list Heap  *ebx *(ebx+4)  *(edi+8) *(edi+0xc)  %eax)  # Function-inouts, Function-inouts
-      # push(vars, v)
+      # push(vars, {v, false})
       (push *(ebp+0x10) *ebx)
       (push *(ebp+0x10) *(ebx+4))
+      (push *(ebp+0x10) 0)  # false
       #
       e9/jump loop/disp32
     }
@@ -3974,9 +3984,9 @@ test-function-header-with-arg:
     2b/subtract *Function-size 4/r32/esp
     89/<- %ecx 4/r32/esp
     (zero-out %ecx *Function-size)
-    # var vars/ebx: (stack (handle var) 16)
-    81 5/subop/subtract %esp 0x80/imm32
-    68/push 0x80/imm32/size
+    # var vars/ebx: (stack live-var 16)
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
     68/push 0/imm32/top
     89/<- %ebx 4/r32/esp
     # convert
@@ -4012,9 +4022,9 @@ test-function-header-with-multiple-args:
     2b/subtract *Function-size 4/r32/esp
     89/<- %ecx 4/r32/esp
     (zero-out %ecx *Function-size)
-    # var vars/ebx: (stack (handle var) 16)
-    81 5/subop/subtract %esp 0x80/imm32
-    68/push 0x80/imm32/size
+    # var vars/ebx: (stack live-var 16)
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
     68/push 0/imm32/top
     89/<- %ebx 4/r32/esp
     # convert
@@ -4083,9 +4093,9 @@ test-function-header-with-multiple-args-and-outputs:
     2b/subtract *Function-size 4/r32/esp
     89/<- %ecx 4/r32/esp
     (zero-out %ecx *Function-size)
-    # var vars/ebx: (stack (handle var) 16)
-    81 5/subop/subtract %esp 0x80/imm32
-    68/push 0x80/imm32/size
+    # var vars/ebx: (stack live-var 16)
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
     68/push 0/imm32/top
     89/<- %ebx 4/r32/esp
     # convert
@@ -5221,7 +5231,7 @@ test-is-identifier-hyphen:
     5d/pop-to-ebp
     c3/return
 
-populate-mu-function-body:  # in: (addr buffered-file), out: (addr function), vars: (addr stack (handle var))
+populate-mu-function-body:  # in: (addr buffered-file), out: (addr function), vars: (addr stack live-var)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -5247,7 +5257,7 @@ $populate-mu-function-body:end:
     c3/return
 
 # parses a block, assuming that the leading '{' has already been read by the caller
-parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn: (addr function), out: (addr handle block)
+parse-mu-block:  # in: (addr buffered-file), vars: (addr stack live-var), fn: (addr function), out: (addr handle block)
     # pseudocode:
     #   var line: (stream byte 512)
     #   var word-slice: slice
@@ -5255,7 +5265,7 @@ parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn
     #   var out-addr: (addr block) = lookup(*out)
     #   out-addr->tag = 0/block
     #   out-addr->var = some unique name
-    #   push(vars, out-addr->var)
+    #   push(vars, {out-addr->var, false})
     #   while true                                  # line loop
     #     clear-stream(line)
     #     read-line-buffered(in, line)
@@ -5316,6 +5326,7 @@ parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn
     # push(vars, out-addr->var)
     (push *(ebp+0xc) *(edi+0xc))  # Block-var
     (push *(ebp+0xc) *(edi+0x10))  # Block-var
+    (push *(ebp+0xc) 0)  # false
     {
 $parse-mu-block:line-loop:
       # line = read-line-buffered(in)
@@ -5433,6 +5444,7 @@ $parse-mu-block:regular-stmt:
     # pop(vars)
     (pop *(ebp+0xc))  # => eax
     (pop *(ebp+0xc))  # => eax
+    (pop *(ebp+0xc))  # => eax
 $parse-mu-block:end:
     # . reclaim locals
     81 0/subop/add %esp 0x214/imm32
@@ -5579,11 +5591,11 @@ $check-no-tokens-left:end:
     5d/pop-to-ebp
     c3/return
 
-parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (addr function), out: (addr handle stmt)
+parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack live-var), fn: (addr function), out: (addr handle stmt)
     # pseudocode:
     #   var v: (handle var)
     #   new-literal(name, v)
-    #   push(vars, v)
+    #   push(vars, {v, false})
     #   parse-mu-block(in, vars, fn, out)
     #   pop(vars)
     #   out->tag = block
@@ -5605,11 +5617,13 @@ parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (ad
     # push(vars, v)
     (push *(ebp+0x10) *ecx)
     (push *(ebp+0x10) *(ecx+4))
+    (push *(ebp+0x10) 0)  # false
     #
     (parse-mu-block *(ebp+0xc) *(ebp+0x10) *(ebp+0x14) *(ebp+0x18))
     # pop v off vars
     (pop *(ebp+0x10))  # => eax
     (pop *(ebp+0x10))  # => eax
+    (pop *(ebp+0x10))  # => eax
     # var out-addr/edi: (addr stmt) = lookup(*out)
     8b/-> *(ebp+0x18) 7/r32/edi
     (lookup *edi *(edi+4))  # => eax
@@ -5633,7 +5647,7 @@ $parse-mu-named-block:end:
     5d/pop-to-ebp
     c3/return
 
-parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)), out: (addr handle stmt)
+parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack live-var), out: (addr handle stmt)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -5658,6 +5672,7 @@ parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)),
     #
     (push *(ebp+0xc) *edx)
     (push *(ebp+0xc) *(edx+4))
+    (push *(ebp+0xc) 0)  # Live-var-register-spilled is unused during parsing
     # either v has no register and there's no more to this line
     (lookup *edx *(edx+4))  # => eax
     8b/-> *(eax+0x18) 0/r32/eax  # Var-register
@@ -5721,8 +5736,8 @@ test-parse-mu-var-def:
     68/push 0/imm32
     89/<- %esi 4/r32/esp
     # var vars/ecx: (stack (addr var) 16)
-    81 5/subop/subtract %esp 0x80/imm32
-    68/push 0x80/imm32/size
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
     68/push 0/imm32/top
     89/<- %ecx 4/r32/esp
     (clear-stack %ecx)
@@ -5764,8 +5779,8 @@ test-parse-mu-reg-var-def:
     68/push 0/imm32
     89/<- %esi 4/r32/esp
     # var vars/ecx: (stack (addr var) 16)
-    81 5/subop/subtract %esp 0x80/imm32
-    68/push 0x80/imm32/size
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
     68/push 0/imm32/top
     89/<- %ecx 4/r32/esp
     (clear-stack %ecx)
@@ -5801,7 +5816,7 @@ test-parse-mu-reg-var-def:
     5d/pop-to-ebp
     c3/return
 
-parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack (handle var)), fn: (addr function), out: (addr handle stmt)
+parse-mu-stmt:  # line: (addr stream byte), vars: (addr stack live-var), fn: (addr function), out: (addr handle stmt)
     # pseudocode:
     #   var name: slice
     #   allocate(Heap, Stmt-size, out)
@@ -5910,7 +5925,7 @@ $parse-mu-stmt:abort:
     cd/syscall  0x80/imm8
     # never gets here
 
-add-operation-and-inputs-to-stmt:  # stmt: (addr stmt), line: (addr stream byte), vars: (addr stack (handle var))
+add-operation-and-inputs-to-stmt:  # stmt: (addr stmt), line: (addr stream byte), vars: (addr stack live-var)
     # pseudocode:
     #   stmt->name = slice-to-string(next-mu-token(line))
     #   while true
@@ -6076,7 +6091,7 @@ $stmt-has-outputs:end:
 
 # if 'name' starts with a digit, create a new literal var for it
 # otherwise return first 'name' from the top (back) of 'vars' and abort if not found
-lookup-var-or-literal:  # name: (addr slice), vars: (addr stack (handle var)), out: (addr handle var)
+lookup-var-or-literal:  # name: (addr slice), vars: (addr stack live-var), out: (addr handle var)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -6133,7 +6148,7 @@ $lookup-var-or-literal:abort:
     # never gets here
 
 # return first 'name' from the top (back) of 'vars' and abort if not found
-lookup-var:  # name: (addr slice), vars: (addr stack (handle var)), out: (addr handle var)
+lookup-var:  # name: (addr slice), vars: (addr stack live-var), out: (addr handle var)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -6165,15 +6180,15 @@ $lookup-var:abort:
     # never gets here
 
 # return first 'name' from the top (back) of 'vars', and 0/null if not found
-lookup-var-helper:  # name: (addr slice), vars: (addr stack (handle var)), out: (addr handle var)
+lookup-var-helper:  # name: (addr slice), vars: (addr stack live-var), out: (addr handle var)
     # pseudocode:
-    #   var curr: (addr handle var) = &vars->data[vars->top - 8]
+    #   var curr: (addr handle var) = &vars->data[vars->top - 12]
     #   var min = vars->data
     #   while curr >= min
     #     var v: (handle var) = *curr
     #     if v->name == name
     #       return
-    #     curr -= 8
+    #     curr -= 12
     #
     # . prologue
     55/push-ebp
@@ -6195,8 +6210,8 @@ lookup-var-helper:  # name: (addr slice), vars: (addr stack (handle var)), out:
     0f 8f/jump-if-> $lookup-var-helper:error1/disp32
     # var min/edx: (addr handle var) = vars->data
     8d/copy-address *(esi+8) 2/r32/edx
-    # var curr/ebx: (addr handle var) = &vars->data[vars->top - 8]
-    8d/copy-address *(esi+ebx) 3/r32/ebx  # vars + 8 + vars->type - 8
+    # var curr/ebx: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(esi+ebx-4) 3/r32/ebx  # vars + 8 + vars->type - 12
     {
       # if (curr < min) return
       39/compare %ebx 2/r32/edx
@@ -6221,8 +6236,8 @@ lookup-var-helper:  # name: (addr slice), vars: (addr stack (handle var)), out:
         # return
         eb/jump $lookup-var-helper:end/disp8
       }
-      # curr -= 8
-      81 5/subop/subtract %ebx 8/imm32
+      # curr -= 12
+      81 5/subop/subtract %ebx 0xc/imm32
       e9/jump loop/disp32
     }
 $lookup-var-helper:end:
@@ -6249,7 +6264,7 @@ $lookup-var-helper:error1:
     # never gets here
 
 # return first 'name' from the top (back) of 'vars' and create a new var for a fn output if not found
-lookup-or-define-var:  # name: (addr slice), vars: (addr stack (handle var)), fn: (addr function), out: (addr handle var)
+lookup-or-define-var:  # name: (addr slice), vars: (addr stack live-var), fn: (addr function), out: (addr handle var)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -6339,8 +6354,8 @@ test-parse-mu-stmt:
     (clear-stream _test-input-stream)
     (write _test-input-stream "increment n\n")
     # var vars/ecx: (stack (addr var) 16)
-    81 5/subop/subtract %esp 0x80/imm32
-    68/push 0x80/imm32/size
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
     68/push 0/imm32/top
     89/<- %ecx 4/r32/esp
     (clear-stack %ecx)
@@ -6358,6 +6373,7 @@ test-parse-mu-stmt:
     #
     (push %ecx *edx)
     (push %ecx *(edx+4))
+    (push %ecx 0)
     # var out/eax: (handle stmt)
     68/push 0/imm32
     68/push 0/imm32
@@ -6394,8 +6410,8 @@ test-parse-mu-stmt-with-comma:
     (clear-stream _test-input-stream)
     (write _test-input-stream "copy-to n, 3\n")
     # var vars/ecx: (stack (addr var) 16)
-    81 5/subop/subtract %esp 0x80/imm32
-    68/push 0x80/imm32/size
+    81 5/subop/subtract %esp 0xc0/imm32
+    68/push 0xc0/imm32/size
     68/push 0/imm32/top
     89/<- %ecx 4/r32/esp
     (clear-stack %ecx)
@@ -6413,6 +6429,7 @@ test-parse-mu-stmt-with-comma:
     #
     (push %ecx *edx)
     (push %ecx *(edx+4))
+    (push %ecx 0)
     # var out/eax: (handle stmt)
     68/push 0/imm32
     68/push 0/imm32
@@ -8069,8 +8086,8 @@ emit-subx-function:  # out: (addr buffered-file), f: (addr function)
     # ecx = f
     8b/-> *(ebp+0xc) 1/r32/ecx
     # var vars/edx: (stack (addr var) 256)
-    81 5/subop/subtract %esp 0x800/imm32
-    68/push 0x800/imm32/size
+    81 5/subop/subtract %esp 0xc00/imm32
+    68/push 0xc00/imm32/size
     68/push 0/imm32/top
     89/<- %edx 4/r32/esp
     # var name/eax: (addr array byte) = lookup(f->name)
@@ -8088,7 +8105,7 @@ emit-subx-function:  # out: (addr buffered-file), f: (addr function)
     # been cleaned up
 $emit-subx-function:end:
     # . reclaim locals
-    81 0/subop/add %esp 808/imm32
+    81 0/subop/add %esp 0xc08/imm32
     # . restore registers
     5a/pop-to-edx
     59/pop-to-ecx
@@ -8153,7 +8170,7 @@ $populate-mu-type-offsets-in-inouts:end:
     5d/pop-to-ebp
     c3/return
 
-emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (addr list stmt), vars: (addr stack (handle var))
+emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (addr list stmt), vars: (addr stack live-var)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -8309,6 +8326,7 @@ $emit-subx-stmt-list:var-def:
         (emit-subx-var-def *(ebp+8) %ecx)
         (push *(ebp+0x10) *(ecx+4))  # Vardef-var
         (push *(ebp+0x10) *(ecx+8))  # Vardef-var
+        (push *(ebp+0x10) 0)  # Live-var-register-spilled = 0 for vars on the stack
         # var-seen? = true
         ba/copy-to-edx 1/imm32/true
         eb/jump $emit-subx-stmt-list:continue/disp8
@@ -8348,13 +8366,14 @@ $emit-subx-stmt-list:end:
     5d/pop-to-ebp
     c3/return
 
-push-output-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (addr reg-var-def), vars: (addr stack (handle var))
+push-output-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (addr reg-var-def), vars: (addr stack live-var)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
     # . save registers
     50/push-eax
     51/push-ecx
+    52/push-edx
     # ecx = stmt
     8b/-> *(ebp+0xc) 1/r32/ecx
     # var sv/eax: (addr stmt-var) = lookup(curr-stmt->outputs)
@@ -8376,8 +8395,10 @@ push-output-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (addr reg-
     # ensure that v is in a register
     81 7/subop/compare *(ecx+0x18) 0/imm32  # Var-register
     0f 84/jump-if-= $push-output-and-maybe-emit-spill:abort/disp32
-    # if not-yet-spilled-this-block?(reg, vars) emit code to spill reg
+    # var emit-spill?/edx: boolean = not-yet-spilled-this-block?(reg, vars)
     (not-yet-spilled-this-block? %ecx *(ebp+0x10))  # => eax
+    89/<- %edx 0/r32/eax
+    # if emit-spill? then emit code to spill reg
     3d/compare-eax-and 0/imm32/false
     74/jump-if-= $push-output-and-maybe-emit-spill:push/disp8
     # TODO: assert(size-of(output) == 4)
@@ -8392,11 +8413,13 @@ push-output-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (addr reg-
 $push-output-and-maybe-emit-spill:push:
     8b/-> *(ebp+0xc) 1/r32/ecx
     (lookup *(ecx+0x14) *(ecx+0x18))  # Regvardef-outputs Regvardef-outputs => eax
-    # push(vars, sv->value)
+    # push(vars, {sv->value, emit-spill?})
     (push *(ebp+0x10) *eax)  # Stmt-var-value
     (push *(ebp+0x10) *(eax+4))  # Stmt-var-value
+    (push *(ebp+0x10) %edx)
 $push-output-and-maybe-emit-spill:end:
     # . restore registers
+    5a/pop-to-edx
     59/pop-to-ecx
     58/pop-to-eax
     # . epilogue
@@ -8416,7 +8439,7 @@ $push-output-and-maybe-emit-spill:abort:
     cd/syscall  0x80/imm8
     # never gets here
 
-emit-subx-cleanup-and-unconditional-nonlocal-branch:  # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack (handle var))
+emit-subx-cleanup-and-unconditional-nonlocal-branch:  # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack live-var)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -8535,7 +8558,7 @@ Reverse-branch:  # (table (handle array byte) (handle array byte))
 
 == code
 
-emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), depth: int, label-suffix: (addr array byte)
+emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stack live-var), depth: int, label-suffix: (addr array byte)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -8549,8 +8572,8 @@ emit-unconditional-jump-to-depth:  # out: (addr buffered-file), vars: (addr stac
     8b/-> *(ebp+0xc) 1/r32/ecx
     # var eax: int = vars->top
     8b/-> *ecx 0/r32/eax
-    # var curr/esi: (addr handle var) = &vars->data[vars->top - 8]
-    8d/copy-address *(ecx+eax) 6/r32/esi  # vars + 8 + vars->top - 8
+    # var curr/esi: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(ecx+eax-4) 6/r32/esi  # vars + 8 + vars->top - 12/Live-var-size
     # var min/ecx: (addr handle var) = vars->data
     8d/copy-address *(ecx+8) 1/r32/ecx
     # edx = depth
@@ -8587,8 +8610,8 @@ $emit-unconditional-jump-to-depth:label-found:
         (write-buffered *(ebp+8) "/disp32\n")
         eb/jump $emit-unconditional-jump-to-depth:end/disp8
       }
-      # curr -= 8
-      81 5/subop/subtract %esi 8/imm32
+      # curr -= 12
+      81 5/subop/subtract %esi 0xc/imm32
       e9/jump loop/disp32
     }
     # TODO: error if no label at 'depth' was found
@@ -8606,7 +8629,7 @@ $emit-unconditional-jump-to-depth:end:
 
 # emit clean-up code for 'vars' until some block depth
 # doesn't actually modify 'vars' so we need traverse manually inside the stack
-emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-depth: int
+emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack live-var), until-block-depth: int
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -8622,8 +8645,8 @@ emit-cleanup-code-until-depth:  # out: (addr buffered-file), vars: (addr stack (
     8b/-> *(ebp+0xc) 1/r32/ecx
     # var esi: int = vars->top
     8b/-> *ecx 6/r32/esi
-    # var curr/esi: (addr handle var) = &vars->data[vars->top - 8]
-    8d/copy-address *(ecx+esi) 6/r32/esi  # vars + 8 + vars->top - 8
+    # var curr/esi: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(ecx+esi-4) 6/r32/esi  # vars + 8 + vars->top - 12/Live-var-size
     # var min/ecx: (addr handle var) = vars->data
     81 0/subop/add %ecx 8/imm32
     # edx = until-block-depth
@@ -8650,9 +8673,9 @@ $emit-cleanup-code-until-depth:loop:
         0f 84/jump-if-= break/disp32
         {
 $emit-cleanup-code-until-depth:check-for-previous-spill:
-          (same-register-spilled-before? %ebx *(ebp+0xc) %esi)  # => eax
+          8b/-> *(esi+8) 0/r32/eax  # Live-var-register-spilled
           3d/compare-eax-and 0/imm32/false
-          0f 85/jump-if-!= break/disp32
+          74/jump-if-= break/disp8
 $emit-cleanup-code-until-depth:reclaim-var-in-register:
           (emit-indent *(ebp+8) *Curr-block-depth)
           (write-buffered *(ebp+8) "8f 0/subop/pop %")
@@ -8677,8 +8700,8 @@ $emit-cleanup-code-until-depth:reclaim-var-on-stack:
         (write-buffered *(ebp+8) "/imm32\n")
       }
 $emit-cleanup-code-until-depth:continue:
-      # curr -= 8
-      81 5/subop/subtract %esi 8/imm32
+      # curr -= 12
+      81 5/subop/subtract %esi 0xc/imm32
       e9/jump loop/disp32
     }
 $emit-cleanup-code-until-depth:end:
@@ -8695,7 +8718,7 @@ $emit-cleanup-code-until-depth:end:
 
 # emit clean-up code for 'vars' until a given label is encountered
 # doesn't actually modify 'vars' so we need traverse manually inside the stack
-emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack (handle var)), until-block-label: (addr array byte)
+emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack live-var), until-block-label: (addr array byte)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -8708,8 +8731,8 @@ emit-cleanup-code-until-target:  # out: (addr buffered-file), vars: (addr stack
     8b/-> *(ebp+0xc) 1/r32/ecx
     # var eax: int = vars->top
     8b/-> *ecx 0/r32/eax
-    # var curr/edx: (addr handle var) = &vars->data[vars->top - 8]
-    8d/copy-address *(ecx+eax) 2/r32/edx  # vars + 8 + vars->top - 8
+    # var curr/edx: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(ecx+eax-4) 2/r32/edx  # vars + 8 + vars->top - 12/Live-var-size
     # var min/ecx: (addr handle var) = vars->data
     81 0/subop/add %ecx 8/imm32
     {
@@ -8731,9 +8754,9 @@ $emit-cleanup-code-until-target:loop:
         0f 84/jump-if-= break/disp32
         {
 $emit-cleanup-code-until-target:check-for-previous-spill:
-          (same-register-spilled-before? %ebx *(ebp+0xc) %edx)  # => eax
+          8b/-> *(esi+8) 0/r32/eax  # Live-var-register-spilled
           3d/compare-eax-and 0/imm32/false
-          75/jump-if-!= break/disp8
+          74/jump-if-= break/disp8
 $emit-cleanup-code-until-target:reclaim-var-in-register:
           (emit-indent *(ebp+8) *Curr-block-depth)
           (write-buffered *(ebp+8) "8f 0/subop/pop %")
@@ -8758,8 +8781,8 @@ $emit-cleanup-code-until-target:reclaim-var-on-stack:
         (write-buffered *(ebp+8) "/imm32\n")
       }
 $emit-cleanup-code-until-target:continue:
-      # curr -= 8
-      81 5/subop/subtract %edx 8/imm32
+      # curr -= 12
+      81 5/subop/subtract %edx 0xc/imm32
       e9/jump loop/disp32
     }
 $emit-cleanup-code-until-target:end:
@@ -8776,7 +8799,7 @@ $emit-cleanup-code-until-target:end:
 # Return true if there isn't a variable in 'vars' with the same block-depth
 # and register as 'v'.
 # 'v' is guaranteed not to be within 'vars'.
-not-yet-spilled-this-block?:  # v: (addr var), vars: (addr stack (handle var)) -> result/eax: boolean
+not-yet-spilled-this-block?:  # v: (addr var), vars: (addr stack live-var) -> result/eax: boolean
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -8790,8 +8813,8 @@ not-yet-spilled-this-block?:  # v: (addr var), vars: (addr stack (handle var)) -
     8b/-> *(ebp+0xc) 1/r32/ecx
     # var eax: int = vars->top
     8b/-> *ecx 0/r32/eax
-    # var curr/edx: (addr handle var) = &vars->data[vars->top - 8]
-    8d/copy-address *(ecx+eax) 2/r32/edx  # vars + 8 + vars->top - 8
+    # var curr/edx: (addr handle var) = &vars->data[vars->top - 12]
+    8d/copy-address *(ecx+eax-4) 2/r32/edx  # vars + 8 + vars->top - 12/Live-var-size
     # var min/ecx: (addr handle var) = vars->data
     8d/copy-address *(ecx+8) 1/r32/ecx
     # var depth/ebx: int = v->block-depth
@@ -8829,8 +8852,8 @@ $not-yet-spilled-this-block?:return-false:
         eb/jump $not-yet-spilled-this-block?:end/disp8
       }
 $not-yet-spilled-this-block?:continue:
-      # curr -= 8
-      81 5/subop/subtract %edx 8/imm32
+      # curr -= 12
+      81 5/subop/subtract %edx 0xc/imm32
       e9/jump loop/disp32
     }
 $not-yet-spilled-this-block?:return-true:
@@ -8848,76 +8871,8 @@ $not-yet-spilled-this-block?:end:
     5d/pop-to-ebp
     c3/return
 
-# is there a var before 'v' with the same block-depth and register on the 'vars' stack?
-# v is guaranteed to be within vars
-# 'start' is provided as an optimization, a pointer within vars
-# *start == v
-same-register-spilled-before?:  # v: (addr var), vars: (addr stack (handle var)), start: (addr var) -> result/eax: boolean
-    # . prologue
-    55/push-ebp
-    89/<- %ebp 4/r32/esp
-    # . save registers
-    51/push-ecx
-    52/push-edx
-    53/push-ebx
-    56/push-esi
-    57/push-edi
-    # ecx = v
-    8b/-> *(ebp+8) 1/r32/ecx
-    # var reg/edx: (addr array byte) = lookup(v->register)
-    (lookup *(ecx+0x18) *(ecx+0x1c))  # Var-register Var-register => eax
-    89/<- %edx 0/r32/eax
-    # var depth/ebx: int = v->block-depth
-    8b/-> *(ecx+0x10) 3/r32/ebx  # Var-block-depth
-    # var min/ecx: (addr handle var) = vars->data
-    8b/-> *(ebp+0xc) 1/r32/ecx
-    8d/copy-address *(ecx+8) 1/r32/ecx
-    # TODO: check that start >= min and start < &vars->data[top]
-    # TODO: check that *start == v
-    # var curr/esi: (addr handle var) = start
-    8b/-> *(ebp+0x10) 6/r32/esi
-    # curr -= 8
-    81 5/subop/subtract %esi 8/imm32
-    {
-$same-register-spilled-before?:loop:
-      # if (curr < min) break
-      39/compare %esi 1/r32/ecx
-      0f 82/jump-if-addr< break/disp32
-      # var x/eax: (addr var) = lookup(*curr)
-      (lookup *esi *(esi+4))  # => eax
-      # if (x->block-depth < depth) break
-      39/compare *(eax+0x10) 3/r32/ebx  # Var-block-depth
-      0f 8c/jump-if-< break/disp32
-      # if (x->register == 0) continue
-      81 7/subop/compare *(eax+0x18) 0/imm32  # Var-register
-      74/jump-if-= $same-register-spilled-before?:continue/disp8
-      # if (x->register == reg) return true
-      (lookup *(eax+0x18) *(eax+0x1c))  # Var-register Var-register => eax
-      (string-equal? %eax %edx)  # => eax
-      3d/compare-eax-and 0/imm32/false
-      b8/copy-to-eax 1/imm32/true
-      75/jump-if-!= $same-register-spilled-before?:end/disp8
-$same-register-spilled-before?:continue:
-      # curr -= 8
-      81 5/subop/subtract %esi 8/imm32
-      e9/jump loop/disp32
-    }
-$same-register-spilled-before?:false:
-    b8/copy-to-eax 0/imm32/false
-$same-register-spilled-before?:end:
-    # . restore registers
-    5f/pop-to-edi
-    5e/pop-to-esi
-    5b/pop-to-ebx
-    5a/pop-to-edx
-    59/pop-to-ecx
-    # . epilogue
-    89/<- %esp 5/r32/ebp
-    5d/pop-to-ebp
-    c3/return
-
 # clean up global state for 'vars' until some block depth
-clean-up-blocks:  # vars: (addr stack (handle var)), until-block-depth: int
+clean-up-blocks:  # vars: (addr stack live-var), until-block-depth: int
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -8934,9 +8889,18 @@ $clean-up-blocks:reclaim-loop:
       # if (vars->top <= 0) break
       8b/-> *esi 0/r32/eax  # Stack-top
       3d/compare-eax-and 0/imm32
-      7e/jump-if-<= break/disp8
-      # var v/eax: (addr var) = lookup(vars[vars->top-8])
-      (lookup *(esi+eax) *(esi+eax+4))  # vars + 8 + vars->top - 8 => eax
+      0f 8e/jump-if-<= break/disp32
+      # var v/eax: (addr var) = lookup(vars[vars->top-12])
+#?       (print-int32-buffered Stderr %eax)
+#?       (write-buffered Stderr ": ")
+#?       (print-int32-buffered Stderr *(esi+eax-4))
+#?       (write-buffered Stderr " ")
+#?       (print-int32-buffered Stderr *(esi+eax))
+#?       (write-buffered Stderr " ")
+#?       (print-int32-buffered Stderr *(esi+eax+4))
+#?       (write-buffered Stderr Newline)
+#?       (flush Stderr)
+      (lookup *(esi+eax-4) *(esi+eax))  # vars + 8 + vars->top - 12 => eax
       # if (v->block-depth < until-block-depth) break
       39/compare *(eax+0x10) 1/r32/ecx  # Var-block-depth
       7c/jump-if-< break/disp8
@@ -8950,6 +8914,7 @@ $clean-up-blocks:reclaim-var-on-stack:
       }
       (pop %esi)  # => eax
       (pop %esi)  # => eax
+      (pop %esi)  # => eax
       e9/jump loop/disp32
     }
 $clean-up-blocks:end:
@@ -9798,7 +9763,7 @@ $emit-get-offset:end:
     5d/pop-to-ebp
     c3/return
 
-emit-subx-block:  # out: (addr buffered-file), block: (addr block), vars: (addr stack (handle var))
+emit-subx-block:  # out: (addr buffered-file), block: (addr block), vars: (addr stack live-var)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -9831,11 +9796,13 @@ $emit-subx-block:check-empty:
       ff 0/subop/increment *Curr-block-depth
       (push *(ebp+0x10) *(esi+0xc))  # Block-var
       (push *(ebp+0x10) *(esi+0x10))  # Block-var
+      (push *(ebp+0x10) 0)  # false
       # emit block->statements
       (lookup *(esi+4) *(esi+8))  # Block-stmts Block-stmts => eax
       (emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
       (pop *(ebp+0x10))  # => eax
       (pop *(ebp+0x10))  # => eax
+      (pop *(ebp+0x10))  # => eax
       ff 1/subop/decrement *Curr-block-depth
       (emit-indent *(ebp+8) *Curr-block-depth)
       (write-buffered *(ebp+8) "}\n")