diff options
author | Kartik Agaram <vc@akkartik.com> | 2020-10-29 22:12:27 -0700 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2020-10-30 06:05:52 -0700 |
commit | 264acd9ec91a7e75dc43ec57d863777b7812cc52 (patch) | |
tree | ea6548648f189160692fb4b0f9df658481ff6096 /apps | |
parent | 8fe51755bf9a0ac7270599cf096e7fc2ffd50c83 (diff) | |
download | mu-264acd9ec91a7e75dc43ec57d863777b7812cc52.tar.gz |
7144 - tmp: redo checks for function outputs
This isn't done, but an intermediate snapshot seems worth capturing. Back in March (commit 6082), I made a plan to check writes to function outputs using liveness analysis. I've been shying away from actually acting on this plan ever since. In recent weeks I've had this gap bite me three times. Returning to the problem now, I think I don't actually need to compute variable liveness. The compiler can, I think, do the same thing for output registers whether their variables are alive or dead. The new rule is this: Once a register gets a function output written to it, no local is popped into it. Instead of popping outer locals to the register, we simply increment the stack and keep going. Since the function output will continue to live on the vars stack past this point (see clean-up-block), any attempts to read shadowed variables will throw an error as usual. This rule is also now easy to explain to people, I think. "You wrote the function output. Now the register can't be used for anything else." It's really cool that this works (if it does). Another fruit from "Mu's lovely property."
Diffstat (limited to 'apps')
-rw-r--r-- | apps/mu.subx | 263 |
1 files changed, 89 insertions, 174 deletions
diff --git a/apps/mu.subx b/apps/mu.subx index 98f77faa..2e04009b 100644 --- a/apps/mu.subx +++ b/apps/mu.subx @@ -2606,58 +2606,7 @@ test-always-shadow-outermost-reg-vars-in-function: 5d/pop-to-ebp c3/return -_pending-test-clobber-dead-local: - # . 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 " var x/ecx: int <- copy 3\n") - (write _test-input-stream " {\n") - (write _test-input-stream " var y/ecx: int <- copy 4\n") - (write _test-input-stream " }\n") - (write _test-input-stream "}\n") - # convert - (convert-mu _test-input-buffered-file _test-output-buffered-file Stderr 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 "foo:" "F - test-clobber-dead-local/0") - (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-clobber-dead-local/1") - (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-clobber-dead-local/2") - (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-clobber-dead-local/3") - (check-next-stream-line-equal _test-output-stream " {" "F - test-clobber-dead-local/4") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-clobber-dead-local/5") - (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-clobber-dead-local/6") - (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 3/imm32" "F - test-clobber-dead-local/7") - (check-next-stream-line-equal _test-output-stream " {" "F - test-clobber-dead-local/8") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-clobber-dead-local/9") - (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 4/imm32" "F - test-clobber-dead-local/10") # no push/pop here - (check-next-stream-line-equal _test-output-stream " }" "F - test-clobber-dead-local/11") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-clobber-dead-local/12") - (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-clobber-dead-local/13") - (check-next-stream-line-equal _test-output-stream " }" "F - test-clobber-dead-local/14") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-clobber-dead-local/15") - (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-clobber-dead-local/16") - (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-clobber-dead-local/17") - (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-clobber-dead-local/18") - (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-clobber-dead-local/19") - # . epilogue - 89/<- %esp 5/r32/ebp - 5d/pop-to-ebp - c3/return - -test-shadow-live-local: +test-shadow-local: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp @@ -2684,29 +2633,29 @@ test-shadow-live-local: #? (rewind-stream _test-output-stream) #? # }}} # check output - (check-next-stream-line-equal _test-output-stream "foo:" "F - test-shadow-live-local/0") - (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-shadow-live-local/1") - (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-shadow-live-local/2") - (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-shadow-live-local/3") - (check-next-stream-line-equal _test-output-stream " {" "F - test-shadow-live-local/4") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-shadow-live-local/5") - (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-shadow-live-local/6") - (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 3/imm32" "F - test-shadow-live-local/7") - (check-next-stream-line-equal _test-output-stream " {" "F - test-shadow-live-local/8") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-shadow-live-local/9") - (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-shadow-live-local/10") - (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 4/imm32" "F - test-shadow-live-local/11") - (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-shadow-live-local/12") - (check-next-stream-line-equal _test-output-stream " }" "F - test-shadow-live-local/13") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-shadow-live-local/14") - (check-next-stream-line-equal _test-output-stream " 41/increment-ecx" "F - test-shadow-live-local/15") - (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-shadow-live-local/16") - (check-next-stream-line-equal _test-output-stream " }" "F - test-shadow-live-local/17") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-shadow-live-local/18") - (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-shadow-live-local/19") - (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-shadow-live-local/20") - (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-shadow-live-local/21") - (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-shadow-live-local/22") + (check-next-stream-line-equal _test-output-stream "foo:" "F - test-shadow-local/0") + (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-shadow-local/1") + (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-shadow-local/2") + (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-shadow-local/3") + (check-next-stream-line-equal _test-output-stream " {" "F - test-shadow-local/4") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-shadow-local/5") + (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-shadow-local/6") + (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 3/imm32" "F - test-shadow-local/7") + (check-next-stream-line-equal _test-output-stream " {" "F - test-shadow-local/8") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-shadow-local/9") + (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-shadow-local/10") + (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 4/imm32" "F - test-shadow-local/11") + (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-shadow-local/12") + (check-next-stream-line-equal _test-output-stream " }" "F - test-shadow-local/13") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-shadow-local/14") + (check-next-stream-line-equal _test-output-stream " 41/increment-ecx" "F - test-shadow-local/15") + (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-shadow-local/16") + (check-next-stream-line-equal _test-output-stream " }" "F - test-shadow-local/17") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-shadow-local/18") + (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-shadow-local/19") + (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-shadow-local/20") + (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-shadow-local/21") + (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-shadow-local/22") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp @@ -2922,7 +2871,7 @@ test-spill-different-register-in-block: 5d/pop-to-ebp c3/return -test-shadow-live-output: +test-shadow-output: # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp @@ -2949,27 +2898,27 @@ test-shadow-live-output: #? (rewind-stream _test-output-stream) #? # }}} # check output - (check-next-stream-line-equal _test-output-stream "foo:" "F - test-shadow-live-output/0") - (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-shadow-live-output/1") - (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-shadow-live-output/2") - (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-shadow-live-output/3") - (check-next-stream-line-equal _test-output-stream " {" "F - test-shadow-live-output/4") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-shadow-live-output/5") - (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 3/imm32" "F - test-shadow-live-output/7") # no push because it's an output reg - (check-next-stream-line-equal _test-output-stream " {" "F - test-shadow-live-output/8") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-shadow-live-output/9") - (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-shadow-live-output/10") - (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 4/imm32" "F - test-shadow-live-output/11") - (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-shadow-live-output/12") - (check-next-stream-line-equal _test-output-stream " }" "F - test-shadow-live-output/13") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-shadow-live-output/14") - (check-next-stream-line-equal _test-output-stream " 41/increment-ecx" "F - test-shadow-live-output/15") - (check-next-stream-line-equal _test-output-stream " }" "F - test-shadow-live-output/17") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-shadow-live-output/18") - (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-shadow-live-output/19") - (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-shadow-live-output/20") - (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-shadow-live-output/21") - (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-shadow-live-output/21") + (check-next-stream-line-equal _test-output-stream "foo:" "F - test-shadow-output/0") + (check-next-stream-line-equal _test-output-stream " # . prologue" "F - test-shadow-output/1") + (check-next-stream-line-equal _test-output-stream " 55/push-ebp" "F - test-shadow-output/2") + (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-shadow-output/3") + (check-next-stream-line-equal _test-output-stream " {" "F - test-shadow-output/4") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-shadow-output/5") + (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 3/imm32" "F - test-shadow-output/7") # no push because it's an output reg + (check-next-stream-line-equal _test-output-stream " {" "F - test-shadow-output/8") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:loop:" "F - test-shadow-output/9") + (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-shadow-output/10") + (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 4/imm32" "F - test-shadow-output/11") + (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %ecx" "F - test-shadow-output/12") + (check-next-stream-line-equal _test-output-stream " }" "F - test-shadow-output/13") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000002:break:" "F - test-shadow-output/14") + (check-next-stream-line-equal _test-output-stream " 41/increment-ecx" "F - test-shadow-output/15") + (check-next-stream-line-equal _test-output-stream " }" "F - test-shadow-output/17") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-shadow-output/18") + (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-shadow-output/19") + (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-shadow-output/20") + (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-shadow-output/21") + (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-shadow-output/21") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp @@ -3048,14 +2997,16 @@ test-local-clobbered-by-fn-output: (check-next-stream-line-equal _test-output-stream " 89/<- %ebp 4/r32/esp" "F - test-local-clobbered-by-fn-output/3") (check-next-stream-line-equal _test-output-stream " {" "F - test-local-clobbered-by-fn-output/4") (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-local-clobbered-by-fn-output/5") - (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 4/imm32" "F - test-local-clobbered-by-fn-output/6") # no push because it's an output reg - (check-next-stream-line-equal _test-output-stream " 89/<- %ecx 0x00000001/r32" "F - test-local-clobbered-by-fn-output/7") - (check-next-stream-line-equal _test-output-stream " }" "F - test-local-clobbered-by-fn-output/8") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-local-clobbered-by-fn-output/9") - (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-local-clobbered-by-fn-output/10") - (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-local-clobbered-by-fn-output/11") - (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-local-clobbered-by-fn-output/12") - (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-local-clobbered-by-fn-output/13") + (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %ecx" "F - test-local-clobbered-by-fn-output/6") + (check-next-stream-line-equal _test-output-stream " b9/copy-to-ecx 4/imm32" "F - test-local-clobbered-by-fn-output/7") + (check-next-stream-line-equal _test-output-stream " 89/<- %ecx 0x00000001/r32" "F - test-local-clobbered-by-fn-output/8") + (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000004/imm32" "F - test-local-clobbered-by-fn-output/9") # not a pop because there's already a function output in the reg + (check-next-stream-line-equal _test-output-stream " }" "F - test-local-clobbered-by-fn-output/10") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-local-clobbered-by-fn-output/11") + (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-local-clobbered-by-fn-output/12") + (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-local-clobbered-by-fn-output/13") + (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-local-clobbered-by-fn-output/14") + (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-local-clobbered-by-fn-output/15") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp @@ -18585,7 +18536,7 @@ $emit-subx-stmt-list:zero-arg-unconditional-loop: # unconditional loops with a target { 0f 84/jump-if-= break/disp32 - (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10)) + (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10) *(ebp+0x14)) e9/jump $emit-subx-stmt-list:clean-up/disp32 } } @@ -18602,7 +18553,7 @@ $emit-subx-stmt-list:unconditional-break: # 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-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10)) + (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10) *(ebp+0x14)) e9/jump $emit-subx-stmt-list:clean-up/disp32 } # }}} @@ -18654,7 +18605,7 @@ $emit-subx-stmt-list:conditional-branch-with-target: ff 0/subop/increment *Curr-block-depth # (emit-reverse-break *(ebp+8) %ecx) - (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10)) + (emit-subx-cleanup-and-unconditional-nonlocal-branch *(ebp+8) %ecx *(ebp+0x10) *(ebp+0x14)) # cleanup epilogue ff 1/subop/decrement *Curr-block-depth (emit-indent *(ebp+8) *Curr-block-depth) @@ -18686,7 +18637,7 @@ $emit-subx-stmt-list:check-for-reg-var-def: 0f 85/jump-if-!= break/disp32 $emit-subx-stmt-list:reg-var-def: # TODO: ensure that there's exactly one output - (push-output-and-maybe-emit-spill *(ebp+8) %ecx *(ebp+0x10) %esi *(ebp+0x14) *(ebp+0x18) *(ebp+0x1c)) + (push-output-and-maybe-emit-spill *(ebp+8) %ecx *(ebp+0x10) %esi *(ebp+0x18) *(ebp+0x1c)) # emit the instruction as usual (emit-subx-stmt *(ebp+8) %ecx Primitives *(ebp+0x18) *(ebp+0x1c)) # @@ -18699,7 +18650,7 @@ $emit-subx-stmt-list:continue: e9/jump loop/disp32 } $emit-subx-stmt-list:emit-cleanup: - (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth) + (emit-cleanup-code-until-depth *(ebp+8) *(ebp+0x10) *Curr-block-depth *(ebp+0x14)) $emit-subx-stmt-list:clean-up: (clean-up-stack-offset-state *(ebp+0x10) *Curr-block-depth) (clean-up-blocks *(ebp+0x10) *Curr-block-depth *(ebp+0x14)) @@ -18715,7 +18666,7 @@ $emit-subx-stmt-list:end: c3/return # 'later-stmts' includes 'stmt', but will behave the same even without it; reg-var-def stmts are guaranteed not to write to function outputs. -push-output-and-maybe-emit-spill: # out: (addr buffered-file), stmt: (addr reg-var-def), vars: (addr stack (handle var)), later-stmts: (addr list stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor) +push-output-and-maybe-emit-spill: # out: (addr buffered-file), stmt: (addr reg-var-def), vars: (addr stack (handle var)), later-stmts: (addr list stmt), err: (addr buffered-file), ed: (addr exit-descriptor) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp @@ -18744,16 +18695,11 @@ push-output-and-maybe-emit-spill: # out: (addr buffered-file), stmt: (addr reg- # ensure that v is in a register 81 7/subop/compare *(ecx+0x18) 0/imm32 # Var-register 0f 84/jump-if-= $push-output-and-maybe-emit-spill:abort/disp32 - # var emit-spill?/edx: boolean = not-yet-spilled-this-block? && will-not-write-some-register?(fn) + # var emit-spill?/edx: boolean = not-yet-spilled-this-block?(v, vars) (not-yet-spilled-this-block? %ecx *(ebp+0x10)) # => eax 89/<- %edx 0/r32/eax 3d/compare-eax-and 0/imm32/false 0f 84/jump-if-= $push-output-and-maybe-emit-spill:push/disp32 - (will-not-write-some-register? %ecx *(ebp+0x14) *(ebp+0x18)) # => eax - 89/<- %edx 0/r32/eax - # check emit-spill? - 3d/compare-eax-and 0/imm32/false - 0f 84/jump-if-= $push-output-and-maybe-emit-spill:push/disp32 # TODO: assert(size-of(output) == 4) # *Curr-local-stack-offset -= 4 81 5/subop/subtract *Curr-local-stack-offset 4/imm32 @@ -18779,14 +18725,14 @@ $push-output-and-maybe-emit-spill:end: $push-output-and-maybe-emit-spill:abort: # error("var '" var->name "' initialized from an instruction must live in a register\n") - (write-buffered *(ebp+0x1c) "var '") - (write-buffered *(ebp+0x1c) *eax) # Var-name - (write-buffered *(ebp+0x1c) "' initialized from an instruction must live in a register\n") - (flush *(ebp+0x1c)) - (stop *(ebp+0x20) 1) + (write-buffered *(ebp+0x18) "var '") + (write-buffered *(ebp+0x18) *eax) # Var-name + (write-buffered *(ebp+0x18) "' initialized from an instruction must live in a register\n") + (flush *(ebp+0x18)) + (stop *(ebp+0x1c) 1) # never gets here -emit-subx-cleanup-and-unconditional-nonlocal-branch: # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack live-var) +emit-subx-cleanup-and-unconditional-nonlocal-branch: # out: (addr buffered-file), stmt: (addr stmt1), vars: (addr stack live-var), fn: (addr function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp @@ -18800,7 +18746,7 @@ emit-subx-cleanup-and-unconditional-nonlocal-branch: # out: (addr buffered-file (lookup *eax *(eax+4)) # Stmt-var-value Stmt-var-value => eax (lookup *eax *(eax+4)) # Var-name Var-name => eax # clean up until target block - (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %eax) + (emit-cleanup-code-until-target *(ebp+8) *(ebp+0x10) %eax *(ebp+0x14)) # emit jump to target block (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "e9/jump ") @@ -18984,7 +18930,7 @@ $emit-unconditional-jump-to-depth:end: # emit clean-up code for 'vars' until some block depth # doesn't actually modify 'vars' so we need traverse manually inside the stack -emit-cleanup-code-until-depth: # out: (addr buffered-file), vars: (addr stack live-var), until-block-depth: int +emit-cleanup-code-until-depth: # out: (addr buffered-file), vars: (addr stack live-var), until-block-depth: int, fn: (addr function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp @@ -19032,8 +18978,7 @@ $emit-cleanup-code-until-depth:check-for-previous-spill: 3d/compare-eax-and 0/imm32/false 74/jump-if-= break/disp8 $emit-cleanup-code-until-depth:reclaim-var-in-register: - (lookup *(ebx+0x18) *(ebx+0x1c)) # Var-register Var-register => eax - (emit-pop-register *(ebp+8) %eax) + (emit-pop-register *(ebp+8) %ebx *(ebp+0xc) *(ebp+0x14)) } eb/jump $emit-cleanup-code-until-depth:continue/disp8 } @@ -19105,16 +19050,21 @@ $emit-push-register:end: 5d/pop-to-ebp c3/return -emit-pop-register: # out: (addr buffered-file), reg: (addr array byte) +emit-pop-register: # out: (addr buffered-file), regvar: (addr var), vars: (addr stack live-var), fn: (addr function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax - # eax = reg - 8b/-> *(ebp+0xc) 0/r32/eax + 51/push-ecx + 56/push-esi + # esi = regvar + 8b/-> *(ebp+0xc) 6/r32/esi + # var reg/ecx: (addr array byte) = lookup(regvar->register) + (lookup *(esi+0x18) *(esi+0x1c)) # Var-register Var-register => eax + 89/<- %ecx 0/r32/eax # var prefix/eax: byte = reg->data[0] - 8a/copy-byte *(eax+4) 0/r32/AL + 8a/copy-byte *(ecx+4) 0/r32/AL 81 4/subop/and %eax 0xff/imm32 # if (prefix == 'x') pop to xmm register { @@ -19124,8 +19074,7 @@ emit-pop-register: # out: (addr buffered-file), reg: (addr array byte) (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "f3 0f 10/-> *esp ") # var prefix/eax: byte = reg->data[3] - 8b/-> *(ebp+0xc) 0/r32/eax - 8a/copy-byte *(eax+7) 0/r32/AL + 8a/copy-byte *(ecx+7) 0/r32/AL 81 4/subop/and %eax 0xff/imm32 (write-byte-buffered *(ebp+8) %eax) (write-buffered *(ebp+8) "/x32\n") @@ -19136,10 +19085,12 @@ emit-pop-register: # out: (addr buffered-file), reg: (addr array byte) # otherwise pop to gp register (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "8f 0/subop/pop %") - (write-buffered *(ebp+8) *(ebp+0xc)) + (write-buffered *(ebp+8) %ecx) (write-buffered *(ebp+8) Newline) $emit-pop-register:end: # . restore registers + 5e/pop-to-esi + 59/pop-to-ecx 58/pop-to-eax # . epilogue 89/<- %esp 5/r32/ebp @@ -19148,7 +19099,7 @@ $emit-pop-register:end: # emit clean-up code for 'vars' until a given label is encountered # doesn't actually modify 'vars' so we need traverse manually inside the stack -emit-cleanup-code-until-target: # out: (addr buffered-file), vars: (addr stack live-var), until-block-label: (addr array byte) +emit-cleanup-code-until-target: # out: (addr buffered-file), vars: (addr stack live-var), until-block-label: (addr array byte), fn: (addr function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp @@ -19188,8 +19139,7 @@ $emit-cleanup-code-until-target:check-for-previous-spill: 3d/compare-eax-and 0/imm32/false 74/jump-if-= break/disp8 $emit-cleanup-code-until-target:reclaim-var-in-register: - (lookup *(ebx+0x18) *(ebx+0x1c)) # Var-register Var-register => eax - (emit-pop-register *(ebp+8) %eax) + (emit-pop-register *(ebp+8) %ebx *(ebp+0xc) *(ebp+0x14)) } eb/jump $emit-cleanup-code-until-target:continue/disp8 } @@ -19369,35 +19319,6 @@ $not-yet-spilled-this-block?:end: 5d/pop-to-ebp c3/return -# could the register of 'v' ever be written to by one of the vars in fn-outputs? -will-not-write-some-register?: # v: (addr var), stmts: (addr list stmt), fn: (addr function) -> result/eax: boolean - # . prologue - 55/push-ebp - 89/<- %ebp 4/r32/esp - # eax = v - 8b/-> *(ebp+8) 0/r32/eax - # var reg/eax: (addr array byte) = lookup(v->register) - (lookup *(eax+0x18) *(eax+0x1c)) # Var-register Var-register => eax - # var target/eax: (addr var) = find-register(fn-outputs, reg) - (find-register *(ebp+0x10) %eax) # => eax - # if (target == 0) return true - { - 3d/compare-eax-and 0/imm32 - 75/jump-if-!= break/disp8 - b8/copy-to-eax 1/imm32/true - eb/jump $will-not-write-some-register?:end/disp8 - } - # return !assigns-in-stmts?(stmts, target) - (assigns-in-stmts? *(ebp+0xc) %eax) # => eax - 3d/compare-eax-and 0/imm32/false - # assume: true = 1, so no need to mask with 0x000000ff - 0f 94/set-if-= %al -$will-not-write-some-register?:end: - # . epilogue - 89/<- %esp 5/r32/ebp - 5d/pop-to-ebp - c3/return - # return fn output with matching register # always returns false if 'reg' is null find-register: # fn: (addr function), reg: (addr array byte) -> result/eax: (addr var) @@ -19617,14 +19538,8 @@ $same-register-spilled-before?:end: # This would be a simple series of pops, if it wasn't for fn outputs, which # can occur anywhere in the stack. # So we have to _compact_ the entire array underlying the stack. -# -# We want to allow a fn output register to be written to by locals before the -# output is set. -# So fn outputs can't just be pushed at the start of the function. -# -# We want to allow other locals to shadow a fn output register after the -# output is set. -# So the output can't just always override anything in the stack. Sequence matters. +# If a register already contains a function output, we drop the var to avoid +# clobbering it. clean-up-blocks: # vars: (addr stack live-var), until-block-depth: int, fn: (addr function) # pseudocode: # to = vars->top (which points outside the stack) |