2012-06-11 14 views

Respuesta

22

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.

+2

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

+0

Entonces, si no estoy interesado en una condición, ¿el salto siempre es mejor? –

+2

@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. –

26

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).

+0

Para ser precisos, todas las instrucciones de salto y bifurcación además de 'jr' usan desplazamientos en código de máquina. – CervEd

11

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.

+0

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

+1

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. –

0

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.

Cuestiones relacionadas