Search code examples
assemblyx86-64integer-division

Do you convert signed integers to hex before doing an `idiv` in 64-bit Intel CPUs?


Suppose you want to divide -198077031546722079779 by 23 using gas. As this dividend is larger than %rax, how do you put it in %rdx:%rax? All the books I read on assembly avoid illustrating idiv with examples, so I am lost.


Solution

  • You put the high bits into rdx and the low bits into rax, as you probably know.

    If you're trying to use the number as a constant in your program, then yes, hex is one possible way to split it up. I converted it using calc, adding 2^128 to express it as an unsigned two's complement number, then using printf("%x") to print it as hex. The result is 0xfffffffffffffff543210543edc9abdd. So we could do

    movq $0xfffffffffffffff5, %rdx
    movq $0x43210543edc9abdd, %rax
    

    It would be nice if you could have gas do the arithmetic for you. It does support arbitrary-precision constants ("bignums") but unfortunately does not let you use them in immediates. Nor can it do any arithmetic with them, as Jester discovered (per comment below).

    However, it does support assembling 128-bit integer data into memory with the .octa directive. So you could do

            .section .rodata
    number:
            .octa -198077031546722079779
    # ...
    movq number(%rip), %rax # x86 is little endian, least significant qword is at lower address
    movq (number+8)(%rip), %rdx
    # ...
    

    Note we are using RIP-relative addressing. If you're in a situation where that isn't appropriate, then just movq number, %rax ; movq number+8, %rdx.

    If you are receiving the number as input at runtime, then you'll need to implement a decimal-to-binary conversion routine (like strtol) but which uses 128-bit arithmetic. So additions will need an add/adc pair, multiplication will need several multiply instructions "longhand" style, etc.