使用单个divl指令的除法和模数(i386,amd64)

7 assembly gcc x86-64 division i386

我试图为gcc提供内联汇编以使用单divl指令获得除法和模数.不幸的是,我在集会上并不擅长.有人可以帮我这个吗?谢谢.

D0S*_*ots 9

你正在寻找这样的东西:

__asm__("divl %2\n"
       : "=d" (remainder), "=a" (quotient)
       : "g" (modulus), "d" (high), "a" (low));
Run Code Online (Sandbox Code Playgroud)

虽然我同意其他评论者,通常GCC会为你做这件事,你应该尽可能避免内联汇编,有时你需要这个结构.

例如,如果高字小于模数,那么执行这样的除法是安全的.但是,GCC不够聪明才能实现这一点,因为在一般情况下,将64位数字除以32位数会导致溢出,因此它会调用库例程来执行额外的工作.(对于64位ISA,替换为128位/ 64位.)

  • 源操作数的“g”不正确,因为 DIV 不支持立即操作数。使用“rm”。(实际上,您可能需要“g”,这样如果您在除数是编译时常量时强制使用 DIV 搬起石头砸自己的脚,那么您的代码将在编译时中断。)您也许可以还使用 __builtin_constant_p` 来检测编译时常数操作数。它应该与 gcc 配合良好,但 clang 在函数内联之前对其进行评估(因此您会得到漏报)。 (2认同)

ray*_*ylu 7

你不应该试着自己优化它.GCC已经这样做了.

volatile int some_a = 18, some_b = 7;

int main(int argc, char *argv[]) {
    int a = some_a, b = some_b;
    printf("%d %d\n", a / b, a % b);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

运行

gcc -S test.c -O
Run Code Online (Sandbox Code Playgroud)

产量

main:
.LFB11:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    some_a(%rip), %esi
    movl    some_b(%rip), %ecx
    movl    %esi, %eax
    movl    %esi, %edx
    sarl    $31, %edx
    idivl   %ecx
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
Run Code Online (Sandbox Code Playgroud)

请注意,余数%edx不会移动,因为它也是传递给printf的第三个参数.

编辑:32位版本不那么令人困惑.传递-m32产生

main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $16, %esp
    movl    some_a, %eax
    movl    some_b, %ecx
    movl    %eax, %edx
    sarl    $31, %edx
    idivl   %ecx
    movl    %edx, 8(%esp)
    movl    %eax, 4(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    $0, %eax
    leave
    ret
Run Code Online (Sandbox Code Playgroud)


CB *_*ley 5

幸运的是,您不必使用内联汇编来实现此目的.gcc会尽可能自动执行此操作.

$ cat divmod.c

struct sdiv { unsigned long quot; unsigned long rem; };

struct sdiv divide( unsigned long num, unsigned long divisor )
{
        struct sdiv x = { num / divisor, num % divisor };
        return x;
}
Run Code Online (Sandbox Code Playgroud)

$ gcc -O3 -std=c99 -Wall -Wextra -pedantic -S divmod.c -o -

        .file   "divmod.c"
        .text
        .p2align 4,,15
.globl divide
        .type   divide, @function
divide:
.LFB0:
        .cfi_startproc
        movq    %rdi, %rax
        xorl    %edx, %edx
        divq    %rsi
        ret
        .cfi_endproc
.LFE0:
        .size   divide, .-divide
        .ident  "GCC: (GNU) 4.4.4 20100630 (Red Hat 4.4.4-10)"
        .section        .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)


Jer*_*fin 4

是的——divl 将在 eax 中产生商,在 edx 中产生余数。使用 Intel 语法,例如:

mov eax, 17
mov ebx, 3
xor edx, edx
div ebx
; eax = 5
; edx = 2
Run Code Online (Sandbox Code Playgroud)