about summary refs log tree commit diff stats
path: root/apps/mu.subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-02-02 00:19:19 -0800
committerKartik Agaram <vc@akkartik.com>2020-02-02 00:19:19 -0800
commit84fd02c9070a44641a8c702244ef1cd5d93efc10 (patch)
treef37d93f4ccb01dafbabba47569707fbabda826a4 /apps/mu.subx
parentbf3de55c3e5f9e0619932c7e6f115cdf4050f825 (diff)
downloadmu-84fd02c9070a44641a8c702244ef1cd5d93efc10.tar.gz
5974 - support for simple early exits
So far we only handle unlabeled break instructions correctly. That part
is elegance itself. But the rest will need more work:

a) For labeled breaks we need to insert code to unwind all intervening
blocks.
b) For unlabeled loops we need to insert code to unwind the current block
and then loop.
c) For labeled loops we need to insert code to unwind all intervening blocks
and then loop.

Is this even worth doing? I think so. It's pretty common for a conditional
block inside a loop to 'continue'. That requires looping to somewhere non-local.
Diffstat (limited to 'apps/mu.subx')
-rw-r--r--apps/mu.subx85
1 files changed, 84 insertions, 1 deletions
diff --git a/apps/mu.subx b/apps/mu.subx
index 9ed65a06..94703d5a 100644
--- a/apps/mu.subx
+++ b/apps/mu.subx
@@ -1179,6 +1179,56 @@ test-convert-function-with-multiple-vars-in-nested-blocks:
     5d/pop-to-ebp
     c3/return
 
+test-convert-function-with-branches-and-local-vars:
+    # . 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)
+    #
+    (write _test-input-stream "fn foo {\n")
+    (write _test-input-stream "  {\n")
+    (write _test-input-stream "    var x: int\n")
+    (write _test-input-stream "    break-if->=\n")
+    (write _test-input-stream "    increment x\n")
+    (write _test-input-stream "  }\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-function-with-branches-and-local-vars/0")
+    (check-next-stream-line-equal _test-output-stream "  # . prologue"          "F - test-convert-function-with-branches-and-local-vars/1")
+    (check-next-stream-line-equal _test-output-stream "  55/push-ebp"           "F - test-convert-function-with-branches-and-local-vars/2")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %ebp 4/r32/esp"  "F - test-convert-function-with-branches-and-local-vars/3")
+    (check-next-stream-line-equal _test-output-stream "  {"                     "F - test-convert-function-with-branches-and-local-vars/4")
+    (check-next-stream-line-equal _test-output-stream "    {"                   "F - test-convert-function-with-branches-and-local-vars/5")
+    (check-next-stream-line-equal _test-output-stream "      68/push 0/imm32"   "F - test-convert-function-with-branches-and-local-vars/6")
+    (check-next-stream-line-equal _test-output-stream "      {"                 "F - test-convert-function-with-branches-and-local-vars/7")
+    (check-next-stream-line-equal _test-output-stream "        0f 8d/jump-if->= break/disp32"  "F - test-convert-function-with-branches-and-local-vars/8")
+    (check-next-stream-line-equal _test-output-stream "        ff 0/subop/increment *(ebp+0xfffffffc)"  "F - test-convert-function-with-branches-and-local-vars/9")
+    (check-next-stream-line-equal _test-output-stream "      }"                 "F - test-convert-function-with-branches-and-local-vars/10")
+    (check-next-stream-line-equal _test-output-stream "      81 0/subop/add %esp 0x00000004/imm32"  "F - test-convert-function-with-branches-and-local-vars/11")
+    (check-next-stream-line-equal _test-output-stream "    }"                   "F - test-convert-function-with-branches-and-local-vars/12")
+    (check-next-stream-line-equal _test-output-stream "  }"                     "F - test-convert-function-with-branches-and-local-vars/13")
+    (check-next-stream-line-equal _test-output-stream "  # . epilogue"          "F - test-convert-function-with-branches-and-local-vars/14")
+    (check-next-stream-line-equal _test-output-stream "  89/<- %esp 5/r32/ebp"  "F - test-convert-function-with-branches-and-local-vars/15")
+    (check-next-stream-line-equal _test-output-stream "  5d/pop-to-ebp"         "F - test-convert-function-with-branches-and-local-vars/16")
+    (check-next-stream-line-equal _test-output-stream "  c3/return"             "F - test-convert-function-with-branches-and-local-vars/17")
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
 #######################################################
 # Parsing
 #######################################################
