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:
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.
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.
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.