La respuesta de @ jalf cubre la mayoría de las razones, pero hay un detalle interesante que no menciona: el núcleo interno similar a RISC no está diseñado para ejecutar un conjunto de instrucciones como ARM / PPC / MIPS. El impuesto x86 no solo se paga en los decodificadores que consumen mucha energía, sino hasta cierto punto en todo el núcleo. es decir, no se trata solo de la codificación de instrucciones x86; es cada instrucción con una semántica extraña.
Supongamos que Intel creó un modo de funcionamiento en el que el flujo de instrucciones era diferente a x86, con instrucciones que se asignaban más directamente a uops. Supongamos también que cada modelo de CPU tiene su propio ISA para este modo, por lo que todavía son libres de cambiar los componentes internos cuando lo deseen y exponerlos con una cantidad mínima de transistores para la decodificación de instrucciones de este formato alternativo.
Presumiblemente, todavía tendría la misma cantidad de registros, asignados al estado arquitectónico x86, por lo que los sistemas operativos x86 pueden guardarlo / restaurarlo en cambios de contexto sin usar el conjunto de instrucciones específicas de la CPU. Pero si descartamos esa limitación práctica, sí, podríamos tener algunos registros más porque podemos usar los registros temporales ocultos normalmente reservados para el microcódigo 1 .
Si solo tenemos decodificadores alternativos sin cambios en las etapas posteriores de la canalización (unidades de ejecución), este ISA todavía tendría muchas excentricidades x86. No sería una arquitectura RISC muy agradable. Ninguna instrucción por sí sola sería muy compleja, pero algunas de las otras locuras de x86 seguirían ahí.
Por ejemplo: los cambios a la izquierda / derecha dejan el indicador de desbordamiento sin definir, a menos que el recuento de cambios sea uno, en cuyo caso OF = la detección de desbordamiento con signo habitual. Locura similar para rota. Sin embargo, las instrucciones RISC expuestas podrían proporcionar cambios sin banderas y así sucesivamente (permitiendo el uso de solo uno o dos de los múltiples uops que generalmente se incluyen en algunas instrucciones x86 complejas). Así que esto realmente no se sostiene como el principal contraargumento.
Si va a hacer un decodificador completamente nuevo para un RISC ISA, puede hacer que elija partes de las instrucciones x86 para exponerlas como instrucciones RISC. Esto mitiga un poco la especialización x86 del núcleo.
La codificación de la instrucción probablemente no sea de tamaño fijo, ya que las uops individuales pueden contener una gran cantidad de datos. Muchos más datos de los que tienen sentido si todos los insns son del mismo tamaño. Una sola uop microfundida puede agregar un operando inmediato de 32 bits y un operando de memoria que usa un modo de direccionamiento con 2 registros y un desplazamiento de 32 bits. (En SnB y versiones posteriores, solo los modos de direccionamiento de registro único pueden microfusarse con operaciones ALU).
Los uops son muy grandes y no muy similares a las instrucciones ARM de ancho fijo. Un conjunto de instrucciones de 32 bits de ancho fijo solo puede cargar inmediatos de 16 bits a la vez, por lo que cargar una dirección de 32 bits requiere un par carga-media baja-inmediata / carga alta-inmediata. x86 no tiene que hacer eso, lo que ayuda a que no sea terrible con solo 15 registros GP que limitan la capacidad de mantener constantes en los registros. (15 es una gran ayuda sobre 7 registros, pero duplicar nuevamente a 31 ayuda mucho menos, creo que se encontró algo de simulación. RSP generalmente no es de propósito general, por lo que es más como 15 registros GP y una pila).
TL; resumen de DR:
De todos modos, esta respuesta se reduce a "el conjunto de instrucciones x86 es probablemente la mejor manera de programar una CPU que debe poder ejecutar instrucciones x86 rápidamente", pero es de esperar que arroje algo de luz sobre las razones.
Formatos de uop internos en el front-end frente al back-end
Consulte también los modos de micro fusión y direccionamiento para ver un caso de diferencias en lo que pueden representar los formatos uop de front-end y back-end en las CPU Intel.
Nota al pie 1 : Hay algunos registros "ocultos" para su uso como temporales por microcódigo. Estos registros se renombran al igual que los registros arquitectónicos x86, por lo que las instrucciones multi-uop pueden ejecutarse fuera de orden.
por ejemplo, xchg eax, ecx
en las CPU de Intel se decodifica como 3 uops ( ¿por qué? ), y nuestra mejor suposición es que estos son uops tipo MOV que lo hacen tmp = eax; ecx=eax ; eax=tmp;
. En ese orden, porque mido la latencia de la dirección dst-> src en ~ 1 ciclo, frente a 2 en el otro sentido. Y estos movimientos no son como mov
instrucciones regulares ; no parecen ser candidatos para la eliminación de mov de latencia cero.
Consulte también http://blog.stuffedcow.net/2013/05/measuring-rob-capacity/ para una mención de intentar medir experimentalmente el tamaño de PRF y tener que tener en cuenta los registros físicos utilizados para mantener el estado arquitectónico, incluidos los registros ocultos.
En el front-end después de los decodificadores, pero antes de la etapa de emisión / cambio de nombre que cambia el nombre de los registros al archivo de registro físico, el formato interno uop usa números de registro similares a los números de registro x86, pero con espacio para abordar estos registros ocultos.
El formato uop es algo diferente dentro del núcleo fuera de orden (ROB y RS), también conocido como back-end (después de la etapa de emisión / cambio de nombre). Los archivos de registro físico int / FP tienen cada uno 168 entradas en Haswell , por lo que cada campo de registro en un uop debe ser lo suficientemente amplio para abordar esa cantidad.
Dado que el renombrador está en el HW, probablemente sería mejor usarlo, en lugar de enviar instrucciones programadas estáticamente directamente al back-end. Así que podríamos trabajar con un conjunto de registros tan grande como los registros arquitectónicos x86 + temporales de microcódigo, no más que eso.
El back-end está diseñado para funcionar con un renombrador de front-end que evita los peligros WAW / WAR, por lo que no podríamos usarlo como una CPU en orden incluso si quisiéramos. No tiene enclavamientos para detectar esas dependencias; que se maneja por problema / cambio de nombre.
Sería bueno si pudiéramos alimentar uops en el back-end sin el cuello de botella de la etapa de emisión / cambio de nombre (el punto más estrecho en las tuberías modernas de Intel, por ejemplo, 4 de ancho en Skylake frente a 4 ALU + 2 carga + 1 puertos de almacenamiento en el back-end). Pero si hiciste eso, no creo que puedas programar estáticamente el código para evitar la reutilización de registros y pisar un resultado que aún es necesario si un error de caché detuvo una carga durante mucho tiempo.
Por lo tanto, necesitamos enviar uops a la etapa de emisión / cambio de nombre, probablemente solo omitiendo la decodificación, no el caché de uop o IDQ. Luego obtenemos un ejecutivo de OoO normal con una detección de peligros sensata. La tabla de asignación de registros solo está diseñada para cambiar el nombre de 16 + algunos registros enteros en el PRF entero de 168 entradas. No podíamos esperar que el HW cambiara el nombre de un conjunto mayor de registros lógicos en el mismo número de registros físicos; eso requeriría una RAT más grande.