diff --git a/apps/factorial.mu b/apps/factorial.mu index 6117d153..de5bcf54 100644 --- a/apps/factorial.mu +++ b/apps/factorial.mu @@ -1,7 +1,7 @@ -# usage is finicky for now: +# usage: # ./translate_mu apps/factorial.mu -# ./a.elf test # any args? run tests -# ./a.elf # no args? run factorial(5) +# ./a.elf test # to run tests +# ./a.elf # to run factorial(5) fn main args: (addr array kernel-string) -> exit-status/ebx: int { var a/eax: (addr array kernel-string) <- copy args var tmp/ecx: int <- length a @@ -14,11 +14,15 @@ fn main args: (addr array kernel-string) -> exit-status/ebx: int { exit-status <- copy tmp break $main-body } - # if (len(args) != 1) run-tests() + # if (args[1] == "test") run-tests() + var tmp2/ecx: int <- copy 1 # we need this just because we don't yet support `index` on literals; requires some translation-time computation + var tmp3/ecx: (addr kernel-string) <- index a, tmp2 + var tmp4/eax: boolean <- kernel-string-equal? *tmp3, "test" + compare tmp4, 0 { break-if-= run-tests - exit-status <- copy 0 + exit-status <- copy 0 # TODO: get at Num-test-failures somehow } } } diff --git a/apps/mu b/apps/mu index 580da3a6..86258bcd 100755 Binary files a/apps/mu and b/apps/mu differ diff --git a/apps/mu.subx b/apps/mu.subx index b65909ad..bd3a4b96 100644 --- a/apps/mu.subx +++ b/apps/mu.subx @@ -32,8 +32,6 @@ # eax ecx edx ebx esi edi # Variables in registers must be primitive 32-bit types. # Variables not explicitly placed in a register are on the stack. -# Variables in registers need not have a name; in that case you refer to them -# directly by the register name. # # Function inputs are always passed in memory (on the stack), while outputs # are always returned in registers. @@ -955,6 +953,52 @@ test-convert-function-with-local-var-dereferenced: 5d/pop-to-ebp c3/return +test-convert-compare-register-with-literal: + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # setup + (clear-stream _test-input-stream) + (clear-stream $_test-input-buffered-file->buffer) + (clear-stream _test-output-stream) + (clear-stream $_test-output-buffered-file->buffer) + c7 0/subop/copy *Next-block-index 1/imm32 + # + (write _test-input-stream "fn foo {\n") + (write _test-input-stream " var x/ecx: int <- copy 0\n") + (write _test-input-stream " compare x, 0\n") + (write _test-input-stream "}\n") + # convert + (convert-mu _test-input-buffered-file _test-output-buffered-file) + (flush _test-output-buffered-file) +#? # dump _test-output-stream {{{ +#? (write 2 "^") +#? (write-stream 2 _test-output-stream) +#? (write 2 "$\n") +#? (rewind-stream _test-output-stream) +#? # }}} + # check output + (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-compare-register-with-literal/0") + (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-compare-register-with-literal/1") + (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-compare-register-with-literal/2") + (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-compare-register-with-literal/3") + (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-compare-register-with-literal/4") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-compare-register-with-literal/5") + (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-convert-compare-register-with-literal/6") + (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 0/imm32" "F - test-convert-compare-register-with-literal/7") + (check-next-stream-line-equal _test-output-stream " 81 7/subop/compare %ecx 0/imm32" "F - test-convert-compare-register-with-literal/8") + (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-convert-compare-register-with-literal/9") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-compare-register-with-literal/10") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-compare-register-with-literal/11") + (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-compare-register-with-literal/12") + (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-compare-register-with-literal/13") + (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-compare-register-with-literal/14") + (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-compare-register-with-literal/15") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + test-convert-function-with-local-var-in-block: # . prologue 55/push-ebp @@ -3958,11 +4002,12 @@ $add-operation-and-inputs-to-stmt:read-inouts: 3d/compare-eax-and 0x2a/imm32/asterisk { 75/jump-if-!= break/disp8 +$add-operation-and-inputs-to-stmt:inout-is-deref: ff 0/subop/increment *ecx ba/copy-to-edx 1/imm32/true } (lookup-var-or-literal %ecx *(ebp+0x10)) # => eax - (append-list Heap %eax *(edi+8)) # Stmt1-inouts or Regvardef-inouts => eax + (append-stmt-var Heap %eax *(edi+8) %edx) # Stmt1-inouts or Regvardef-inouts => eax 89/<- *(edi+8) 0/r32/eax # Stmt1-inouts or Regvardef-inouts e9/jump loop/disp32 } @@ -5467,6 +5512,38 @@ $emit-subx-stmt-list:array-length: e9/jump $emit-subx-statement:end/disp32 } # }}} + # array index {{{ + # TODO: support literal index + { + # if (!string-equal?(var->operation, "index")) break + (string-equal? *(ecx+4) "index") # Stmt1-operation => eax + 3d/compare-eax-and 0/imm32 + 0f 84/jump-if-= break/disp32 +$emit-subx-stmt-list:index: + (emit-indent *(ebp+8) *Curr-block-depth) + (write-buffered *(ebp+8) "8d/copy-address *(") + # inouts[0]->register " + " + 8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts + 8b/-> *eax 0/r32/eax # List-value + (write-buffered *(ebp+8) *(eax+0x10)) # Var-register => eax + # + (write-buffered *(ebp+8) " + ") + # inouts[1]->register + 8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts + 8b/-> *(eax+4) 0/r32/eax # List-next + 8b/-> *eax 0/r32/eax # List-value + (write-buffered *(ebp+8) *(eax+0x10)) # Var-register => eax + # + (write-buffered *(ebp+8) "<<2 + 4) ") + # outputs[0] "/r32" + 8b/-> *(ecx+0xc) 0/r32/eax # Stmt1-outputs + 8b/-> *eax 0/r32/eax # List-value + (get Registers *(eax+0x10) 8 "Registers") # Var-register => eax + (print-int32-buffered *(ebp+8) *eax) + (write-buffered *(ebp+8) "/r32\n") + e9/jump $emit-subx-statement:end/disp32 + } + # }}} # if stmt matches a primitive, emit it { $emit-subx-statement:check-for-primitive: @@ -7225,11 +7302,26 @@ emit-subx-call-operand: # out: (addr buffered-file), s: (handle stmt-var) 8b/-> *(ebp+0xc) 1/r32/ecx # var operand/esi: (handle var) = s->value 8b/-> *ecx 6/r32/esi # Stmt-var-value - # if (operand->register) emit "%__" + # if (operand->register && s->is-deref?) emit "*__" { +$emit-subx-call-operand:check-for-register-indirect: 81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register 74/jump-if-= break/disp8 -$emit-subx-call-operand:register: + 81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref + 74/jump-if-= break/disp8 +$emit-subx-call-operand:register-indirect: + (write-buffered *(ebp+8) " *") + (write-buffered *(ebp+8) *(esi+0x10)) # Var-register + e9/jump $emit-subx-call-operand:end/disp32 + } + # if (operand->register && !s->is-deref?) emit "%__" + { +$emit-subx-call-operand:check-for-register-direct: + 81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register + 74/jump-if-= break/disp8 + 81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref + 75/jump-if-!= break/disp8 +$emit-subx-call-operand:register-direct: (write-buffered *(ebp+8) " %") (write-buffered *(ebp+8) *(esi+0x10)) # Var-register e9/jump $emit-subx-call-operand:end/disp32 @@ -7278,6 +7370,7 @@ emit-subx-var-as-rm32: # out: (addr buffered-file), s: (handle stmt-var) 8b/-> *ecx 6/r32/esi # Stmt-var-value # if (operand->register && s->is-deref?) emit "*__" { +$emit-subx-var-as-rm32:check-for-register-indirect: 81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register 74/jump-if-= break/disp8 81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref @@ -7288,6 +7381,7 @@ $emit-subx-var-as-rm32:register-indirect: } # if (operand->register && !s->is-deref?) emit "%__" { +$emit-subx-var-as-rm32:check-for-register-direct: 81 7/subop/compare *(esi+0x10) 0/imm32 # Var-register 74/jump-if-= break/disp8 81 7/subop/compare *(ecx+8) 0/imm32/false # Stmt-var-is-deref diff --git a/mu_instructions b/mu_instructions index 35f9e141..017527b9 100644 --- a/mu_instructions +++ b/mu_instructions @@ -205,4 +205,14 @@ loop label {.name="loop", .inouts=[label], break {.name="break", .subx-name="e9/jump break/disp32"} break label {.name="break", .inouts=[label], .subx-name="e9/jump", .disp32=inouts[0] ":break"} +Array operations + +var/reg <- length var2/reg2: (addr array T) + {.name="length", .inouts=[reg2], .outputs=[reg1], .subx-name="8b/copy-from", .rm32="*" inouts[0], .r32=outputs[0]} +var/reg <- index arr/rega: (addr array T), idx/regi: int + {.name="index", .inouts=[rega, regi], .outputs=[reg], .subx-name="8d/copy-address", .rm32="*(" inouts[0] "+" inouts[1] "<<2)", .r32=outputs[0]} +var/reg <- index arr/rega: (addr array T), n +compare var, n {.name="compare", .inouts=[var, n], .subx-name="81 7/subop/compare", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]} + {.name="index", .inouts=[rega, n], .outputs=[reg], .subx-name="8d/copy-address", .rm32="*(" inouts[0] "+" inouts[1] "<<2)", .r32=outputs[0]} + vim:ft=c:nowrap diff --git a/mu_summary b/mu_summary index 8eafc4e3..3a93d109 100644 --- a/mu_summary +++ b/mu_summary @@ -194,3 +194,9 @@ Similarly, conditional loops: loop-if-addr<= label loop-if-addr>= loop-if-addr>= label + +## Array operations + + var/reg: int <- length var: (addr array T) + var/reg: (addr T) <- index var: (addr array T), idx: int + var/reg: (addr T) <- index var: (addr array T), n