¿Por qué MIPS usa R0 como "cero" cuando solo puede XOR dos registros para producir 0?

9

Creo que estoy buscando una respuesta a una pregunta de trivia. Estoy tratando de entender por qué la arquitectura MIPS usa un valor explícito de "cero" en un registro cuando puede lograr lo mismo simplemente al XORAR cualquier registro en 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 utilizarías muchos valores "cero". Leí los artículos originales de Hennessey y, de hecho, solo asigna un cero sin ninguna justificación real.

¿Existe una razón lógica para que exista 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: Otorgué la recompensa a la persona que tenía la información sobre MIPS y los códigos de condición. La respuesta realmente radica en la arquitectura MIPS para las condiciones. Aunque inicialmente no quería asignar tiempo a esto, revisé la arquitectura de opensparc , MIPS-V , y OpenPOWER (este documento fue interno) y aquí están los resumen de los resultados. El registro R0 es necesario para la comparación en las sucursales debido a la arquitectura de la tubería.

  • el número entero se compara con cero y la rama (bgez, bgtz, blez, bltz)
  • entero compara dos registros y rama (beq, bne)
  • los enteros comparan dos registros y captura (teq, tge, tlt, tne)
  • registro de comparación de enteros e inmediato y captura (teqi, tgei, tlti, tnei)

Simplemente se reduce a cómo se ve el hardware en la implementación. En el manual 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 solo para comparar un registro con cero (Alpha, MIPS), o   Dos registros solo para igualdad (MIPS). Este diseño fue motivado por la observación de que un   la instrucción combinada de comparación y derivación de ts en una tubería regular evita la condición adicional   Estado del código o uso de un registro temporal, y reduce el tamaño del código estático y la instrucción dinámica   ir a buscar trac. Otro punto es que las comparaciones con cero requieren un retardo 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 caros como   La magnitud aritmética se compara. Otra ventaja de una instrucción fusionada de comparación y ramificación   es que las ramas se observan antes en el flujo de instrucciones de front-end, por lo que se pueden predecir   más temprano. Quizás haya una ventaja para un diseño con códigos de condición en el caso de que múltiples   Las sucursales pueden tomarse en base a los mismos códigos de condición, pero creemos que este caso es relativamente   raro.

El documento MIPS-V no golpea al autor de la sección citada. Agradezco a todos por su tiempo y consideración.

    
pregunta b degnan

4 respuestas

10

El registro cero en las CPU RISC es útil por dos razones:

Es una constante útil

Dependiendo de las restricciones de la ISA, no puedes usar un literal en algunas instrucciones de codificación, pero puedes estar seguro de que puedes usar ese r0 para obtener 0.

Puede usarse 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 de cero para poder sintetizar otras instrucciones útiles. Sintetizar las instrucciones es bueno porque al tener menos instrucciones reales, se necesitan 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. Puedes usar ese espacio para tener, por ejemplo. Compensaciones de direcciones más grandes y / o literales.

La semántica del registro cero es como /dev/zero en * nix systems: todo lo que se escriba en él se descarta, y siempre se lee 0.

Veamos algunos ejemplos de cómo podemos hacer pseudo-instrucciones con la ayuda de r0 zero-register:

; ### Hypothetical CPU ###

; Assembler with syntax:
; op rd, rm, rn 
; => rd: destination, rm: 1st operand, rn: 2nd operand
; literal as #lit

; On an CPU architecture with a status register (which contains arithmetic status
; flags), 'sub' can be used, with r0 as destination to discard result.
cmp rn, rm     ; => sub r0, rn, rm

; 'add' instruction can be used as a 'mov' instruction:
mov rd, rm     ; => add rd, rm, r0
mov rd, #lit   ; => add rd, r0, #lit

; Negate:
neg rd, rm     ; => sub rd, r0, rm

; On CPU without status flags,
nop            ; => add r0, r0, r0

; RISC-V's 'jal' instruction -- Jump and Link: Jump to PC-relative instruction,
; save return address into rd; we can synthesize a 'jmp' instruction out of it.
jmp dest       ; => jal r0, dest

