Dans la visite x86-64 des manuels Intel , j'ai lu
Le fait le plus surprenant est peut-être qu'une instruction telle que met
MOV EAX, EBX
automatiquement à zéro les 32 bits supérieurs duRAX
registre.
La documentation Intel (3.4.1.1 Registres à usage général en mode 64 bits en architecture de base manuelle) citée à la même source nous dit:
- Les opérandes 64 bits génèrent un résultat 64 bits dans le registre à usage général de destination.
- Les opérandes 32 bits génèrent un résultat 32 bits, étendu de zéro à un résultat 64 bits dans le registre général de destination.
- Les opérandes 8 bits et 16 bits génèrent un résultat 8 bits ou 16 bits. Les 56 bits ou 48 bits supérieurs (respectivement) du registre universel de destination ne sont pas modifiés par l'opération. Si le résultat d'une opération 8 bits ou 16 bits est destiné au calcul d'adresse 64 bits, étendez explicitement le registre à 64 bits.
Dans les assemblages x86-32 et x86-64, des instructions 16 bits telles que
mov ax, bx
ne montrez pas ce genre de comportement "étrange" que le mot supérieur de eax est mis à zéro.
Ainsi: 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).
r32
opérande de destination zéro le 32 haut, plutôt que de fusionner. Par exemple, certains assembleurs remplacerontpmovmskb r64, xmm
parpmovmskb r32, xmm
, en enregistrant un REX, car la version de destination 64 bits se comporte de la même manière. Même si la section Operation du manuel répertorie les 6 combinaisons de 32 / 64bit dest et 64/128 / 256b source séparément, l'extension zéro implicite du formulaire r32 duplique l'extension zéro explicite du formulaire r64. Je suis curieux de connaître la mise en œuvre de HW ...xor eax,eax
ouxor r8d,r8d
est le meilleur moyen de mettre à zéro RAX ou R8 (l'enregistrement d'un préfixe REX pour RAX, et XOR 64 bits n'est même pas géré spécialement sur Silvermont). En relation: Comment fonctionnent exactement les registres partiels sur Haswell / Skylake? L'écriture d'AL semble avoir une fausse dépendance à RAX, et AH est incohérenteRéponses:
Je ne suis pas AMD ou ne parle pas pour eux, mais j'aurais fait de la même manière. Parce que la remise à zéro de la moitié supérieure ne crée pas une dépendance à la valeur précédente, que le processeur devrait attendre. Le mécanisme de renommage de registre serait essentiellement vaincu s'il n'était pas fait de cette façon.
De cette façon, vous pouvez écrire du code rapide en utilisant des valeurs 32 bits en mode 64 bits sans avoir à rompre explicitement les dépendances tout le temps. Sans ce comportement, chaque instruction 32 bits en mode 64 bits devrait attendre quelque chose qui s'est passé auparavant, même si cette partie haute ne serait presque jamais utilisée. (La création de
int
64 bits gaspillerait l'encombrement du cache et la bande passante mémoire; x86-64 prend en charge le plus efficacement les tailles d'opérande 32 et 64 bits )Le comportement des tailles d'opérande 8 et 16 bits est étrange. La folie des dépendances est l'une des raisons pour lesquelles les instructions 16 bits sont désormais évitées. x86-64 a hérité de 8086 pour 8 bits et 386 pour 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 de registres partiels? pour des détails pratiques sur la manière dont les écritures dans les registres partiels 8 et 16 bits (et les lectures ultérieures du registre complet) sont gérées par des processeurs réels.
la source
Cela économise simplement de l'espace dans les instructions et le jeu d'instructions. Vous pouvez déplacer de petites valeurs immédiates vers un registre 64 bits à l'aide d'instructions existantes (32 bits).
Cela vous évite également d'avoir à encoder des valeurs de 8 octets pour
MOV RAX, 42
, quandMOV EAX, 42
peut être réutilisé.Cette optimisation n'est pas aussi importante pour les opérations 8 et 16 bits (car elles sont plus petites), et changer les règles à cet endroit casserait également l'ancien code.
la source
XOR EAX, EAX
carXOR RAX, RAX
il faudrait un préfixe REX.[rsi + edx]
n'est pas autorisé). Bien sûr, éviter les fausses dépendances / les blocages de registres partiels (l'autre réponse) est une autre raison majeure.Sans zéro s'étendant à 64 bits, cela signifierait qu'une instruction lisant à partir de
rax
aurait 2 dépendances pour sonrax
opérande (l'instruction qui écriteax
et l'instruction qui écritrax
avant), cela signifie que 1) le ROB devrait avoir des entrées pour plusieurs dépendances pour un seul opérande, ce qui signifie que le ROB exigerait plus de logique et de transistors et prendrait plus d'espace, et que l'exécution serait plus lente en attendant une deuxième dépendance inutile qui pourrait prendre des siècles à s'exécuter; ou bien 2), ce qui, je suppose, arrive avec les instructions 16 bits, l'étape d'allocation s'arrête probablement (c'est-à-dire que si le RAT a une allocation active pour uneax
écriture et qu'uneeax
lecture apparaît, elle se bloque jusqu'à ce que l'ax
écriture se retire).Le seul avantage d'une extension non nulle est de s'assurer que les bits d'ordre supérieur de
rax
sont inclus, par exemple, s'il contient à l'origine 0xffffffffffffffff, le résultat serait 0xffffffff00000007, mais il y a très peu de raisons pour que l'ISA fasse cette garantie à un tel coût, et il est plus probable que l'avantage de l'extension zéro soit en fait plus nécessaire, ce qui permet d'économiser la ligne de code supplémentairemov rax, 0
. En garantissant qu'il sera toujours zéro étendu à 64 bits, les compilateurs peuvent travailler avec cet axiome à l'esprit tandis que dedansmov rdx, rax
,rax
n'a qu'à attendre sa seule dépendance, ce qui signifie qu'il peut commencer l'exécution plus rapidement et se retirer, libérant des unités d'exécution. En outre, il permet également des idiomes zéro plus efficaces commexor eax, eax
zérorax
sans nécessiter un octet REX.la source
cmovbe
c'est 2 uops maiscmovb
c'est 1). Mais aucun processeur qui renomme un registre partiel ne le fait comme vous le suggérez. Au lieu de cela, ils insèrent un uop de fusion si un reg partiel est renommé séparément du reg complet (c'est-à-dire qu'il est "sale"). Voir Pourquoi GCC n'utilise-t-il pas de registres partiels? et Comment fonctionnent exactement les registres partiels sur Haswell / Skylake? L'écriture d'AL semble avoir une fausse dépendance à RAX, et AH est incohérenteThis gives a delay of 5 - 6 clocks. The reason is that a temporary register has been assigned to AL to make it independent of AH. The execution unit has to wait until the write to AL has retired before it is possible to combine the value from AL with the value of the rest of EAX
Je ne trouve pas d'exemple de la `` fusion uop '' qui serait utilisée pour résoudre ce problème, même chose pour un décrochage partiel du drapeaumov al, [mem]
va de même pour une charge micro-fusionnée + ALU- merge, renommer uniquement AH, et un uop de fusion AH pose toujours problème seul. Les mécanismes de fusion d'indicateurs partiels dans ces processeurs varient, par exemple Core2 / Nehalem se bloque toujours pour les indicateurs partiels, contrairement à partial-reg.