Skip to content

Niveau 3 · Chapitre 3.2

Tour d’horizon du jeu d’instructions

Les quelques dizaines d’instructions x86-64 qui forment la quasi-totalité du code réel, famille par famille — déplacer des données, calculer, manipuler des bits, comparer et choisir, appeler et sauter — chacune en direct dans le simulateur.

Le chapitre précédent a montré que 20 mnémoniques représentent environ 93 % des instructions des programmes ordinaires. Ce chapitre passe ces instructions en revue, regroupées selon ce qu’elles font. Chaque groupe a une démo : avancez pas à pas et regardez les registres changer.

Déplacer des données

InstructionEffet
mov dst, srccopier
movzx dst, srccopier une valeur plus petite en remplissant le haut de zéros (non signé)
movsx dst, src / movsxdcopier une valeur plus petite en remplissant le haut avec son bit de signe (signé)
lea dst, [adresse]calculer une adresse, sans lire la mémoire
xchg a, béchanger deux valeurs
push src / pop dstdéposer une valeur sur la pile / en retirer une (rsp bouge de 8)
Live · Déplacer des données
program— ▸ is the next instruction
  1. mov ebx, 0xff80
  2. movzx eax, bl ; bl = 0x80 → 128
  3. movsx ecx, bl ; bl = 0x80 → -128
  4. lea edx, [rbx+rbx*4] ; 5 × rbx, sans accès mémoire
  5. xchg eax, ecx
  6. push rax
  7. pop rdi
step 0
Loading emulator…
The instructions the compiler generated, and the registers, flags and stack they change.

Le même octet, 0x80, devient 128 avec movzx et -128 (0xffffff80) avec movsx : tout dépend de si la source est vue comme non signée ou signée. En C, c’est la différence entre unsigned char et signed char. lea calcule rbx + rbx*4 = 5 × 0xff80 = 0x4fd80 sans toucher à la mémoire ni aux drapeaux, d’où son usage par les compilateurs pour les petites multiplications. push puis pop fait passer rax dans rdi par la pile, et rsp descend de 8 puis remonte.

Calculer

InstructionEffet
add / subadditionner / soustraire, en positionnant les drapeaux
inc / decajouter / retirer 1 (sans toucher à CF)
negchanger le signe : 0 − x
imul dst, src (et une forme à 3 opérandes)multiplication signée, en gardant la moitié basse
mul src / imul srcmultiplication complète : rdx:rax = rax × src
div src / idiv srcdiviser rdx:rax par src : quotient dans rax, reste dans rdx
cdq / cqoétendre le signe de eax dans edx (rax dans rdx) avant une division signée
Live · Calculer
program— ▸ is the next instruction
  1. mov eax, 100
  2. mov ecx, 7
  3. add eax, ecx ; 107
  4. sub eax, 9 ; 98
  5. imul eax, ecx ; 686
  6. add eax, 3 ; 689
  7. cdq ; edx = signe de eax (0)
  8. idiv ecx ; 689 / 7 → eax = 98, edx = 3
  9. neg eax ; -98
step 0
Loading emulator…
The instructions the compiler generated, and the registers, flags and stack they change.

La division est à part. Elle ne prend pas d’opérande de destination : elle divise toujours la valeur de 64 bits edx:eax (ou celle de 128 bits rdx:rax), et elle écrit deux résultats, le quotient dans eax et le reste dans edx. C’est pourquoi une division signée est presque toujours précédée de cdq ou cqo dans le code compilé. Diviser par zéro ne produit pas de valeur : cela déclenche une exception du processeur, que l’OS transforme en plantage (SIGFPE sous Linux).

La division est aussi lente : souvent 10 à 40 cycles selon le CPU et la taille des opérandes, contre 1 pour une addition. Les compilateurs l’évitent quand ils le peuvent : diviser par une constante se compile en général en une multiplication par un « nombre magique » suivie d’un décalage.

Bits et décalages

InstructionEffet
and / or / xor / notET, OU, OU exclusif, NON bit à bit
shldécalage à gauche : × 2ⁿ
shrdécalage à droite en remplissant de zéros : ÷ 2ⁿ non signé
sardécalage à droite en recopiant le bit de signe : ÷ 2ⁿ signé, arrondi vers le bas
rol / rorrotation : les bits qui sortent d’un côté rentrent de l’autre
bt, popcnt, bsf…tester un bit, compter les 1, trouver le premier 1
Live · Bits et décalages
program— ▸ is the next instruction
  1. mov eax, 0xb6 ; 1011 0110
  2. and eax, 0x0f ; garder les 4 bits bas : 0110 = 6
  3. or eax, 0x80 ; mettre le bit 7 à 1 : 0x86
  4. xor eax, 0xff ; inverser les 8 bits bas : 0x79
  5. shl eax, 4 ; × 16 : 0x790
  6. shr eax, 8 ; ÷ 256 : 7
  7. mov ecx, -16
  8. sar ecx, 2 ; -16 / 4 = -4 (signe conservé)
  9. mov edx, -16
  10. shr edx, 2 ; 0x3ffffffc (signe perdu)
