7
votes

Division et modulus utilisant une instruction de divl unique (I386, AMD64)

J'essayais de trouver une assemblée en ligne pour GCC pour obtenir la division et le module à l'aide d'instructions de divl divl . Malheureusement, je ne suis pas si bon à l'assemblée. Quelqu'un pourrait-il s'il vous plaît aidez-moi à ce sujet? Merci.


2 commentaires

Voir Stackoverflow.com/Questtions/3323445/... Où je l'ai utilisé comme exemple de MSVC Inline ASM vs. GNU C en ligne ASM. (Y compris une fonction d'emballage DIVL DIVL DIVL En ligne, avec une seule instruction à l'intérieur de l'instruction ASM en ligne, identique à la réponse correcte de D0SSBOots ici.)


Voir aussi Stackoverflow.com/Questtions/32741032/... pour une double duplicata, montrant que vous n'avez pas besoin d'utiliser Inline ASM (le compilateur le fait pour vous), mais aussi montrer comment le faire correctement avec inline ASM. (Pas de différence significative pour la réponse de Dosboot)


5 Réponses :


3
votes

Oui - Un DIVL produira le quotient dans EAX et le reste dans EDX. Utilisation de Syntaxe Intel, par exemple:

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


6 commentaires

Jerry, c'est drôle parce que gcc n'essaie même pas d'optimiser deux de ces opérations en une. Je vous achèterai une bière si vous pouviez me donner une assemblée GCC afin que je puisse l'utiliser comme une fonction en ligne ou quelque chose de ...


Fausse alarme. Pardon. GCC le fait pour moi si je n'oublie pas de spécifier les indicateurs requis.


Cette réponse est plutôt hors tension, car la question est spécifique à la GCC et vous y répondez avec l'assemblage dans Intel Syntax Charles et Raylu sont tout autour mieux.


@EventRancarroll: Ce qu'il a demandé était une séquence d'instructions de montage qui produirait à la fois le quotient et le reste en utilisant uniquement une seule instruction DIV. Aucun de ces "réponses" ne tente même de fournir cela. Ils sont tous deux des commentaires vraiment, pas des réponses. La seule autre réponse réelle ici est des dosants '.


Non ce n'est pas ce qu'il demande, lisez la question suivante: "J'essayais de trouver Assemblée en ligne pour GCC pour obtenir la division et le module à l'aide d'une seule instruction DIVL . "


@Evancarroll: Oui, "asssidbly". Aucune des réponses que vous semblez aimer montrer comment faire la tâche dans la langue d'assemblage du tout . Ils viennent juste de défendre l'utilisation de C ++. Ce n'est clairement pas un même vaguement similaire à répondre à la question qu'il a posée.



8
votes

Vous ne devriez pas essayer d'optimiser cette vous-même. GCC fait déjà cela.

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


6 commentaires

En fait, gcc ne fonctionne pas. Du moins pas avec des drapeaux, je me sers. Peut-être que je manque quelque chose.


Je suis sur GCC (Debian 4.5.2-4) 4.5.2, mais même 4,3 cela. Êtes-vous en train de passer -o?


J'utilise GCC 4.6 .. mais sacrément! J'ai passé 30 minutes à jouer avec cela avant de demander ici et il s'avère que je viens d'oublier de préciser '-O3'. J'avais '-MTune = natif "mais ça n'a pas aidé. Merci! Je devrais avoir un peu de sommeil ...


Pourquoi seulement -o ? Utilisez au moins -O2 de sorte que GCC peephole le signe-extension dans edx avec CDQ (AKA CLTD in & t Syntaxe, IIRC). Quoi qu'il en soit, si vous écrivez une fonction qui prend deux arguments et stocke les résultats des globaux (ou renvoyez-les comme une structure, ou renvoyez une et rangez l'autre), il évitera d'optimiser l'optimisation mais nécessite moins d'instructions si vous écrivez un < Code> Main () et utilisez volatile . Voir Stackoverflow.com/Questtions/32741032/...


Pour montrer le minimum nécessaire pour obtenir le comportement décrit dans la question.


Ok, mais cela montre gcc rendre le code maladroites, et comment vous devez réellement compiler. (Et BTW, l'OP demandait divl , vous auriez pu utiliser non signé ne doit donc à zéro EDX au lieu de signe s'étendant dans EDX: EAX).



5
votes

Heureusement, vous n'avez pas à recourir à l'assemblage ligne pour y parvenir. gcc fera automatiquement quand il le peut.

$ cat divmod.c code> p>

        .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


1 commentaires

Oui tu as raison. S'avère que j'avais trop de bière et j'ai oublié d'allumer des optimisations. Merci. J'ai suscité tout le monde.



9
votes

Vous cherchez quelque chose comme ceci:

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


1 commentaires

"g" pour la source opérande n'est pas correct, car DIV ne supporte pas les opérandes immédiats. Utilisez "rm" . (En fait, vous voudrez peut-être « g » si votre code cassera à la compilation si vous jamais vous tirer une balle dans le pied en forçant l'utilisation de DIV lorsque le diviseur est une constante de compilation .) Vous pourriez peut-être aussi utiliser __ builtin_constant_p pour détecter la compilation constante opérandes. Il faut bien travailler avec gcc, mais il évalue clang avant que la fonction inline (donc vous obtenez des faux négatifs).



1
votes

Voici un exemple dans le code du noyau Linux sur DIVL

    /*
 * 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;                          \
})


0 commentaires