Comment multiplier un registre par 37 en utilisant seulement 2 instructions leal consécutives dans x86?

disons que %edi contient x et que je veux finir avec 37*x en n'utilisant que 2 instructions leal consécutives, comment m'y prendre?

par exemple pour obtenir 45x vous feriez

leal (%edi, %edi, 8), %edi   
leal (%edi, %edi, 4), %eax (to be returned)

Je ne peux pas pour la vie de moi comprendre quels nombres mettre à la place des 8 et 4 de sorte que le résultat (%eax) sera 37x

2
demandé sur Peter Cordes 2017-09-29 04:44:12

1 réponses

À -O3 , gcc va émettre (Godbolt compilateur explorer) :

int mul37(int a)  { return a*37; }

    leal    (%rdi,%rdi,8), %eax      # eax = a * 9
    leal    (%rdi,%rax,4), %eax      # eax = a + 4*(a*9)
    ret

qui utilise 37 = 9*4 + 1 , ne détruisant pas la valeur originale a avec la première lea donc il peut utiliser les deux dans le 2.

vous êtes en bonne compagnie en ne repérant pas celui - ci, bien que: clang récent (3.8 et plus récent) sera normalement utiliser 2 lea instructions au lieu d'un imul (par exemple pour *15 ), mais il manque celui-ci et utilise:

    imull   , %edi, %eax
    ret

Il fait le faire *21 avec le même motif que gcc utilise, comme 5*4 + 1 . (clang3.6 et les versions antérieures toujours utilisé imul sauf s'il y a une instruction unique alternative shl ou lea )

ICC et MSVC utilisent également imul, mais ils ne semblent pas aimer utiliser 2 lea instructions, de sorte que le imul est "sur le but".

voir le lien godbolt pour une variété de multiplicateurs avec gcc7.2 contre clang5.0. Il est intéressant d'essayer gcc -m32 -mtune=pentium ou même pentium3 pour voir combien d'autres instructions gcc wiling à utiliser à l'époque. Même si P2 / P3 a une latence de 4 cycles pour imul r, r, i , donc c'est un peu fou. Pentium a 9 cycle imul et aucun OOO pour cacher la latence, il est donc logique d'essayer de l'éviter.

mtune=silvermont ne devrait probablement être prêt à remplacer 32 bits imul par une seule instruction, car il a une latence de 3 cycles / 1C de débit multiplier, mais le décodage est souvent le goulot d'étranglement (selon le brouillard D'Agner, http://agner.org/optimize / ). Vous pouvez même considérer imul , %edi, %eax (ou d'autres puissances de 2) au lieu de mov / shl , car imul-immediate est une copie-et-multiplier.


ironiquement, gcc manque le * 45 , et utilise des imul , tandis que clang utilise 2 lea . Crois qu'il est temps de déposer certains manqué-l'optimisation des rapports de bogues. si 2 LEAs sont meilleurs que 1 IMUL, ils doivent être utilisés dans la mesure du possible.

"1519310920 plus Anciens" clang (3,7 et plus) utilise des imul à moins qu'une seule lea fera l'affaire. Je n'ai pas cherché dans le changelog pour voir s'ils ont fait des benchmarks pour décider de favoriser la latence plutôt que le débit.


Related: Using LEA on values that are not addresses / pointers? réponse canonique sur les raisons pour lesquelles LEA utilise la syntaxe mémoire-opérande et l'encodage machine, même si c'est un shift+add instruction (et fonctionne sur un ALU, pas AGU, dans la plupart des microarchitectures modernes.)

6
répondu Peter Cordes 2018-06-14 01:53:50