2017-09-29 5 views
2

Dites% edi contient x et je veux finir avec 37 * x en utilisant seulement 2 instructions de leal consécutives, comment j'y arriverais?Comment multiplier un registre par 37 en utilisant seulement 2 instructions de leal consécutives en x86?

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 me comprendre ce que le nombre de mettre en place des 8 et 4, de sorte que le résultat (% eax) sera 37x

+1

Avez-vous essayé de demander à un compilateur? https://godbolt.org/g/nMbujJ. Astuce: vous devez changer plus que les facteurs d'échelle. Le 2ème LEA utilise l'entrée originale + le premier résultat LEA. –

+1

De plus, compte tenu de votre choix de registres, cela ressemble à l'utilisation d'un code 64 bits pour l'ABI System V. Il n'y a aucun avantage à utiliser un préfixe de remplacement de taille d'adresse pour obtenir des modes d'adressage 32 bits en mode 64 bits. Il est toujours prudent de laisser 'lea' tronquer un mode d'adressage 64 bits sur 32 bits. https://stackoverflow.com/questions/34377711/which-2s-complement-integer-operations-can-be-used-with-zeroing-high-bits-in –

Répondre

6

At -O3, gcc will emit:

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

    leal (%rdi,%rdi,8), %eax 
    leal (%rdi,%rax,4), %eax 
    ret 

C'est en utilisant 37 = 9*4 + 1

Vous êtes en bonne compagnie en ne notant pas celui-ci, cependant: clang récent utilisera normalement 2 instructions lea au lieu d'un imul (par ex. pour *15), mais il manque celui-ci et utilise:

imull $37, %edi, %eax 
    ret 

Il ne fait *21 avec le même schéma que les usages gcc, comme 5*4 + 1. (Clang3.6 et toujours utilisé plus tôt imul moins qu'il y ait une alternative simple instruction shl ou lea)

CPI et MSVC utilisent également imul, mais ils ne semblent pas aimer à l'aide en utilisant 2 lea instructions, de sorte que le imul est "à dessein" là-bas.

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

mtune=silvermont devrait probablement être prêt à remplacer 32 bits imul avec une seule instruction, car il a une latence/débit de 3 1c cycle de multiplication, mais décodage est souvent le goulot d'étranglement (selon Agner Fog, http://agner.org/optimize/). Vous pouvez même envisager imul $64, %edi, %eax (ou d'autres puissances de 2) au lieu de mov/shl, car imul-immédiat est une copie-et-multiplier.


Paradoxalement, gcc manque le x45 cas, et utilise imul, tandis que clang utilise 2 lea s. Supposons qu'il est temps de déposer des rapports de bug d'optimisation manquée. Si 2 leas sont meilleurs que 1 imul, ils devraient être utilisés autant que possible.

+0

"clang utilisera normalement 2 instructions lea au lieu d'un imul "J'ai fait l'observation inverse.D'après ce que j'ai vu, Clang a tendance à préférer émettre un seul 'IMUL' sur n'importe quelle autre séquence d'instructions. J'ai pensé à signaler cela comme un défaut d'optimisation il y a quelques temps, mais j'ai alors décidé que cela n'avait pas d'importance. L'impact sur les performances est pratiquement incommensurable. :-) –

+0

@CodyGray: qui a changé dans clang3.7 ou quelque chose. Je suppose qu'ils ont décidé de favoriser la latence sur le débit. Mais oui, les plus âgés préféraient «imul» à moins de pouvoir utiliser un seul «lea». –