168 votes

Pourquoi les instructions x86-64 sur les registres 32 bits mettent-elles à zéro la partie supérieure du registre 64 bits complet ?

Dans le cadre de la x86-64 Visite des manuels d'Intel Je lis

Le fait le plus surprenant est peut-être qu'une instruction telle que MOV EAX, EBX met automatiquement à zéro les 32 bits supérieurs de RAX registre.

La documentation d'Intel (3.4.1.1 General-Purpose Registers in 64-Bit Mode dans le manuel Basic Architecture) citée à la même source nous indique :

  • Les opérandes de 64 bits génèrent un résultat de 64 bits dans le registre polyvalent de destination.
  • Les opérandes de 32 bits génèrent un résultat de 32 bits, étendu à zéro pour obtenir un résultat de 64 bits dans le registre polyvalent de destination.
  • Les opérandes de 8 et 16 bits génèrent un résultat de 8 ou 16 bits. Les 56 bits ou 48 bits supérieurs (respectivement) du registre polyvalent de destination ne sont pas modifiés par l'opération. Si le résultat d'une opération sur 8 ou 16 bits est destiné à un calcul d'adresse sur 64 bits, il faut explicitement étendre le registre en signe à l'ensemble des 64 bits.

Dans l'assemblage x86-32 et x86-64, les instructions à 16 bits telles que

mov ax, bx

ne montrent pas ce genre de comportement "étrange" selon lequel le mot supérieur de eax est mis à zéro.

Par conséquent, quelle est la raison pour laquelle ce comportement a été introduit ? À première vue, cela semble illogique (mais la raison pourrait être que je suis habitué aux bizarreries de l'assemblage x86-32).

126voto

harold Points 14256

Je ne suis pas l'AMD et je ne parle pas en leur nom, mais j'aurais agi de la même manière. Parce que la mise à zéro de la moitié haute ne crée pas de dépendance à l'égard de la valeur précédente, que le CPU devrait attendre. Les renommage de registre Le mécanisme serait essentiellement mis en échec si l'on ne procédait pas de cette manière.

Ainsi, vous pouvez écrire du code rapide en utilisant des valeurs 32 bits en mode 64 bits sans avoir à rompre explicitement les dépendances en permanence. Sans ce comportement, chaque instruction 32 bits en mode 64 bits devrait attendre quelque chose qui s'est produit auparavant, même si cette partie élevée ne serait presque jamais utilisée. (Réalisation int Le 64 bits gaspillerait l'empreinte de la mémoire cache et la bande passante de la mémoire ; x86-64 prend en charge de la manière la plus efficace les tailles d'opérandes 32 et 64 bits )

Le comportement pour les tailles d'opérandes de 8 et 16 bits est étrange. La folie de la dépendance est l'une des raisons pour lesquelles les instructions 16 bits sont évitées aujourd'hui. La x86-64 a hérité de la 8086 pour les 8 bits et de la 386 pour les 16 bits, et a décidé de faire fonctionner les registres 8 et 16 bits de la même manière en mode 64 bits qu'en mode 32 bits.


Voir aussi Pourquoi GCC n'utilise-t-il pas les registres partiels ? pour obtenir des détails pratiques sur la manière dont les écritures dans les registres partiels de 8 et 16 bits (et les lectures ultérieures du registre complet) sont gérées par les CPU réels.

13voto

Bo Persson Points 42821

Cela permet simplement d'économiser de l'espace dans les instructions et dans le jeu d'instructions. Vous pouvez déplacer de petites valeurs immédiates vers un registre 64 bits en utilisant les instructions existantes (32 bits).

Cela vous évite également d'avoir à encoder des valeurs de 8 octets pour les MOV RAX, 42 , lorsque MOV EAX, 42 peuvent être réutilisés.

Cette optimisation n'est pas aussi importante pour les opérations sur 8 et 16 bits (parce qu'elles sont plus petites), et changer les règles dans ces cas-là casserait aussi du vieux code.

5voto