step 0
Loading emulator…
The instructions the compiler generated, and the registers, flags and stack they change.
  • and avec un masque garde certains bits, or les met à 1 et xor les inverse.
  • xor eax, eax est la façon habituelle de mettre un registre à 0, et test eax, eax celle de vérifier s’il vaut 0.
  • sar et shr ne diffèrent que sur les nombres négatifs. sar garde -16 négatif (-4) ; shr le traite comme un énorme nombre non signé.
  • sar arrondit aussi vers moins l’infini : -17 sar 2 vaut -5, alors que -17 / 4 vaut -4 en C. C’est pourquoi une division signée par une puissance de deux se compile avec quelques instructions de plus autour du sar.

Comparer et choisir

Ces instructions comparent, puis agissent sur le résultat à travers les drapeaux. Le chapitre sur les drapeaux détaille chaque condition.

InstructionEffet
cmp a, bcalculer a − b, positionner les drapeaux, jeter le résultat
test a, bcalculer a ET b, positionner les drapeaux, jeter le résultat
jcc étiquettesauter si la condition cc est vraie : je, jne, jl, jg, jb, ja…
setcc r8mettre un octet à 1 ou 0 selon la condition
cmovcc dst, srccopier seulement si la condition est vraie, sans saut
Live · Comparer et choisir
program— ▸ is the next instruction
  1. mov eax, 5
  2. cmp eax, 3 ; 5 − 3 : plus grand
  3. setg bl ; bl = 1
  4. mov ecx, 100
  5. cmovg eax, ecx ; plus grand → eax = 100
  6. test eax, eax ; eax vaut-il zéro ?
  7. jz done ; non : on continue
  8. mov edx, 1
  9. done:
  10. nop
step 0
Loading emulator…
The instructions the compiler generated, and the registers, flags and stack they change.

cmp suivi d’un saut conditionnel, c’est ainsi que se compilent tous les if, les boucles et les switch. setcc et cmovcc prennent le même genre de décision sans sauter, ce qui évite les mauvaises prédictions de branchement.

Appels et sauts

InstructionEffet
jmp ciblesauter sans condition ; la cible peut aussi être dans un registre (jmp rax)
call cibleempiler l’adresse de retour, puis sauter
retdépiler l’adresse de retour dans rip
leavedéfaire un cadre de pile : mov rsp, rbp + pop rbp
Live · Appeler une fonction
program— ▸ is the next instruction
  1. _start:
  2. mov edi, 6
  3. call triple ; empile l’adresse de retour
  4. jmp end
  5. mov eax, 0 ; jamais exécuté
  6. triple:
  7. lea eax, [rdi+rdi*2]
  8. ret ; retour au jmp
  9. end:
  10. nop
step 0
Loading emulator…
The instructions the compiler generated, and the registers, flags and stack they change.

call empile l’adresse du jmp avant de sauter à triple. ret la dépile, et l’exécution reprend juste après l’appel, avec eax = 18. La façon dont circulent les arguments et la valeur de retour est le sujet du chapitre sur les conventions d’appel.

Le reste du jeu d’instructions

Quelques familles apparaissent plus rarement, mais vous les croiserez :

  • Les instructions de chaîne — movsb, stosb, cmpsb, scasb avec un préfixe rep — copient, remplissent, comparent ou parcourent des blocs entiers de mémoire en une seule instruction. rep stosb sert à memset. Le chapitre sur le microcode montre comment une telle instruction devient une boucle.
  • Les instructions système : syscall demande un service au système d’exploitation, int3 est le point d’arrêt d’un octet qu’insèrent les débogueurs (0xcc), hlt arrête le CPU jusqu’à la prochaine interruption, et nop ne fait rien — utile comme remplissage.
  • Virgule flottante et SIMD : les instructions SSE et AVX travaillent sur les registres xmm, ymm et zmm. Elles traitent les float et double (addsd, mulss…) et opèrent sur plusieurs valeurs à la fois (paddd, vaddps…). L’ancienne pile flottante x87 survit surtout dans le code 32 bits.

À retenir

  • Déplacer des données : mov, movzx/movsx (élargissement non signé ou signé), lea (calcul d’adresse sans mémoire), push/pop.
  • Calculer : add, sub, inc, dec, neg, imul. La division utilise implicitement rdx:rax et rend un quotient et un reste.
  • Bits : and/or/xor avec des masques. shl/shr/sar multiplient et divisent par des puissances de deux ; sar garde le signe.
  • Décider : cmp/test positionnent les drapeaux, puis jcc, setcc ou cmovcc agissent en conséquence.
  • Contrôle : jmp, call, ret — plus les instructions de chaîne, système et SIMD pour les tâches spéciales.

Dans ce niveau

  1. 3.1Ce qu’est une instruction
  2. 3.2Tour d’horizon du jeu d’instructions
  3. 3.3Les registres
  4. 3.4Drapeaux, cmp et sauts conditionnels
  5. 3.5Conventions d’appel (System V, cdecl)
  6. 3.6Assembleur, éditeur de liens et chargeur
  7. 3.7Directives, sections et macros