Reputation: 792
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!
Upvotes: 25
Views: 29768
Reputation: 471399
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.
Upvotes: 12
Reputation: 30449
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
Upvotes: 30