Lewis Kelsey Points 181

Si le zéro n'est pas étendu à 64 bits, cela signifierait qu'une instruction lisant de rax aurait 2 dépendances pour son rax (l'instruction qui écrit dans eax et l'instruction qui écrit dans rax avant elle), il en résulterait une décrochage partiel du registre ce qui commence à devenir délicat lorsqu'il y a 3 largeurs possibles. rax et eax écrire dans le registre complet, ce qui signifie que le jeu d'instructions 64 bits n'introduit pas de nouvelles couches de renommage partiel.

mov rdx, 1
mov rax, 6
imul rax, rdx
mov rbx, rax
mov eax, 7 //retires before add rax, 6
mov rdx, rax // has to wait for both imul rax, rdx and mov eax, 7 to finish before dispatch to the execution units, even though the higher order bits are identical anyway

Le seul avantage de ne pas étendre à zéro est de s'assurer que les bits de poids fort de rax sont inclus, par exemple, s'il contient à l'origine 0xffffffffffffffffffff, le résultat sera 0xffffffff00000007, mais il y a très peu de raisons pour l'ISA de faire cette garantie à un tel coût, et il est plus probable que l'avantage de l'extension zéro soit en fait davantage requis, ce qui permet d'économiser la ligne de code supplémentaire. mov rax, 0 . En garantissant qu'il sera toujours égal à zéro étendu à 64 bits, les compilateurs peuvent travailler en gardant cet axiome à l'esprit pendant que dans mov rdx, rax , rax ne doit attendre qu'une seule dépendance, ce qui signifie qu'il peut commencer l'exécution plus rapidement et se retirer, libérant ainsi des unités d'exécution. En outre, cela permet également d'utiliser des idiomes zéro plus efficaces tels que xor eax, eax à zéro rax sans nécessiter d'octet REX.

4voto

supercat Points 25534

D'un point de vue matériel, la possibilité de mettre à jour la moitié d'un registre a toujours été quelque peu coûteuse, mais sur le 8088 original, il était utile de permettre au code assembleur écrit à la main de traiter le 8088 comme ayant soit deux registres 16 bits non liés à une pile et huit registres 8 bits, soit six registres 16 bits non liés à une pile et zéro registre 8 bits, soit d'autres combinaisons intermédiaires de registres 16 bits et 8 bits. Cette utilité valait bien le coût supplémentaire.

Lorsque le 80386 a ajouté des registres de 32 bits, il n'a pas été possible d'accéder à la moitié supérieure d'un registre, mais une instruction telle que ROR ESI,16 serait suffisamment rapide pour qu'il soit encore intéressant de pouvoir conserver deux valeurs de 16 bits dans l'ESI et de passer de l'une à l'autre.

Avec la migration vers l'architecture x64, l'augmentation du nombre de registres et d'autres améliorations architecturales ont réduit la nécessité pour les programmeurs de faire entrer le maximum d'informations dans chaque registre. En outre, le renommage des registres a augmenté le coût des mises à jour partielles des registres. Si le code devait faire quelque chose comme :

    mov rax,[whatever]
    mov [something],rax
    mov rax,[somethingElse]
    mov [yetAnother],rax

le renommage des registres et la logique connexe permettraient à l'unité centrale d'enregistrer le fait que la valeur chargée à partir de [whatever] devra être écrit dans something et ensuite, tant que les deux dernières adresses sont différentes, autoriser la charge de somethingElse et l'enregistrer dans yetAnother à traiter sans avoir à attendre que les données soient effectivement lues dans la base de données de l whatever . Si la troisième instruction était mov eax,[somethingElse Cependant, s'il était spécifié que les bits supérieurs ne sont pas affectés, la quatrième instruction ne pourrait pas stocker RAX avant que le premier chargement ne soit terminé, et même si l'on autorise le chargement de EAX serait difficile, car le processeur devrait tenir compte du fait que la moitié inférieure est disponible, alors que la moitié supérieure ne l'est pas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X