La réponse de @ jalf couvre la plupart des raisons, mais il y a un détail intéressant qu'il ne mentionne pas: le noyau interne de type RISC n'est pas conçu pour exécuter un jeu d'instructions comme ARM / PPC / MIPS. La taxe x86 n'est pas seulement payée dans les décodeurs gourmands en énergie, mais dans une certaine mesure dans tout le noyau. c'est-à-dire que ce n'est pas seulement l'encodage des instructions x86; c'est chaque instruction avec une sémantique étrange.
Supposons qu'Intel ait créé un mode de fonctionnement dans lequel le flux d'instructions était autre chose que x86, avec des instructions plus directement mappées sur uops. Supposons également que chaque modèle de processeur a son propre ISA pour ce mode, afin qu'ils soient toujours libres de changer les éléments internes quand ils le souhaitent, et de les exposer avec une quantité minimale de transistors pour le décodage d'instructions de cet autre format.
Vraisemblablement, vous n'auriez toujours que le même nombre de registres, mappés à l'état architectural x86, de sorte que les systèmes d'exploitation x86 peuvent le sauvegarder / le restaurer sur des commutateurs de contexte sans utiliser le jeu d'instructions spécifique au processeur. Mais si nous supprimons cette limitation pratique, oui, nous pourrions avoir quelques registres supplémentaires car nous pouvons utiliser les registres temporaires cachés normalement réservés au microcode 1 .
Si nous avons juste des décodeurs alternatifs sans modification des étapes ultérieures du pipeline (unités d'exécution), cet ISA aurait encore de nombreuses excentricités x86. Ce ne serait pas une très belle architecture RISC. Aucune instruction ne serait très complexe, mais certaines des autres folies de x86 seraient toujours là.
Par exemple: les décalages gauche / droite laissent l'indicateur de débordement indéfini, à moins que le nombre d'équipes ne soit égal à un, auquel cas OF = la détection de débordement signée habituelle. Folie similaire pour les rotations. Cependant, les instructions RISC exposées pourraient fournir des décalages sans indicateur et ainsi de suite (permettant l'utilisation d'un ou deux des multiples uops qui entrent généralement dans certaines instructions x86 complexes). Donc, cela ne constitue pas vraiment le principal contre-argument.
Si vous comptez créer un tout nouveau décodeur pour un RISC ISA, vous pouvez lui demander de choisir des parties des instructions x86 à exposer en tant qu'instructions RISC. Cela atténue quelque peu la spécialisation x86 du noyau.
Le codage des instructions ne serait probablement pas de taille fixe, car des uops uniques peuvent contenir beaucoup de données. Beaucoup plus de données que cela n'a de sens si toutes les insns sont de la même taille. Un seul uop micro-fusionné peut ajouter un immédiat 32 bits et un opérande mémoire qui utilise un mode d'adressage avec 2 registres et un déplacement 32 bits. (Dans SnB et versions ultérieures, seuls les modes d'adressage à registre unique peuvent micro-fusionner avec les opérations ALU).
Les uops sont très volumineux et pas très similaires aux instructions ARM à largeur fixe. Un jeu d'instructions 32 bits à largeur fixe ne peut charger que des instantanés 16 bits à la fois, de sorte que le chargement d'une adresse 32 bits nécessite une paire charge immédiate faible moitié / charge élevée immédiate. x86 n'a pas à faire cela, ce qui aide à ne pas être terrible avec seulement 15 registres GP limitant la capacité de conserver des constantes dans les registres. (15 est une grande aide sur 7 registres, mais doubler à nouveau à 31 aide beaucoup moins, je pense que certaines simulations ont été trouvées. RSP n'est généralement pas à usage général, donc c'est plus comme 15 registres GP et une pile.)
TL; Résumé DR:
Quoi qu'il en soit, cette réponse se résume à "le jeu d'instructions x86 est probablement le meilleur moyen de programmer un processeur qui doit être capable d'exécuter rapidement des instructions x86", mais nous espérons que cela permet de mieux comprendre les raisons.
Formats uop internes dans le front-end vs le back-end
Voir également Micro fusion et modes d'adressage pour un cas de différences dans ce que les formats uop front-end et back-end peuvent représenter sur les processeurs Intel.
Note de bas de page 1 : Il existe des registres «cachés» à utiliser comme temporaires par microcode. Ces registres sont renommés comme les registres architecturaux x86, de sorte que les instructions multi-uop peuvent s'exécuter dans le désordre.
par exemple xchg eax, ecx
sur les processeurs Intel décodent en 3 uops ( pourquoi? ), et notre meilleure estimation est que ce sont des uops de type MOV qui le font tmp = eax; ecx=eax ; eax=tmp;
. Dans cet ordre, parce que je mesure la latence de la direction dst-> src à ~ 1 cycle, contre 2 pour l'inverse. Et ces mouvements ne sont pas comme des mov
instructions régulières ; ils ne semblent pas être des candidats à l'élimination des mouvements sans latence.
Voir également http://blog.stuffedcow.net/2013/05/measuring-rob-capacity/ pour une mention de la tentative de mesurer expérimentalement la taille du PRF et de la nécessité de tenir compte des registres physiques utilisés pour conserver l'état architectural, y compris les registres cachés.
Dans le front-end après les décodeurs, mais avant l'étape d'émission / de changement de nom qui renomme les registres dans le fichier de registre physique, le format uop interne utilise des numéros de registre similaires aux numéros de reg x86, mais avec de la place pour adresser ces registres cachés.
Le format uop est quelque peu différent à l'intérieur du noyau en désordre (ROB et RS), alias back-end (après l'étape d'émission / de changement de nom). Les fichiers de registre physique int / FP ont chacun 168 entrées dans Haswell , donc chaque champ de registre dans un uop doit être suffisamment large pour en traiter autant.
Puisque le renommage est présent dans le HW, nous ferions probablement mieux de l'utiliser, au lieu de fournir des instructions programmées statiquement directement au back-end. Nous pourrions donc travailler avec un ensemble de registres aussi grand que les registres architecturaux x86 + les temporaires du microcode, pas plus que cela.
Le back-end est conçu pour fonctionner avec un renommage frontal qui évite les risques WAW / WAR, nous ne pouvons donc pas l'utiliser comme un processeur en ordre même si nous le voulions. Il n'a pas de verrouillages pour détecter ces dépendances; qui est géré par problème / renommer.
Cela pourrait être intéressant si nous pouvions alimenter les uops dans le back-end sans le goulot d'étranglement de l'étape de problème / renommer (le point le plus étroit des pipelines Intel modernes, par exemple 4-large sur Skylake contre 4 ALU + 2 ports de chargement + 1 port de stockage dans le back-end). Mais si vous avez fait cela, je ne pense pas que vous puissiez planifier statiquement du code pour éviter la réutilisation des registres et marcher sur un résultat qui est toujours nécessaire si un échec de cache a bloqué une charge pendant une longue période.
Nous avons donc à peu près besoin de fournir des uops à l'étape de problème / renommer, probablement en contournant uniquement le décodage, pas le cache uop ou IDQ. Ensuite, nous obtenons un exécutable OoO normal avec une détection des dangers sensée. La table d'allocation de registres est uniquement conçue pour renommer 16 + quelques registres d'entiers en PRF d'entiers à 168 entrées. Nous ne pouvions pas nous attendre à ce que le matériel renomme un plus grand ensemble de registres logiques sur le même nombre de registres physiques; cela prendrait un RAT plus grand.