about summary refs log tree commit diff stats
path: root/apps/mu.subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-11-17 20:54:39 -0800
committerKartik Agaram <vc@akkartik.com>2019-11-17 21:01:36 -0800
commit3b40e3c331312c78d252fab02bd5f8dd53ce59f4 (patch)
tree8fe5a22f39517e945716ee3dcfd7940b30140763 /apps/mu.subx
parenteafdbfc103f3e2d539efa31244b2b2c768877775 (diff)
downloadmu-3b40e3c331312c78d252fab02bd5f8dd53ce59f4.tar.gz
5750
We can now compile primitive statements while selecting the right template
to code-gen each one from. Even when multiple templates have the same name.
Diffstat (limited to 'apps/mu.subx')
-rw-r--r--apps/mu.subx436
1 files changed, 411 insertions, 25 deletions
diff --git a/apps/mu.subx b/apps/mu.subx
index ddac7bb8..9a54e06e 100644
--- a/apps/mu.subx
+++ b/apps/mu.subx
@@ -1086,19 +1086,6 @@ $emit-subx-imm32:end:
     5d/pop-to-ebp
     c3/return
 
-#?     # var args/ecx : (list var) = stmt->inouts
-#?     8b/-> *(ebp+0xc) 1/r32/ecx
-#?     8b/-> *(ecx+4) 1/r32/ecx  # Stmt-inouts
-#?     {
-#?       # if (curr == null) break
-#?       81 7/subop/compare %ecx 0/imm32
-#?       74/jump-if-equal break/disp8
-#?       #
-#?       (emit-subx-call-operand *(ebp+8) *ecx)
-#?       # args = args->next
-#?       8b/-> *(ecx+4) 1/r32/ecx
-#?     }
-
 emit-subx-call:  # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function)
     # . prologue
     55/push-ebp
@@ -1213,6 +1200,7 @@ find-matching-primitive:  # primitives : (address primitive), stmt : (address st
     # var curr/ecx : (address primitive) = primitives
     8b/-> *(ebp+8) 1/r32/ecx
     {
+$find-matching-primitive:loop:
       # if (curr == null) break
       81 7/subop/compare %ecx 0/imm32
       74/jump-if-equal break/disp8
@@ -1224,8 +1212,9 @@ find-matching-primitive:  # primitives : (address primitive), stmt : (address st
         89/<- %eax 1/r32/ecx
         eb/jump $find-matching-function:end/disp8
       }
+$find-matching-primitive:next-primitive:
       # curr = curr->next
-      8b/-> *(ecx+0x18) 1/r32/ecx  # Primitive-next
+      8b/-> *(ecx+0x1c) 1/r32/ecx  # Primitive-next
       eb/jump loop/disp8
     }
     # return null
@@ -1257,23 +1246,219 @@ $mu-stmt-matches-function?:end:
     c3/return
 
 mu-stmt-matches-primitive?:  # stmt : (address statement), primitive : (address primitive) => result/eax : boolean
+    # A mu stmt matches a primitive if the name matches, all the inout vars
+    # match, and all the output vars match.
+    # Vars match if types match and registers match.
+    # In addition, a stmt output matches a primitive's output if types match
+    # and the primitive has a wildcard register.
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
     # . save registers
     51/push-ecx
-    # return primitive->name == stmt->operation
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # ecx = stmt
     8b/-> *(ebp+8) 1/r32/ecx
