diff options
-rw-r--r-- | apps/mu.subx | 215 |
1 files changed, 177 insertions, 38 deletions
diff --git a/apps/mu.subx b/apps/mu.subx index e4b8604b..f01b50cf 100644 --- a/apps/mu.subx +++ b/apps/mu.subx @@ -640,9 +640,11 @@ parse-mu: # in : (address buffered-file) # else if slice-starts-with?(word-slice, "#") # comment # continue # end of line # else if slice-equal(word-slice, "fn") - # var new-function : (handle function) = new function - # populate-mu-function-header(in, new-function) - # populate-mu-function-body(in, new-function) + # var new-function : (handle function) = allocate(function) + # var vars : (ref stack (address var) 256) + # populate-mu-function-header(in, new-function, vars) + # populate-mu-function-body(in, new-function, vars) + # assert(vars->top == 0) # *curr-function = new-function # curr-function = &new-function->next # else @@ -655,6 +657,7 @@ parse-mu: # in : (address buffered-file) 50/push-eax 51/push-ecx 52/push-edx + 53/push-ebx 57/push-edi # var line/ecx : (ref stream byte 512) 81 5/subop/subtract %esp 0x200/imm32 @@ -668,6 +671,11 @@ parse-mu: # in : (address buffered-file) 89/<- %edx 4/r32/esp # var curr-function/edi : (handle function) = Program bf/copy-to-edi Program/imm32 + # var vars/ebx : (ref stack (address var) 256) + 81 5/subop/subtract %esp 0x400/imm32 + 68/push 0x400/imm32/length + 68/push 0/imm32/top + 89/<- %ebx 4/r32/esp { $parse-mu:line-loop: (clear-stream %ecx) @@ -700,10 +708,14 @@ $parse-mu:fn: (slice-equal? %edx "fn") 3d/compare-eax-and 0/imm32 0f 84/jump-if-equal break/disp32 - # var new-function/eax : (handle function) = populate-mu-function() + # var new-function/eax : (handle function) = populate-mu-function(in, new-function, vars) (allocate Heap *Function-size) # => eax - (populate-mu-function-header %ecx %eax) - (populate-mu-function-body *(ebp+8) %eax) + (zero-out %eax *Function-size) + (populate-mu-function-header %ecx %eax %ebx) + (populate-mu-function-body *(ebp+8) %eax %ebx) + # assert(vars->top == 0) + 81 7/subop/compare *ebx 0/imm32 + 75/jump-if-not-equal $parse-mu:error2/disp8 # *curr-function = new-function 89/<- *edi 0/r32/eax # curr-function = &new-function->next @@ -711,13 +723,14 @@ $parse-mu:fn: e9/jump $parse-mu:line-loop/disp32 } # otherwise abort - e9/jump $parse-mu:abort/disp32 + e9/jump $parse-mu:error1/disp32 } # end line loop $parse-mu:end: # . reclaim locals - 81 0/subop/add %esp 0x214/imm32 + 81 0/subop/add %esp 0x630/imm32 # . restore registers 5f/pop-to-edi + 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax @@ -726,7 +739,7 @@ $parse-mu:end: 5d/pop-to-ebp c3/return -$parse-mu:abort: +$parse-mu:error1: # error("unexpected top-level command: " word-slice "\n") (write-buffered Stderr "unexpected top-level command: ") (write-slice-buffered Stderr %edx) @@ -738,6 +751,19 @@ $parse-mu:abort: cd/syscall 0x80/imm8 # never gets here +$parse-mu:error2: + # error(vars->top " vars not reclaimed after fn '" new-function->name "'\n") + (print-int32-buffered Stderr *ebx) + (write-buffered Stderr " vars not reclaimed after fn '") + (write-slice-buffered Stderr *eax) # Function-name + (write-buffered Stderr "'\n") + (flush Stderr) + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + # scenarios considered: # ✗ fn foo # no block # ✓ fn foo { @@ -749,12 +775,13 @@ $parse-mu:abort: # ✓ fn foo x : int { # ✓ fn foo x: int { # ✓ fn foo x: int -> y/eax: int { -populate-mu-function-header: # first-line : (address stream byte), out : (handle function) +populate-mu-function-header: # first-line : (address stream byte), out : (handle function), vars : (address stack (address var)) # pseudocode: # var name : (ref slice) # next-word(first-line, name) # assert(name not in '{' '}' '->') # out->name = slice-to-string(name) + # var next-offset : int = 8 # ## inouts # while true # ## name @@ -763,6 +790,10 @@ populate-mu-function-header: # first-line : (address stream byte), out : (handl # if (name == '->') break # 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(var) + # push(vars, var) # out->inouts = append(out->inouts, v) # ## outputs # while true @@ -770,6 +801,7 @@ populate-mu-function-header: # first-line : (address stream byte), out : (handl # name = next-word(first-line) # assert(name not in '{' '}' '->') # var v : (handle var) = parse-var-with-type(name, first-line) + # assert(v->register != null) # out->outputs = append(out->outputs, v) # done: # @@ -779,6 +811,8 @@ populate-mu-function-header: # first-line : (address stream byte), out : (handl # . save registers 50/push-eax 51/push-ecx + 52/push-edx + 53/push-ebx 57/push-edi # edi = out 8b/-> *(ebp+0xc) 7/r32/edi @@ -786,26 +820,29 @@ populate-mu-function-header: # first-line : (address stream byte), out : (handl 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-word *(ebp+8) %ecx) # error checking # if (word-slice == '{') abort (slice-equal? %ecx "{") # => eax 3d/compare-eax-and 0/imm32 - 0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32 + 0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32 # if (word-slice == '->') abort (slice-equal? %ecx "->") # => eax 3d/compare-eax-and 0/imm32 - 0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32 + 0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32 # if (word-slice == '}') abort (slice-equal? %ecx "}") # => eax 3d/compare-eax-and 0/imm32 - 0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32 + 0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32 # save function name (slice-to-string Heap %ecx) # => eax 89/<- *edi 0/r32/eax # Function-name # save function inouts { +$populate-mu-function-header:check-for-inout: (next-word *(ebp+8) %ecx) # if (word-slice == '{') goto done (slice-equal? %ecx "{") # => eax @@ -818,31 +855,47 @@ populate-mu-function-header: # first-line : (address stream byte), out : (handl # if (word-slice == '}') abort (slice-equal? %ecx "}") # => eax 3d/compare-eax-and 0/imm32 - 0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32 + 0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32 + # var var/ebx : (handle var) = parse-var-with-type(word-slice, first-line) + (parse-var-with-type %ecx *(ebp+8)) # => eax + 89/<- %ebx 0/r32/eax + # assert(var->register == null) + 81 7/subop/compare *(ebx+0x10) 0/imm32 # Var-register + 0f 85/jump-if-not-equal $populate-mu-function-header:error2/disp32 + # var->stack-offset = next-offset + 89/<- *(ebx+0xc) 2/r32/edx # Var-stack-offset + # next-offset += size-of(var) + (size-of %ebx) # => eax + 01/add %edx 0/r32/eax # - (parse-var-with-type %ecx *(ebp+8)) - (append-list Heap %eax *(edi+8)) # Function-inouts => eax + (append-list Heap %ebx *(edi+8)) # Function-inouts => eax 89/<- *(edi+8) 0/r32/eax # Function-inouts + # e9/jump loop/disp32 } # save function outputs { +$parse-var-with-type:check-for-out: (next-word *(ebp+8) %ecx) # if (word-slice == '{') break (slice-equal? %ecx "{") # => eax 3d/compare-eax-and 0/imm32 - 75/jump-if-not-equal break/disp8 + 0f 85/jump-if-not-equal break/disp32 # if (word-slice == '->') abort (slice-equal? %ecx "->") # => eax 3d/compare-eax-and 0/imm32 - 0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32 + 0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32 # if (word-slice == '}') abort (slice-equal? %ecx "}") # => eax 3d/compare-eax-and 0/imm32 - 0f 85/jump-if-not-equal $populate-mu-function-header:abort/disp32 + 0f 85/jump-if-not-equal $populate-mu-function-header:error1/disp32 # - (parse-var-with-type %ecx *(ebp+8)) - (append-list Heap %eax *(edi+0xc)) # Function-outputs + (parse-var-with-type %ecx *(ebp+8)) # => eax + 89/<- %ebx 0/r32/eax + # assert(var->register != null) + 81 7/subop/compare *(ebx+0x10) 0/imm32 # Var-register + 0f 84/jump-if-equal $populate-mu-function-header:error3/disp32 + (append-list Heap %ebx *(edi+0xc)) # Function-outputs => eax 89/<- *(edi+0xc) 0/r32/eax # Function-outputs e9/jump loop/disp32 } @@ -853,6 +906,8 @@ $populate-mu-function-header:end: 81 0/subop/add %esp 8/imm32 # . restore registers 5f/pop-to-edi + 5b/pop-to-ebx + 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilogue @@ -860,7 +915,7 @@ $populate-mu-function-header:end: 5d/pop-to-ebp c3/return -$populate-mu-function-header:abort: +$populate-mu-function-header:error1: # error("function header not in form 'fn <name> {'") (write-buffered Stderr "function header not in form 'fn <name> [inouts] [-> outputs] {' -- '") (flush Stderr) @@ -874,6 +929,34 @@ $populate-mu-function-header:abort: cd/syscall 0x80/imm8 # never gets here +$populate-mu-function-header:error2: + # error("function input '" var "' cannot be in a register") + (write-buffered Stderr "function input '") + (write-buffered Stderr *ebx) # Var-name + (write-buffered Stderr "' cannot be in a register") + (flush Stderr) + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +$populate-mu-function-header:error3: + # error("function input '" var "' must be in a register") + (write-buffered Stderr "function input '") + (write-buffered Stderr *eax) # Var-name + (write-buffered Stderr " must be in a register'") + (flush Stderr) + (rewind-stream *(ebp+8)) + (write-stream 2 *(ebp+8)) + (write-buffered Stderr "'\n") + (flush Stderr) + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + test-function-header-with-arg: # 'foo n : int {' # . prologue @@ -919,22 +1002,25 @@ test-function-header-with-multiple-args: (check-strings-equal *ecx "foo") # Function-name # edx : (handle list var) = result->inouts 8b/-> *(ecx+8) 2/r32/edx # Function-inouts +$test-function-header-with-multiple-args:inout0: # ebx : (handle var) = result->inouts->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0") # Var-name - (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-arg/inout:0/type") # Var-type + (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args/inout:0/type") # Var-type # edx = result->inouts->next 8b/-> *(edx+4) 2/r32/edx # List-next +$test-function-header-with-multiple-args:inout1: # ebx = result->inouts->next->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "b" "F - test-function-header-with-multiple-args/inout:1") # Var-name - (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-arg/inout:1/type") # Var-type + (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args/inout:1/type") # Var-type # edx = result->inouts->next->next 8b/-> *(edx+4) 2/r32/edx # List-next +$test-function-header-with-multiple-args:inout2: # ebx = result->inouts->next->next->value 8b/-> *edx 3/r32/ebx # List-value (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2") # Var-name - (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-arg/inout:2/type") # Var-type + (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-multiple-args/inout:2/type") # Var-type # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp @@ -1038,7 +1124,8 @@ parse-var-with-type: # name: (address slice), first-line: (address stream byte) 56/push-esi 57/push-edi # var result/edi : (handle var) = allocate(Heap, Var-size) - (allocate Heap *Var-size) + (allocate Heap *Var-size) # => eax + (zero-out %eax *Var-size) 89/<- %edi 0/r32/eax # esi = name 8b/-> *(ebp+8) 6/r32/esi @@ -1046,6 +1133,7 @@ parse-var-with-type: # name: (address slice), first-line: (address stream byte) 68/push 0/imm32/end 68/push 0/imm32/start 89/<- %ecx 4/r32/esp +$parse-var-with-type:save-name: # save v->name (next-token-from-slice *esi *(esi+4) 0x2f %ecx) # Slice-start, Slice-end, '/' # . end/edx = s->end @@ -1070,9 +1158,11 @@ parse-var-with-type: # name: (address slice), first-line: (address stream byte) 75/jump-if-not-equal break/disp8 89/<- *(ecx+4) 0/r32/eax } +$parse-var-with-type:write-name: (slice-to-string Heap %ecx) # => eax 89/<- *edi 0/r32/eax # Var-name # save v->register +$parse-var-with-type:save-register: (next-token-from-slice %edx *(esi+4) 0x2f %ecx) # end, name->end, '/' # . if s ends with ':', decrement s->end { @@ -1096,9 +1186,12 @@ parse-var-with-type: # name: (address slice), first-line: (address stream byte) } # if (!slice-empty?(s)) v->register = slice-to-string(s) { - (slice-empty? %ecx) - 3d/compare-eax-and 0/imm32 - 75/jump-if-not-equal break/disp8 +$parse-var-with-type:write-register: + # HACK: s->end can be less than s->start with all the decrements above + # That's probably a sign we have the wrong algorithm for this function. + 8b/-> *ecx 0/r32/eax + 39/compare 0/r32/eax *(ecx+4) + 76/jump-if-lesser-or-equal break/disp8 (slice-to-string Heap %ecx) 89/<- *(edi+0x10) 0/r32/eax # Var-register } @@ -1285,8 +1378,8 @@ test-parse-var-with-trailing-characters: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp - # (eax..ecx) = "x/eax:" - b8/copy-to-eax "x/eax:"/imm32 + # (eax..ecx) = "x:" + b8/copy-to-eax "x:"/imm32 8b/-> *eax 1/r32/ecx 8d/copy-address *(eax+ecx+4) 1/r32/ecx 05/add-to-eax 4/imm32 @@ -1302,7 +1395,7 @@ test-parse-var-with-trailing-characters: 8b/-> *eax 2/r32/edx # Var-name (check-strings-equal %edx "x" "F - test-var-with-trailing-characters/name") 8b/-> *(eax+0x10) 2/r32/edx # Var-register - (check-strings-equal %edx "eax" "F - test-var-with-trailing-characters/register") + (check-ints-equal %edx 0 "F - test-var-with-trailing-characters/register") 8b/-> *(eax+4) 2/r32/edx # Var-type (check-ints-equal %edx 1 "F - test-var-with-trailing-characters/type") # . epilogue @@ -1310,6 +1403,35 @@ test-parse-var-with-trailing-characters: 5d/pop-to-ebp c3/return +test-parse-var-with-register-and-trailing-characters: + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # (eax..ecx) = "x/eax:" + b8/copy-to-eax "x/eax:"/imm32 + 8b/-> *eax 1/r32/ecx + 8d/copy-address *(eax+ecx+4) 1/r32/ecx + 05/add-to-eax 4/imm32 + # var slice/ecx : (ref slice) = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/<- %ecx 4/r32/esp + # _test-input-stream contains "int," + (clear-stream _test-input-stream) + (write _test-input-stream "int,") + # + (parse-var-with-type %ecx _test-input-stream) + 8b/-> *eax 2/r32/edx # Var-name + (check-strings-equal %edx "x" "F - test-var-with-register-and-trailing-characters/name") + 8b/-> *(eax+0x10) 2/r32/edx # Var-register + (check-strings-equal %edx "eax" "F - test-var-with-register-and-trailing-characters/register") + 8b/-> *(eax+4) 2/r32/edx # Var-type + (check-ints-equal %edx 1 "F - test-var-with-register-and-trailing-characters/type") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + # identifier starts with a letter or '$' or '_' # no constraints at the moment on later letters # all we really want to do so far is exclude '{', '}' and '->' @@ -1675,7 +1797,6 @@ parse-mu-block: # in : (address buffered-file) -> result/eax : (handle block) 51/push-ecx 52/push-edx 53/push-ebx - 56/push-esi 57/push-edi # var line/ecx : (ref stream byte 512) 81 5/subop/subtract %esp 0x200/imm32 @@ -1689,6 +1810,7 @@ parse-mu-block: # in : (address buffered-file) -> result/eax : (handle block) 89/<- %edx 4/r32/esp # edi = result (allocate Heap *Stmt-size) # => eax + (zero-out %eax *Stmt-size) 89/<- %edi 0/r32/eax { # line loop $parse-mu-block:line-loop: @@ -1776,7 +1898,6 @@ $parse-mu-block:end: 81 0/subop/add %esp 0x214/imm32 # . restore registers 5f/pop-to-edi - 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx @@ -1933,7 +2054,8 @@ parse-mu-stmt: # line : (address stream byte) -> result/eax : (handle stmt) 68/push 0/imm32/start 89/<- %ecx 4/r32/esp # result/edi : (handle stmt) - (allocate Heap *Stmt-size) + (allocate Heap *Stmt-size) # => eax + (zero-out %eax *Stmt-size) 89/<- %edi 0/r32/eax # result->tag = 1/stmt c7 0/subop/copy *edi 1/imm32/stmt1 # Stmt-tag @@ -2081,11 +2203,10 @@ parse-var: # ad: allocation-descriptor, name: (address slice) -> result/eax: (h (slice-to-string Heap %ecx) # => eax 89/<- %ecx 0/r32/eax (allocate *(ebp+8) *Var-size) # => eax + (zero-out %eax *Var-size) 89/<- *eax 1/r32/ecx # Var-name # var->type = int c7 0/subop/copy *(eax+4) 1/imm32/int-type # Var-type - # var->stack-offset = 8 - c7 0/subop/copy *(eax+0xc) 8/imm32 # Var-stack-offset $parse-var:end: # . restore registers 59/pop-to-ecx @@ -2178,6 +2299,7 @@ new-block: # ad: allocation-descriptor, data: (handle list statement) -> result 51/push-ecx # (allocate *(ebp+8) *Stmt-size) # => eax + (zero-out %eax *Stmt-size) c7 0/subop/copy *eax 0/imm32/tag/block # Stmt-tag 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *(eax+4) 1/r32/ecx # Block-statements @@ -2197,6 +2319,7 @@ new-stmt: # ad: allocation-descriptor, operation: string, inouts: (handle list 51/push-ecx # (allocate *(ebp+8) *Stmt-size) # => eax + (zero-out %eax *Stmt-size) c7 0/subop/copy *eax 1/imm32/tag/regular-stmt # Stmt-tag 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *(eax+4) 1/r32/ecx # Stmt1-operation @@ -2220,6 +2343,7 @@ new-vardef: # ad: allocation-descriptor, name: string, type: int -> result/eax: 51/push-ecx # (allocate *(ebp+8) *Stmt-size) # => eax + (zero-out %eax *Stmt-size) c7 0/subop/copy *eax 2/imm32/tag/var-on-stack # Stmt-tag 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *(eax+4) 1/r32/ecx # Vardef-name @@ -2241,6 +2365,7 @@ new-regvardef: # ad: allocation-descriptor, name: string, type: int, register: 51/push-ecx # (allocate *(ebp+8) *Stmt-size) # => eax + (zero-out %eax *Stmt-size) c7 0/subop/copy *eax 3/imm32/tag/var-in-register 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *(eax+4) 1/r32/ecx # Regvardef-name @@ -2264,6 +2389,7 @@ new-named-block: # ad: allocation-descriptor, name: string, data: (handle list 51/push-ecx # (allocate *(ebp+8) *Stmt-size) # => eax + (zero-out %eax *Stmt-size) c7 0/subop/copy *eax 4/imm32/tag/named-block 8b/-> *(ebp+0xc) 1/r32/ecx 89/<- *(eax+4) 1/r32/ecx # Named-block-name @@ -2366,6 +2492,18 @@ $check-mu-types:end: 5d/pop-to-ebp c3/return +size-of: # n : (address var) + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # hard-coded since we only support 'int' types for now + b8/copy-to-eax 4/imm32 +$size-of:end: + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + ####################################################### # Code-generation ####################################################### @@ -2438,11 +2576,12 @@ emit-subx-block: # out : (address buffered-file), block : (handle block) 8b/-> *(esi+4) 6/r32/esi # Block-statements # { -$emit-subx-block:stmt: +$emit-subx-block:check-empty: 81 7/subop/compare %esi 0/imm32 0f 84/jump-if-equal break/disp32 (write-buffered *(ebp+8) "{\n") { +$emit-subx-block:stmt: 81 7/subop/compare %esi 0/imm32 74/jump-if-equal break/disp8 (emit-subx-statement *(ebp+8) *esi 0 Primitives 0) # TODO: initialize vars and functions |