about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-03-10 16:20:33 -0700
committerKartik Agaram <vc@akkartik.com>2020-03-10 16:20:33 -0700
commitfed9e7135c13ef8fa745afd2075eb2ff529d6241 (patch)
tree57256cf84b95791ea0592add00049b2ed02de5f5
parent9f8524a95cb213dd83f6f3045c09357165d0f70a (diff)
downloadmu-fed9e7135c13ef8fa745afd2075eb2ff529d6241.tar.gz
6116 - stack locations now computed during codegen
We can't do it during parsing time because we may not have all type definitions
available yet. Mu supports using types before defining them.

At first I thought I should do it in populate-mu-type-sizes (appropriately
renamed). But there's enough complexity to tracking when stuff lands on
the stack that it's easiest to do while emitting code.

I don't think we need this information earlier in the compiler. If I'm
right, it seems simpler to colocate the computation of state close to where
it's used.
-rwxr-xr-xapps/mubin183373 -> 182906 bytes
-rw-r--r--apps/mu.subx157
2 files changed, 91 insertions, 66 deletions
diff --git a/apps/mu b/apps/mu
index 314f2383..8c30c331 100755
--- a/apps/mu
+++ b/apps/mu
Binary files differdiff --git a/apps/mu.subx b/apps/mu.subx
index 191c94b3..4a4bde35 100644
--- a/apps/mu.subx
+++ b/apps/mu.subx
@@ -327,9 +327,9 @@ Var-name:  # (handle array byte)
   0/imm32
 Var-type:  # (handle tree type-id)
   4/imm32
-Var-block-depth:  # int
+Var-block-depth:  # int -- not available until code-generation time
   8/imm32
-Var-offset:  # int
+Var-offset:  # int -- not available until code-generation time
   0xc/imm32
 Var-register:  # (handle array byte) -- name of a register
   0x10/imm32
@@ -2748,7 +2748,6 @@ populate-mu-function-header:  # first-line: (addr stream byte), out: (handle fun
     #   next-mu-token(first-line, name)
     #   assert(name not in '{' '}' '->')
     #   out->name = slice-to-string(name)
-    #   var next-offset: int = 8
     #   ## inouts
     #   while true
     #     ## name
@@ -2758,8 +2757,6 @@ populate-mu-function-header:  # first-line: (addr stream byte), out: (handle fun
     #     assert(name != '}')
     #     var v: (handle var) = parse-var-with-type(name, first-line)
     #     assert(v->register == null)
-    #     v->stack-offset = next-offset
-    #     next-offset += size-of(v)
     #     # v->block-depth is implicitly 0
     #     out->inouts = append(out->inouts, v)
     #     push(vars, v)
@@ -2788,8 +2785,6 @@ populate-mu-function-header:  # first-line: (addr stream byte), out: (handle fun
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
-    # var next-offset/edx = 8
-    ba/copy-to-edx 8/imm32
     # read function name
     (next-mu-token *(ebp+8) %ecx)
     # error checking
@@ -2833,11 +2828,6 @@ $populate-mu-function-header:check-for-inout:
       # assert(v->register == null)
       81 7/subop/compare *(ebx+0x10) 0/imm32  # Var-register
       0f 85/jump-if-!= $populate-mu-function-header:error2/disp32
-      # v->stack-offset = next-offset
-      89/<- *(ebx+0xc) 2/r32/edx  # Var-offset
-      # next-offset += size-of(v)
-      (size-of %ebx)  # => eax
-      01/add %edx 0/r32/eax
       # v->block-depth is implicitly 0
       #
       # out->inouts = append(out->inouts, v)
@@ -4056,9 +4046,6 @@ populate-mu-function-body:  # in: (addr buffered-file), out: (handle function),
     8b/-> *(ebp+8) 6/r32/esi
     # edi = out
     8b/-> *(ebp+0xc) 7/r32/edi
-    # initialize some global state
-    c7 0/subop/copy *Curr-block-depth 1/imm32
-    c7 0/subop/copy *Next-local-stack-offset -4/imm32
     # var eax: (handle block) = parse-mu-block(in, vars, fn)
     (parse-mu-block %esi *(ebp+0x10) %edi)  # => eax
     # out->body = eax
@@ -4073,26 +4060,11 @@ $populate-mu-function-body:end:
     5d/pop-to-ebp
     c3/return
 
-== data
-
-# Global state added to each var record when parsing a function
-
-Curr-block-depth:  # (addr int)
-    0/imm32
-Next-local-stack-offset:  # (addr int)
-    -4/imm32
-
-Next-block-index:  # (addr int)
-    1/imm32
-
-== code
-
 # 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: (handle function) -> result/eax: (handle block)
     # pseudocode:
     #   var line: (stream byte 512)
     #   var word-slice: slice
