From 01a28c56c77a265fc6dd9c29ba1f9abc04fbab7e Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Mon, 17 Feb 2020 23:35:44 -0800 Subject: 6019 - finish supporting all branch primitives I'd been thinking I didn't need unconditional `break` instructions, but I just realized that non-local unconditional breaks have a use. Stop over-thinking this, just support everything. The code is quite duplicated. --- apps/mu.subx | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 233 insertions(+), 6 deletions(-) (limited to 'apps/mu.subx') diff --git a/apps/mu.subx b/apps/mu.subx index fa32de32..ee40b43c 100644 --- a/apps/mu.subx +++ b/apps/mu.subx @@ -1531,6 +1531,190 @@ test-convert-function-with-nonlocal-branches-and-loops-and-local-vars: 5d/pop-to-ebp c3/return +test-convert-function-with-nonlocal-unconditional-break-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) + c7 0/subop/copy *Next-block-index 1/imm32 + # + (write _test-input-stream "fn foo {\n") + (write _test-input-stream " a: {\n") + (write _test-input-stream " var x: int\n") + (write _test-input-stream " {\n") + (write _test-input-stream " var y: int\n") + (write _test-input-stream " break a\n") + (write _test-input-stream " increment x\n") + (write _test-input-stream " }\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-nonlocal-unconditional-break-and-local-vars/0") + (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/1") + (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/2") + (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/3") + (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/4") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/5") + (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/6") + (check-next-stream-line-equal _test-output-stream "a:loop:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/7") + (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/8") + (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/9") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/10") + (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/11") + (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/12") + (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/13") + (check-next-stream-line-equal _test-output-stream " e9/jump a:break/disp32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/14") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/15") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/16") + (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/17") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/18") + (check-next-stream-line-equal _test-output-stream "a:break:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/19") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/20") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/21") + (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/22") + (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/23") + (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/24") + (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-nonlocal-unconditional-break-and-local-vars/25") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-convert-function-with-unconditional-break-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) + c7 0/subop/copy *Next-block-index 1/imm32 + # + (write _test-input-stream "fn foo {\n") + (write _test-input-stream " {\n") + (write _test-input-stream " var x: int\n") + (write _test-input-stream " {\n") + (write _test-input-stream " var y: int\n") + (write _test-input-stream " break\n") + (write _test-input-stream " increment x\n") + (write _test-input-stream " }\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-unconditional-break-and-local-vars/0") + (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-unconditional-break-and-local-vars/1") + (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-unconditional-break-and-local-vars/2") + (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-unconditional-break-and-local-vars/3") + (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-break-and-local-vars/4") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-unconditional-break-and-local-vars/5") + (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-break-and-local-vars/6") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-convert-function-with-unconditional-break-and-local-vars/7") + (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/8") + (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-unconditional-break-and-local-vars/9") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-unconditional-break-and-local-vars/10") + (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/11") + (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/12") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-break-and-local-vars/13") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-unconditional-break-and-local-vars/14") + (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-unconditional-break-and-local-vars/15") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-break-and-local-vars/16") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-convert-function-with-unconditional-break-and-local-vars/17") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-unconditional-break-and-local-vars/18") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-unconditional-break-and-local-vars/19") + (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-unconditional-break-and-local-vars/20") + (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-unconditional-break-and-local-vars/21") + (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-unconditional-break-and-local-vars/22") + (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-unconditional-break-and-local-vars/23") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-convert-function-with-nonlocal-unconditional-loop-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) + c7 0/subop/copy *Next-block-index 1/imm32 + # + (write _test-input-stream "fn foo {\n") + (write _test-input-stream " a: {\n") + (write _test-input-stream " var x: int\n") + (write _test-input-stream " {\n") + (write _test-input-stream " var y: int\n") + (write _test-input-stream " loop a\n") + (write _test-input-stream " increment x\n") + (write _test-input-stream " }\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-nonlocal-unconditional-loop-and-local-vars/0") + (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/1") + (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/2") + (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/3") + (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/4") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/5") + (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/6") + (check-next-stream-line-equal _test-output-stream "a:loop:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/7") + (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/8") + (check-next-stream-line-equal _test-output-stream " {" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/9") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:loop:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/10") + (check-next-stream-line-equal _test-output-stream " 68/push 0/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/11") + (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/12") + (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/13") + (check-next-stream-line-equal _test-output-stream " e9/jump a:loop/disp32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/14") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/15") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000003:break:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/16") + (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/17") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/18") + (check-next-stream-line-equal _test-output-stream "a:break:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/19") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/20") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/21") + (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/22") + (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/23") + (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/24") + (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-function-with-nonlocal-unconditional-loop-and-local-vars/25") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + ####################################################### # Parsing ####################################################### @@ -4518,32 +4702,74 @@ $emit-subx-stmt-list:branch-stmt-and-var-seen: (string-equal? *(ecx+4) "loop") # Stmt1-operation => eax 3d/compare-eax-and 0/imm32/false 0f 84/jump-if-= break/disp32 +$emit-subx-stmt-list:unconditional-loop: 81 7/subop/compare *(ecx+8) 0/imm32 # Stmt1-inouts # simple unconditional loops without a target {{{ { 0f 85/jump-if-!= break/disp32 +$emit-subx-stmt-list:zero-arg-unconditional-loop: (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth) (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "e9/jump loop/disp32") (write-buffered *(ebp+8) Newline) - (clean-up-blocks *(ebp+0x10) *Curr-block-depth) - e9/jump $emit-subx-stmt-list:end/disp32 # skip remaining statements; they're dead code + e9/jump $emit-subx-stmt-list:cleanup/disp32 # skip remaining statements; they're dead code } # }}} # unconditional loops with a target {{{ { 0f 84/jump-if-= break/disp32 - # TODO +$emit-subx-stmt-list:unconditional-loop-with-target: + # var target/ebx: (addr array byte) = curr-stmt->inouts->value->name + 8b/-> *(ecx+8) 3/r32/ebx # Stmt1-inouts + 8b/-> *ebx 3/r32/ebx # List-value + 8b/-> *ebx 3/r32/ebx # Var-name + # clean up until target block + (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %ebx) + # emit jump to target block + (emit-indent *(ebp+8) *Curr-block-depth) + (write-buffered *(ebp+8) "e9/jump ") + (write-buffered *(ebp+8) %ebx) + (write-buffered *(ebp+8) ":loop/disp32\n") + # done + e9/jump $emit-subx-stmt-list:cleanup/disp32 } # }}} } # }}} + # unconditional breaks {{{ + { + # if (!string-equal?(var->operation, "break")) break + (string-equal? *(ecx+4) "break") # Stmt1-operation => eax + 3d/compare-eax-and 0/imm32/false + 0f 84/jump-if-= break/disp32 +$emit-subx-stmt-list:unconditional-break: + 81 7/subop/compare *(ecx+8) 0/imm32 # Stmt1-inouts + # simple unconditional breaks without a target + 0f 84/jump-if-= $emit-subx-stmt-list:emit-cleanup/disp32 # easy: just skip remaining statements + # unconditional breaks with a target {{{ +$emit-subx-stmt-list:unconditional-break-with-target: + # var target/ebx: (addr array byte) = curr-stmt->inouts->value->name + 8b/-> *(ecx+8) 3/r32/ebx # Stmt1-inouts + 8b/-> *ebx 3/r32/ebx # List-value + 8b/-> *ebx 3/r32/ebx # Var-name + # clean up until target block + (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %ebx) + # emit jump to target block + (emit-indent *(ebp+8) *Curr-block-depth) + (write-buffered *(ebp+8) "e9/jump ") + (write-buffered *(ebp+8) %ebx) + (write-buffered *(ebp+8) ":break/disp32\n") + # done + e9/jump $emit-subx-stmt-list:cleanup/disp32 + # }}} + } + # }}} # conditional branches {{{ # simple conditional branches without a target {{{ 81 7/subop/compare *(ecx+8) 0/imm32 # Stmt1-inouts { 0f 85/jump-if-!= break/disp32 -$emit-subx-stmt-list:zero-arg-branch: +$emit-subx-stmt-list:zero-arg-conditional-branch: # var old-block-depth/eax: int = Curr-block-depth - 1 8b/-> *Curr-block-depth 3/r32/ebx # cleanup prologue @@ -4579,7 +4805,7 @@ $emit-subx-stmt-list:zero-arg-branch: # conditional branches with an explicit target {{{ { 0f 84/jump-if-= break/disp32 -$emit-subx-stmt-list:branch-with-target: +$emit-subx-stmt-list:conditional-branch-with-target: # var target/ebx: (addr array byte) = curr-stmt->inouts->value->name 8b/-> *(ecx+8) 3/r32/ebx # Stmt1-inouts 8b/-> *ebx 3/r32/ebx # List-value @@ -4659,8 +4885,9 @@ $emit-subx-stmt-list:continue: 8b/-> *(esi+4) 6/r32/esi # List-next e9/jump loop/disp32 } -$emit-subx-stmt-list:cleanup: +$emit-subx-stmt-list:emit-cleanup: (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth) +$emit-subx-stmt-list:cleanup: (clean-up-blocks *(ebp+0x10) *Curr-block-depth) $emit-subx-stmt-list:end: # . restore registers -- cgit 1.4.1-2-gfad0