-    8b/-> *(ebp+0xc) 0/r32/eax
-    (string-equal? *ecx *eax)  # => eax
+    # edx = primitive
+    8b/-> *(ebp+0xc) 2/r32/edx
+    {
+$mu-stmt-matches-primitive?:check-name:
+      # if (primitive->name != stmt->operation) return false
+      (string-equal? *ecx *edx)  # => eax
+      3d/compare-eax-and 0/imm32
+      75/jump-if-not-equal break/disp8
+      b8/copy-to-eax 0/imm32
+      e9/jump $mu-stmt-matches-primitive?:end/disp32
+    }
+$mu-stmt-matches-primitive?:check-inouts:
+    # curr = stmt->inouts
+    8b/-> *(ecx+4) 6/r32/esi  # Stmt-inouts
+    # curr2 = primitive->inouts
+    8b/-> *(edx+4) 7/r32/edi  # Primitive-inouts
+    {
+      # if (curr == 0) return (curr2 == 0)
+      {
+        81 7/subop/compare %esi 0/imm32
+        75/jump-if-not-equal break/disp8
+        {
+          81 7/subop/compare %edi 0/imm32
+          75/jump-if-not-equal break/disp8
+          # return true
+          b8/copy-to-eax 1/imm32
+          e9/jump $mu-stmt-matches-primitive?:end/disp32
+        }
+        # return false
+        b8/copy-to-eax 0/imm32
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+      # if (curr2 == 0) return false
+      {
+        81 7/subop/compare %edi 0/imm32
+        75/jump-if-not-equal break/disp8
+        b8/copy-to-eax 0/imm32
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+      # if (curr != curr2) return false
+      {
+        (operand-matches-primitive? *esi *edi)  # => eax
+        3d/compare-eax-and 0/imm32
+        75/jump-if-not-equal break/disp8
+        b8/copy-to-eax 0/imm32
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+      # curr=curr->next
+      8b/-> *(ecx+4) 1/r32/ecx  # Operand-next
+      # curr2=curr2->next
+      8b/-> *(edx+4) 2/r32/edx  # Operand-next
+    }
+$mu-stmt-matches-primitive?:check-outputs:
+    # ecx = stmt
+    8b/-> *(ebp+8) 1/r32/ecx
+    # edx = primitive
+    8b/-> *(ebp+0xc) 2/r32/edx
+    # curr = stmt->outputs
+    8b/-> *(ecx+8) 6/r32/esi  # Stmt-outputs
+    # curr2 = primitive->outputs
+    8b/-> *(edx+8) 7/r32/edi  # Primitive-outputs
+    {
+      # if (curr == 0) return (curr2 == 0)
+      {
+        81 7/subop/compare %esi 0/imm32
+        75/jump-if-not-equal break/disp8
+        {
+          81 7/subop/compare %edi 0/imm32
+          75/jump-if-not-equal break/disp8
+          # return true
+          b8/copy-to-eax 1/imm32
+          e9/jump $mu-stmt-matches-primitive?:end/disp32
+        }
+        # return false
+        b8/copy-to-eax 0/imm32
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+      # if (curr2 == 0) return false
+      {
+        81 7/subop/compare %edi 0/imm32
+        75/jump-if-not-equal break/disp8
+        b8/copy-to-eax 0/imm32
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+      # if (curr != curr2) return false
+      {
+        (output-operand-matches-primitive? *esi *edi)  # => eax
+        3d/compare-eax-and 0/imm32
+        75/jump-if-not-equal break/disp8
+        b8/copy-to-eax 0/imm32
+        e9/jump $mu-stmt-matches-primitive?:end/disp32
+      }
+      # curr=curr->next
+      8b/-> *(ecx+4) 1/r32/ecx  # Operand-next
+      # curr2=curr2->next
+      8b/-> *(edx+4) 2/r32/edx  # Operand-next
+    }
+$mu-stmt-matches-primitive?:return-true:
+    b8/copy-to-eax 1/imm32
 $mu-stmt-matches-primitive?:end:
     # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
     59/pop-to-ecx
     # . epilogue
     89/<- %esp 5/r32/ebp
     5d/pop-to-ebp
     c3/return
 
+operand-matches-primitive?:  # var1 : (address var), var2 : (address var) => result/eax : boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    56/push-esi
+    57/push-edi
+    # esi = var1
+    8b/-> *(ebp+8) 6/r32/esi
+    # edi = var2
+    8b/-> *(ebp+0xc) 7/r32/edi
+    # if (var1->type != var2->type) return false
+    # if (var1->register != var1->register) return false
+    {
+      # if addresses are equal, don't return here
+      8b/-> *(esi+0x10) 0/r32/eax
+      39/compare *(edi+0x10) 0/r32/eax
+      74/jump-if-equal break/disp8
+      # if either address is 0, return false
+      3d/compare-eax-and 0/imm32
+      74/jump-if-equal  $operand-matches-primitive?:end/disp8  # eax goes from meaning var1->register to result
+      81 7/subop/compare *(edi+0x10) 0/imm32
+      74/jump-if-equal  $operand-matches-primitive?:end/disp8  # eax goes from meaning var1->register to result
+      # if string contents don't match, return false
+      (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register
+      3d/compare-eax-and 0/imm32
+      b8/copy-to-eax 0/imm32/false
+      74/jump-if-equal $operand-matches-primitive?:end/disp8
+    }
+    # return true
+    b8/copy-to-eax 1/imm32/true
+$operand-matches-primitive?:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# like operand-matches-primitive? but also handles "*" register in primitive
+output-operand-matches-primitive?:  # var : (address var), primout-var : (address var) => result/eax : boolean
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    56/push-esi
+    57/push-edi
+    # esi = var
+    8b/-> *(ebp+8) 6/r32/esi
+    # edi = primout-var
+    8b/-> *(ebp+0xc) 7/r32/edi
+    # if (var->type != primout-var->type) return false
+    # return false if var->register doesn't match primout-var->register
+    {
+      # if addresses are equal, don't return here
+      8b/-> *(esi+0x10) 0/r32/eax
+      39/compare *(edi+0x10) 0/r32/eax
+      74/jump-if-equal break/disp8
+      # if either address is 0, return false
+      3d/compare-eax-and 0/imm32
+      74/jump-if-equal  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
+      81 7/subop/compare *(edi+0x10) 0/imm32
+      74/jump-if-equal  $operand-matches-primitive?:end/disp8  # eax goes from meaning var->register to result
+      # if primout-var->register is "*", return true
+      (string-equal? *(edi+0x10) "*")  # Var-register
+      3d/compare-eax-and 0/imm32
+      b8/copy-to-eax 1/imm32/true
+      75/jump-if-not-equal $operand-matches-primitive?:end/disp8
+      # if string contents don't match, return false
+      (string-equal? *(esi+0x10) *(edi+0x10))  # Var-register Var-register
+      3d/compare-eax-and 0/imm32
+      b8/copy-to-eax 0/imm32/false
+      74/jump-if-equal $operand-matches-primitive?:end/disp8
+    }
+    # return true
+    b8/copy-to-eax 1/imm32/true
+$output-operand-matches-primitive?:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
 test-emit-subx-statement-primitive:
     # Primitive operation on a variable on the stack.
     #   increment foo
@@ -1305,21 +1490,25 @@ test-emit-subx-statement-primitive:
     68/push 1/imm32/type-int
     68/push "foo"/imm32
     89/<- %ecx 4/r32/esp
+#? $aa-var-in-ecx:
     # vars/edx : (stack 1)
     51/push-ecx/var-foo
     68/push 1/imm32/data-length
     68/push 1/imm32/top
     89/<- %edx 4/r32/esp
-    # operand/esi : (list var)
+#? $aa-vars-in-edx:
+    # operand/ebx : (list var)
     68/push 0/imm32/next
     51/push-ecx/var-foo
-    89/<- %esi 4/r32/esp
+    89/<- %ebx 4/r32/esp
+#? $aa-stmt-operand-in-ebx:
     # stmt/esi : statement
     68/push 0/imm32/next
     68/push 0/imm32/outputs
-    56/push-esi/operands
+    53/push-ebx/operands
     68/push "increment"/imm32/operation
     89/<- %esi 4/r32/esp
+#? $aa-stmt-in-esi:
     # primitives/ebx : primitive
     68/push 0/imm32/next
     68/push 0/imm32/no-imm32
@@ -1327,9 +1516,10 @@ test-emit-subx-statement-primitive:
     68/push 1/imm32/rm32-is-first-inout
     68/push "ff 0/subop/increment"/imm32/subx-name
     68/push 0/imm32/outputs
-    51/push-ecx/inouts  # hack; in practice we won't have the same var in function definition and call
+    53/push-ebx/inouts  # hack; in practice we won't have the same var in function definition and call
     68/push "increment"/imm32/name
     89/<- %ebx 4/r32/esp
+$aa-primitive-in-ebx:
     # convert
     (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0)
     (flush _test-output-buffered-file)
@@ -1361,7 +1551,7 @@ test-emit-subx-statement-primitive-register:
     #
     # There's a primitive with this info:
     #   name: 'increment'
-    #   inout: int/reg
+    #   out: int/reg
     #   value: 'ff 0/subop/increment'
     #
     # There's nothing in functions.
@@ -1384,13 +1574,13 @@ test-emit-subx-statement-primitive-register:
     68/push 1/imm32/data-length
     68/push 1/imm32/top
     89/<- %edx 4/r32/esp
-    # operand/esi : (list var)
+    # operand/ebx : (list var)
     68/push 0/imm32/next
     51/push-ecx/var-foo
-    89/<- %esi 4/r32/esp
+    89/<- %ebx 4/r32/esp
     # stmt/esi : statement
     68/push 0/imm32/next
-    56/push-esi/outputs
+    53/push-ebx/outputs
     68/push 0/imm32/inouts
     68/push "increment"/imm32/operation
     89/<- %esi 4/r32/esp
@@ -1433,6 +1623,202 @@ test-emit-subx-statement-primitive-register:
     5d/pop-to-ebp
     c3/return
 
+test-emit-subx-statement-select-primitive:
+    # Select the right primitive between overloads.
+    #   foo <- increment
+    # =>
+    #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
+    #
+    # There's a variable on the var stack as follows:
+    #   name: 'foo'
+    #   type: int
+    #   register: 'eax'
+    #
+    # There's two primitives, as follows:
+    #   - name: 'increment'
+    #     out: int/reg
+    #     value: 'ff 0/subop/increment'
+    #   - name: 'increment'
+    #     inout: int/mem
+    #     value: 'ff 0/subop/increment'
+    #
+    # There's nothing in functions.
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream _test-output-buffered-file->buffer)
+    # var-foo/ecx : var in eax
+    68/push "eax"/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    68/push 1/imm32/type-int
+    68/push "foo"/imm32
+    89/<- %ecx 4/r32/esp
+    # vars/edx : (stack 1)
+    51/push-ecx/var-foo
+    68/push 1/imm32/data-length
+    68/push 1/imm32/top
+    89/<- %edx 4/r32/esp
+    # real-outputs/edi : (list var)
+    68/push 0/imm32/next
+    51/push-ecx/var-foo
+    89/<- %edi 4/r32/esp
+    # stmt/esi : statement
+    68/push 0/imm32/next
+    57/push-edi/outputs
+    68/push 0/imm32/inouts
+    68/push "increment"/imm32/operation
+    89/<- %esi 4/r32/esp
+    # formal-var/ebx : var in any register
+    68/push Any-register/imm32
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    68/push 1/imm32/type-int
+    68/push "dummy"/imm32
+    89/<- %ebx 4/r32/esp
+    # formal-outputs/ebx : (list var)
+    68/push 0/imm32/next
+    53/push-ebx/formal-var
+    89/<- %ebx 4/r32/esp
+    # primitive1/ebx : primitive
+    68/push 0/imm32/next
+    68/push 0/imm32/no-imm32
+    68/push 0/imm32/no-r32
+    68/push 3/imm32/rm32-in-first-output
+    68/push "ff 0/subop/increment"/imm32/subx-name
+    53/push-ebx/outputs/formal-outputs
+    68/push 0/imm32/inouts
+    68/push "increment"/imm32/name
+    89/<- %ebx 4/r32/esp
+    # primitives/ebx : primitive
+    53/push-ebx/next
+    68/push 0/imm32/no-imm32
+    68/push 0/imm32/no-r32
+    68/push 1/imm32/rm32-is-first-inout
+    68/push "ff 0/subop/increment"/imm32/subx-name
+    68/push 0/imm32/outputs
+    57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
+    68/push "increment"/imm32/name
+    89/<- %ebx 4/r32/esp
+    # convert
+    (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0)
+    (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 "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive/0")
+    # . reclaim locals
+    81 0/subop/add %esp 0x48/imm32
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+test-emit-subx-statement-select-primitive-2:
+    # Select the right primitive between overloads.
+    #   foo <- increment
+    # =>
+    #   ff 0/subop/increment %eax  # sub-optimal, but should suffice
+    #
+    # There's a variable on the var stack as follows:
+    #   name: 'foo'
+    #   type: int
+    #   register: 'eax'
+    #
+    # There's two primitives, as follows:
+    #   - name: 'increment'
+    #     out: int/reg
+    #     value: 'ff 0/subop/increment'
+    #   - name: 'increment'
+    #     inout: int/mem
+    #     value: 'ff 0/subop/increment'
+    #
+    # There's nothing in functions.
+    #
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # setup
+    (clear-stream _test-output-stream)
+    (clear-stream _test-output-buffered-file->buffer)
+    # var-foo/ecx : var in eax
+    68/push "eax"/imm32/register
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    68/push 1/imm32/type-int
+    68/push "foo"/imm32
+    89/<- %ecx 4/r32/esp
+    # vars/edx : (stack 1)
+    51/push-ecx/var-foo
+    68/push 1/imm32/data-length
+    68/push 1/imm32/top
+    89/<- %edx 4/r32/esp
+    # inouts/edi : (list var)
+    68/push 0/imm32/next
+    51/push-ecx/var-foo
+    89/<- %edi 4/r32/esp
+    # stmt/esi : statement
+    68/push 0/imm32/next
+    68/push 0/imm32/outputs
+    57/push-edi/inouts
+    68/push "increment"/imm32/operation
+    89/<- %esi 4/r32/esp
+    # formal-var/ebx : var in any register
+    68/push Any-register/imm32
+    68/push 0/imm32/no-stack-offset
+    68/push 1/imm32/block-depth
+    68/push 1/imm32/type-int
+    68/push "dummy"/imm32
+    89/<- %ebx 4/r32/esp
+    # operand/ebx : (list var)
+    68/push 0/imm32/next
+    53/push-ebx/formal-var
+    89/<- %ebx 4/r32/esp
+    # primitive1/ebx : primitive
+    68/push 0/imm32/next
+    68/push 0/imm32/no-imm32
+    68/push 0/imm32/no-r32
+    68/push 3/imm32/rm32-in-first-output
+    68/push "ff 0/subop/increment"/imm32/subx-name
+    53/push-ebx/outputs/formal-outputs
+    68/push 0/imm32/inouts
+    68/push "increment"/imm32/name
+    89/<- %ebx 4/r32/esp
+    # primitives/ebx : primitive
+    53/push-ebx/next
+    68/push 0/imm32/no-imm32
+    68/push 0/imm32/no-r32
+    68/push 1/imm32/rm32-is-first-inout
+    68/push "ff 0/subop/increment"/imm32/subx-name
+    68/push 0/imm32/outputs
+    57/push-edi/inouts/real-outputs  # hack; in practice we won't have the same var in function definition and call
+    68/push "increment"/imm32/name
+    89/<- %ebx 4/r32/esp
+    # convert
+    (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0)
+    (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 "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive-2/0")
+    # . reclaim locals
+    81 0/subop/add %esp 0x48/imm32
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
 test-emit-subx-statement-function-call:
     # Call a function on a variable on the stack.
     #   f foo