about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-02-21 10:05:04 -0800
committerKartik Agaram <vc@akkartik.com>2020-02-21 10:09:59 -0800
commitfee1bbd8b4681acca7daba1b3b70ce6d26e52fb4 (patch)
treeceae46b4b130d02eac6de7cc2379ebcd61bf9564
parent1837f6117632c80214f45ed94a0d3406810e431f (diff)
downloadmu-fee1bbd8b4681acca7daba1b3b70ce6d26e52fb4.tar.gz
6041 - array indexing starting to work
And we're using it now in factorial.mu!

In the process I had to fix a couple of bugs in pointer dereferencing.

There are still some limitations:
a) Indexing by a literal doesn't work yet.
b) Only arrays of ints supported so far.

Looking ahead, I'm not sure how I can support indexing arrays by non-literals
(variables in registers) unless the element size is a power of 2.
-rw-r--r--apps/factorial.mu14
-rwxr-xr-xapps/mubin149979 -> 152298 bytes
-rw-r--r--apps/mu.subx104
-rw-r--r--mu_instructions10
-rw-r--r--mu_summary6
5 files changed, 124 insertions, 10 deletions
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
--- a/apps/mu
+++ b/apps/mu
Binary files differdiff --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
+      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
-$emit-subx-call-operand:register:
+      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