Recherche…


Introduction

La famille x86 existe depuis longtemps et, en tant que telle, de nombreuses astuces et techniques ont été découvertes et développées qui sont de notoriété publique - ou peut-être moins publiques. La plupart de ces astuces tirent parti du fait que de nombreuses instructions font effectivement la même chose, mais différentes versions sont plus rapides, permettent d'économiser de la mémoire ou n'affectent pas les indicateurs. Voici quelques astuces qui ont été découvertes. Chacun a ses avantages et ses inconvénients, donc devrait être répertorié.

Remarques

En cas de doute, vous pouvez toujours vous reporter au manuel de référence sur l’optimisation des architectures d’Intel 64 et IA-32 , qui est une excellente ressource de la société derrière l’architecture x86.

Remise à zéro d'un registre

La manière évidente de mettre un registre à zéro est de MOV dans un 0 - par exemple:

B8 00 00 00 00    MOV eax, 0

Notez qu'il s'agit d'une instruction de 5 octets.

Si vous souhaitez contourner les indicateurs ( MOV n'affecte jamais les indicateurs), vous pouvez utiliser l'instruction XOR pour XOR-bit au niveau du registre avec lui-même:

33 C0             XOR eax, eax

Cette instruction ne nécessite que 2 octets et s'exécute plus rapidement sur tous les processeurs .

Déplacer le drapeau Carry dans un registre

Contexte

Si le drapeau Carry ( C ) contient une valeur que vous souhaitez mettre dans un registre, la méthode naïve consiste à faire quelque chose comme ceci:

    mov  al, 1
    jc   NotZero
    mov  al, 0
NotZero:

Utilisez 'sbb'

Une manière plus directe, en évitant le saut, consiste à utiliser "Subtract with Borrow":

    sbb  al,al    ; Move Carry to al

Si C est zéro, alors al sera zéro. Sinon, ce sera 0xFF ( -1 ). Si vous en avez besoin pour être 0x01 , ajoutez:

    and  al, 0x01 ; Mask down to 1 or 0

Avantages

  • A peu près la même taille
  • Deux ou un instructions en moins
  • Pas de saut coûteux

Les inconvénients

  • C'est opaque pour un lecteur peu familier avec la technique
  • Il modifie les autres drapeaux

Tester un registre pour 0

Contexte

Pour savoir si un registre contient un zéro, la technique naïve consiste à le faire:

    cmp   eax, 0

Mais si vous regardez l'opcode pour cela, vous obtenez ceci:

83 F8 00      cmp   eax, 0

Utiliser le test

    test   eax, eax      ; Equal to zero?

Examinez l'opcode que vous obtenez:

85 c0         test   eax, eax

Avantages

  • Seulement deux octets!

Les inconvénients

  • Opaque pour un lecteur peu familier avec la technique

Vous pouvez également jeter un œil à la question Q & R sur cette technique .

Appels système Linux avec moins de ballonnement

Dans Linux 32 bits, les appels système sont généralement effectués à l’aide de l’instruction sysenter (je dis généralement que les anciens programmes utilisent le maintenant int 0x80 déconseillé int 0x80 ), mais cela peut prendre beaucoup de place dans un programme. peut raccourcir pour raccourcir et accélérer les choses.
C'est généralement la disposition d'un appel système sur Linux 32 bits:

mov eax, <System call number>
mov ebx, <Argument 1> ;If applicable
mov ecx, <Argument 2> ;If applicable
mov edx, <Argument 3> ;If applicable
push <label to jump to after the syscall>
push ecx
push edx
push ebp
mov ebp, esp
sysenter

C'est énorme! Mais il y a quelques astuces que nous pouvons tirer pour éviter ce désordre.
La première consiste à définir ebp à la valeur de esp diminuée de la taille de 3 registres 32 bits, soit 12 octets. C'est génial tant que vous ne voulez pas écraser ebp, edx et ecx avec des erreurs (par exemple lorsque vous déplacez une valeur dans ces registres de toute façon), nous pouvons le faire en utilisant l'instruction LEA pour ne pas avoir besoin pour affecter la valeur de ESP lui-même.

mov eax, <System call number>
mov ebx, <Argument 1>
mov ecx, <Argument 2>
mov edx, <Argument 3>
push <label to jump to after the syscall>
lea ebp, [esp-12]
sysenter

Cependant, nous n'avons pas fini, si l'appel système est sys_exit, nous pouvons éviter de pousser n'importe quoi sur la pile!

mov eax, 1
xor ebx, ebx ;Set the exit status to 0
mov ebp, esp
sysenter

Multipliez par 3 ou 5

Contexte

Pour obtenir le produit d'un registre et d'une constante et le stocker dans un autre registre, la manière naïve consiste à le faire:

    imul ecx, 3      ; Set ecx to 5 times its previous value
    imul edx, eax, 5 ; Store 5 times the contend of eax in edx

Utiliser lea

Les multiplications sont des opérations coûteuses. Il est plus rapide d'utiliser une combinaison de changements et d'ajouts. Pour le cas particulier de multiplication du contenu d'un registre 32 ou 64 bits qui n'est pas esp ou rsp de 3 ou 5, vous pouvez utiliser l'instruction lea. Cela utilise le circuit de calcul d'adresse pour calculer le produit rapidement.

    lea ecx, [2*ecx+ecx] ; Load 2*ecx+ecx = 3*ecx into ecx
    lea edx, [4*edx+edx] ; Load 4*edx+edx = 5*edx into edx

De nombreux assembleurs comprendront aussi

    lea ecx, [3*ecx]
    lea edx, [5*edx]

Pour tous les multiplicables possibles autres que ebp ou rbp , la rbp l'instruction résultante est la même qu'avec l'utilisation d' imul .

Avantages

  • Exécute beaucoup plus vite

Les inconvénients

  • Si votre multiplicande est ebp ou rbp il faut un octet de plus en utilisant imul
  • Plus à taper si votre assembleur ne prend pas en charge les raccourcis
  • Opaque pour un lecteur peu familier avec la technique


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow