assembly - jal - mips instructions
Cómo calcular la dirección de destino de salto y la dirección de destino de la sucursal? (4)
Soy nuevo en el lenguaje ensamblador . Estaba leyendo acerca de la arquitectura de MIPS y estoy atrapado con Jump Target Address y Branch Target Address y cómo calcular cada uno de ellos.
1. Cálculo de dirección de sucursal
En la instrucción de rama MIPS, solo tiene 16 bits de desplazamiento para determinar la siguiente instrucción. Necesitamos un registro agregado a este valor de 16 bits para determinar la siguiente instrucción y este registro está realmente implicado por la arquitectura. Es un registro de PC ya que la PC se actualiza (PC + 4) durante el ciclo de búsqueda para que contenga la dirección de la siguiente instrucción. También limitamos la distancia de bifurcación a -2^15 to +2^15 - 1
instrucción de la instrucción de bifurcación (instrucción después de). Sin embargo, esto no es un problema real ya que la mayoría de las sucursales son locales de todos modos.
Entonces paso a paso:
- Signo extender el valor de desplazamiento de 16 bits para preservar su valor.
- Multiplique el valor resultante con 4. La razón detrás de esto es que si vamos a ramificar alguna dirección, y la PC ya está alineada con las palabras, entonces el valor inmediato también debe estar alineado con la palabra. Sin embargo, no tiene sentido hacer la alineación de palabras inmediata porque estaríamos desperdiciando dos bits al forzarlos a ser 00.
- Ahora tenemos una dirección de 32 bits. Agregue este valor a PC + 4 y esa es su dirección de sucursal.
2. Cálculo de dirección de salto
Para la instrucción Jump, Mips tiene solo 26 bits para determinar la ubicación de Jump. Además, los saltos son relativos a la PC en MIPS. Al igual que la rama, el valor de salto inmediato debe estar alineado con la palabra, por lo tanto, debemos multiplicar la dirección de 26 bits por cuatro.
Nuevamente paso a paso:
- Multiplica el valor de 26 bit con 4.
- Dado que estamos saltando en relación con el valor de PC, concatene los primeros cuatro bits del valor de PC a la izquierda de nuestra dirección de salto.
- La dirección resultante es el valor de salto.
En otras palabras, reemplace los 28 bits inferiores de la PC con los 26 bits más bajos de la instrucción captada, desplazados a la izquierda por 2 bits.
Fuente: Bilkent University CS 224 Course Slides
Creo que sería bastante difícil calcularlos porque la dirección de destino de la sucursal se determina en tiempo de ejecución y esa predicción se realiza en hardware. Si explicara el problema un poco más en profundidad y describiera lo que está tratando de hacer, sería un poco más fácil ayudarlo. (:
Para funciones pequeñas como esta, puede contar a mano cuántos saltos hay en el objetivo, a partir de la instrucción bajo la instrucción de bifurcación. Si se ramifica hacia atrás, ese número de salto será negativo. si ese número no requiere los 16 bits, entonces para cada número a la izquierda del número de saltos más significativo, hazlos de 1, si el número de saltos es positivo hazlos todos 0 ya que la mayoría de las ramas están cerca de ellos objetivos, esto le ahorra una gran cantidad de aritmética extra para la mayoría de los casos.
- chris
Por lo general, no tiene que preocuparse por calcularlos como lo hará su ensamblador (o vinculador) para hacer los cálculos correctos. Digamos que tienes una pequeña función:
func:
slti $t0, $a0, 2
beq $t0, $zero, cont
ori $v0, $zero, 1
jr $ra
cont:
...
jal func
...
Al traducir el código anterior a una secuencia binaria de instrucciones, el ensamblador (o el enlazador si lo ensambló primero en un archivo objeto) se determinará en qué lugar de la memoria residirá la función (ignoremos el código independiente de posición por el momento). El lugar en memoria en el que residirá normalmente se especifica en el ABI o se le proporciona si está utilizando un simulador (como SPIM que carga el código a 0x400000
; tenga en cuenta que el enlace también contiene una buena explicación del proceso).
Suponiendo que estamos hablando del caso SPIM y nuestra función está primero en la memoria, la instrucción slti
residirá en 0x400000
, el beq
en 0x400004
y así sucesivamente. ¡Ahora casi llegamos! Para la instrucción beq
, la dirección de destino de la rama es la de cont
( 0x400010
) mirando una referencia de instrucción MIPS , vemos que está codificada como una inmediata de 16 bits con respecto a la siguiente instrucción (dividida por 4, ya que todas las instrucciones deben residir en una Dirección de 4 bytes alineados de todos modos).
Es decir:
Current address of instruction + 4 = 0x400004 + 4 = 0x400008
Branch target = 0x400010
Difference = 0x400010 - 0x400008 = 0x8
To encode = Difference / 4 = 0x8 / 4 = 0x2 = 0b10
Codificación de beq $t0, $zero, cont
0001 00ss ssst tttt iiii iiii iiii iiii
---------------------------------------
0001 0001 0000 0000 0000 0000 0000 0010
Como puede ver, puede realizar una bifurcación dentro de -0x1fffc .. 0x20000
bytes. Si por alguna razón necesitas saltar más, puedes usar un trampolín (un salto incondicional al objetivo real colocado dentro del límite dado).
Las direcciones de objetivo de salto son, a diferencia de las direcciones de destino de rama, codificadas usando la dirección absoluta (nuevamente dividida por 4). Como la codificación de la instrucción usa 6 bits para el código de operación, esto solo deja 26 bits para la dirección (efectivamente 28 dado que los 2 últimos bits serán 0), por lo tanto, los 4 bits más significativos del registro de PC se usan cuando se forma la dirección ( no importará a menos que intente saltar a través de los límites de 256 MB).
Volviendo al ejemplo anterior, la codificación para jal func
es:
Destination address = absolute address of func = 0x400000
Divided by 4 = 0x400000 / 4 = 0x100000
Lower 26 bits = 0x100000 & 0x03ffffff = 0x100000 = 0b100000000000000000000
0000 11ii iiii iiii iiii iiii iiii iiii
---------------------------------------
0000 1100 0001 0000 0000 0000 0000 0000
Puede verificar esto rápidamente y jugar con diferentes instrucciones, usando este ensamblador MIPS en línea que encontré (tenga en cuenta que no es compatible con todos los slti
, por ejemplo, slti
, así que lo cambié a slt
aquí):
00400000: <func> ; <input:0> func:
00400000: 0000002a ; <input:1> slt $t0, $a0, 2
00400004: 11000002 ; <input:2> beq $t0, $zero, cont
00400008: 34020001 ; <input:3> ori $v0, $zero, 1
0040000c: 03e00008 ; <input:4> jr $ra
00400010: <cont> ; <input:5> cont:
00400010: 0c100000 ; <input:7> jal func