Lo que distingue a las computadoras de las simples calculadoras es su capacidad para tomar decisiones basadas en los datos de la entrada. En los lenguajes de alto nivel esta acción se realiza con la expresión if algunas veces acompañada con expresiones goto y etiquetas . La arquitectura MIPS cuenta con dos instrucciones de brincos condicionales.
La primera:
beq registro1, registro2, L1
compara el contenido del registro1 con el del registro2, si son iguales, la siguiente instrucción será la que se encuentra ubicada en la etiqueta L1 (beq – branch if equal).
La segunda instrucción es:
bne registro1, registro2, L1
y en este caso el brinco se realizará si los contenidos de los registros no son iguales (bne – branch if not equal).
Ejemplo: Compilando una expresión if en un brinco condicional.
Para el siguiente código:
if ( i == j ) goto L1
Suponiendo que las cinco variables (f a j) se asocian con los registros ($s0 a $s4) ¿Cuál es el código MIPS compilado?
f = g + h;
L1: f = f – i;
Respuesta:
beq $s3, $s4, L1 # Se realiza la comparación
add $s0, $s1, $s2 # Si no fueron iguales se hace la suma
L1: sub $s0, $s0, $s3 # Si ocurrió la igualdad, la siguiente instrucción es la
# resta
Los compiladores se encargan de colocar etiquetas cuando éstas no aparecen en el código de alto nivel. Esta es otra ventaja de la escritura de programas en lenguajes de alto nivel.
Como un complemento a los brincos condicionales, la arquitectura MIPS cuenta con la instrucción:
j Etiqueta
Por medio de la cual se realiza un salto incondicional, de manera que la siguiente instrucción a ejecutarse es la que se especifica después de la etiqueta.
Ejemplo: Compilando una estructura if-then-else.
Para el siguiente código:
if ( i == j )
Si nuevamente las se asocian con los registros ($s0 a $s4) ¿Cuál es el código MIPS compilado?
f = g + h;
else
f = g – h;
Respuesta:
El compilador generará una serie de etiquetas en forma automática, de acuerdo al flujo del programa. Una opción es la siguiente:
De manera que si i == j se continúa con la suma y luego un salto a la etiqueta exit. En caso de que la igualdad no se cumpla, se hace el salto a la etiqueta else:
bne $s3, $s4, Else # Si no son iguales brinca a la etiqueta Else
add $s0, $s1, $s2 # Si fueron iguales se hace la suma
j Exit # y se salta a la etiqueta Exit.
Else: add $s0, $s1, $s2 # Si no fueron iguales se hace la resta
Exit: # y se termina la decisión.
Además de las elecciones entre dos alternativas, con estas instrucciones es posible la ejecución de ciclos repetitivos.
Ejemplo: Compilando un lazo simple.
Se tiene el lazo en C:
Loop: g = g + A[i]
i = i + j;
if( i != h ) goto Loop;
Suponiendo que las variables g, h i y j se asocian con los registros $s1, $s2, $s3 y $s4, respectivamente y que el registro base del Arreglo A es $s5 ¿Cuál es el código MIPS compilado?
Respuesta:
Primero se requiere obtener el valor de A[i] en un registro temporal:
Loop: add $t0, $s3, $s3 # $t0 = i + i = 2i
add $t0, $t0, $t0 # $t0 = 2i + 2i = 4i
add $t0, $t0, $s5 # $t0 contiene la dirección de A[i]
lw $t1, 0( $t0) # $t1 = A[i]
Luego se realizan las sumas:
add $s1, $s1, $t1 # g = g + A[i]
add $s3, $s3, $s4 # i = i + j
Por último se hace el brinco condicional:
bne $s3, $s2, Loop # Si I != j continúa en el lazo.
Las sentencias goto son poco usadas por los entendidos con la programación estructurada, pero con estas instrucciones es posible la compilación de los ciclos: while y do-while.
Ejemplo: Compilando un ciclo while.
Se tiene el ciclo repetitivo:
while (save[i] == k)
i = i + j;
Si las variables i, j y k se asocian con los registros $s3, $s4 y $s5, respectivamente y que el registro base del Arreglo save es $s6 ¿Cuál es el código MIPS compilado?
Respuesta:
Para que pueda compararse el valor de save[i], debe obtenerse en un registro temporal:
Loop:add $t1, $s3, $s3 # $t1 = i + i = 2i
add $t1, $t1, $t1 # $t1 = 2i + 2i = 4i
add $t1, $t1, $s6 # $t1 contiene la dirección de save[i]
lw $t1, 0( $t1) # $t1 = save[i]
Ahora es posible comparar a save[i] con k, si son diferentes, termina el ciclo:
bne $t1, $s5, Exit # si save[i] es diferente de k, termina el ciclo.
Dentro del ciclo se realiza la suma:
add $s3, $s3, $s4 # i = i + j
El ciclo se repite:
j Loop # Salta a la siguiente iteración
Exit:
La prueba de igualdad o desigualdad para un salto es la mas popular, sin embargo algunas veces es útil evaluar si una variable es menor que otra, por ejemplo en los ciclos repetitivos for, en los cuales se va incrementando (o decrementando) una variable y se continúa en el ciclo mientras sea menor que otra (o mayor que 0).
La instrucción MIPS slt (set on less than) compara dos registros y modifica a un tercero de acuerdo con el resultado de la comparación. Por ejemplo:
slt $t0, $s1, $s2
Pondrá un 1 en $t0 si $s1 < $s2, en caso contrario, $t0 contendrá 0.
Ejemplo: Prueba de la instrucción slt.
¿Cuál es el código que prueba si una variable a (asociada con $s0) es menor que una variable b (asociada con $s1) y brinca a la etiqueta less si la condición se mantiene?
Respuesta:
slt $t0, $s0, $s1 # $t0 = 1 si a < b y $t0 = 0 en caso contrario
bne $t0, $zero, less # $t0 no es igual a 0, brinca a la etiqueta less
Notar que se esta usando el hecho de que el registro cero contiene el valor 0.
Las estructuras de decisión if-then-else son ampliamente usadas, sin embargo en muchos programas se tiene diferentes alternativas a seguir después de evaluar una expresión. Nos estamos refiriendo a las estructuras similares a la estructura switch-case del lenguaje C. Se espera que una estructura de este estilo sea mas eficiente que múltiples comparaciones individuales. Para conseguirlo, los compiladores deben generar una tabla de direcciones de salto, de manera que se obtiene la dirección destino de la tabla y se realice el salto en forma inmediata.
Para tales situaciones, la arquitectura MIPS incluye a la instrucción jr (jump register) la cual realizará un salto incondicional a la dirección contenida en el registro especificado en la instrucción.
Ejemplo: Compilando una estructura switch-case.
El siguiente código C selecciona entre cuatro alternativas dependiendo si el valor de k es 0, 1, 2 o 3:
switch ( k ) {
case 0: f = i + h; break; /* k = 0 */
case 1: f = g + h; break; /* k = 1 */
case 2: f = g - h; break; /* k = 2 */
case 3: f = i - j; break; /* k = 3 */
}
Suponer que las seis variable f a k corresponden a los registros $s0 al $s5 y que el registro $t2 contiene 4. ¿Cuál es el correspondiente código MIPS?
Respuesta:
El objetivo es evaluar a la variable k para indexar a la tabla de direcciones, y posteriormente saltar al valor cargado. Pero primero es necesario asegurarse que k está en un caso válido:
slt $t3, $s5, $zero # Prueba si k < 0
bne $t3, $zero, Exit # Si k < 0, termina
slt $t3, $s5, $t2 # Prueba si k <40
beq $t3, $zero, Exit # Si k >= 4, termina
Si el valor de k es válido, para que pueda utilizarse como índice, debe multiplicarse por 4
add $t1, $s5, $s5 # $t1 = k + k = 2k
add $t1, $t1, $t1 # $t1 = 2k + 2k = 4k
Supongamos que existen cuatro palabras secuenciales en memoria que inician en la dirección contenida en $t4 y contienen la dirección correspondiente a las etiquetas L0, L1, L2 y L3. Para obtener la dirección adecuada para el salto se utilizan las instrucciones:
add $t1, $t1, $t4 # $t1 = dirección de la tabla_de_saltos[k]
lw $t0, 0( $t1) # $t1 = tabla_de_saltos[k]
Un salto a registro desviará el flujo del programa a la opción correspondiente:
jr $t0 # salto basado en el registro t0.
Las instrucciones que se realizarán en cada caso, de acuerdo con el valor de k son:
L0: add $s0, $s3, $s4 # k = 0 => f = i + j
j Exit
L1: add $s0, $s1, $s2 # k = 1 => f =g + h
j Exit
L2: sub $s0, $s1, $s2 # k = 2 => f = g - h
j Exit
L3: sub $s0, $s3, $s4 # k = 3 => f = i – j
Exit: # fin del switch-case