; You can even load from an absolute (direct) address, for a usually small range
; of addresses by using a literal offset as an address.
ld rd, addr    ; => ld rd, [r0, #addr]

El caso de MIPS

Observé más de cerca el conjunto de instrucciones MIPS. Hay un puñado de pseudo-instrucciones que usan $zero ; Se utilizan principalmente para las ramas. Aquí hay algunos ejemplos de lo que he encontrado:

move $rt, $rs          => add $rt, $rs, $zero

not $rt, $rs           => nor $rt, $rs, $zero

b Label                => beq $zero, $zero, Label ; a small relative branch

bgt $rs, $rt, Label    => slt $at, $rt, $rs
                          bne $at, $zero, Label

blt $rs, $rt, Label    => slt $at, $rs, $rt
                          bne $at, $zero, Label

bge $rs, $rt, Label    => slt $at, $rs, $rt
                          beq $at, $zero, Label

ble $rs, $rt, Label    => slt $at, $rt, $rs
                          beq $at, $zero, Label

En cuanto a por qué ha encontrado solo una instancia del registro $zero en su desensamblaje, tal vez sea su desensamblador lo suficientemente inteligente como para transformar secuencias de instrucciones conocidas en su pseudo-instrucción equivalente.

¿Es el registro cero realmente útil?

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. (El registro es un poco especial, en algunos contextos de codificación es un registro cero, en otros, en cambio, designa el puntero de pila )

    
respondido por el Jarhmander
6

Disclamer: Realmente no conozco el ensamblador MIPS, pero el registro de valor 0 no es exclusivo de esta arquitectura, y creo que se usa de la misma manera que en otras arquitecturas RISC que conozco.

XORAR un registro para obtener 0 le costará una instrucción, mientras que usar un registro de valor 0 predefinido no lo hará.

Por ejemplo, la instrucción mov RX, RY a menudo se implementa como add RX, RY, R0 . Sin un registro de valor 0, tendría que xor RZ, RZ cada vez que quiera usar mov .

Otro ejemplo es la instrucción cmp y sus variantes (como "comparar y saltar", "comparar y mover", etc.), donde se usa cmp RX, R0 para probar números negativos.

    
respondido por el Dmitry Grigoryev
6

La mayoría de las implementaciones de ARM / POWER / SPARC tienen un registro RAZ oculto

¡Podría pensar que ARM32, SPARC, etc. no tienen un registro de 0, pero en realidad sí! En el 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 usa ese registro cero para simplificar la decodificación de instrucciones.

Considere un diseño ARM32 moderno típico que tenga un registro invisible de software, digamos R16 conectado a 0. Considere la carga ARM32, muchos casos de instrucciones de carga ARM32 caen en una de estas formas (ignore la indexación previa a la publicación por un tiempo para mantenerla). la discusión simple) ...

LDR ra, [rb] // NOTE:The ! is optional and represents address writeback.
LDR ra, [rb, rc](!)
LDR ra, [rb, #k](!)

Dentro del procesador, esto se decodifica a un general

ldr.uop ra, rb, rx, rc, #c // Internal decoded instruction format.

antes de entrar en la etapa de emisión donde se leen los registros. Tenga en cuenta que rx representa el registro para escribir de nuevo la dirección actualizada. Aquí hay algunos ejemplos de decodificación:

LDR R0, [R1]      ==> ldr.uop R0, R1, R16, R16, #0 // Writeback to NULL. 
LDR R0, [R1, R2]! ==> ldr.uop R0, R1, R1, R2,   #0 // Writeback to R1.
LDR R0, [R1, #2]  ==> ldr.uop R0, R1, R16, R16, #2 // Writeback to NULL.

En el nivel del circuito, las tres cargas son en realidad la misma instrucción interna y una forma 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 se descodifican naturalmente sin ninguna lógica adicional. Asignar una clase de instrucciones a un único formato interno ayuda mucho en las implementaciones superescalares, ya que reduce la complejidad de la 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 deshabilitar la escritura, etc.

La mayoría de las implementaciones de procesadores, independientemente de la arquitectura, terminan con un modelo de registro RAZ desde el principio. El canal de MIPS esencialmente comienza en un punto que en otras arquitecturas se llevaría a cabo en 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 moderno y el hecho de que MIPS sea visible para el software es definitivamente un punto positivo dado que agiliza 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 tierra. Debido a que RAZ está disponible para el ensamblador, hay muchas instrucciones de psuedo disponibles para MIPS y se puede pensar en esto como empujar 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 Al igual que con otras arquitecturas. El registro RAZ es una buena idea y es por eso que ARMv8 lo copió.

Si ARM32 tuviera un registro de $ 0, la lógica de decodificación se habría vuelto más simple 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 habría necesidad de diferenciar entre multiplicación corta (MUL) y larga (UMULL / SMULL) ya que la multiplicación corta podría considerarse como una multiplicación larga con el registro alto establecido en $ 0, etc.

Dado que MIPS fue diseñado inicialmente por un equipo pequeño, la simplicidad del diseño era importante y, por lo tanto, se eligió explícitamente $ 0 en el espíritu de RISC. ARM32 conserva muchas de las características tradicionales de CISC a nivel arquitectónico.

    
respondido por el Revanth Kamaraj
3

La vinculación de algunas pistas a tierra al final de su banco de registros es barata (más barato que hacerlo un registro completo).

Hacer el xor real requiere 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.

Las CPU modernas también tienen un registro (oculto) de valor 0 que pueden usar como resultado de una instrucción xor eax eax a través del cambio de nombre del registro.

    
respondido por el ratchet freak

Lea otras preguntas en las etiquetas

Comentarios Recientes

Y finalmente, ¿por qué la V1 no se codificó como 16 bits? Esto no puede ser solo un error de fragmentación de bits. Como señala Densbader, MIPS utiliza su terminador interno PCF9 para incluir posiciones de bits adyacentes para codificar estos valores, mientras que compararlos por separado empeora aún más la implementación. Podría provocar datos corruptos dentro del bus, leer columnas incorrectamente en MBR (si la columna original es de longitud volátil o no), etc. La imagen CIFRS "u": 0x102DSo, ¿qué está... Lees verder