Creo que estoy buscando una respuesta a una pregunta de trivia. Estoy tratando de entender por qué la arquitectura MIPS usa un valor "cero" explícito en un registro cuando puede lograr lo mismo simplemente haciendo XOR de cualquier registro contra sí mismo. Se podría decir que la operación ya está hecha para usted; sin embargo, realmente no puedo imaginar una situación en la que usarías muchos valores "cero". Leí los documentos originales de Hennessey, y simplemente asigna un cero de hecho sin ninguna justificación real.
¿Existe una razón lógica para tener una asignación binaria codificada de cero?
Actualización: en 8k de un ejecutable de xc32-gcc para el núcleo MIPS en el PIC32MZ, tengo una sola instancia de "cero".
add t3,t1,zero
la respuesta real: otorgé la recompensa a la persona que tenía la información sobre MIPS y los códigos de condición. La respuesta en realidad radica en la arquitectura MIPS para las condiciones. Aunque inicialmente no quería asignar tiempo a esto, revisé la arquitectura para opensparc , MIPS-V y OpenPOWER (este documento era interno) y aquí están los resultados resumidos. El registro R0 es necesario para la comparación en ramas debido a la arquitectura de la tubería.
- comparación de enteros contra cero y rama (bgez, bgtz, blez, bltz)
- entero compara dos registros y rama (beq, bne)
- entero compara dos registros y trap (teq, tge, tlt, tne)
- entero comparar registro e inmediato y trampa (teqi, tgei, tlti, tnei)
Simplemente se reduce a cómo se ve el hardware en la implementación. Del manual de MIPS-V, hay una cita sin referencia en la página 68:
Las ramas condicionales fueron diseñadas para incluir operaciones de comparación aritmética entre dos registros (como también se hace en PA-RISC y Xtensa ISA), en lugar de usar códigos de condición (x86, ARM, SPARC, PowerPC), o para comparar solo un registro contra cero ( Alfa, MIPS), o dos registros solo para igualdad (MIPS). Este diseño fue motivado por la observación de que una instrucción combinada de comparación y ramificación se convierte en una tubería regular, evita el estado del código de condición adicional o el uso de un registro temporal, y reduce el tamaño del código estático y el seguimiento de búsqueda de instrucciones dinámicas. Otro punto es que las comparaciones contra cero requieren un retraso de circuito no trivial (especialmente después del cambio a la lógica estática en procesos avanzados) y, por lo tanto, son casi tan caras como la magnitud aritmética. Otra ventaja de una instrucción fusionada de comparar y bifurcar es que las bifurcaciones se observan antes en el flujo de instrucciones de front-end y, por lo tanto, se pueden predecir antes. Quizás haya una ventaja en un diseño con códigos de condición en el caso de que se puedan tomar varias ramas en función de los mismos códigos de condición, pero creemos que este caso es relativamente raro.
El documento MIPS-V no afecta al autor de la sección citada. Agradezco a todos por su tiempo y consideración.
fuente
Respuestas:
El registro cero en las CPU RISC es útil por dos razones:
Es una constante útil
Dependiendo de las restricciones de la ISA, no puede usar un literal en la codificación de algunas instrucciones, pero puede estar seguro de que puede usarlo
r0
para obtener 0.Se puede usar para sintetizar otras instrucciones
Este es quizás el punto más importante. Como diseñador de ISA, puede intercambiar un registro de propósito general con un registro cero para poder sintetizar otras instrucciones útiles. Sintetizar instrucciones es bueno porque al tener menos instrucciones reales, necesita menos bits para codificar una operación en un código de operación, lo que libera espacio en el espacio de codificación de instrucciones. Puede usar ese espacio para tener, por ejemplo, compensaciones de dirección más grandes y / o literales.
La semántica del registro cero es como
/dev/zero
en los sistemas * nix: todo lo escrito en él se descarta y siempre se lee 0.Veamos algunos ejemplos de cómo podemos hacer pseudoinstrucciones con la ayuda del
r0
registro cero:El caso de MIPS
Miré más de cerca el conjunto de instrucciones MIPS. Hay un puñado de pseudoinstrucciones que usa
$zero
; Se utilizan principalmente para las ramas. Aquí hay algunos ejemplos de lo que he encontrado:En cuanto a por qué ha encontrado solo una instancia del
$zero
registro en su desensamblaje, tal vez sea su desensamblador lo suficientemente inteligente como para transformar secuencias conocidas de instrucciones en su pseudoinstrucción equivalente.¿Es realmente útil el registro cero ?
Bueno, aparentemente, ARM considera que tener un registro cero es lo suficientemente útil como para que en su (algo) nuevo núcleo ARMv8-A, que implementa AArch64, ahora haya un registro cero en modo de 64 bits; no había un registro cero antes. (Sin embargo, el registro es un poco especial, en algunos contextos de codificación es un registro cero, en otros designa el puntero de la pila )
fuente
slt
,slti
,sltu
).La mayoría de las implementaciones ARM / POWER / SPARC tienen un registro RAZ oculto
Puede pensar que ARM32, SPARC, etc. no tienen un registro 0, ¡pero de hecho lo tienen! A nivel de microarquitectura, la mayoría de los ingenieros de diseño de CPU agregan un registro 0 que puede ser invisible para el software (el registro cero de ARM es invisible) y usan ese registro cero para simplificar la decodificación de instrucciones.
Considere un diseño ARM32 moderno típico que tiene un registro invisible de software, digamos R16 conectado a 0. Considere la carga ARM32, muchos casos de instrucción de carga ARM32 caen en una de estas formas (ignore la indexación previa y posterior por un tiempo para mantener la discusión simple ) ...
Dentro del procesador, esto decodifica a un general
antes de entrar en la etapa de emisión donde se leen los registros. Tenga en cuenta que rx representa el registro para reescribir la dirección actualizada. Aquí hay algunos ejemplos de decodificación:
A nivel de circuito, las tres cargas son en realidad la misma instrucción interna y una manera fácil de obtener este tipo de ortogonalidad es crear un registro de tierra R16. Dado que R16 siempre está conectado a tierra, estas instrucciones naturalmente se decodifican correctamente sin ninguna lógica adicional. La asignación de una clase de instrucciones a un único formato interno ayuda enormemente en las implementaciones superescalares, ya que reduce la complejidad lógica.
Otra razón es una forma simplificada de tirar las escrituras. Las instrucciones pueden deshabilitarse simplemente configurando el registro de destino y las banderas en R16. No es necesario crear ninguna otra señal de control para desactivar la reescritura, etc.
La mayoría de las implementaciones de procesador, independientemente de la arquitectura, terminan con un modelo de registro RAZ al principio de la tubería. La tubería MIPS esencialmente comienza en un punto que en otras arquitecturas sería de algunas etapas.
MIPS tomó la decisión correcta
Por lo tanto, un registro de lectura como cero es casi obligatorio en cualquier implementación de procesador moderna y MIPS que lo hace visible para el software es definitivamente un punto positivo dado cómo simplifica la lógica de decodificación interna. Los diseñadores de procesadores MIPS no necesitan agregar un registro RAZ adicional ya que $ 0 ya está en el suelo. Dado que RAZ está disponible para el ensamblador, MIPS tiene muchas instrucciones de psuedo disponibles y uno puede pensar que esto empuja parte de la lógica de decodificación al ensamblador en lugar de crear formatos dedicados para cada tipo de instrucción para ocultar el registro RAZ del software como con otras arquitecturas. El registro RAZ es una buena idea y por eso ARMv8 lo copió.
Si ARM32 tuviera un registro de $ 0, la lógica de decodificación se habría simplificado y la arquitectura habría sido mucho mejor en términos de velocidad, área y potencia. Por ejemplo, de las tres versiones de LDR presentadas anteriormente, solo se necesitarían 2 formatos. Del mismo modo, no es necesario reservar la lógica de decodificación para las instrucciones MOV y MVN. Además, CMP / CMN / TST / TEQ se volvería redundante. Tampoco sería necesario diferenciar entre la multiplicación corta (MUL) y la multiplicación larga (UMULL / SMULL), ya que la multiplicación corta podría considerarse una multiplicación larga con el registro alto establecido en $ 0, etc.
Dado que MIPS fue diseñado inicialmente por un pequeño equipo, la simplicidad del diseño era importante y, por lo tanto, $ 0 se eligió explícitamente en el espíritu de RISC. ARM32 conserva muchas características tradicionales de CISC a nivel arquitectónico.
fuente
Descargo de responsabilidad: Realmente no conozco el ensamblador MIPS, pero el registro de valor 0 no es exclusivo de esta arquitectura, y supongo que se usa de la misma manera que en otras arquitecturas RISC que conozco.
XORing un registro para obtener 0 le costará una instrucción, mientras que el uso de un registro de valor 0 predefinido no lo hará.
Por ejemplo, la
mov RX, RY
instrucción a menudo se implementa comoadd RX, RY, R0
. Sin un registro de valor 0, tendría que hacerloxor RZ, RZ
cada vez que quiera usarmov
.Otro ejemplo es la
cmp
instrucción y sus variantes (como "comparar y saltar", "comparar y mover", etc.), dondecmp RX, R0
se usa para probar números negativos.fuente
MOV Rx,Ry
comoAND Rx,Ry,Ry
?mov RX, Imm
omov RX, mem[RY]
si su conjunto de instrucciones solo admite un único valor inmediato y un único acceso a la memoria por instrucción.mov
es un mal ejemplo; puede implementarlo con un 0 inmediato en lugar de un registro cero. por ejori dst, src, 0
. Pero sí, necesitaría un código de operación para que mov-inmediato se registre si no lo hubiera hechoaddiu $dst, $zero, 1234
,lui
pero para los 16 bits inferiores en lugar de los 16 superiores. Y no podría usarnor
osub
construir un operando no / neg .Atar algunos cables a tierra al final de su banco de registro es barato (más barato que convertirlo en un registro completo).
Hacer el xor real toma un poco de energía y tiempo para cambiar las puertas y luego almacenarlo en el registro, ¿por qué pagar ese costo cuando un valor 0 existente puede estar fácilmente disponible?
Los cpus modernos también tienen un registro de valor 0 (oculto) que pueden usar como resultado de una
xor eax eax
instrucción a través del cambio de nombre del registro.fuente
R0
no está en conectar a tierra algunos cables, sino en el hecho de que tiene que reservar un código para él en cada instrucción que se ocupa de los registros.std::memory_order_consume
) requieren que XOR propague la dependencia.lui
pero no desplazado a la izquierda por 16. Entonces todavía puede poner un número pequeño en un registro con una sola instrucción. Permitir solo cero con una dependencia falsa sería una locura. (El MIPS normal crea valores distintos de cero conaddiu $dst, $zero, 1234
oori
, por lo que su argumento de "costo de energía" se desglosa. Si desea evitar activar una ALU, debe incluir un código de operación para que mov-inmediatamente se registre en lugar de tener el software ADD u OR un inmediato con cero.)