-    #   increment *Curr-block-depth
     #   result/eax = allocate(Heap, Stmt-size)
     #   result->tag = 0/block
     #   result->name = some unique name
@@ -4122,7 +4094,6 @@ parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn
     #     else
     #       stmt = parse-mu-stmt(line, vars, fn)
     #       append-to-block(result, stmt)
-    #   decrement *Curr-block-depth
     #   return result
     #
     # . prologue
@@ -4154,8 +4125,6 @@ parse-mu-block:  # in: (addr buffered-file), vars: (addr stack (handle var)), fn
     89/<- *(edi+8) 0/r32/eax  # Block-var
     # push result->var to vars
     (push *(ebp+0xc) %eax)
-    # increment *Curr-block-depth
-    ff 0/subop/increment *Curr-block-depth
     {
 $parse-mu-block:line-loop:
       # line = read-line-buffered(in)
@@ -4240,8 +4209,6 @@ $parse-mu-block:regular-stmt:
       (append-to-block Heap %edi %eax)
       e9/jump loop/disp32
     } # end line loop
-    # decrement *Curr-block-depth
-    ff 1/subop/decrement *Curr-block-depth
     #
     (pop *(ebp+0xc))  # => eax
     # return result
@@ -4327,6 +4294,14 @@ $new-block-name:end:
     5d/pop-to-ebp
     c3/return
 
+== data
+
+# Global state added to each var record when parsing a function
+Next-block-index:  # (addr int)
+    1/imm32
+
+== code
+
 check-no-tokens-left:  # line: (addr stream byte)
     # . prologue
     55/push-ebp
@@ -4377,7 +4352,6 @@ $check-no-tokens-left:end:
 parse-mu-named-block:  # name: (addr slice), in: (addr buffered-file), vars: (addr stack (handle var)), fn: (handle function) -> result/eax: (handle stmt)
     # pseudocode:
     #   var v: (handle var) = new-literal(name)
-    #   v->block-depth = *Curr-block-depth  # containing block depth
     #   push(vars, v)
     #   result = parse-mu-block(in, vars, fn)
     #   pop(vars)
@@ -4427,9 +4401,6 @@ parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -
     (next-mu-token *(ebp+8) %ecx)
     (parse-var-with-type %ecx *(ebp+8))  # => eax
     89/<- %edx 0/r32/eax
-    # v->block-depth = *Curr-block-depth
-    8b/-> *Curr-block-depth 0/r32/eax
-    89/<- *(edx+8) 0/r32/eax
     #
     (push *(ebp+0xc) %edx)
     # either v has no register and there's no more to this line
@@ -4437,9 +4408,6 @@ parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -
     3d/compare-eax-and 0/imm32
     {
       75/jump-if-!= break/disp8
-      # v->stack-offset = *Next-local-stack-offset
-      8b/-> *Next-local-stack-offset 0/r32/eax
-      89/<- *(edx+0xc) 0/r32/eax  # Var-offset
       # TODO: ensure that there's nothing else on this line
       (new-var-def Heap %edx)  # => eax
       eb/jump $parse-mu-var-def:end/disp8
@@ -4457,11 +4425,6 @@ parse-mu-var-def:  # line: (addr stream byte), vars: (addr stack (handle var)) -
       (add-operation-and-inputs-to-stmt %eax *(ebp+8) *(ebp+0xc))
     }
 $parse-mu-var-def:end:
-    # *Next-local-stack-offset -= size-of(v)
-    50/push-eax
-    (size-of %edx)  # => eax
-    29/subtract-from *Next-local-stack-offset 0/r32/eax
-    58/pop-to-eax
     # . reclaim locals
     81 0/subop/add %esp 8/imm32
     # . restore registers
@@ -4494,8 +4457,6 @@ test-parse-mu-var-def:
     # setup
     (clear-stream _test-input-stream)
     (write _test-input-stream "n: int\n")  # caller has consumed the 'var'
-    c7 0/subop/copy *Curr-block-depth 1/imm32
-    c7 0/subop/copy *Next-local-stack-offset -4/imm32
     # var vars/ecx: (stack (addr var) 4)
     81 5/subop/subtract %esp 0x10/imm32
     68/push 0x10/imm32/length
@@ -4509,14 +4470,10 @@ test-parse-mu-var-def:
     8b/-> *(eax+4) 0/r32/eax  # Vardef-var
     (check-strings-equal *eax "n" "F - test-parse-mu-var-def/var-name")  # Var-name
     (check-ints-equal *(eax+0x10) 0 "F - test-parse-mu-var-def/var-register")  # Var-register
