Je pense que je cherche une réponse à une question triviale. J'essaie de comprendre pourquoi l'architecture MIPS utilise une valeur "zéro" explicite dans un registre alors que vous pouvez réaliser la même chose en XOR simplement n'importe quel registre contre lui-même. On pourrait dire que l'opération est déjà faite pour vous; cependant, je ne peux pas vraiment imaginer une situation où vous utiliseriez beaucoup de valeurs "zéro". J'ai lu les articles originaux de Hennessey, et il attribue simplement un zéro en fait sans aucune justification réelle.
Existe-t-il une raison logique d'avoir une affectation binaire codée en dur de zéro?
mise à jour: Dans 8k d'un exécutable de xc32-gcc pour le noyau MIPS dans le PIC32MZ, j'ai une seule instance de "zéro".
add t3,t1,zero
la réponse réelle: j'ai attribué la prime à la personne qui avait les informations sur MIPS et les codes de condition. La réponse réside en fait dans l'architecture MIPS pour les conditions. Bien que je ne veuille initialement pas y consacrer de temps, j'ai examiné l'architecture pour opensparc , MIPS-V et OpenPOWER (ce document était interne) et voici les résultats résumés. Le registre R0 nécessaire à la comparaison sur les branches en raison de l'architecture du pipeline.
- comparaison entre un entier et zéro et une branche (bgez, bgtz, blez, bltz)
- entier comparer deux registres et branche (beq, bne)
- entier compare deux registres et piège (teq, tge, tlt, tne)
- registre de comparaison entier et immédiat et piège (teqi, tgei, tlti, tnei)
Cela se résume simplement à l'apparence du matériel dans la mise en œuvre. Dans le manuel MIPS-V, il y a une citation non référencée à la page 68:
Les branches conditionnelles ont été conçues pour inclure des opérations de comparaison arithmétique entre deux registres (comme cela est également fait dans PA-RISC et Xtensa ISA), plutôt que d'utiliser des codes de condition (x86, ARM, SPARC, PowerPC), ou pour comparer uniquement un registre contre zéro ( Alpha, MIPS), ou deux registres uniquement pour l'égalité (MIPS). Cette conception a été motivée par l'observation qu'une instruction combinée de comparaison et de dérivation se transforme en un pipeline régulier, évite un état de code de condition supplémentaire ou l'utilisation d'un registre temporaire, et réduit la taille du code statique et la récupération dynamique des instructions trac. Un autre point est que les comparaisons avec zéro nécessitent un retard de circuit non trivial (en particulier après le passage à la logique statique dans les processus avancés) et sont donc presque aussi chères que les comparaisons d'amplitude arithmétique. Un autre avantage d'une instruction de comparaison et de dérivation fusionnée est que les dérivations sont observées plus tôt dans le flux d'instructions frontales et peuvent donc être prédites plus tôt. Il y a peut-être un avantage à une conception avec des codes de condition dans le cas où plusieurs branches peuvent être prises sur la base des mêmes codes de condition, mais nous pensons que ce cas est relativement rare.
Le document MIPS-V ne frappe pas l'auteur de la section citée. Je remercie chacun pour son temps et sa considération.
la source
Réponses:
Le registre zéro sur les processeurs RISC est utile pour deux raisons:
C'est une constante utile
Selon les restrictions de l'ISA, vous ne pouvez pas utiliser un littéral dans le codage de certaines instructions, mais vous pouvez être sûr de pouvoir l'utiliser
r0
pour obtenir 0.Il peut être utilisé pour synthétiser d'autres instructions
C'est peut-être le point le plus important. En tant que concepteur ISA, vous pouvez échanger un registre à usage général contre un registre zéro pour pouvoir synthétiser d'autres instructions utiles. La synthèse d'instructions est bonne car en ayant moins d'instructions réelles, vous avez besoin de moins de bits pour encoder une opération dans un opcode, ce qui libère de l'espace dans l'espace d'encodage des instructions. Vous pouvez utiliser cet espace pour avoir, par exemple, des décalages d'adresse et / ou des littéraux plus importants.
La sémantique du registre zéro est comme
/dev/zero
sur les systèmes * nix: tout ce qui y est écrit est supprimé et vous relisez toujours 0.Voyons quelques exemples de la façon dont nous pouvons faire des pseudo-instructions à l'aide du
r0
registre zéro:Le cas de MIPS
J'ai regardé de plus près le jeu d'instructions MIPS. Il existe une poignée de pseudo-instructions qui utilisent
$zero
; ils sont principalement utilisés pour les branches. Voici quelques exemples de ce que j'ai trouvé:Quant à savoir pourquoi vous n'avez trouvé qu'une seule instance du
$zero
registre dans votre désassemblage, c'est peut-être votre désassembleur qui est assez intelligent pour transformer des séquences d'instructions connues en leur pseudo-instruction équivalente.Le registre zéro est-il vraiment utile?
Eh bien, apparemment, ARM trouve un registre zéro suffisamment utile pour que dans leur (quelque peu) nouveau cœur ARMv8-A, qui implémente AArch64, il y ait maintenant un registre zéro en mode 64 bits; il n'y avait pas de registre zéro auparavant. (Le registre est un peu spécial cependant, dans certains contextes d'encodage, c'est un registre nul, dans d'autres, il désigne plutôt le pointeur de pile )
la source
slt
,slti
,sltu
).La plupart des implémentations ARM / POWER / SPARC ont un registre RAZ caché
Vous pourriez penser que ARM32, SPARC etc. n'ont pas de registre 0 mais en fait ils en ont! Au niveau de la micro-architecture, la plupart des ingénieurs en conception de CPU ajoutent un registre 0 qui peut être invisible pour le logiciel (le registre zéro d'ARM est invisible) et utilisent ce registre zéro pour rationaliser le décodage des instructions.
Considérez une conception ARM32 moderne typique qui a un registre logiciel invisible, par exemple R16 câblé à 0. Considérez la charge ARM32, de nombreux cas d'instructions de chargement ARM32 se présentent sous l'une de ces formes (Ignorez l'indexation pré-post pendant un certain temps pour garder la discussion simple ) ...
À l'intérieur du processeur, cela décode en général
avant d'entrer dans la phase d'émission où les registres sont lus. Notez que rx représente le registre pour réécrire l'adresse mise à jour. Voici quelques exemples de décodage:
Au niveau du circuit, les trois charges sont en fait la même instruction interne et un moyen facile d'obtenir ce type d'orthogonalité est de créer un registre de masse R16. Puisque R16 est toujours mis à la terre, ces instructions décodent naturellement correctement sans aucune logique supplémentaire. Le mappage d'une classe d'instructions à un seul format interne aide grandement dans les implémentations superscalaires car il réduit la complexité logique.
Une autre raison est un moyen simplifié de jeter les écritures. Les instructions peuvent être désactivées en réglant simplement le registre et les indicateurs de destination sur R16. Il n'est pas nécessaire de créer un autre signal de contrôle pour désactiver l'écriture différée, etc.
La plupart des implémentations de processeur, quelle que soit l'architecture, aboutissent très tôt à un modèle de registre RAZ dans le pipeline. Le pipeline MIPS commence essentiellement à un point qui, dans d'autres architectures, en serait à quelques étapes.
MIPS a fait le bon choix
Ainsi, un registre lu comme zéro est presque obligatoire dans toute implémentation de processeur moderne et MIPS le rendant visible pour le logiciel est certainement un point positif étant donné la façon dont il rationalise la logique de décodage interne. Les concepteurs de processeurs MIPS n'ont pas besoin d'ajouter un registre RAZ supplémentaire car 0 $ est déjà au sol. Étant donné que RAZ est disponible pour l'assembleur, beaucoup d'instructions de support sont disponibles pour MIPS et on peut penser à cela comme pousser une partie de la logique de décodage à l'assembleur lui-même au lieu de créer des formats dédiés pour chaque type d'instruction pour masquer le registre RAZ du logiciel comme avec d'autres architectures. Le registre RAZ est une bonne idée et c'est pourquoi ARMv8 l'a copié.
Si ARM32 avait un registre à 0 $, la logique de décodage serait devenue plus simple et l'architecture aurait été bien meilleure en termes de vitesse, de surface et de puissance. Par exemple, sur les trois versions de LDR présentées ci-dessus, seuls 2 formats seraient nécessaires. De même, il n'est pas nécessaire de réserver la logique de décodage pour les instructions MOV et MVN. De plus, CMP / CMN / TST / TEQ deviendrait redondant. Il ne serait pas non plus nécessaire de faire la différence entre une multiplication courte (MUL) et une multiplication longue (UMULL / SMULL) car une multiplication courte pourrait être considérée comme une multiplication longue avec le registre haut réglé à 0 $, etc.
Étant donné que MIPS a été initialement conçu par une petite équipe, la simplicité de conception était importante et donc 0 $ a été explicitement choisi dans l'esprit de RISC. ARM32 conserve de nombreuses fonctionnalités traditionnelles du CISC au niveau architectural.
la source
Disclamer: Je ne connais pas vraiment l'assembleur MIPS, mais le registre de valeur 0 n'est pas unique à cette architecture, et je suppose qu'il est utilisé de la même manière que dans d'autres architectures RISC que je connais.
XOR un registre pour obtenir 0 vous coûtera une instruction, alors que l'utilisation d'un registre de valeur 0 prédéfini ne le sera pas.
Par exemple, l'
mov RX, RY
instruction est souvent implémentée en tant queadd RX, RY, R0
. Sans registre à valeur 0, vous devriez le faire àxor RZ, RZ
chaque fois que vous souhaitez l'utilisermov
.Un autre exemple est l'
cmp
instruction et ses variantes (comme "comparer et sauter", "comparer et déplacer", etc.), oùcmp RX, R0
est utilisé pour tester les nombres négatifs.la source
MOV Rx,Ry
en œuvre en tant queAND Rx,Ry,Ry
?mov RX, Imm
oumov RX, mem[RY]
si votre jeu d'instructions ne prend en charge qu'une seule valeur immédiate et un seul accès à la mémoire par instruction.mov
est un mauvais exemple; vous pouvez l'implémenter avec un 0 immédiat au lieu d'un registre nul. par exempleori dst, src, 0
. Mais oui, vous auriez besoin d'un opcode pour mov-immediate pour vous inscrire si vous n'en aviez pasaddiu $dst, $zero, 1234
, commelui
pour les 16 bits inférieurs au lieu des 16. Et vous ne pouviez pas utilisernor
ousub
pour construire un opérande non / neg .Lier quelques pistes à la terre à la fin de votre banque de registres est bon marché (moins cher que d'en faire un registre complet).
Faire le xor réel prend un peu de puissance et de temps pour commuter les portes et ensuite le stocker dans le registre, pourquoi payer ce coût lorsqu'une valeur 0 existante peut facilement être disponible.
Les processeurs modernes ont également un registre de valeur 0 (caché) qu'ils peuvent utiliser à la suite d'une
xor eax eax
instruction via un changement de nom de registre.la source
R0
n'est pas de mettre à la terre quelques fils, mais de devoir lui réserver un code dans chaque instruction traitant des registres.std::memory_order_consume
) nécessitent que XOR propage la dépendance.lui
mais pas décalé à gauche de 16. Ainsi, vous pouvez toujours mettre un petit nombre dans un registre avec une instruction. Autoriser uniquement zéro avec une fausse dépendance serait insensé. (Le MIPS normal crée des valeurs non nulles avecaddiu $dst, $zero, 1234
ouori
, donc votre argument "coût d'énergie" se décompose. Si vous vouliez éviter de lancer une ALU, vous incluriez un opcode pour mov-immediate à enregistrer au lieu d'avoir le logiciel ADD ou OR un immédiat avec zéro.)