Skip to content

Niveau 1 · Chapitre 1.1

Les registres

Le stockage propre au CPU : les 16 registres généraux x86-64, leurs noms 32/16/8 bits, la règle d’extension par zéro, et pourquoi ils battent la mémoire.

Presque chaque instruction exécutée par un CPU lit ou écrit un registre : une petite case de stockage nommée, située dans le processeur lui-même. La mémoire contient des gigaoctets ; les registres, quelques dizaines de valeurs. Mais c’est là que le travail se fait — calculs, comparaisons et adresses passent tous par eux.

Pourquoi les registres existent

Atteindre la mémoire principale est lent. Un registre se lit en moins d’un cycle ; le cache le plus proche demande quelques cycles ; la mémoire principale peut en demander une centaine ou plus. Chaque valeur qu’une instruction va chercher en mémoire doit d’abord transiter par le bus.

La différence se voit dans le simulateur. Ouvrez l’onglet CPU & buses de la démo ci-dessous et observez le compteur bus cycles en avançant :

Live · Registres ou mémoire
program— ▸ is the next instruction
  1. mov ecx, 5
  2. mov eax, ecx ; registre → registre
  3. mov DWORD PTR [rbp-4], ecx ; registre → mémoire (écriture)
  4. mov edx, DWORD PTR [rbp-4] ; mémoire → registre (lecture)
step 0
Loading emulator…
  • mov eax, ecx coûte 1 lecture : seulement la lecture de l’instruction elle-même. La valeur circule à l’intérieur du CPU.
  • L’écriture coûte la lecture de l’instruction plus 1 écriture sur le bus de données.
  • La lecture coûte la lecture de l’instruction plus 1 lecture.

Voilà pourquoi les compilateurs s’efforcent de garder les variables les plus utilisées dans des registres, et pourquoi le code non optimisé — qui range chaque variable en mémoire — est bien plus lent.

Les registres généraux

Le x86-64 possède 16 registres généraux de 64 bits. Les huit premiers portent des noms hérités du 8086 16 bits, quand chacun avait sa spécialité :

RegistreRôle historiqueUsage typique aujourd’hui
raxaccumulateurvaleurs de retour, calcul
rbxbaseusage général (préservé entre appels)
rcxcompteurcompteurs de boucle, 4e argument
rdxdonnées3e argument, moitié haute de mul/div
rsiindex source2e argument, source des chaînes
rdiindex destination1er argument, destination des chaînes
rbppointeur de basepointeur de cadre (code non optimisé)
rsppointeur de piletoujours le sommet de la pile
r8–r15(ajoutés par AMD64)5e/6e arguments, usage général

Seul rsp est vraiment spécial : il pointe toujours sur le sommet de la pile, et push, pop, call et ret l’utilisent implicitement. Pour le matériel, les autres sont interchangeables — leurs rôles viennent des conventions, que vous verrez dans les conventions d’appel.

En x86 32 bits, seuls les huit premiers existent, sur 32 bits : eax, ebx, ecx, edx, esi, edi, ebp, esp.

Un registre, plusieurs noms

Chaque registre de 64 bits est accessible par morceaux. Pour rax :

NomBitsPartie de rax
rax64tout
eax32bits 0–31
ax16bits 0–15
ah8bits 8–15
al8bits 0–7

Le même schéma vaut pour rbx/ebx/bx/bh/bl, rcx et rdx. rsi, rdi, rbp et rsp ont des formes 8 bits sil, dil, bpl, spl, et les nouveaux registres utilisent des suffixes : r8d (32), r8w (16), r8b (8).

La démo ci-dessous remplit rax avec un motif reconnaissable, puis écrit dans des morceaux de plus en plus petits. Avancez pas à pas en surveillant rax dans le panneau des registres :

Live · Sous-registres
program— ▸ is the next instruction
  1. mov rax, 0x1122334455667788
  2. mov al, 0xff ; seuls les bits 0–7 changent
  3. mov ah, 0xee ; seuls les bits 8–15 changent
  4. mov ax, 0x1234 ; seuls les bits 0–15 changent
  5. mov eax, 0xdeadbeef ; …et les 32 bits hauts sont effacés !
step 0
Loading emulator…
Aprèsrax
mov al, 0xff0x11223344556677ff
mov ah, 0xee0x112233445566eeff
mov ax, 0x12340x1122334455661234
mov eax, 0xdeadbeef0x00000000deadbeef

La règle d’extension par zéro

Cette dernière ligne surprend tout le monde une fois : écrire un registre 32 bits efface les 32 bits hauts du registre 64 bits, alors qu’écrire un registre 8 ou 16 bits laisse le reste intact.

AMD a conçu le x86-64 ainsi délibérément. Si écrire eax devait préserver la moitié haute, le CPU devrait fusionner l’ancienne et la nouvelle valeur — une dépendance supplémentaire qui ralentit tout. Comme la plupart du code manipule des int 32 bits, rendre ces écritures « propres » est un gros gain.

Cela explique aussi deux idiomes omniprésents dans le code compilé :

  • mov eax, eax semble ne rien faire, mais il étend par zéro eax dans rax — ce qui convertit un unsigned int en valeur 64 bits.
  • xor eax, eax est la façon la plus courte de mettre tout rax à 0.

Les registres qu’on ne peut pas écrire avec mov

  • rip, le pointeur d’instruction, contient l’adresse de la prochaine instruction. On ne l’écrit jamais directement : jmp, call, ret et les sauts conditionnels le modifient. On peut le lire via l’adressage : lea rax, [rip+message] calcule l’adresse de message relativement à l’instruction courante — c’est ainsi que le code indépendant de la position retrouve ses données.
  • rflags contient les drapeaux d’état écrits par l’arithmétique et lus par les sauts conditionnels — le sujet du chapitre suivant.

Il existe d’autres registres : les registres SIMD xmm0–xmm15 (et leurs formes plus larges ymm/zmm) pour les flottants et le calcul vectoriel, les registres de segment, et les registres de contrôle utilisés par le système d’exploitation. Les seize registres généraux sont ceux que vous lirez dans presque chaque ligne de désassemblage.

À retenir

  • Les registres sont le stockage de travail du CPU ; la mémoire, atteinte par le bus, est bien plus lente.
  • Le x86-64 a 16 registres généraux de 64 bits ; seul rsp a un rôle imposé par le matériel.
  • rax, eax, ax, ah et al sont des vues des mêmes bits.
  • Écrire un registre 32 bits met la moitié haute à zéro ; écrire 8 ou 16 bits, non.
  • rip et rflags sont modifiés par les instructions elles-mêmes, pas par mov.

Dans ce niveau

  1. 1.1Les registres
  2. 1.2Drapeaux, cmp et sauts conditionnels
  3. 1.3Conventions d’appel (System V, cdecl)
  4. 1.4Assembleur, éditeur de liens et chargeur
  5. 1.5Directives, sections et macros