X86 assembly - Handling the IDIV instruction
Solution 1
The first part of Mysticials answer is correct, idiv
does a 128/64 bit division, so the value of rdx
, which holds the upper 64 bit from the dividend must not contain a random value. But a zero extension is the wrong way to go.
As you have signed variables, you need to sign extend rax
to rdx:rax
. There is a specific instruction for this, cqto
(convert quad to oct) in AT&T and cqo
in Intel syntax. AFAIK newer versions of gas accept both names.
movq %rdx, %rbx
cqto # sign extend rax to rdx:rax
idivq %rbx
Solution 2
The idivq
instruction divides a 128-bit integer (rdx:rax
) by the given source operand.
-
rax
holds the lower 64-bits of the dividend. -
rdx
holds the upper 64-bits of the dividend.
When the quotient doesn't fit into 64-bits, idiv
will fault (#DE exception, which the OS handles by delivering a SIGFPE signal as required by POSIX for arithmetic exceptions).
Since you're compiling code that uses signed int
, you also need to sign extend rax
to rdx:rax
, that means copying the rax
sign bit to every bit of rdx
and is accomplished with cqo alias cqto:
movq %rdx, %rbx # or load into RBX or RCX in the first place
cqo
idivq %rbx # signed division of RDX:RAX / RBX
If you'd been doing unsigned
division, you'd zero RDX to zero-extend RAX into RDX:RAX:
movq %rdx, %rbx
xor %edx, %edx # zero "rdx"
divq %rbx # unsigned division of RDX:RAX / RBX
Also note that in the x86-64 System V ABI, int
is a 32-bit signed type, not 64-bit. Widening it to 64-bit is legal in this case (because the result is the same) but makes your code slower, especially for division.
Related videos on Youtube
elyas-bhy
Updated on September 29, 2020Comments
-
elyas-bhy over 3 years
I am currently writing a simple C compiler, that takes a .c file as input and generates assembly code (X86, AT&T syntax). Everyting is good, but when I try to execute a IDIVQ instruction, I get a floating-point exception. Here's my input:
int mymain(int x){ int d; int e; d = 3; e = 6 / d; return e; }
And here is my generated code:
mymain: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 movq %rdi, -40(%rbp) movq $3, -8(%rbp) movq $6, %rax movq -8(%rbp), %rdx movq %rdx, %rbx idivq %rbx movq %rax, -16(%rbp) movq -16(%rbp), %rax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size mymain, .-mymain
According to http://www.cs.virginia.edu/~evans/cs216/guides/x86.html, idivq %rbx should produce 6/d (the quotient) in %rax. But I'm getting a floating-point exception, and I can't seem to find the problem.
Any help will be much appreciated!
-
Michael Burr about 12 yearsUnrelated to this question, but should you be doing
movq %rdi, -40(%rbp)
without having adjusted theesp
register? Or is that OK because of the x64 'red zone'? -
Peter Cordes over 3 years@MichaelBurr: That's ok because the x86-64 System V guarantees a red-zone of 128 bytes below RSP; this is what mainstream compilers like GCC do: Why is there no "sub rsp" instruction in this function prologue and why are function parameters stored at negative rbp offsets?. And of course you shouldn't adjust ESP, that would truncate RSP to 32 bits and make later stack operations fault, if you did any before
leave
.
-
-
marekb about 12 yearsZeroing rdx will work with positive numbers, but in case of negative rax probably rdx = -1 is needed... Isn't it?
-
Michael Burr about 12 yearsI think marekb is right - shouldn't the
xorq
instrcution be acqo
instruction to sign extendrax
intordx:rax
? -
Marco van de Voort about 12 yearsIn this case, with signed typing: I think so yes.
-
elyas-bhy about 12 yearsIndeed, I was running my tests and encountered an error when handling signed values. I haven't seen this instruction before, but it seems to solve the issue now. Thank you!
-
Peter Cordes almost 8 yearsUnsigned:
xor %rdx,%rdx
/div
. Signed:cqo
/idiv
. Using idiv after zeroing rdx is only appropriate if your divisor is signed but your dividend is unsigned. (And you want a signed result). A C compiler should only generate that as an optimization after casting auint64_t
to an__int128_t
, when it can prove there won't be a divide exception. Also note that 64bitdiv
is somewhat faster than 64bitidiv
on recent Intel CPUs.