Деление и модуль с использованием одной инструкции divl (i386, amd64) - PullRequest
7 голосов
/ 10 апреля 2011

Я пытался придумать встроенную сборку для gcc, чтобы получить и деление, и модуль, используя одну инструкцию divl. К сожалению, я не так хорош в сборке. Может ли кто-нибудь помочь мне в этом? Спасибо.

Ответы [ 4 ]

9 голосов
/ 28 мая 2012

Вы ищете что-то вроде этого:

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

Хотя я согласен с другими комментаторами, что обычно GCC сделает это за вас, и вам следует избегать встроенной сборки, когда это возможно, иногда вам нужна эта конструкция.

Например, если старшее слово меньше модуля, тогда можно безопасно выполнить такое деление следующим образом.Тем не менее, GCC не достаточно умен, чтобы понять это, потому что в общем случае деление 64-битного числа на 32-битное число может привести к переполнению, и поэтому он вызывает библиотечную процедуру для дополнительной работы.(Замените на 128-битные / 64-битные для 64-битных ISA.)

7 голосов
/ 10 апреля 2011

Вы не должны пытаться оптимизировать это самостоятельно. 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;
}

Запуск

gcc -S test.c -O

выходы

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

Обратите внимание, что остаток,% 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
5 голосов
/ 10 апреля 2011

К счастью, вам не нужно прибегать к встроенной сборке, чтобы добиться этого.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;
}

$ 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
3 голосов
/ 10 апреля 2011

Да - divl произведет частное в eax, а остаток в edx. Используя синтаксис Intel, например:

mov eax, 17
mov ebx, 3
xor edx, edx
div ebx
; eax = 5
; edx = 2
...