@@ -4153,9 +4203,12 @@ emit-subx-stmt-list:  # out: (addr buffered-file), stmts: (handle list stmt), va
     # . save registers
     50/push-eax
     51/push-ecx
+    52/push-edx
     56/push-esi
     # esi = stmts
     8b/-> *(ebp+0xc) 6/r32/esi
+    # var var-seen?/edx: boolean <- copy false
+    ba/copy-to-edx 0/imm32/false
     #
     {
 $emit-subx-stmt-list:loop:
@@ -4173,7 +4226,32 @@ $emit-subx-stmt-list:block:
       {
 $emit-subx-stmt-list:check-for-stmt:
         81 7/subop/compare *ecx 1/imm32/stmt1  # Stmt-tag
-        75/jump-if-!= break/disp8
+        0f 85/jump-if-!= break/disp32
+        {
+          # if !var-seen? break
+          81 7/subop/compare %edx 0/imm32/false
+          0f 84/jump-if-= break/disp32
+$emit-subx-stmt-list:check-for-break:
+          # if (!string-starts-with?(var->operation, "break")) break
+          (string-starts-with? *(ecx+4) "break")  # Stmt1-operation => eax
+          3d/compare-eax-and 0/imm32
+          0f 84/jump-if-= break/disp32
+$emit-subx-stmt-list:break:
+          # create a new block for the remaining statements
+          (emit-indent *(ebp+8) *Curr-block-depth)
+          (write-buffered *(ebp+8) "{\n")
+          ff 0/subop/increment *Curr-block-depth
+          (emit-subx-stmt-list *(ebp+8) %esi *(ebp+0x10))
+          ff 1/subop/decrement *Curr-block-depth
+          (emit-indent *(ebp+8) *Curr-block-depth)
+          (write-buffered *(ebp+8) "}\n")
+          # return $emit-subx-stmt-list
+          e9/jump $emit-subx-stmt-list:reclaim-loop/disp32
+        }
+        {
+$emit-subx-stmt-list:check-for-loop:
+          # TODO
+        }
 $emit-subx-stmt-list:stmt:
         (emit-subx-statement *(ebp+8) %ecx Primitives *Program)
       }
@@ -4184,6 +4262,8 @@ $emit-subx-stmt-list:check-for-vardef:
 $emit-subx-stmt-list:vardef:
         (emit-subx-var-def *(ebp+8) %ecx)
         (push *(ebp+0x10) *(ecx+4))  # Vardef-var
+        # var-seen? = true
+        ba/copy-to-edx 1/imm32/true
       }
       {
 $emit-subx-stmt-list:check-for-regvardef:
@@ -4206,6 +4286,8 @@ $emit-subx-stmt-list:regvardef:
         (push *(ebp+0x10) %eax)
         # emit the instruction as usual
         (emit-subx-statement *(ebp+8) %ecx Primitives *Program)
+        # var-seen? = true
+        ba/copy-to-edx 1/imm32/true
       }
       {
 $emit-subx-stmt-list:check-for-named-block:
@@ -4258,6 +4340,7 @@ $emit-subx-stmt-list:reclaim-var-on-stack:
 $emit-subx-stmt-list:end:
     # . restore registers
     5e/pop-to-esi
+    5a/pop-to-edx
     59/pop-to-ecx
     58/pop-to-eax
     # . epilogue