-    (check-ints-equal *(eax+8) 1 "F - test-parse-mu-reg-var-def/output-block-depth")  # Var-block-depth
-    (check-ints-equal *(eax+0xc) -4 "F - test-parse-mu-reg-var-def/output-stack-offset")  # Var-offset
     # ensure type is int
     8b/-> *(eax+4) 0/r32/eax  # Var-type
     (check-ints-equal *eax 1 "F - test-parse-mu-var-def/var-type:0")  # Tree-left
     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-var-def/var-type:0")  # Tree-right
-    # globals
-    (check-ints-equal *Next-local-stack-offset -8 "F - test-parse-mu-reg-var-def/Next-local-stack-offset")
     # . epilogue
     89/<- %esp 5/r32/ebp
     5d/pop-to-ebp
@@ -4530,8 +4487,6 @@ test-parse-mu-reg-var-def:
     # setup
     (clear-stream _test-input-stream)
     (write _test-input-stream "n/eax: int <- copy 0\n")  # caller has consumed the 'var'
-    c7 0/subop/copy *Curr-block-depth 1/imm32
-    c7 0/subop/copy *Next-local-stack-offset -4/imm32
     # var vars/ecx: (stack (addr var) 4)
     81 5/subop/subtract %esp 0x10/imm32
     68/push 0x10/imm32/length
@@ -4547,14 +4502,10 @@ test-parse-mu-reg-var-def:
     8b/-> *eax 0/r32/eax  # Stmt-var-value
     (check-strings-equal *eax "n" "F - test-parse-mu-reg-var-def/output-name")  # Var-name
     (check-strings-equal *(eax+0x10) "eax" "F - test-parse-mu-reg-var-def/output-register")  # Var-register
