Puede consultar Wikipedia o short summary for students. Todo el mundo dice que hay dos instrucciones para lo mismo. Pero nadie dice por qué?¿Cuál es la diferencia entre la rama incondicional y el salto incondicional (instrucciones en MIPS)?
¿Cuál es la diferencia entre la rama incondicional y el salto incondicional (instrucciones en MIPS)?
Respuesta
Las ramas permiten las condiciones. Pero permitir las condiciones toma más bits en la instrucción. Por lo tanto, la dirección de una rama es solo de 2^16 bits y solo le permite ramificar 2^15 - 1 instrucciones hacia atrás o 2^15 instrucciones hacia adelante.
Un salto es incondicional y los bits guardados dejando fuera la condición se pueden utilizar para la dirección. Un salto permite una dirección de 26 bits y, por lo tanto, puede saltar mucho más en el código que una rama. A expensas de no ser condicional.
Tenga en cuenta que esto puede ser cierto para MIPS, pero no para todos los mnemónicos de la CPU. Z80 tiene saltos relativos, saltos condicionales y saltos relativos condicionales. Una rama es una palabra que no existe en el ensamblaje Z80. – TheBlastOne
Entonces, si no estoy interesado en una condición, ¿el salto siempre es mejor? –
@HelloWorld ¿Normalmente? Sí. ¿Pero siempre? No. Una situación en la que puedo pensar es en un dispositivo con una memoria muy pequeña. En ese caso, no tendría que irse demasiado y querría guardar la mayor cantidad de bits posible. Entonces, una rama incondicional sería más apropiada. –
Las derivaciones (b
) utilizan un desplazamiento relativo de la PC, mientras que los saltos (j
) usan direcciones absolutas. La distinción es importante para el código independiente de posición. Además, solo se pueden usar saltos para la transferencia de control indirecto (jr
, usando un valor de registro).
Para ser precisos, todas las instrucciones de salto y bifurcación además de 'jr' usan desplazamientos en código de máquina. – CervEd
Como ya se mencionó, la rama tiene menos bits, un rango más corto y es relativa. Jump tiene más bits y es absoluto.
Tome este ejemplo
b l0
nop
beq $0,$1,l1
nop
j l2
nop
l0: .word 0,0
l1: .word 0,0
l2: .word 0,0
y se obtiene este
00000000 <l0-0x1c>:
0: 10000006 b 1c <l0>
4: 00000000 nop
8: 10010006 beq zero,at,24 <l1>
c: 00000000 nop
10: 0800000b j 2c <l2>
14: 00000000 nop
18: 00000000 nop
0000001c <l0>:
...
00000024 <l1>:
...
0000002c <l2>:
...
ahora lo que las otras respuestas no pueden haber mencionado es que el incondicional rama se codifica, al menos por el ensamblador de GNU, como una rama si es igual, con el mismo registro. No hay una rama incondicional en mips, hay una rama si es igual y una rama si no es igual a lo que puedo decir.
Usted ve arriba el salto usa un 0xB que es la dirección de palabra, 0xB * 4 = 0x2C la dirección del destino, donde los condicionales usan el direccionamiento relativo pc + (signed_offset * 4) donde pc = instruction_address + 4; O tome instruction_address + 4 + (signed_offset * 4) para obtener la dirección de destino.
El uso del alias b para la bifurcación en lugar de j para el salto creará código independiente de la posición. Jump no tendrá que volver a enlazarse si te mueves, para saltos cercanos probablemente sea mejor usar una rama en lugar de saltar aunque sea un alias. Si eres un purista, entonces puedes usar la instrucción real beq $ 0, $ 0, etiqueta o elegir cualquier registro beq $ 4, $ 4, etiqueta. registrarse 0 siendo especial y rápido puede ser la mejor opción.
Gracias. Sabemos que la rama es una pseudo-instrucción. Me gusta su intento de insinuar que el uso de una instrucción puede ser preferible a la otra. Pero, creo que la velocidad de acceso es igual para todos los registros (la CPU cos es sincrónica) – Val
podría causar un bloqueo o bloqueo agregar r1, r2, algo; beq r1, r1, en algún lugar se puede ejecutar de forma diferente porque puede o no esperar a que la instrucción anterior complete la ejecución y la reescritura, que agregar r1, r2, algo; beq r0, r0, en alguna parte, ya que r0 nunca se escribe. Solo depende del núcleo, etc. aislado sí, uno de los registros es tan bueno como otro para la velocidad de lectura. –
Una rama de salto e incondicional, en MIPS, no son lo mismo.
Tanto las instrucciones de derivación como las de salto escriben datos en el registro del contador de programas para que en el próximo ciclo de recuperación, se obtenga una instrucción diferente en lugar de la instrucción siguiente en línea en la memoria del programa. En ese sentido, llevan a cabo el mismo tipo de operación.
Donde difieren es que las ramas son condicionales, solo cambian la próxima instrucción que se ejecutará si se cumple una condición determinada. Esto puede ilustrarse mediante la diferencia en el código de ejecución en una declaración if
o llamando a una función.
if (a == 0) {
a = 1
}
setAtoOne()
La declaración if
salta a la instrucción para establecer a = 1
sólo si a = 0
. La función saltará a esa instrucción independientemente.
En este caso, estamos hablando de una rama donde la condición es siempre cierta. Es sólo otra manera de escribir
beq $zero, $zero, (int)offset
$ cero es siempre igual a $ cero, por lo que siempre se traslada al desplazamiento especificado. Es así si la instrucción
if (true) { a = 1 }
Hay otra diferencia entre las instrucciones de salto y de bifurcación. Las instrucciones de salto especifican una dirección absoluta en la que se configurará la PC, mientras que las instrucciones de salto compensan la dirección en el contador de programa.
PC = 32-bit address # Jump
PC += 16-bits lower
En realidad, esto no es estrictamente cierto. Escribimos el ensamblado con direcciones y desviaciones absolutas, pero tanto en saltos como en ramas se compila con un desplazamiento. Esta es la razón por la que no puede saltar ni ramificarse en ningún lugar de la memoria, espere utilizar el salto para registrar las instrucciones jr
. Esto se debe a un diseño fundamental de MIPS, instrucciones de una palabra de longitud fija.
Todas las instrucciones MIPS son de 1 palabra (es decir, 4 bytes/32 bits) de largo. Contienen una identificación para la instrucción (llamada op-code) que es de 6 bits junto con otra información necesaria para ejecutar la instrucción. Esta puede ser la identificación de registros o valores 'inmediatos', básicamente enteros codificados en la instrucción.
Cada byte en la memoria en MIPS tiene una dirección entre 0x00000000
- 0xFFFFFFFF
. Para llegar a uno de esos bytes, necesitamos especificar la dirección. Si tuviera la suerte de almacenar la dirección en un registro, simplemente jr
y usar la dirección ya almacenada en el registro. Sin embargo, no lo somos.
Esto se vuelve problemático, solo tenemos 32 bits para nuestras instrucciones y necesitaríamos todos esos bits para especificar la dirección en ese rango. También tuvimos que renunciar a 6 bits para ser utilizados por el procesador para identificar las instrucciones. Ahora nos quedan 26 bits.
Lo que es peor es que cuando ramificamos, necesitamos 10 bits adicionales para especificar los dos registros que estamos comparando para nuestra condición. La solución es usar compensaciones.
Digamos que estamos en la dirección 0x12345678
y estamos ejecutando un salto incondicional a la siguiente dirección en la memoria j 0x1234567c
. Este es el código de ensamblaje y mostraré cómo se traduce esto en código de máquina y se ejecuta.
Primero engañamos un poco. Sabemos que las instrucciones son una palabra (4 bytes) y en MIPS se especifica que deben estar dentro del límite de palabras. Esto significa que todas las instrucciones tienen direcciones separadas por 4 bytes, lo que significa que siempre terminan en 00 en representación binaria. Genial, podemos afeitar esos dos bits sin sentido. También nos afeitamos de los primeros 6, pero no se preocupe, los recuperaremos más tarde.
jump 0001 0010 0011 0100 0101 0110 0111 1100
jump 00010010 0011 0100 0101 0110 0111 1100
0000 1000 1000 1101 0001 0101 1001 1111 #in machine code # jump op = 0000 10
When we execute this we take
00 1000 1101 0001 0101 1001 1111
0000 0000 1000 1101 0001 0101 1001 1111 # extend >> 6
0000 0010 0011 0100 0101 0110 0111 1100 # << 2
Then we AND the PC (where we're executing from) and 0xf0000000
0001 0010 0011 0100 0101 0110 0111 1000
1111 0000 0000 0000 0000 0000 0000 0000
AND
0001 0000 0000 0000 0000 0000 0000 0000
We know take the result of this and OR it with our instruction integer
0001 0000 0000 0000 0000 0000 0000 0000
0000 0010 0011 0100 0101 0110 0111 1100
OR
0001 0010 0011 0100 0101 0110 0111 1100
Which is 0x1234567c
en Hex y hacia donde queremos ir, ahora saltamos allí. Es por eso que no puede saltar más allá de 256MB (2^28 bits) de su instrucción actual (a menos que salte al valor de un registro jr
)
La misma idea básica se aplica a las sucursales, excepto que ahora también tiene los 2 registros comparados (que requieren 10 bits) por lo que solo tienes 16 bits que puedes usar para compensar, por lo que no puedes saltar tan lejos con las ramas.
Generalmente, esto está bien porque usamos ramas en un procedimiento, para implementar bucles y llevar a cabo asignaciones condicionales.
Esto es todo una consecuencia del diseño de la arquitectura MIPS. Hubiera sido posible tener instrucciones donde la única diferencia entre ramas y saltos hubiera sido los aspectos condicionales y donde una rama 'incondicional' se hubiera comportado igual que un salto incondicional.
- 1. const_cast en la plantilla. ¿Hay algún modificador incondicional?
- 2. Instrucción de salto en la Asamblea MIPS
- 3. MIPS rango de instrucción de salto
- 4. ¿cuál es la diferencia entre:.! y: r !?
- 5. Cuál es la diferencia entre $ y jQuery
- 6. ¿Cuál es la diferencia entre dict() y {}?
- 7. Cuál es la diferencia entre $ (...) y `...`
- 8. ¿Cuál es la diferencia entre .ToString (+) y ""
- 9. ¿Cuál es la diferencia entre "$^N" y "$ +"?
- 10. ¿Cuál es la diferencia entre `##` y `hashCode`?
- 11. ¿Cuál es la diferencia entre + = y = +?
- 12. ¿Cuál es la diferencia entre ".equals" y "=="?
- 13. Cuál es la diferencia entre = y: =
- 14. ¿Cuál es la diferencia entre [indefinido] y [,]?
- 15. ¿Cuál es la diferencia entre {0} y ""?
- 16. ¿Cuál es la diferencia entre " " y ""?
- 17. ¿Cuál es la diferencia entre el casting y la conversión?
- 18. ¿Cuál es la diferencia entre el colado y la coerción?
- 19. ¿cuál es la diferencia entre el complemento y la biblioteca?
- 20. ¿cuál es la diferencia entre el material y la textura?
- 21. ¿Cuál es la diferencia entre el marco y la arquitectura?
- 22. ¿Cuál es la diferencia entre la tubería y el zócalo?
- 23. ¿Cuál es la diferencia entre el operador "=" y "==" en Bash?
- 24. ¿Cuál es la diferencia entre: = y + = en el archivo make?
- 25. ARM NEON: ¿Cuál es la diferencia entre vld4_f32 y vld4q_f32?
- 26. ¿Cuál es la diferencia entre el
- 27. Backbone.Js - ¿Cuál es la diferencia entre el y $ el?
- 28. ¿Cuál es la diferencia entre el mango y el hilo?
- 29. ¿Cuál es la diferencia entre el NSImage y el UIImage?
- 30. ¿Cuál es la diferencia entre MOV y LEA?
la primera diferencia es el formato: uno es tipo-R y el otro es tipo-J –