I wrote the following C program:
#include "stdio.h"
__declspec(noinline) void DivideTest(int num, int denom)
{
int quo = num / denom;
int rem = num % denom;
printf("Quotient: %dnRemainder: %dn", quo, rem);
}
int main(int argc, char* argv[])
{
//Use volatile variables to prevent result from being hardcoded.
volatile int num = 20;
volatile int denom = 3;
DivideTest(num, denom);
return 0;
}
As expected, the output is as follows:
Quotient: 6
Remainder: 2
However, when I compile in release mode (i.e. with optimizations enabled), debug the program in Visual Studio, and look at the disassembly, it shows DivideTest using a two-operand idiv:
_DivideTest:
push esi
mov esi,edx
mov eax,ecx
cdq
idiv eax,esi
Disassembling using dumpbin produces the same result. But every source I’ve found (
example) says that idiv can only take one operand.
When I try to assemble code using a two-operand idiv, it fails, as I would expect based on the documentation. Why does the disassembly show a two-operand idiv?
2
Answers
The disassembly is technically wrong. It just shows how
idiv
works. Theidiv
instruction always useseax
(in factedx:eax
pair) to get the dividend from and to store the result (quotient and remainder).Your disassembler (the Visual Studio debugger?) is inventing its own asm syntax to describe
idiv
.In the machine code, the EDX:EAX operand (dividend input and rem:quo output) is implicit, implied by the opcode, which is why there’s no way to choose different registers.
Mainstream asm syntax (including AT&T, and also Intel’s and AMD’s manuals) mirror that choice, making it a one-operand instruction with only the explicit divisor.
But there’s no reason a flavour of assembly couldn’t instead require EAX as a first operand, e.g. as a way to set the operand-size when there’s a memory source operand. (Like
idiv eax, [ecx]
instead ofidiv dword ptr [ecx]
). MASM already does that forrep movsd
vs.rep movs es:[edi], ds:[esi]
, which Intel actually documents, note the 2nd and 3rd paragraphs of the Description. See also Assembly: what's the difference between `stos m32` and `stosd` mnemonic? for another example.It’s a bit weird that it only uses EAX instead of EDX:EAX or edx,eax since it’s making up its own syntax anyway. What’s the point of making one register operand explicit when there’s still another that’s implicit? I guess you could argue that EDX:EAX is a pair addressed by its lower half.
Of course, it’s easier to read disassembly and to copy/paste it into asm source files you’re working on if it uses the same syntax as an assembler you’re using. I don’t know if MASM accepts this or not. If it does, it should only accept
eax
(orrax
/ax
/al
) as the first operand. (I’ve heard of bad assemblers that accept misleading operands as the placeholder for an implicit operand or otherwise assembling a non-encodeable instruction to machine code that does something different.)This is different from CS:APP example uses idivq with two operands? – that’s a case of totally fake assembly purportedly generated by GCC, but actually made up by some clowns hired by a publisher to mess up the practice problems in the global edition of CS:APP 3e. In that case they’re showing a form with an immediate source and RCX destination, neither of which are possible!