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
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.
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.)