6464 - support temporaries in fn output registers
The rule: emit spills for a register unless the output is written somewhere in the current block after the current instruction. Including in nested blocks. Let's see if this is right.
This commit is contained in:
parent
04b3afd67e
commit
20411cc442
273
apps/mu.subx
273
apps/mu.subx
|
@ -1575,7 +1575,7 @@ test-shadow-live-output:
|
|||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
_pending-test-local-clobbered-by-output:
|
||||
test-local-clobbered-by-output:
|
||||
# also doesn't spill
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
|
@ -8080,6 +8080,7 @@ emit-subx-function: # out: (addr buffered-file), f: (addr function)
|
|||
50/push-eax
|
||||
51/push-ecx
|
||||
52/push-edx
|
||||
57/push-edi
|
||||
# initialize some global state
|
||||
c7 0/subop/copy *Curr-block-depth 1/imm32
|
||||
c7 0/subop/copy *Curr-local-stack-offset 0/imm32
|
||||
|
@ -8096,10 +8097,13 @@ emit-subx-function: # out: (addr buffered-file), f: (addr function)
|
|||
(write-buffered *(ebp+8) %eax)
|
||||
(write-buffered *(ebp+8) ":\n")
|
||||
(emit-subx-prologue *(ebp+8))
|
||||
# var outputs/edi: (addr list var) = lookup(f->outputs)
|
||||
(lookup *(ecx+0x10) *(ecx+0x14)) # Function-outputs Function-outputs => eax
|
||||
89/<- %edi 0/r32/eax
|
||||
# var body/eax: (addr block) = lookup(f->body)
|
||||
(lookup *(ecx+0x18) *(ecx+0x1c)) # Function-body Function-body => eax
|
||||
#
|
||||
(emit-subx-block *(ebp+8) %eax %edx)
|
||||
(emit-subx-block *(ebp+8) %eax %edx %edi)
|
||||
(emit-subx-epilogue *(ebp+8))
|
||||
# TODO: validate that *Curr-block-depth and *Curr-local-stack-offset have
|
||||
# been cleaned up
|
||||
|
@ -8107,6 +8111,7 @@ $emit-subx-function:end:
|
|||
# . reclaim locals
|
||||
81 0/subop/add %esp 0xc08/imm32
|
||||
# . restore registers
|
||||
5f/pop-to-edi
|
||||
5a/pop-to-edx
|
||||
59/pop-to-ecx
|
||||
58/pop-to-eax
|
||||
|
@ -8170,7 +8175,7 @@ $populate-mu-type-offsets-in-inouts:end:
|
|||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
emit-subx-stmt-list: # out: (addr buffered-file), stmts: (addr list stmt), vars: (addr stack live-var)
|
||||
emit-subx-stmt-list: # out: (addr buffered-file), stmts: (addr list stmt), vars: (addr stack live-var), fn-outputs: (addr list var)
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
|
@ -8197,7 +8202,7 @@ $emit-subx-stmt-list:check-for-block:
|
|||
81 7/subop/compare *ecx 0/imm32/block # Stmt-tag
|
||||
75/jump-if-!= break/disp8
|
||||
$emit-subx-stmt-list:block:
|
||||
(emit-subx-block *(ebp+8) %ecx *(ebp+0x10))
|
||||
(emit-subx-block *(ebp+8) %ecx *(ebp+0x10) *(ebp+0x14))
|
||||
}
|
||||
{
|
||||
$emit-subx-stmt-list:check-for-stmt:
|
||||
|
@ -8337,7 +8342,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))
|
||||
(push-output-and-maybe-emit-spill *(ebp+8) %ecx *(ebp+0x10) %esi *(ebp+0x14))
|
||||
# emit the instruction as usual
|
||||
(emit-subx-stmt *(ebp+8) %ecx Primitives)
|
||||
# var-seen? = true
|
||||
|
@ -8366,7 +8371,8 @@ $emit-subx-stmt-list:end:
|
|||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
push-output-and-maybe-emit-spill: # out: (addr buffered-file), stmt: (addr reg-var-def), vars: (addr stack live-var)
|
||||
# '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-outputs: (addr list var)
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
|
@ -8395,12 +8401,16 @@ 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?(reg, vars)
|
||||
# var emit-spill?/edx: boolean = not-yet-spilled-this-block? && will-not-write-some-register?(fn-outputs)
|
||||
(not-yet-spilled-this-block? %ecx *(ebp+0x10)) # => eax
|
||||
89/<- %edx 0/r32/eax
|
||||
# if emit-spill? then emit code to spill reg
|
||||
3d/compare-eax-and 0/imm32/false
|
||||
74/jump-if-= $push-output-and-maybe-emit-spill:push/disp8
|
||||
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
|
||||
|
@ -8871,6 +8881,247 @@ $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-outputs: (addr list var) -> 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 output var with matching register
|
||||
# always returns false if 'reg' is null
|
||||
find-register: # fn-outputs: (addr list var), reg: (addr array byte) -> result/eax: (addr var)
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
# . save registers
|
||||
51/push-ecx
|
||||
# var curr/ecx: (addr list var) = fn-outputs
|
||||
8b/-> *(ebp+8) 1/r32/ecx
|
||||
{
|
||||
$find-register:loop:
|
||||
# if (curr == 0) break
|
||||
81 7/subop/compare %ecx 0/imm32
|
||||
74/jump-if-= break/disp8
|
||||
# eax = curr->value->register
|
||||
(lookup *ecx *(ecx+4)) # List-value List-value => eax
|
||||
(lookup *(eax+0x18) *(eax+0x1c)) # Var-register Var-register => eax
|
||||
# if (eax == reg) return curr->value
|
||||
$find-register:compare:
|
||||
(string-equal? *(ebp+0xc) %eax) # => eax
|
||||
{
|
||||
3d/compare-eax-and 0/imm32/false
|
||||
74/jump-if-= break/disp8
|
||||
$find-register:found:
|
||||
(lookup *ecx *(ecx+4)) # List-value List-value => eax
|
||||
eb/jump $find-register:end/disp8
|
||||
}
|
||||
# curr = lookup(curr->next)
|
||||
(lookup *(ecx+8) *(ecx+0xc)) # List-next List-next => eax
|
||||
89/<- %ecx 0/r32/eax
|
||||
#
|
||||
eb/jump loop/disp8
|
||||
}
|
||||
$find-register:end:
|
||||
# . restore registers
|
||||
59/pop-to-ecx
|
||||
# . epilogue
|
||||
89/<- %esp 5/r32/ebp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
assigns-in-stmts?: # stmts: (addr list stmt), v: (addr var) -> result/eax: boolean
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
# . save registers
|
||||
51/push-ecx
|
||||
# var curr/ecx: (addr list stmt) = stmts
|
||||
8b/-> *(ebp+8) 1/r32/ecx
|
||||
{
|
||||
# if (curr == 0) break
|
||||
81 7/subop/compare %ecx 0/imm32
|
||||
74/jump-if-= break/disp8
|
||||
# if assigns-in-stmt?(curr->value, v) return true
|
||||
(lookup *ecx *(ecx+4)) # List-value List-value => eax
|
||||
(assigns-in-stmt? %eax *(ebp+0xc)) # => eax
|
||||
3d/compare-eax-and 0/imm32/false
|
||||
75/jump-if-!= break/disp8
|
||||
# curr = lookup(curr->next)
|
||||
(lookup *(ecx+8) *(ecx+0xc)) # List-next List-next => eax
|
||||
89/<- %ecx 0/r32/eax
|
||||
#
|
||||
eb/jump loop/disp8
|
||||
}
|
||||
$assigns-in-stmts?:end:
|
||||
# . restore registers
|
||||
59/pop-to-ecx
|
||||
# . epilogue
|
||||
89/<- %esp 5/r32/ebp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
assigns-in-stmt?: # stmt: (addr stmt), v: (addr var) -> result/eax: boolean
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
# . save registers
|
||||
51/push-ecx
|
||||
# ecx = stmt
|
||||
8b/-> *(ebp+8) 1/r32/ecx
|
||||
# if stmt is a stmt1, return assigns-in-stmt-vars?(stmt->outputs, v)
|
||||
{
|
||||
81 7/subop/compare *ecx 1/imm32/stmt1 # Stmt-tag
|
||||
75/jump-if-!= break/disp8
|
||||
(lookup *(ecx+0x14) *(ecx+0x18)) # Stmt1-outputs Stmt1-outputs => eax
|
||||
(assigns-in-stmt-vars? %eax *(ebp+0xc)) # => eax
|
||||
eb/jump $assigns-in-stmt?:end/disp8
|
||||
}
|
||||
# if stmt is a block, return assigns-in-stmts?(stmt->stmts, v)
|
||||
{
|
||||
81 7/subop/compare *ecx 0/imm32/block # Stmt-tag
|
||||
75/jump-if-!= break/disp8
|
||||
(lookup *(ecx+4) *(ecx+8)) # Block-stmts Block-stmts => eax
|
||||
(assigns-in-stmts? %eax *(ebp+0xc)) # => eax
|
||||
eb/jump $assigns-in-stmt?:end/disp8
|
||||
}
|
||||
# otherwise return false
|
||||
b8/copy 0/imm32/false
|
||||
$assigns-in-stmt?:end:
|
||||
# . restore registers
|
||||
59/pop-to-ecx
|
||||
# . epilogue
|
||||
89/<- %esp 5/r32/ebp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
assigns-in-stmt-vars?: # stmt-var: (addr stmt-var), v: (addr var) -> result/eax: boolean
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
# . save registers
|
||||
51/push-ecx
|
||||
# var curr/ecx: (addr stmt-var) = stmt-var
|
||||
8b/-> *(ebp+8) 1/r32/ecx
|
||||
{
|
||||
# if (curr == 0) break
|
||||
81 7/subop/compare %ecx 0/imm32
|
||||
74/jump-if-= break/disp8
|
||||
# eax = lookup(curr->value)
|
||||
(lookup *ecx *(ecx+4)) # Stmt-var-value Stmt-var-value => eax
|
||||
# if (eax == v && curr->is-deref? == false) return true
|
||||
{
|
||||
39/compare *(ebp+0xc) 0/r32/eax
|
||||
75/jump-if-!= break/disp8
|
||||
81 7/subop/compare *(ecx+0x10) 0/imm32/false # Stmt-var-is-deref
|
||||
75/jump-if-!= break/disp8
|
||||
b8/copy-to-eax 1/imm32/true
|
||||
eb/jump $assigns-in-stmt-vars?:end/disp8
|
||||
}
|
||||
# curr = lookup(curr->next)
|
||||
(lookup *(ecx+8) *(ecx+0xc)) # Stmt-var-next Stmt-var-next => eax
|
||||
89/<- %ecx 0/r32/eax
|
||||
#
|
||||
eb/jump loop/disp8
|
||||
}
|
||||
$assigns-in-stmt-vars?:end:
|
||||
# . restore registers
|
||||
59/pop-to-ecx
|
||||
# . epilogue
|
||||
89/<- %esp 5/r32/ebp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
# is there a var before 'v' with the same block-depth and register on the 'vars' stack?
|
||||
# v is guaranteed to be within vars
|
||||
# 'start' is provided as an optimization, a pointer within vars
|
||||
# *start == v
|
||||
same-register-spilled-before?: # v: (addr var), vars: (addr stack (handle var)), start: (addr var) -> result/eax: boolean
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
# . save registers
|
||||
51/push-ecx
|
||||
52/push-edx
|
||||
53/push-ebx
|
||||
56/push-esi
|
||||
57/push-edi
|
||||
# ecx = v
|
||||
8b/-> *(ebp+8) 1/r32/ecx
|
||||
# var reg/edx: (addr array byte) = lookup(v->register)
|
||||
(lookup *(ecx+0x18) *(ecx+0x1c)) # Var-register Var-register => eax
|
||||
89/<- %edx 0/r32/eax
|
||||
# var depth/ebx: int = v->block-depth
|
||||
8b/-> *(ecx+0x10) 3/r32/ebx # Var-block-depth
|
||||
# var min/ecx: (addr handle var) = vars->data
|
||||
8b/-> *(ebp+0xc) 1/r32/ecx
|
||||
81 0/subop/add %ecx 8/imm32
|
||||
# TODO: check that start >= min and start < &vars->data[top]
|
||||
# TODO: check that *start == v
|
||||
# var curr/esi: (addr handle var) = start
|
||||
8b/-> *(ebp+0x10) 6/r32/esi
|
||||
# curr -= 8
|
||||
81 5/subop/subtract %esi 8/imm32
|
||||
{
|
||||
$same-register-spilled-before?:loop:
|
||||
# if (curr < min) break
|
||||
39/compare %esi 1/r32/ecx
|
||||
0f 82/jump-if-addr< break/disp32
|
||||
# var x/eax: (addr var) = lookup(*curr)
|
||||
(lookup *esi *(esi+4)) # => eax
|
||||
# if (x->block-depth < depth) break
|
||||
39/compare *(eax+0x10) 3/r32/ebx # Var-block-depth
|
||||
0f 8c/jump-if-< break/disp32
|
||||
# if (x->register == 0) continue
|
||||
81 7/subop/compare *(eax+0x18) 0/imm32 # Var-register
|
||||
74/jump-if-= $same-register-spilled-before?:continue/disp8
|
||||
# if (x->register == reg) return true
|
||||
(lookup *(eax+0x18) *(eax+0x1c)) # Var-register Var-register => eax
|
||||
(string-equal? %eax %edx) # => eax
|
||||
3d/compare-eax-and 0/imm32/false
|
||||
b8/copy-to-eax 1/imm32/true
|
||||
75/jump-if-!= $same-register-spilled-before?:end/disp8
|
||||
$same-register-spilled-before?:continue:
|
||||
# curr -= 8
|
||||
81 5/subop/subtract %esi 8/imm32
|
||||
e9/jump loop/disp32
|
||||
}
|
||||
$same-register-spilled-before?:false:
|
||||
b8/copy-to-eax 0/imm32/false
|
||||
$same-register-spilled-before?: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
|
||||
|
||||
# clean up global state for 'vars' until some block depth
|
||||
clean-up-blocks: # vars: (addr stack live-var), until-block-depth: int
|
||||
# . prologue
|
||||
|
@ -9763,7 +10014,7 @@ $emit-get-offset:end:
|
|||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
emit-subx-block: # out: (addr buffered-file), block: (addr block), vars: (addr stack live-var)
|
||||
emit-subx-block: # out: (addr buffered-file), block: (addr block), vars: (addr stack live-var), fn-outputs: (addr list var)
|
||||
# . prologue
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
|
@ -9799,7 +10050,7 @@ $emit-subx-block:check-empty:
|
|||
(push *(ebp+0x10) 0) # false
|
||||
# emit block->statements
|
||||
(lookup *(esi+4) *(esi+8)) # Block-stmts Block-stmts => eax
|
||||
(emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10))
|
||||
(emit-subx-stmt-list *(ebp+8) %eax *(ebp+0x10) *(ebp+0x14))
|
||||
(pop *(ebp+0x10)) # => eax
|
||||
(pop *(ebp+0x10)) # => eax
|
||||
(pop *(ebp+0x10)) # => eax
|
||||
|
|
Loading…
Reference in New Issue