7

divl単一の命令を使用して除算とモジュラスの両方を取得するために、gccのインラインアセンブリを考え出そうとしていました。残念ながら、私は組み立てがあまり得意ではありません。誰かがこれについて私を助けてくれませんか?ありがとうございました。

4

5 に答える 5

9

あなたはこのようなものを探しています:

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

私は他のコメント提供者に同意しますが、通常はGCCがこれを行い、可能な場合はインラインアセンブリを避ける必要がありますが、この構成が必要になる場合もあります。

たとえば、上位ワードがモジュラスよりも小さい場合は、このように除算を実行しても安全です。ただし、GCCはこれを実現するのに十分なほど賢くはありません。一般的な場合、64ビットの数値を32ビットの数値で除算するとオーバーフローが発生する可能性があるため、ライブラリルーチンを呼び出して余分な作業を行います。(64ビットISAの場合は128ビット/ 64ビットに置き換えてください。)

于 2012-05-28T08:03:40.393 に答える
8

これを自分で最適化しようとしないでください。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に渡される3番目の引数でもあるため、移動されないことに注意してください。

編集: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
于 2011-04-09T23:30:20.783 に答える
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;
}

$ 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
于 2011-04-09T23:32:12.473 に答える
3

はい-divlはeaxで商を生成し、残りはedxで生成します。Intel構文を使用します。例:

mov eax, 17
mov ebx, 3
xor edx, edx
div ebx
; eax = 5
; edx = 2
于 2011-04-09T23:23:56.217 に答える
1

これは、divlに関するLinuxカーネルコードの例です。

    /*
 * do_div() is NOT a C function. It wants to return
 * two values (the quotient and the remainder), but
 * since that doesn't work very well in C, what it
 * does is:
 *
 * - modifies the 64-bit dividend _in_place_
 * - returns the 32-bit remainder
 *
 * This ends up being the most efficient "calling
 * convention" on x86.
 */
#define do_div(n, base)                     \
({                              \
    unsigned long __upper, __low, __high, __mod, __base;    \
    __base = (base);                    \
    if (__builtin_constant_p(__base) && is_power_of_2(__base)) { \
        __mod = n & (__base - 1);           \
        n >>= ilog2(__base);                \
    } else {                        \
        asm("" : "=a" (__low), "=d" (__high) : "A" (n));\
        __upper = __high;               \
        if (__high) {                   \
            __upper = __high % (__base);        \
            __high = __high / (__base);     \
        }                       \
        asm("divl %2" : "=a" (__low), "=d" (__mod)  \
            : "rm" (__base), "0" (__low), "1" (__upper));   \
        asm("" : "=A" (n) : "a" (__low), "d" (__high)); \
    }                           \
    __mod;                          \
})
于 2019-12-09T15:02:20.713 に答える