-    (check-ints-equal *(eax+8) 1 "F - test-parse-mu-reg-var-def/output-block-depth")  # Var-block-depth
-    (check-ints-equal *(eax+0xc) 0 "F - test-parse-mu-reg-var-def/output-stack-offset")  # Var-offset
     # ensure type is int
     8b/-> *(eax+4) 0/r32/eax  # Var-type
     (check-ints-equal *eax 1 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-left
     (check-ints-equal *(eax+4) 0 "F - test-parse-mu-reg-var-def/output-type:0")  # Tree-right
-    # globals
-    (check-ints-equal *Next-local-stack-offset -8 "F - test-parse-mu-reg-var-def/Next-local-stack-offset")
     # . epilogue
     89/<- %esp 5/r32/ebp
     5d/pop-to-ebp
@@ -5201,9 +5152,6 @@ new-literal:  # ad: (addr allocation-descriptor), name: (addr slice) -> result/e
     (new-var *(ebp+8) %ecx)  # => eax
     # result->type = type
     89/<- *(eax+4) 2/r32/edx  # Var-type
-    # result->block-depth = *Curr-block-depth
-    8b/-> *Curr-block-depth 1/r32/ecx
-    89/<- *(eax+8) 1/r32/ecx  # Var-block-depth
 $new-literal:end:
     # . restore registers
     5a/pop-to-edx
@@ -6138,6 +6086,15 @@ $type-equal?:end:
 # Code-generation
 #######################################################
 
+== data
+
+Curr-block-depth:  # (addr int)
+    0/imm32
+Next-local-stack-offset:  # (addr int)
+    -4/imm32
+
+== code
+
 emit-subx:  # out: (addr buffered-file)
     # . prologue
     55/push-ebp
@@ -6173,6 +6130,8 @@ emit-subx-function:  # out: (addr buffered-file), f: (handle function)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
+    # some preprocessing
+    (populate-mu-type-offsets-in-inouts *(ebp+0xc))
     # . save registers
     50/push-eax
     51/push-ecx
@@ -6190,12 +6149,15 @@ emit-subx-function:  # out: (addr buffered-file), f: (handle function)
     #
     (write-buffered %edi *ecx)
     (write-buffered %edi ":\n")
-    # Important: each block's depth during code-generation should be identical
-    # to what it was during parsing.
+    # initialize some global state
     c7 0/subop/copy *Curr-block-depth 1/imm32
+    c7 0/subop/copy *Next-local-stack-offset -4/imm32
+    #
     (emit-subx-prologue %edi)
     (emit-subx-block %edi *(ecx+0x10) %edx)  # Function-body
     (emit-subx-epilogue %edi)
+    # TODO: validate that *Curr-block-depth and *Next-local-stack-offset have
+    # been cleaned up
 $emit-subx-function:end:
     # . reclaim locals
     81 0/subop/add %esp 408/imm32
@@ -6209,6 +6171,48 @@ $emit-subx-function:end:
     5d/pop-to-ebp
     c3/return
 
+populate-mu-type-offsets-in-inouts:  # f: (handle function)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    57/push-edi
+    # var next-offset/edx: int = 8
+    ba/copy-to-edx 8/imm32
+    # var curr/ecx: (handle list var) = f->inouts
+    8b/-> *(ebp+8) 1/r32/ecx
+    8b/-> *(ecx+8) 1/r32/ecx  # Function-inouts
+    {
+$populate-mu-type-offsets-in-inouts:loop:
+      81 7/subop/compare %ecx 0/imm32
+      74/jump-if-= break/disp8
+      # var v/ebx: (handle var) = curr->value
+      8b/-> *ecx 3/r32/ebx  # List-value
+      # v->offset = next-offset
+      89/<- *(ebx+0xc) 2/r32/edx  # Var-offset
+      # next-offset += size-of(v)
+      (size-of %ebx)  # => eax
+      01/add %edx 0/r32/eax
+      # curr = curr->next
+      8b/-> *(ecx+4) 1/r32/ecx  # List-next
+      eb/jump loop/disp8
+    }
+$populate-mu-type-offsets-in-inouts:end:
+    # . restore registers
+    5f/pop-to-edi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
 emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (handle list stmt), vars: (addr stack (handle var))
     # . prologue
     55/push-ebp
@@ -6408,6 +6412,9 @@ compute-reg-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (handle re
     # var output/ecx: (handle var) = curr-stmt->outputs->value
     8b/-> *(ecx+0xc) 1/r32/ecx  # Regvardef-inouts
     8b/-> *ecx 1/r32/ecx  # List-value
+    # v->block-depth = *Curr-block-depth
+    8b/-> *Curr-block-depth 0/r32/eax
+    89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
     # var reg/eax: (handle array byte) = output->register
     8b/-> *(ecx+0x10) 0/r32/eax  # Var-register
     # ensure that output is in a register
@@ -6417,6 +6424,9 @@ compute-reg-and-maybe-emit-spill:  # out: (addr buffered-file), stmt: (handle re
     (already-spilled-this-block? %ecx *(ebp+0x10))  # => eax
     3d/compare-eax-and 0/imm32/false
     75/jump-if-!= $compute-reg-and-maybe-emit-spill:end/disp8
+    # TODO: assert(sizeof(output) == 4)
+    # *Next-local-stack-offset -= 4
+    81 5/subop/subtract *Next-local-stack-offset 4/imm32
     # emit spill
     (emit-indent *(ebp+8) *Curr-block-depth)
     (write-buffered *(ebp+8) "ff 6/subop/push %")
@@ -6981,8 +6991,18 @@ emit-subx-var-def:  # out: (addr buffered-file), stmt: (handle stmt)
     51/push-ecx
     # eax = stmt
     8b/-> *(ebp+0xc) 0/r32/eax
+    # var v/ecx: (handle var)
+    8b/-> *(eax+4) 1/r32/ecx
+    # v->offset = *Next-local-stack-offset
+    8b/-> *Next-local-stack-offset 0/r32/eax
+    89/<- *(ecx+0xc) 0/r32/eax  # Var-offset
+    # v->block-depth = *Curr-block-depth
+    8b/-> *Curr-block-depth 0/r32/eax
+    89/<- *(ecx+8) 0/r32/eax  # Var-block-depth
     # var n/eax: int = size-of(stmt->var)
-    (size-of *(eax+4))  # Vardef-var => eax
+    (size-of %ecx)  # Vardef-var => eax
+    # *Next-local-stack-offset -= n
+    29/subtract-from *Next-local-stack-offset 0/r32/eax
     # while n > 0
     {
       3d/compare-eax-with 0/imm32
@@ -7421,6 +7441,10 @@ emit-subx-block:  # out: (addr buffered-file), block: (handle block), vars: (add
     56/push-esi
     # esi = block
     8b/-> *(ebp+0xc) 6/r32/esi
+    # block->var->block-depth = *Curr-block-depth
+    8b/-> *(esi+8) 0/r32/eax  # Block-var
+    8b/-> *Curr-block-depth 1/r32/ecx
+    89/<- *(eax+8) 1/r32/ecx  # Var-block-depth
     # var stmts/eax: (handle list stmt) = block->statements
     8b/-> *(esi+4) 0/r32/eax  # Block-stmts
     #
@@ -7430,8 +7454,9 @@ $emit-subx-block:check-empty:
       0f 84/jump-if-= break/disp32
       (emit-indent *(ebp+8) *Curr-block-depth)
       (write-buffered *(ebp+8) "{\n")
-      # var v/ecx: (addr array byte) = block->var->name
+      # var v/ecx: (handle var)
       8b/-> *(esi+8) 1/r32/ecx  # Block-var
+      #
       (write-buffered *(ebp+8) *ecx)  # Var-name
       (write-buffered *(ebp+8) ":loop:\n")
       ff 0/subop/increment *Curr-block-depth