web-dev-qa-db-fra.com

x86_64 enregistre rax / eax / ax / al en écrasant tout le contenu du registre

Comme cela est largement annoncé, les processeurs modernes x86_64 ont des registres 64 bits qui peuvent être utilisés de manière rétro-compatible en tant que registres 32 bits, registres 16 bits et même registres 8 bits, par exemple:

0x1122334455667788
  ================ rax (64 bits)
          ======== eax (32 bits)
              ====  ax (16 bits)
              ==    ah (8 bits)
                ==  al (8 bits)

Un tel schéma peut être pris à la lettre, c’est-à-dire que l’on ne peut toujours accéder qu’à la partie du registre en utilisant un nom désigné à des fins de lecture ou d’écriture, ce qui serait hautement logique. En fait, cela est vrai pour tout ce qui est jusqu'à 32 bits:

mov  eax, 0x11112222 ; eax = 0x11112222
mov  ax, 0x3333      ; eax = 0x11113333 (works, only low 16 bits changed)
mov  al, 0x44        ; eax = 0x11113344 (works, only low 8 bits changed)
mov  ah, 0x55        ; eax = 0x11115544 (works, only high 8 bits changed)
xor  ah, ah          ; eax = 0x11110044 (works, only high 8 bits cleared)
mov  eax, 0x11112222 ; eax = 0x11112222
xor  al, al          ; eax = 0x11112200 (works, only low 8 bits cleared)
mov  eax, 0x11112222 ; eax = 0x11112222
xor  ax, ax          ; eax = 0x11110000 (works, only low 16 bits cleared)

Cependant, les choses semblent assez délicates dès que nous arrivons aux choses 64 bits:

mov  rax, 0x1111222233334444 ;           rax = 0x1111222233334444
mov  eax, 0x55556666         ; actual:   rax = 0x0000000055556666
                             ; expected: rax = 0x1111222255556666
                             ; upper 32 bits seem to be lost!
mov  rax, 0x1111222233334444 ;           rax = 0x1111222233334444
mov  ax, 0x7777              ;           rax = 0x1111222233337777 (works!)
mov  rax, 0x1111222233334444 ;           rax = 0x1111222233334444
xor  eax, eax                ; actual:   rax = 0x0000000000000000
                             ; expected: rax = 0x1111222200000000
                             ; again, it wiped whole register

Un tel comportement me semble très ridicule et illogique. Il semblerait que d'essayer d'écrire quoi que ce soit sur eax par n'importe quel moyen entraîne l'effacement des 32 bits élevés du registre rax.

Donc, j'ai 2 questions:

  1. Je crois que ce comportement délicat doit être documenté quelque part, mais je n'arrive pas à trouver d'explication détaillée (expliquant comment exactement 32 bits de registre 64 bits ont été effacés) où que ce soit. Ai-je raison de dire que l'écriture sur eax efface toujours rax, ou est-ce quelque chose de plus compliqué? Cela s'applique-t-il à tous les registres 64 bits ou y a-t-il des exceptions?

    Un question étroitement liée mentionne le même comportement, mais hélas, il n’existe à nouveau aucune référence exacte à la documentation.

    En d'autres termes, j'aimerais un lien vers la documentation qui spécifie ce comportement.

  2. Est-ce juste moi ou tout cela semble être vraiment étrange et illogique (c'est-à-dire eax-ax-ah-al, rax-ax-ah-al ayant un comportement et rax-eax en ayant un autre)? Peut-être me manque-t-il une sorte de point vital ici sur pourquoi cela a-t-il été mis en œuvre comme ça

    Une explication sur "pourquoi" serait hautement appréciée.

67
GreyCat

Le modèle de processeur décrit dans le manuel du processeur Intel/AMD est un modèle assez imparfait pour le moteur d'exécution réel d'un noyau moderne. En particulier, la notion de registre de processeur ne correspond pas à la réalité, il n’existe pas de registre EAX ou RAX.

L'une des tâches principales du décodeur d'instructions consiste à convertir les instructions x86/x64 héritées en micro-ops, instructions d'un processeur de type RISC. Petites instructions faciles à exécuter simultanément et permettant de tirer parti de plusieurs sous-unités d'exécution. Permettant jusqu'à 6 instructions à exécuter en même temps.

Pour que cela fonctionne, la notion de registres de processeurs est également virtualisée. Le décodeur d'instructions alloue un registre à partir d'une grande banque de registres. Lorsque l'instruction est retirée, la valeur de ce registre alloué dynamiquement est réécrite dans le registre contenant la valeur de, par exemple, RAX.

Pour que cela fonctionne correctement et efficacement, en permettant l'exécution simultanée de nombreuses instructions, il est très important que ces opérations ne soient pas interdépendantes. Et le pire type que vous puissiez avoir est que la valeur du registre dépend d'autres instructions. Le registre EFLAGS est notoire, de nombreuses instructions le modifient.

Même problème avec la façon dont vous aimez cela fonctionne. Gros problème, il faut fusionner deux valeurs de registre lorsque l’instruction est retirée. Créer une dépendance aux données qui va obstruer le noyau. En forçant la valeur 32 bits supérieure à 0, cette dépendance disparaît instantanément, il n'est plus nécessaire de fusionner. Warp 9 vitesse d'exécution.

71
Hans Passant