windowsvisual-c++x86visual-studio-debuggingdisassembly

Why does the disassembly of this function show a two-operand idiv?


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: %d\nRemainder: %d\n", 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?


Solution

  • 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 of idiv dword ptr [ecx]). MASM already does that for rep 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 (or rax/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!