How do I use the IDIV instruction? (also: Reserving the AX register)
pwaller opened this issue · 12 comments
The IDIV instruction puts its result in the AX register. The problem I have is that I can't figure out how to prevent GeneralPurposeRegister64
from allocating AX, so my pseudoregisters end up overwriting the input and result.
How do I prevent pseudoregisters from allocating AX?
Unless there is a bug in PeachPy, it should be handled automatically, i.e. PeachPy would not allocate a virtual register to AX
if the livespan of the virtual register overlaps with IDIV
instruction.
Maybe this is a bug, then? In this case I move 10 into rax
, 4 into rbx
, emit IDIV(rbx)
which afaiu modifies the rax
(SIC) register such that it contains the result of the division and the remainder.
r
in this case ends up being ax
, even though I'm using it for the division. What am I missing?
with Function("foo", (), int64_t) as function:
r = GeneralPurposeRegister64()
MOV(r, 5)
MOV(rax, 10)
MOV(rbx, 4)
IDIV(bl)
MOV([r10], ah)
RETURN(r)
// func foo() int64
TEXT ·foo(SB),4,$0-8
MOVQ $5, AX
MOVQ $10, AX
MOVQ $4, BX
IDIVB BX
MOVB AH, 0(R10)
MOVQ AX, ret+0(FP)
RET
Looks like a bug
Is there any workaround to force the register allocator not to use AX?
How is the register allocator supposed to work? Is it supposed to spot that I'm using the AX register already explictly via its physical name, or does it know about the semantics of the IDIV instruction, or both?
- PeachPy knows (through
opcodes
package) thatIDIV
instruction writes output toAX
&DX
registers - PeachPy performs liveness analysis to detect the lifespans of both virtual and physical registers.
- Register allocator ensures that registers with overlapping lifespans do not share the same physical register.
I instrumented the code and found that the general purpose register dict is empty in _bind_registers
. Should that be the case?
I have made a simpler case which I think reproduces the underlying problem in #63 and doesn't involve the IDIV instruction, just MOVs.
With #64, this now has the correct behaviour as I would expect. This example now choses CX
for the virtual register r
instead of AX
, because it knows AX
is live after the IDIV
.
with Function("foo", (), int64_t) as function:
r = GeneralPurposeRegister64()
MOV(r, 5)
IDIV(bl)
RETURN(r)
// func foo() int64
TEXT ·foo(SB),4,$0-8
MOVQ $5, CX
IDIVB BX
MOVQ CX, ret+0(FP)
RET
This is mostly fixed with 8f742b7, but there is another problem related to high 8-bit registers specifically (ah
, bh
, ch
, dh
).
I hope so, but didn't test