diff options
author | Kartik Agaram <vc@akkartik.com> | 2020-03-10 16:20:33 -0700 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2020-03-10 16:20:33 -0700 |
commit | fed9e7135c13ef8fa745afd2075eb2ff529d6241 (patch) | |
tree | 57256cf84b95791ea0592add00049b2ed02de5f5 | |
parent | 9f8524a95cb213dd83f6f3045c09357165d0f70a (diff) | |
download | mu-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-x | apps/mu | bin | 183373 -> 182906 bytes | |||
-rw-r--r-- | apps/mu.subx | 157 |
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 |