arquitectura del conjunto de instrucciones adaptado a partir de la presentación del prof. d....
TRANSCRIPT
Arquitectura del Conjunto Arquitectura del Conjunto de Instruccionesde Instrucciones
Adaptado a partir de la presentación del Prof. D. Patterson’s, 2000 UCB
Conjunto de Instrucciones
Sirve para comandar el hardware del computador, es necesario que manejemos su estructura:
Las palabras del lenguaje de máquina son llamadas instrucciones;
El vocabulario forma el conjunto de instrucciones, que determina la interfaz hardware/software.
Veremos el conjunto de instrucciones orientado a los programadores (lenguaje assembly) y a las máquina (lenguaje de máquina)
Se mostrará también las relaciones entre un lenguaje de alto nivel (como C) y el assembly.
Conjunto de Instrucciones
Los lenguajes de máquina son bastante parecidos entre si. Aprendiendo bien uno queda fácil aprender otro.
Esto ocurre porque: Todos son basados en los mismos principios
(arquitectura de Von Neumann); Existe un conjunto de operaciones básicas que
todas las máquinas deben suministrar; Los diseñadores tiene el mismo objetivo:
encontrar un lenguaje que torne fácil la construcción del hardware y de los compiladores, maximizando el desempeño y minimizando los costos SIMPLICIDAD
Implementación de Programas Un Programa de Computador es, básicamente, una
secuencia de comandos o instrucciones representando un algoritmo que debe ser ejecutado por la máquina.
Normalmente los programadores usan Lenguajes de Programación de Alto Nível (LAN), como Pascal e C, C++, JAVA, etc.
Estos lenguajes corresponden a un nivel de abstracción elevado.
Los lenguajes de los procesadores como el WNEANDER o el MIPS corresponden a un nivel de abstracción bajo, y son denominados “Linguajes de Bajo Nível (LBN)”
Por esta razón es necesario un proceso de traducción.
El problema de Traducir un programa
Los lenguajes LBNs son definidos por una serie de Mnemónicos, que son, básicamente, símbolos alfabéticos que representan un código binario
Por ejemplo, en le caso del WNEANDER la instrucción de adicción es representada por ADD
Sabemos que esta instrucción va sumar al acumulador el valor almacenado en una dirección especificada en el segundo byte da instrucción.
Esto muestra que la instrucción ADD sigue un formato: el primer byte es el código correspondiente al
Mnemónico de la instrucción El segundo byte corresponde a la dirección del
segundo operando de la instrucción. El primer operando ya se encuentra en el acumulador
Lenguaje de Montaje del Lenguaje de Máquina
Entretanto, para que un programa sea entendido por la máquina, es necesario que sus instrucciones estén codificadas en la forma binaria, en la forma de 0s y 1s.
En el WNEANDER esto queda claro, pues para usar la instrucción ADD debemos primero digitar su código de operación (OP-CODE)
Esto es: ADD corresponde a 30H o sea 001100002
El conjunto de instrucciones de una arquitectura (en la forma de Mnemónicos) corresponde al Lenguajes de Montaje de la arquitectura (Lenguaje ASSEMBLY)
El conjunto de instrucciones de una arquitectura (en la forma binaria) corresponde al Lenguaje de Máquina
El problema de traducir un programa
Diferentes Niveles de Abstracción
procesador
Lenguaje de máquina
Lenguaje de bajo nivel (asembler)
Lenguaje de alto nivel (C++)
Lenguaje natural
Traductores queda claro que tanto los programas
implementados en LANs como en LBN requieren ser traducidos a lenguaje de máquina del procesador
El proceso de traducción de un lenguaje de alto nivel (LAN) a lenguaje de máquina es realizado por compiladores o interpretadores
El proceso de traducción de un lenguaje de montaje para lenguaje de máquina es realizado por traductores, denominados Montadores (o Assemblers).
Compiladores e Interpretadores Compiladores son traductores que después de
varias fases (análisis léxico, análisis sintáctico, análisis semántico, generación de código intermediario, optimización de código y generación de código de montaje) generan un programa ejecutable.
En la verdad, este programa ejecutable deberá ser cargado en la memoria para ser ejecutado. Quine realiza esta tarea es un programa del sistema operacional (programa cargador o loader)
Los Interpretadores no generan código ejecutable.
Los interpretadores traducen cada instrucción del programa (en LAN o Assembly) y la ejecutan
Maneras como se realiza una traducción
Tenemos tres tipos de traductores: Montadores, Compiladores e Interpretadores
montador ejecuciónCódigo
fuente
(leng. Montaje)
Código
Objeto
compilador ejecuciónCódigo
fuente
(LAN)
Código
Objeto
Interpretador ejecuciónCódigo
fuente
(LAN o Assembly)
El problema de Traducir un programa de una LAN para una
LBN Vamos suponer un programa en C que haga
lo seguiente:
Problema: Traducir el código en C al conjunto de instruciones del WNEANDER
if (a == 0) a = a + b;else a = a - b;
Tradução feita para o WNEANDER
Programa Traduzido
Código do programa0 20 81 LDA 81 2 60 NOT 3 30 82 ADD 82 5 10 83 STA 83 7 20 80 LDA 80 9 A0 10 JZ 10 B 30 81 ADD 81 D 10 84 STA 84 F F0 HLT10 30 83 ADD 8312 10 84 STA 8414 F0 HLT
Área de dados80 10 81 20 82 1 83 E0 84 30
•O operando a está na posição 80H
•O operando b está na posição 81H
• -b é calculado e armazenado em 83H
• O resultado final é armazenado em 84H
Conjunto de Instrucciones
El conjunto de instrucciones iniciales que estudiaremos será la del MIPS, utilizado por diversas empresas (NEC, Nintendo, Silicon Graphics, Sony, …)
Se Utilizará un simulador de MIPS llamado SPIM, que tiene versiones para Unix, Windows y DOS. El SPIM puede ser bajado en la dirección URL:
http://www.mkp.com/cod2e.htm
CPU
Registers
$0
$31
Arithmeticunit
Multiplydivide
Lo Hi
Coprocessor 1 (FPU)
Registers
$0
$31
Arithmeticunit
Registers
BadVAddr
Coprocessor 0 (traps and memory)
Status
Cause
EPC
Memory
Organización del
MIPS R2000
Fig. A.18
ISA do MIPS (simplificado) Categorias de Instrucciones:
Load/Store Computación Jump y Desvio Punto Flotante Administración de Memoria Especiales
R0 - R31
PCHI
LO
OP
OP
OP
rs rt rd sa funct
rs rt inmediato
Destino del jump
3 Formatos de Instrucción: todos con 32 bits
Registros
Organización de la estructuras de almacenamiento programables registros memoria: flat, segmentada Modos de direccionamiento y de acceso a
datos e instrucciones. Tipos de datos y estructuras de los datos
Codificación y representación (próximo capítulo)
Formatos de instrucciones. Conjunto de instrucciones (o código de
operación) ULA, transferencia de control, tratamiento de
excepciones.
Componentes de un ISA
add a, b, c# a = b + cadd a, a, d# a = b + c + dadd a, a, e# a = b + c + d + e
Operaciones del Hardware
Todo computador debe ser capaz de realizar operaciones aritméticas.
Instrucciones aritméticas en el MIPS tienen formato fijo, realizando solamente una operación y teniendo tres “variables”
Solamente una Instrucción por linea
Comentarios
ej: a = b + c + d + e
ej..: add a,b,c a = b + c
Operaciones del Hardware
Exigir que toda instrucción tenga exactamente tres operandos conlleva a la filosofía de mantener el hardware simple: hardware para número variable de parámetros es mas complejo que para número fijo.
Principio #1 para diseño: Simplicidad favorece la regularidad
Ejemplo
Cuál será código generado por un compilador C para el siguiente trecho de programa?
add a, b, c #a = b + csub d, a, e #d = a - e
a = b + c;d = a – e;
Ejemplo 2
Cuál será código generado por un compilador C para el siguiente trecho de programa?
add t0, g, h #temporal t0 = g + hadd t1, i, j #temporal t1 = i + jsub f, t0, t1 #f = (g + h) – (I + j)
Solamente una operación es hecha por instrucción: necesidad de variables temporales...
f = (g + h) – (i + j);
Operandos y Registradores
Al contrario de los lenguajes de alto nivel, assembly no permite el uso de variables.
operandos en assembly son registradores:
Número limitado de direcciones especiales construidos directamente en el hardware;
Bloques básicos para construcción de computadores, pues son primitivas usadas en diseño de hardware que también son observadas por los programadores;
Registradores: beneficios
Registradores en el hardware, dentro del procesador más rápidos que la memoria
Registradores son de más fácil utilización por los compiladores:
Es un espacio para almacenamiento temporal.
pueden almacenar variables para reducir el tráfico de la memoria y mejorar la densidad de código (una vez que los registradores pueden ser especificados con menos bits que una dirección de memoria)
Operandos y Registradores
Registradores en el MIPS son de 32 bits;En el MIPS, bloques de 32 bits son
llamados palabras;Número de registradores es limitado:
MIPS 32 registradores, numerados desde 0 a 31
Principio #2 para diseño: pequeño es más rápido
Un numero muy grande de registradores aumentaría el período del clock.
Operandos y Registradores
A pesar que podemos referirnos a los registradores a través de números, en MIPS existe una convención de utilizar nombres en la forma $xy
Usaremos:
$s0, $s1, $s2, … para registradores que corresponden a variables en C
$t0, $t1, $t2, … para registradores temporales necesarios para compilar el programa en instrucciones MIPS
Ejemplo
Cuál será código generado por un compilador C para el siguiente trecho de programa?
add $t0, $s1, $s2 #temporal t0 = g + hadd $t1, $s3, $s4 #temporal t1 = i + jsub $s0, $t0, $t1 #f = (g + h) – (I + j)
Las variables f, g, h, i, j pueden ser mapeadas en los registradores $s0, $s1, $s2, $s3 e $s4,
respectivamente.
f = (g + h) – (i + j)
Operandos y Registradores
Estructuras de datos pueden ser bastante complejas, con un número de elementos muy grande para poder ser almacenados en los registradores Estructuras de datos, como vectores, son
almacenadas en la memoria
Operaciones aritméticas en MIPS solo pueden ser hechas cuando los operandos están en registradores
MIPS debe suministrar instrucciones para transferir datos entre la memoria y los registradores
Para acceder una palabra en la memoria, la instrucción debe suministrar una dirección de la memoria
Memoria
La Memoria es solamente un gran vector unidimensional, con la dirección actuando como índice en el vector, comenzando en 0.
3210
100
1010
11
Procesador
DatosDirecciones
Memoria
Transfiriendo datos de la memoria
La instrucción de transferencia de datos de la memoria para el registrador es llamada load.
Formato:
Dirección de la memoria accedida es dada por la suma de la constante (llamada “offset”) con el contenido del registrador base
En MIPS, el nombre de la instrucción es:
lw (load word)
lw registrador destino, constante (registrador base)
EjemploSuponga que tenemos un apuntador a para un entero
que está en la memoria, con la dirección de memoria dado en el registrador $s3 (o sea, el apuntador a está en $s3). Suponga además que el compilador asoció las variables g y h a los registradores $s1 y $s2. Cuál es el código para el siguiente trecho en C?
Primero tenemos que tomar el operando que está en la memoria y transferirlo a un registrador:
g = h + *a;
lw $t0, 0($s3)#temporal t0 = *a
add $s1, $s2, $t0 #g = h + *a
Resumen de la traducción
g = h + *a;
lw $t0, 0($s3)#temporal t0 = *a
add $s1, $s2, $t0 #g = h + *a
Vector A = [0,0,0,0,15], con 5 posiciones, comenzando en la dirección de memoria 102. Esta dirección es llamada de dirección base del vector. Así, 102 es la dirección de A[0],103 y de A[1], ...,106 y de A[4].
Como un vector aparece en la memoria
Datos
Direcciones
Dirección basede A
... 5 10 0 0 0 0 15 42 ... ... 100 101 102 103 104 105 106 107 ...
Ejemplo
Suponga que el vector A tiene 100 posiciones, y que el compilador asoció las variables g y h a los registradores $s1 y $s2. Tenemos además que la dirección base del vector A es dado en $s3. Cuál es el código para:
Primero tenemos que ubicar el operando que está en la memoria y transferirlo a un registrador:
g = h + A[8] ?
lw $t0, 8($s3) #temporal t0 = A[8]
add $s1, $s2, $t0 #g = h + A[8]
Resumen de la Traducción
g = h + A[8]
lw $t0, 8($s3) #temporal t0 = A[8]
add $s1, $s2, $t0 #g = h + A[8]
Comentario sobre la traducción anterior
La traducción anterior no toma en cuenta el hecho de como el MIPS direciona las palabras de la memoria
Esto será tratado posteriormente....
Transferencia de la memoria Ya que los bytes (8 bits) son útiles en muchos
programas, la mayor parte de las arquitecturas permiten acceder bytes;
Por lo tanto, la dirección de una palabra es la dirección de uno de los 4 bytes dentro de la palabra
Así, direcciones de palabras consecutivas difieren en 4 12
840
100
1010
11
Procesador
Datosdirecciones
Memoria
Cada posición del vector (de enteros) es una palabra, y por lo tanto ocupa 4 bytes
Vector A = [0,0,0,0,15], con 5 posiciones, comenzando en la dirección de memoria 408. Así, 408 es la dirección de A[0], 412 la de A[1], 416 la de A[2], 420 la de A[3] y 424 la de A[4].
Cómo un vector aparece en la memoria (2)
Datos
Ddirecciones
Dirección basede A
... 5 10 0 0 0 0 15 42 ... ... 400 404 408 412 416 420 424 428 ...
Ejemplo
Suponga que el vector A tiene 100 posiciones, y que el compilador asoció la variable h al registrador $s2. Tenemos además que la dirección base del vector A es dada en $s3. Cuál es el código para:
La novena posición del vector A, A[8], está en el offset 8 x 4 = 32
La décima-tercera posición del vector A, A[12], está en offset 12 x 4 = 48
A[12] = h + A[8] ?
lw $t0, 32($s3)#temporal t0 = A[8]add $t0, $s2, $t0 #temporal t0 = h + A[8]
sw $t0, 48($s3) #carga A[12] en $t0!!!!
Resumen de la traducción (real en el MIPS)
A[12] = h + A[8]
lw $t0, 32($s3)#temporal t0 = A[8]add $t0, $s2, $t0 #temporal t0 = h + A[8]
Transferiendo datos para la memoria
La instrucción de transferencia de datos de un registrador para la memoria es llamada store.
Formato:
Dirección de memoria accedida es dada por la suma de la constante (llamada de offset) con el contenido del registrador base
En MIPS, el nombre de la instrucción es:
sw (store word)
sw registrador funte, constante (registrador base)
Dirección absoluta de la posición 3 (i = 3) del vector
Para obtener la dirección absoluta requerimos:
0 1 2 3 4 5 6 7 8 9 ...
Registrador base
eje. $s2
Variable i ( i = 3) ($s4)
desplazamiento(offset)offset = 4*i
dirección = $s2 + 4*3
Ejemplo: y si el índice fuera una variable?
Suponga que el vector A tiene 100 posiciones, y que el compilador asoció las variables g, h y i a los registradores $s1, $s2 e $s4. Tenemos además que la dirección base del vector A es dado en $s3. Cual es el código para:
Requerimos primero que calcular la dirección de A[i]. Antes de sumar i a la dirección base de A, debemos multiplicar i por 4. Vamos hacer esto por ahora de la siguiente forma:
g = h + A[i] ?
add $t1, $s4, $s4 # $t1 = 2 * iadd $t1, $t1, $t1 # $t1 = 4 * i
Ejemplo: y si el índice fuera una variable?
Para coger A[i], sumamos $t1 con la dirección base de A, dada en $s3:
Ahora podemos cargar A[i] para un registrador temporal, y realizar la suma, que será almacenada en g:
add $t1, $t1, $s3 # $t1 = dirección de A[i]
lw $t0, 0($t1) #temporal $t0 = A[i]add $s1, $s2, $t0 #g = h + A[i]
Resumen de la Traducción
g = h + A[i]
add $t1, $s4, $s4 # $t1 = 2 * iadd $t1, $t1, $t1 # $t1 = 4 * iadd $t1, $t1, $s3 # $t1 = dirección de A[i] lw $t0, 0($t1) #temporal $t0 = A[i]add $s1, $s2, $t0 #g = h + A[i]
Ejercicio
Tenemos además que la dirección base del vector A está dado en $s2, y que las variables i y g son dadas en $s0 y $s1, respectivamente . Cual sería el código para:
A[i+g] = g + A[i] – A[0]?
Soluciónadd $t0, $s0, $s0 # $t0 = 2*iadd $t0, $t0, $t0 # $t0 = 4*iadd $t0, $t0, $s2# $t0 = dirección de A[i]lw $t1, 0($t0) # $t1 = A[i]add $t1, $s1, $t1# $t1 = g + A[i]lw $t0, 0($s2) # $t0 = A[0]sub $t1, $t1, $t0 # $t1 = g + A[i] – A[0]add $t0, $s0, $s1 # $t0 = i + gadd $t0, $t0, $t0 # $t0 = 2 * (i + g)add $t0, $t0, $t0 # $t0 = 4 * (i + g)add $t0, $t0, $s2# $t0 = dirección de A[i + g]sw $t1, 0($t0) # A[i + g] = g + A[i] – A[0]
A[i+g] = g + A[i] – A[0]?i,g,A = $s0, $s1,$s2
Utilizando los registradores
Muchos programas tiene más variables de que el número de registradores que tiene una máquina.
El compilador intenta mantener las variables más usadas en los registradores y coloca el resto en la memoria, utilizando loads y stores para mover los datos entre la memoria y los registradores:
Los compiladores tiene que utilizar los registradores de forma eficiente.
MIPS requiere que todas las palabras comiencen en direcciones que son múltiplos de 4 bytes
LLamamos esto de alineamiento: objetos tiene que tener direcciones que sean múltiplos de sus tamaños.
0 1 2 3Alineado
No alineado
Un comentario sobre la memoria: alineamiento
Procesadores puoden numerar bytes dentro de una palabra, de tal forma que el byte con el menor número es el más a la izquierda o el más a la derecha. Esto es llamado de byte order.
Ej: .byte 0, 1, 2, 3
Big endian: IBM 360/370, Motorola 68k, MIPS, Sparc, HP PA
Little Endian: Intel 80x86, MIPS, DEC Vax, DEC Alpha
3 2 1 0little endian
0 1 2 3big endian
Otro comentario: Byte order
Representando Instrucciones en un Computador
Números son armacenados en el hardware en base 2, o binária.
Instrucciones se pueden representar como números. En la verdad, cada pedazo de la instrucción es un número, y el posicionamento de un lado a otro es los que forma la instrcción.
Existe una convención en el MIPS para asociar nombres de registradores a sus números:
$s0, $s1, …, $s7 16, 17, …, 23$t0, $t1, …, $t7 8, 9, …, 15
Representando Instrucciones en un Computador
Lenguaje de máquina para la instrucción:
Cada segmento es se llama campo. El primero y el último, juntos, informan que la instrucción es una adición. El segundo dice cual es el primer registrador fuente (17 = $s1) y el tercero, el segundo registrador fuente (18 = $s2). El cuarto campo es el registrador destino (8 = $t0). El quinto campo no es usado en esta instrucción, y por esto tiene valor cero.
0 17 18 8 0 32
add $t0, $s1, $s2
También podemos representar los números en binario:
Para distinguir del lenguaje Assembly, llamamos esta representación numérica del lenguaje de máquina, y la secuencia de tales instrucciones de código de máquina
Llamamos esta estructura de campos del formato de la instrucción. Tales instrucciones en MIPS poseen 32 bits (“simplicidad favorece regularidad”).
000000 10001 10010 01000 00000 100000
Representando Instrucciones en un Computador
6 bits 5 bits 5 bits 5 bits 5 bits 6 bits
Campos del MIPS
Daremos nombres a los campos para simplificar la discusión:
op: operación básica de la instrucción (opcode); rs: primer registrador fuente; rt: segundo registrador fuente; rd: registrador destino, recibe el resultado de la
operación shamt: cantidad de shift (se verá posteriormente) funct: función; selecciona el tipo específico de la
operación dada en el campo op, tambiém llamada de código de la función.
op rs rt rd shamt funct 6 bits 5 bits 5 bits 5 bits 5
bits 6 bits
Campos MIPS
Un problema sucede cuando una instrucción necesita de campos más largos que los mostrados en este ejemplo.
Ex.: La instrucción lw requiere especificar dos registradores y una constante. Si la constante fuera a ser representada en el campo de uno de los registradores, su valor se limitará máximo hasta 32 (2^5). Obviamente, este valor es muy pequeño para ser útil.
Así, tenemos un conflicto entre el deseo de que todas las instrucciones tengan el mismo tamaño y el deseo de que tengamos un solo formato de instrucciones.
Campos MIPS
Principio #3 para diseño: buenos diseños demandan buenos compromisos.
El Compromiso escogido por los diseñadores del MIPS: mantener todas las instrucciones con el mismo tamaño diferentes tipos de instrucción puedem tener diferentes formatos de instrucción.
El formato mostrado anteriormente es el llamado
de tipo-R (de registrador) o formato-R
Un segundo formato es el usado por las instrucciones de transferencia de datos: tipo-I o
formato-I
Campos MIPS
Formato tipo-I:
La dirección de 16 bits significa que una instrucción lw puede cargar cualquier palabra dentro de una región de 2^15 o 32768 bytes (2^13 o 8192 palabras) en relación a la dirección en el registrador base rs.
op rs rt dirección
6 bits 5 bits 5 bits 16 bits
Campos MIPS
Ej: lw $t0, 32($s3) #temporal $t0 = A[8]
En este ejemplo, rs recibe 19 ($s3), rt recibe 8 ($t0) y el campo dirección recibe el valor 32. Op en este caso es 35 (lw). En una instrucción de load, el campo rt determina el registrador destino!!
A pesar que e hardware queda más complejo se utilizan diferentes formatos de instrucción, podemos reducir este aumento de complejidad manteniendo cierta similaridad entre los formatos (ej.: 3 primeros campos en los formatos tipo-R y tipo-I son los mismos; el largo del último campo tipo-I es igual a la suma de los 3 últimos tipo-R).
op rs rt dirección
Campos MIPS
El primer campo determina el tipo del formato.
Pregunta: por que no utilizar solamente un campo de 12 bits para los campos op y funct en las instrucciones tipo-R??
Codificación de algunas instrucciones estudiadas
Instru. Formato
op
rs rt rd shamt
funct
Direcc.
add R 0 reg
reg
reg
0 32 no
sub R 0 reg
reg
reg
0 34 no
lw I 35
reg
reg
no no no direc.
sw I 43
reg
reg
no no no direc.
$s0, $s1, …, $s7 16, 17, …, 23$t0, $t1, …, $t7 8, 9, …, 15
Ejemplo de una compilación manual
Suponga que $t1 tiene la direción base de A y que $s2 corresponda a h, traduzca la seguiente linea en C a código de máquina MIPS: A[300] = h + A[300];
Primeiro, temos que el código een assembly correspondiente es:
Cuál es el código de máquina de estas 3 instrucciones?
lw $t0,1200($t1) # $t0 = A[300]add $t0, $s2, $t0 # $t0 = h + A[300]sw $t0, 1200($t1) # A[300] = h + A[300]
Ejemplo de una compilación manual
lw $t0,1200($t1) add $t0, $s2, $t0sw $t0, 1200($t1)
op
rs rt
rd
direcc /shamt
func
35
9 8 1200
0 18
8 8 0 32
43
9 8 1200
op rs rt rd direcci /shamt
func
100011
01001
01000
0000 0100 1011 0000
000000
10010
01000
01000
00000 100000
101011
01001
01000
0000 0100 1011 0000
Idea general: concepto de programa almacenado
Computadores modernos son construidos basados en dos principios fundamentales:
1. Las Instrucciones pueden ser representadas como números
2. Los Programas pueden ser almacenados en memória para ser ledos o escritos de la misma forma que los números
El Concepto de programa almacenado,es fundamental en Computación!!!
Instruciones para toma de decisiones (control de flujo)
Lo que distingue un computador de una calculadora simple es la habilidad de tomar decisiones.
Con base en la entrada y en los resultados computados, diferentes instrucciones son ejecutadas.
En lenguajes de alto nivel, una de las formas de tomar las decisiones es a través de las instrucciones if y goto.
En MIPS, tenemos dos instrucciones que actúan de manera similar las instrucciones que combinan if con goto:
1. beq registr1, registr2, L1 #branch if equal
2. bne registr1, registr2, L1 #branch if not equal
rótulo (label)
Instrucciones de decisión en MIPS
en C, esto sería equivalente a:
beq registr1, registr2, L1
Semántica: “Desvie si (valores en los registradores son)
iguales”
if (registr1== registr2) goto L1
Instrucciones de decisión en MIPS(II)
en C, esto sería equivalente a:
Estas instrucciones se llaman de desvío condicional.
bne registr1, registr2, L1
Semántica: “desvie si (los valores en los registradores) no son iguales”
if (registr1!=registr2) goto L1
Ejemplo
Si las variables f, g, h, i, j corresponden a los registradores $s0 a $s4, cual es el código compilado para el seguiente programa en C?
Como las instrucciones son armacenadas en la memoria, ellas tiene direcciones también!!
if (i == j) goto L1; f = g + h;L1: f = f – i;
beq $s3, $s4, L1 # va para L1 si $s3 == $s4 add $s0, $s1, $s2 # f = g + h (se i != j)
L1: sub $s0, $s0, $s3 # f = f – i (se i == j)
Instrucciones para toma de decisiones
El montador (assembler) hace que el compilador o el programador en lenguaje assembly no tenga que estar calculando direcciones para los desvíos (branches) al permitir el uso de labels.
Compiladores crean desvíos y rótulos sen que el programador de lenguaje de alto nivel los tenga que especificar. Esta es una de las razones de por que es más rápido programar en lenguajes de alto nivel.
Desvío incondicional en MIPS MIPS tiene un desvío incondicional:
llamada de instrucción de salto (jump): salte al rótulo especificado, (incondicionalmente!)
En C, esto sería equivalente a: goto label
Podemos pensar que esto es equivalente a:
Ya que la condición siempre es satisfecha
Existe un formato de instrucción para desvío (tipo-J o formato-J), como se verá adelante.
j rótulo
beq $0,$0,rótulo
Ejemplo: código para if
Si las variables f, g, h, i, j corresponden a los registradores $s0 a $s4, cuál es el código compilado para el siguiente trecho en C?
Queremos implementar el siguiente flujo:
Fin
i == j?
f=g+h f=g-h
(falso) i != j
(verdadero) i == j
if (i == j) f = g + h; else f = g – h;
Ejemplo: código para if
Primero debemos saber si i es igual a j:
Si i == j, sumamos g con h y almacenamos en f
Requerimos ahora ir para el fin del if. Para esto utilizamos un desvío incondicional ( j, de jump ):
Tratamos ahora el caso en que i != j
bne $s3, $s4, else # vá para else si i != j
add $s0, $s1, $s2 # f = g + h (se i == j)
j Fim # vá para Fim
Else: sub $s0, $s1, $s2 # f = g – h (se i != j)Fin:
Resumen de la traducción de if then else
if (i == j) f = g + h; else f = g – h;
bne $s3, $s4, Else # vá para else si i != j
add $s0, $s1, $s2 # f = g + h (se i == j)
j Fin # vá para FinElse: sub $s0, $s1, $s2 # f = g – h (si i != j)Fin:
Loops
Decisiones son importantes para escoger entre dos alternativas, y para iterar una computación (loop). Usamos las mismas instrucciones assembly para las dos situaciones.
Todo depende de donde colocamos el rótulo para el cual saltaremos.
Ejemplo de loop Si las variable g, h, i, j corresponden a los registradores
$s1 a $s4, y la dirección base del vector A (de 100 elementos) está en $s5, compile el siguiente trecho dado C.
Loop: g = g + A[i];if ( (i = i + j) != h ) goto Loop;
Loop: add $t1, $s3, $s3 # $t1 = 2 * iadd $t1, $t1, $t1 # $t1 = 4 *
iadd $t1, $t1, $s5 # $t1 =
direcc. de A[i]lw $t0, 0($t1) # $t0 = A[i]add $s1, $s1, $t0 # g = g +
A[i]add $s3, $s3, $s4 # i = i + jbne $s3, $s2, Loop # Loop se
(i != h)
Compilando un lazo do tipo while
Los programadores normalmente no utilizan instrucciones goto (su uso es considerado una mal práctica de programación !!!). Asim, el compilador tiene que traducir los loops tradicionales en lenguaje MIPS.
Ejemplo en C:
Si i, j, e k corresponden a los registradores $s3, $s4 e $s5, y la dirección base del vector save está en el registrador $s6, cuál es le código assembly MIPS correspondiente?
while (save[j] == k)
i = i + j;
Compilando un lazo do tipo while
Tenemos inicialmente que cargar save[i] a un registrador temporal:
Ahora realizamos el test del loop, si save[i] != k
Debemos ahora regresar al while en el inicio del loop
Loop: add $t1, $s3, $s3 # $t1 = 2 * iadd $t1, $t1, $t1 # $t1 = 4 * i
add $t1, $t1, $s6 # $t1 = direccion de save[i]lw $t0, 0($t1) # $t0 = save[i]
bne $t0, $s5, Fin # vá para Fim se save[i] != kadd $s3, $s3, $s4 # i = i + j
j Loop # va para LoopFin:
Resumen de la traducción de while
while (save[j] == k) i = i +
j;
Loop: add $t1, $s3, $s3 # $t1 = 2 * iadd $t1, $t1, $t1 # $t1 = 4 * i
add $t1, $t1, $s6 # $t1 = direccion de save[i]
lw $t0, 0($t1) # $t0 = save[i]bne $t0, $s5, Fin # va par Fim si save[i] != kadd $s3, $s3, $s4 # i = i + jj Loop # va para Loop
Fin:
Comparando dos registradores
Los tests de igualdad o desiguald son probablemente los más populares, pero hay veces que queremos testar si una variable es menor que otra
Por ejemplo, un for puede querer testar si un índice es menor de cero.
En MIPS, tenemos una instrucción que compara los valores de dos registradores, e atribuye 1 a un tercer registrador si el primero registrador es menor que el segundo, y 0 en caso contrario:
Si $s3 < $s4, $t0 recibe 1, caso contrario, recibe 0
slt (set on less than) slt $t0, $s3, $s4
Compilando un lazo del tipo while
Compiladores MIPS utilizam las instrucciones slt, beq, bne, y el valor fijo cero para criar todas las relaciones de comparación: igual, diferente, menor que, menor o igual a, mayor que, mayor o igual a.
El registrador $cero (registrador 0) tiene su valor fijado en cero.
Ejemplo: desvío si menor que
Cuál el código para testar, si la variable a, mapeada en el registrador $s0, es menor que la variable b (registrador $s1), y desviar para el rótulo Menor si la condición fuera satisfecha?
Primero usamos a instrucción slt y un registrador temporal:
Registrador $t0 es 1 si a < b. Por lo tanto, testamos si $t0 no es 0:
slt $t0, $s0, $s1 # $t0 = (a < b)?
bne $t0, cero, Menor #vá para Menor si $t0!=0#o sea, si (a < b)
traducción del if (a < b) then... else
if (i < j) f = g + h;
else f = g – h;
slt $t0, $s0, $s1 # $t0 = (a < b)?
beq $t0, cero, Else # va para else si a >= b
add $s0, $s1, $s2 # f = g + h (se i == j)
j Fin # vá para FinElse: sub $s0, $s1, $s2 # f = g – h (se i != j)Fin:
Por que no utilizar una instrucción solo “desvie si es
menor que”?
MIPS no tiene instrucciones como “desvie si menor que” porque son muy complicadas: aumentarían el período del clock o serian necesarios más ciclos de clock por instrucción (además de exigir la necesidad de más hardware).
Las dos instrucciones slt y bne son más rápidas y más útiles, pues también sirven para otros propósitos.
otro tipo de desvío incondicional
Hasta ahora vimos una instrucción de desvío incondicional, a través de la instrucción
En esta instrucción, tenemos que especificar un rótulo, o sea una dirección fija, para la cual el Program Counter será desviado.
En diversas situaciones, puede ser interesante que desviemos para una dirección variable, almacenada en un registrador. Por lo tanto, existe la instrucción jr:
j Rótulo # desvio para el rótulo
jr registrador #desvio para dirección
#contenida en el registrador
Instrucción switch
El linguaje C define la instrucción switch, que permite que el programador seleccione una alternativa entre varias, dependiendo de un valor único.
Cómo compilar el siguinte trecho de código en C?switch (k) {
case 0: f = i + j; break; case 1: f = g + h; break; case 2: f = g – h; break; case 3: f = i – j; break;
}
Instrucción switch
Una forma de realizar esto es tratar el comando switch como una secuencia de instrucciones if:
if ( (k >= 0) && (k <= 3)) {if (k == 0)
f = i + j;else if (k == 1)
f = g + h;else if (k == 2)
f = g - h;else
f = i + j;}
Instrucción switch
De esta forma, si tenemos n casos posible, tendremos, en media, que verificar n/2 casos contra k para encontrar el caso deseado.
Cómo podemos implementar el comando switch de manera más eficiente?
En algunos casos, podemos utilizar una tabla de direcciones, de tal forma que al acceder la tabla en la posición correspondiente a k, TabEnd[k], obtengamos la dirección del label deseado.
De esta manera, podemos tomar la decisión en tiempo constante.
Instrucción switch
Mejorando la instrucción switch:
Vamos suponer que las variables f, g, h, i, j, k están en los registradores $s0 a $s5, y que $t2 contiene el valor 4.
switch (k) {case 0: f = i + j; break;case 1: f = g + h; break;case 2: f = g – h; break;case 3: f = i – j; break;
}
Comando switch
Utilizaremos la variable k como índice en la tabla de direcciones, y desviamos de acuerdo con el valor cargado;
Primero, tenemos que verificar si 0 <= k <= 3:
Vamos utilizar k para indexar; por esto tenemos que multiplicar k por 4.
slt $t3, $s5, $cero #verifica si k<0bne $t3, $cero, Fin #si k<0, se va para Finslt $t3, $s5, $t2 #verifica si k<4beq $t3, $cero, Fin #si k>=4 va para Fin
Instrucción switch
Suponga que el vector de direcciones TabEnd, cuya dirección está en $t4, pose cuatro posiciones, con las direcciones correspondientes a los labels L0, L1, L2 y L3:
Ahora saltamos para la dirección presente en $t0:
add $t1, $s5, $s5 # t1 = 2 * kadd $t1, $t1, $t1 # t1 = 4 * k
add $t1, $t1, $t4 # t1 = direccion de TabEnd[k]lw $t0, 0($t1) # t0 = TabEnd[k]
jr$t0
Instrucción switch
Por fin tratamos los casos del switch:
L0: add $s0, $s3, $s4 # f = i+jj Fin
L1: add $s0, $s1, $s2 # f = g + hj Fin
L2: sub $s0, $s1, $s2# f = g – h j Fin
L3: sub $s0, $s3, $s4# f = i – j (no requiere saltar# para el Fin, ya que la próxima# instrucción es Fin)
Fin:
Resumen: instrucciones estudiadas hasta ahora
Categoria Instrucción Ejemplo Semántica Adición add $s1, $s2, $s3 $s1 = $s2 + $s3 Aritmética Subtración sub $s1, $s2, $s3 $s1 = $s2 - $s3 Load word lw $s1, 100($s2) $s1 = Mem[$s2 + 100] Transferencia
de datos Store word sw $s1, 100($s2) Mem[$s2 + 100] = $s1 Branch on equal
beq $s1, $s2, L If ($s1 == $s2) goto L
Branch on not equal
bne $s1, $s2, L If ($s1 != $s2) goto L
Desvio Condicional
Set on less than
slt $s1, $s2, $s3 if ($s2 < $s3) $s1 = 1; else $s1 = 0;
Jump j 2500 goto 10000 Desvio Incondicional Jump register jr $t1 goto $t1
Instrucciones para soportar llamada procedimentos o
subrutinas Procedimientos o subrutinas son utilizadas por
los programadores para: Facilitar la compresión y mantenimiento del
código; Posibilitar el reaprovechamiento de código.
El código de un procedimiento queda “aislado”, siendo la interfaz con el código restante, la establecidad por los argumentos de entrada y por los resultados de salida.
Instrucciones para soportar llamada procedimentos o
subrutinas En la ejecución de un procedimiento, un
programa debe seguir los siguientes pasos: Colocar los argumentos (parámetros) en un
lugar donde el procedimiento puede accederlos;
Transferir el control al procedimiento; Ejecutar la tarea deseada (del procedimiento). Colocar el resultado de la ejecución en un
lugar en el cual el código que llamó el procedimiento pueda accederlo;
Retornar el control para el lugar de origen.
Instrucciones de soporte a procedimentos
Cuál es el lugar más rápido donde se pueden almacenar datos en un computador?
Registrador. Se deben utilizar registradores siempre que sea posible
El Software para MIPS utiliza los siguientes registradores para llamada de procedimientos:
$a0 - $a3: cuatro registradores que son utilizados para pasar parámetros para los procedimientos;
$v0 - $v1: dos registradores que son utilizados para retornar valores calculados por los procedimientos (resultados)
$ra: registrador utilizado para retornar al ponto de origen (ra = return address).
Instrucciones para soportar llamada procedimientos o
subrutinas
Ejecución del procedimento
Prepara argumentos para el procedimento
$a0-$a3
Realiza desvio para el procedimento
$ra; desvio
Valores de retorno del procedimento $v0-$v1
Continuación del programa
Retorno para la continuación del programa jr $ra
C
MIPS
Ejemplo de procedimento... suma(a,b);... /* a:$s0; b:$s1 */}
int suma(int x, int y) { /* x:$a0; y:$a1 */return x+y;
} Direccción 1000 add $a0,$s0,$zero # x = a
1004 add $a1,$s1,$zero # y = b 1008 addi $ra,$zero,1016 # $ra = 10161012 j suma # desvio para suma1016 ...2000 suma: add $v0,$a0,$a12004 jr $ra #regre. p/ origen, #a la direicc 1016
Instucción jal (jump and link)
El MIPS tiene una instrucción de salto solo para procedimientos; ella causa un desvío para la dirección, y al mismo tiempo guarda la dirección de la próxima instrucción en $ra: jal (jump and link).
jal label #desvio para el procedimento que tiene
#dirección marcado por label
jal (jump and link)
Link, en este caso, quiere decir que es utilizado, el registrador $ra, para guardar una referencia a la instrucción que sigue a la instrucción jal, o sea, la instrucción jal es equivalente al siguiente código:
Por qué existe a instrucción jal??Procedimientos son muy comunes: “Haga el caso común el más rápido”
$ra = PC + 4j Rótulo
C
MIPS
Ejemplo de procedimento (revisado)
... suma(a,b);... /* a:$s0; b:$s1 */}
int suma(int x, int y) { /* x:$a0; y:$a1 */return x+y;
} dirección
1000 add $a0,$s0,$zero # x = a1004 add $a1,$s1,$zero # y = b 1008 jal suma #prepara $ra y
#jump p/ proc suma1012 ...2000 suma: add $v0,$a0,$a12004 jr $ra # regre p/ origen,
# a la direcc 1012
Ejercicio
Cuál es le código en assembly del MIPS equivalente al siguiente instrucción en C, estando i en $s0 y j en $s1?
i = sub(i,j); /* sub(a, b) {return a-b;} */...add $a0, $s0, $zero #a0 = iadd $a1, $s1, $zero #a1 = jjal sub #Llama la fun. subadd $s0, $v0, $zero # i = sub(i,j)...
sub: sub $v0, $a0, $a1 # $v0 = a – bjr $ra # retorne p/
origen
Usando más registradores Suponga que un procedimiento requiere más
registradores de que los 4 registradores de argumentos y 2 de retorno.
El código que llama este procedimiento puede estar utilizando diversos registradores, de tal forma que el procedimiento no puede utilizar los registradores de cualquier forma, ya que valores importantes se podrían perder.
Así, Cualquier registrador que el procedimiento utilice y que pueda ser de interés del código “que efectua el llamado” debe tener su valor restaurado al valor anterior a la ejecucción del procedimento.
Usando más registradores
Cómo llevar a cabo esto?
Se realiza el Proceso conocido como register spilling: Consiste en el uso de una pila, una estructura
de datos do tipo LIFO (last-in first-out); Se tiene un apuntador para el tope de la pila; Este apuntador es ajustado en una palabra
cada vez que un registrador es colocado en la pila (operación conocida por push), o retirado de la pila (operación conocida por pop).
En el MIPS, un registrador es utilizado solamente para indicar el tope de la pila: sp (stack pointer)
Usando más registradores Por razones históricas, la pila “crece” de la
mayor dirección a la menor dirección:
Para colocar un valor en la pila (push), debemos decrementar $sp en una palabra y mover el valor deseado para la posición de memoria apuntada por $sp;
Para retirar un valor de la pila (pop), debemos leer este valor de la posición de memoria apuntado por $sp, y entonces incrementar $sp en una palabra.
0
Dirección
Código Programa
Estático Variábles declaradas unavez para todo programa
HeapEspacio explicitamente creadomalloc: apuntadores en C
Pila Espacio para que los procedimentosalmacenen información
$sp stack
pointer
Alocando memória en C
Ejemplo: ejemplo_proc Suponga que tenemos el seguiente código:
Vamos generar el código correspondiente en assembly MIPS.
int ejemplo_proc (int g, int j, int i, int h) {
int f;f = (g+h) – (i+j);return f;
}
Ejemplo: ejemplo_proc Los argumentos g, h ,i e j corresponden a los
registradores $a0, $a1, $a2 y $a3, y f corresponde a $s0. Primero colocamos el rótulo del procedimiento:
ejemplo_proc: Debemos entonces almacenar los registradores
que serán utilizados por el procedimiento. Como estaremos utilizando los registradores $t0, $t1 e $s0, vamos a colocarlos en la pila:subi $sp, $sp, 12 #crea espacio para 3 itens mas en la
pilasw $t1, 8(sp) #empila $t1sw $t2, 4(sp) #empila $t2sw $s0, 0(sp) #empila $s0
Ejemplo: ejemplo_proc
Como quedó la pila?
$sp
Valores empilados antes de la
función
Pila antes de la
función
$s0
$t2
$t1
$sp
Valores empilados antes de la
función
Pila durante ejecución de la
función
Ejemplo: ejemplo_proc
Las próximas instrucciones corresponden al cuerpo del procedimiento:
El resultado debe ser almacenado en el registrador $v0:
add $v0, $s0, $zero # retorna f en $v0
add $t0, $a0, $a1 # $t0 = g + hadd $t1, $a2, $a3 # $t1 = i + jsub $s0, $t0, $t1 # f = $t0 = (g+h) – (i+j)
Ejemplo: ejemplo_proc
Antes de salir del procedimiento, debemos restaurar los valores de $t0, $t1 y $s0, retirándolos de la pila:
Regresamos entonces para la instrucción siguiente al punto en que la función ejemplo_proc fue llamada:
lw $s0, 0($sp) # desempila $s0lw $t0, 4($sp) # desempila $t0lw $t1, 8 ($sp) # desempila $t1addi $sp, $sp, 12 # retira 3 itens de la pila
jr $ra # retorna a la subrutina que llamo
# este procedimento
Instrucciones de soporte a procedimentos
En el ejemplo anterior, utilizamos registradores temporales y asumimos que los valores de ellos deberían ser guardados y restaurados.
Para evitar que los registradores que no son utilizados sean empilados y desempilados, MIPS ofrece dos clases de registradores: $t0-$t9: 10 registradores temporales que no
son preservados por la función que es llamada. $s0-$s7: 8 registradores que tienen sus valores
preservados en el proceso de llamada de procedimiento. De esta forma, si estos registradores fueran utilizados por el procedimiento, se debe almacenar sus valores empilandolos en el inicio del procedimiento y desempilandolos en el final.
Instrucciones de soporte a procedimentos
Esta simple convención permite que nos se pierda tanto tiempo empilando y desempilando registradores
En el ejemplo, tendríamos que preservar solamente el valor de $s0.
que hacer entonces si tenemos un código que utiliza registradores temporales y va llamar una función??
Pueden aparecer problemas cuando hacemos que funciones chamen otras funciones (por ejemplo, funciones recursivas)??
Procedimentos anidados Suponga que el programa principal llama la
función A con el argumento 3, al colocar 3 en $a0 y utiliza la instrucción jal A.
Suponga además que la función A llama la función B con argumento 7, al colocar 7 en $a0 y utiliza la instrucción jal B.
Una vez que A todavía no terminó de ejecutarse tendremos un conflicto en el uso del registrador $a0.
Un problema también aparece para el registrador $ra, que ahora contiene el valor para el retorno de B.
Se no tenemos cuidado, podríamos no ser capaces ni de regresar al programa principal!!!
Cómo resolver este problema?
Procedimentos anidados:convención sobre registradores
Una solución es empilar todos los registradores que requieren ser preservados.
Para esto, tenemos que establecer una convención entre la subrutina que llama a la función y la subrutina llamada, para establecer cuales registradores serán preservados, y por quien ?
Definiciones: llamadora: función que realiza la llamada, utilizando
jal; Llamada: función siendo llamada.
Podemos pensar en estas convenciones como un “contrato” entre la llamadora y la llamada;
Por que utilizar convenciones para llamadas de procedimentos?
Si tanto la sub-rutina llamadora como la llamada obedecieran la convención, tendremos los siguientes beneficios:
Personas que nunca conversaran pueden escribir funciones que funcionan juntas;
Funciones que llaman otras funciones – como las recursivas – funcionan correctamente.
Atención!! Llamadora o llamada no representa una propiedad de la función, pero si el papel que la función ejerce en una llamada de procedimiento específica. Así, una función puede ejercer el papel de llamadora y de llamada, solo que en diferentes llamadas de procedimiento.
Derechos de la Llamadora y de la Llamada
Derechos de la Llamada: Utilizar libremente los registradores v, a, y t; Asumir que los argumentos son pasados
corretcamente.
Registradores que deben ser preservados por la Llamadora: Dirección de retorno $ra Argumentos $a0, $a1, $a2, $a3 Valores de retorno $v0, $v1 Registradores temporales $t0 - $t9
Derechos de la Llamadora Utilizar los registradores s, sin que ellos sean
alterados por la Llamada Asumir que los valores de retorno y la pila están
correctos
Registradores que deben ser preservados por la Llamada: Registradores $s $s0 - $s7
Ejemplo: suma_recursiva Suponga que tenemos el siguiente código, que
calcula la suma n + (n-1) + … + 2 + 1 de forma recursiva:
Vamos generar el código correspondiente en assembly MIPS.
int suma_recursiva (int n) {
if (n < 1)return 0;
elsereturn n +
suma_recursiva(n-1)}
Ejemplo: suma_recursiva El parámetro n corresponde al registrador $a0. Debemos inicialmente colocar un rótulo para la
función, y salvar la dirección de retorno, almacenada en $ra, o parámetro $a0:
En la primera vez que suma_recursiva es llamada, el valor de $ra que es almacenado corresponde a una dirección que está en la sub-rutina que llama esta función.
Suma_recursiva:subi $sp, $sp, 8 # prepara la pila para recibir
2# valores
sw $ra, 4($sp) # empila $rasw $a0, 0($sp) # empila $a0
Ejemplo: suma_recursiva Vamos ahora a compilar o cuerpo de la función.
Inicialmente, verificamos si n < 1:
Si n <1, la función debe retornar el valor 0. No podemos olvidarnos de restaurar la pila.
Por qué no cargamos los valores de $a0 y $ra antes de ajustar $sp??
slti $t0, $a0, 1 # testa si n < 1beq $t0, $zero, L1 # si n>=1, va para L1
add $v0, $zero, $zero # valor de retorno es0add $sp, $sp, 8 # remueve 2 itens de la pilajr $ra # retorne para depues de jal
Ejemplo: suma_recursiva Si n >=1, decrementamos n y llamamos
nuevamente la función suma_recursiva con el nuevo valor de n.
Cuando la suma para (n-1) es calculada, el programa vuelve a ejecutar en la próxima instrucción. Restauramos la dirección de retorno y el argumento anterior, e incrementamos el apuntador de tope de la pila:
L1:subi $a0, $a0, 1 # argumento pasa a ser (n-1)jal suma_recursiva # calcula la suma para (n-1)
lw $a0, 0($sp) # restaura el valor de nlw $ra, 4($sp) # restaura la dirección de retornoaddi $sp, $sp, 8 # retira 2 itens de la pila.
Ejemplo: suma_recursiva Ahora el registrador $v0 recibe la suma de
argumento antiguo $a0 con el valor actual en $v0 (suma_recursiva para n-1):
Por último, regresamos a la instrucción siguiente a la que llamó el procedimiento:
add $v0, $a0, $v0 # retorne n + suma_recursiva(n-1)
jr $ra # retorne para el “llamador”
Ejemplo: suma_recursiva Ahora el registrador $v0 recibe la suma de
argumento antiguo $a0 con el valor actual en $v0 (suma_recursiva para n-1):
Por último, regresamos a la instrucción siguiente a la que llamó el procedimiento:
add $v0, $a0, $v0 # retorne n + suma_recursiva(n-1)
jr $ra # retorne para el “llamador”
Trabajando con caracteres y strings
La mayor parte de los computadores hoy utiliza bytes para representar caracteres, casi siempre utilizan la codificación ASCII (American Standard Code for Information Interchange).
La tabla de ASCII, debe ser utilizada como fuente de consulta.
Instrucciones de transferencia de palabras (lw, sw) son suficientes para transferir bytes también. En caso, bytes individuales podrían ser leidos o escritos, a través del uso de máscaras, con las instrucciones and y or lógicos.
Trabajando con caracteres y strings
Ya que gran parte de los programas utilizan texto, MIPS definió instrucciones específicas para mover bytes.
Lectura de bytes:
load byte (lb): le un byte de la memoria, colocándolo en los 8 bits mas a la derecha de n registrador
Ej: lb $t0, 0($sp) # el byte que está en el tope de la pila
Trabajando con caracteres y strings
Escritura de bytes:
store byte (sb): escribe en la memoria el byte que está en los 8 bits más a la derecha de un registrador
Ej: sb $t0, 0($sp) # escribe byte en el tope de la pila
Trabajando con caracteres y strings
Caracteres son normalmente combinados en strings o cadenas de caracteres, que poseen un número variable de caracteres.
Existen 3 opciones para representar un string:
1. La primera posición del string es reservado para almacenar el ancho de la cadena;
Ej: “casa” = 4 ´c´ ´a´ ´s´ ´a´ = 4 99 97 115 97
Cuál e el tamaño máximo del string si cada posición ocupa un byte?
Trabajando con caracteres y strings
2. El string es almacenada en una estructura, en la que una varible dice el ancho de la cadena y otra trae los caracteres que componen la cadena;
Ej: “casa” = {4, ´c´ ´a´ ´s´ ´a´} = {4, 99 97 115 97}
3. La última posición de una cadena es indicada por un caracter especial.
Ej: “casa” = ´c´ ´a´ ´s´ ´a´ 0 = 99 97 115 97 0
Esta es la representación utilizada por el lenguaje C, siendo el marcador de final de string el valor 0.
Ejemplo: string_copy Suponga que tenemos el siguiente código:
void strcpy(char x[], char y[]) {
int i;i = 0;while ((x[i] = y[i]) != 0)
i = i + 1;}
Vamos generar el código correspondiente een assembly MIPS…..
Ejemplo: string copy Las direcciones base de x y de y están en $a0 y
$a1, entretanto que i está en $s0. Requerimos entonces el valor de $s0:
strcpy:addi $sp, $sp, -4 #espacio para 1 ítem en la pilasw $s0, 0($sp) # salva $s0
Inicializamos i con 0:
add $s0, $zero, $zero # i = 0 + 0
Ejemplo: string copy Podemos hacer un lazo entonces; La dirección de
y[i] es formada adicionando i con la dirección base de y:
L1:add $t1, $a1, $s0 # $t1 = dirección de y[i]
Por que no multiplicamos i por 4?? y es un vector de bytes, y no de palabras!!!
Ahora cargamos el caracter y[i]:
lb $t2, 0($t1) # $t2 = y[i]
Ejemplo: string copy Preparamos ahora la dirección de x[i] en $t3, y
almacenamos y[i]:
add $t3, $a0, $s0 # $t3 = direccíon de x[i]sb $t2, 0($t3) # x[i] = y[i]
Si el caracter y[i] fuera 0, salimos del loop:
beq $t2, $zero, L2 # se y[i] == 0, desvie para L2
Si no, incremente i e iteramos nuevamente:
addi $s0, $s0, 1 # $s0 = $s0 + 1j L1 # desvie para L1
Ejemplo: string copy Restauramos $s0, ajustamos $sp y retornamos:
L2: # fin del stringlw $s0, 0($sp) # restaura $s0addi $sp, $sp, 4# retira un item de la pilajr $ra # retorna para el llamador
Podríamos haber alocado el registrador $t0 para i, y en este caso no tendríamos que empilar y desempilar $s0.
Percibimos que es mejor utilizar primero los registradores $t que $s en funciones que no llaman otra función.
Patrón Unicode Se está tornando cada vez mas común otro tipo
de codificación para caracteres, el patrón Unicode. Java utiliza este patrón.
Este patrón, de poder representar todos los caracteres latinos, puede representar símbolos de letras orientales, entre otros.
Un caracter pasa a ocupar 16 bits. Cuantos símbolos se pueden representar?
MIPS incluye instrucciones para trabajar con 16 bits, pero no estudiaremos. Tienen que consultarlas en el manual o en libro.
Visión General Arquitectura del Conjunto de Instrucciones
(utilizando ISA del MIPS como ejemplo) Operandos y direccionamiento (Sec. 3.3, 3.8)
Organización de los registradores Transferencia de memoria y direccionamiento
Formato de instrucciones Operaciones
Lógico-aritméticas Toma de decisiones e desvíos Jumps para procedimientos
Pila (procesador Forth):una dirección: add tope tope + próximo
Acumulador (1 registrador - acc):1 dirección: add A acc acc + mem[A]1+x direción: addx A acc acc + mem[A+x]
Registradores de propósito general (GPR):2 direcciones: add A,B EA(A) EA(A) + EA(B)3 direcciones: add A,B,C EA(A) EA(B) + EA(C)
Load/Store:3 address: add Ra,Rb,Rc Ra Rb + Rc
load Ra,Rb Ra mem[Rb] store Ra,Rb mem[Rb] Ra
Arquitectura de registradores
Código para C = A + B para 4 organizaciones de registradores.:
Pilla Acumulador Registrador Registrador (reg-mem) (load-store)
Push A Load A Load R1,A Load R1,APush B Add B Add R1,B Load R2,BAdd Store C Store C, R1 Add R3,R1,R2Pop C Store C,R3
=> La Organización de registradores es un atributo del ISA!
Comparación: Bytes por instrucción? Número de instrucciones? Ciclos por instrucción?
Desde 1975 casi todas las máquinas utilizam GPRs.
La organización de registradores afecta la
programación
Other Kinds of Operands (Sec. 3.8)
Immediate: numerical constants Often appear in code, so there are special
instructions for them Add Immediate:addi $s0,$s1,10 (in MIPS)f = g + 10 (in C)where $s0,$s1 are associated with f,g
Syntax similar to add instruction, except that last argument is a number instead of a register
One particular immediate, the number zero (0), appears very often in code; so we define register zero ($0 or $zero) to always 0
This is defined in hardware, so an instruction likeaddi $0,$0,5 will not do anything
Outline Instruction set architecture
(using MIPS ISA as an example) Operands and addressing
Register organization Memory transfer and addressing
Instruction format Operations
Arithmetic and logical Decision making and branches Jumps for procedures
Called the “address” of a word
Computers needed to access 8-bit bytes as well as words (4 bytes/word)
Today, machines address memory as bytes, hence word addresses differ by 4 Memory[0], Memory[4], Memory[8], … This is also why lw and sw use bytes in offset
Addressing: Byte versus Word Every word in memory has an address,
similar to an index in an array Early computers numbered words like C
numbers elements of an array: Memory[0], Memory[1], Memory[2], …
MIPS Data Transfer Instructions
Instruction CommentSW 500(R4), R3 Store wordSH 502(R2), R3 Store halfSB 41(R3), R2 Store byteLW R1, 30(R2) Load wordLH R1, 40(R3) Load halfwordLHU R1, 40(R3) Load halfword unsignedLB R1, 40(R3) Load byteLBU R1, 40(R3) Load byte unsignedLUI R1, 40 Load Upper Immediate
(16 bits shifted left by 16)
What does it mean?
lb $t1, 0($t0) F7 Sign-extended
lbu $t2, 0($t0) F7
$t0
$t1
$t2
F7 F012 …… F7F7
FFFFFF
000000 Zero-extended
Load Byte Unsigned
Addressing ModesAddressing mode Example MeaningRegister Add R4,R3 R4 R4+R3Immediate Add R4,#3 R4 R4+3Displacement Add R4,100(R1) R4
R4+Mem[100+R1]Register indirect Add R4,(R1) R4 R4+Mem[R1]Indexed / Base Add R3,(R1+R2) R3
R3+Mem[R1+R2]Direct / Absolute Add R1,(1001) R1 R1+Mem[1001]Memory indirect Add R1,@(R3) R1 R1+Mem[Mem[R3]]Auto-increment Add R1,(R2)+ R1 R1+Mem[R2]
R2 R2+d Auto-decrement Add R1,-(R2) R2 R2-d
R1 R1+Mem[R2]Scaled Add R1,100(R2)[R3] R1 R1+
Mem[100+R2+R3*d]
Outline Instruction set architecture
(using MIPS ISA as an example) Operands and addressing
Register organization Memory transfer and addressing
Instruction format (Sec. 3.4) Operations
Arithmetic and logical Decision making and branches Jumps for procedures
6 5 5 16opcode rs rt immediate
I-Format Instructions Define the following “fields”:
opcode: uniquely specifies an I-format instruction
rs: specifies the only register operand rt: specifies register which will receive result
of computation (target register) addi, slti, slitu, immediate is sign-extended
to 32 bits, and treated as a signed integer 16 bits can be used to represent immediate
up to 216 different values Key Concept: Only one field is inconsistent
with R-format. Most importantly, opcode is still in same location
I-Format Example MIPS Instruction:
addi $21,$22,-50 opcode = 8 (look up in table) rs = 22 (register containing operand) rt = 21 (target register) immediate = -50 (by default, this is decimal)
8 22 21 -50
001000 10110 10101 1111111111001110
decimal representation:
binary representation:
I-Format Problem
What if immediate is too big to fit in immediate field?
Load Upper Immediate:lui register, immediate
puts 16-bit immediate in upper half (high order half) of the specified register, and sets lower half to 0s
addi $t0,$t0, 0xABABCDCDbecomes:
lui $at, 0xABABori $at, $at,
0xCDCD add $t0,$t0,$at
0000 … 0000
LUI R1
R1
Outline Instruction set architecture
(using MIPS ISA as an example) Operands and addressing
Register organization Memory transfer and addressing
Instruction format Operations
Arithmetic and logical Decision making and branches Jumps for procedures
Instruction Example Meaning Commentsadd add $1,$2,$3 $1 = $2 + $3 3 operands; exception possiblesubtract sub $1,$2,$3 $1 = $2 ?$3 3 operands; exception possibleadd immediate addi $1,$2,100 $1 = $2 + 100 + constant; exception possibleadd unsigned addu $1,$2,$3 $1 = $2 + $3 3 operands; no exceptionssubtract unsigned subu $1,$2,$3 $1 = $2 ?$3 3 operands; no exceptionsadd imm. unsign. addiu $1,$2,10 $1 = $2 + 10 + constant; no exceptionsmultiply mult $2,$3 Hi, Lo = $2 x $364-bit signed productmul. unsigned multu$2,$3 Hi, Lo = $2 x $364-bit unsigned productdivide div $2,$3 Lo = $2 / $3, Lo = quotient, Hi = remainder
Hi = $2 mod $3 divide unsigned divu $2,$3 Lo = $2 / $3, Unsigned quotient & remainder
Hi = $2 mod $3Move from Hi mfhi $1 $1 = Hi Used to get copy of HiMove from Lo mflo $1 $1 = Lo Used to get copy of Lo
MIPS Arithmetic Instructions
Bitwise Operations Up until now, we’ve done arithmetic (add,
sub, addi ) and memory access (lw and sw) All of these instructions view contents of
register as a single quantity (such as a signed or unsigned integer)
New Perspective: View contents of register as 32 bits rather than as a single 32-bit number
Since registers are composed of 32 bits, we may want to access individual bits rather than the whole.
Introduce two new classes of instructions: Logical Operators Shift Instructions
Logical Operators Logical instruction syntax:
1 2,3,4where
1) operation name2) register that will receive value3) first operand (register)4) second operand (register) or immediate
(numerical constant) Instruction names:
and, or: expect the third argument to be a register andi, ori: expect the third argument to be
immediate MIPS Logical Operators are all bitwise, meaning
that bit 0 of the output is produced by the respective bit 0’s of the inputs, bit 1 by the bit 1’s, etc.
Uses for Logical Operators AND operator can be used to set certain portions
of a bitstring to 0s, while leaving the rest alone => mask
Example:1011 0110 1010 0100 0011 1101 1001 10100000 0000 0000 0000 0000 1111 1111 1111
The result of anding these two is:0000 0000 0000 0000 0000 1101 1001 1010
In MIPS assembly: andi $t0,$t0,0xFFF Similarly, OR operator can be used to force
certain bits of a string to 1s For example, if $t0 contains 0x12345678, then after
ori $t0, $t0, 0xFFFF$t0 contains 0x1234FFFF (e.g. the high-order 16 bits are untouched, while the low-order 16 bits are set to 1s)
Mask:
Shift Instructions (1/3) Move (shift) all the bits in a word to the left or
right by a number of bits, filling the emptied bits with 0s.
Example: shift right by 8 bits0001 0010 0011 0100 0101 0110 0111 1000
0000 0000 0001 0010 0011 0100 0101 0110
Example: shift left by 8 bits0001 0010 0011 0100 0101 0110 0111 1000
0011 0100 0101 0110 0111 1000 0000 0000
Shift Instructions (2/3) Shift Instruction Syntax:
1 2,3,4where
1) operation name2) register that will receive value3) first operand (register)4) second operand (register)
MIPS has three shift instructions: sll (shift left logical): shifts left, fills empties
with 0s srl (shift right logical): shifts right, fills
empties with 0s sra (shift right arithmetic): shifts right, fills
empties by sign extending
Shift Instructions (3/3) Example: shift right arith by 8 bits
0001 0010 0011 0100 0101 0110 0111 1000
0000 0000 0001 0010 0011 0100 0101 0110
Example: shift right arith by 8 bits1001 0010 0011 0100 0101 0110 0111 1000
1111 1111 1001 0010 0011 0100 0101 0110
Uses for Shift Instructions (1/2) Suppose we want to get byte 1 (bit 15 to bit
8) of a word in $t0. We can use:sll $t0,$t0,16srl $t0,$t0,24
0001 0010 0011 0100 0101 0110 0111 1000
0101 0110 0111 1000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0101 0110
Uses for Shift Instructions (2/2) Shift for multiplication: in binary
Multiplying by 4 is same as shifting left by 2: 112 x 1002 = 11002
10102 x 1002 = 1010002
Multiplying by 2n is same as shifting left by n Since shifting is so much faster than
multiplication (you can imagine how complicated multiplication is), a good compiler usually notices when C code multiplies by a power of 2 and compiles it to a shift instruction:
a *= 8; (in C)would compile to:
sll $s0,$s0,3 (in MIPS)
MIPS Logical Instructions
Instruction Example Meaning Commentand and $1,$2,$3 $1 = $2 & $3 3 reg. operands; Logical ANDor or $1,$2,$3 $1 = $2 | $3 3 reg. operands; Logical ORxor xor $1,$2,$3 $1 = $2 ?$3 3 reg. operands; Logical XORnor nor $1,$2,$3 $1 = ~($2 |$3) 3 reg. operands; Logical NORand immediate andi $1,$2,10 $1 = $2 & 10 Logical AND reg, zero exten.or immediate ori $1,$2,10 $1 = $2 | 10 Logical OR reg, zero exten.xor immediate xori $1, $2,10 $1 = ~$2 &~10 Logical XOR reg, constantshift left logical sll $1,$2,10 $1 = $2 << 10 Shift left by constantshift right logical srl $1,$2,10 $1 = $2 >> 10 Shift right by constantshift right arithm. sra $1,$2,10 $1 = $2 >> 10 Shift right (sign extend) shift left logical sllv $1,$2,$3 $1 = $2 << $3 Shift left by variableshift right logical srlv $1,$2, $3 $1 = $2 >> $3 Shift right by variableshift right arithm. srav $1,$2, $3 $1 = $2 >> $3 Shift right arith. by variable
Outline Instruction set architecture
(using MIPS ISA as an example) Operands and addressing
Register organization Memory transfer and addressing
Instruction format Operations
Arithmetic and logical Decision making and branches (Sec. 3.5) Jumps for procedures
C
MIPS
Immediate in Inequalities There is also an immediate version of slt to
test against constants: slti
if (g >= 1) goto Loop Loop: . . . slti $t0,$s0,1 # $t0 = 1 if $s0<1 (g<1)beq $t0,$0,Loop # goto Loop if $t0==0
Unsigned inequality: sltu, sltiu$s0 = FFFF FFFAhex, $s1 = 0000 FFFAhex slt $t0, $s0, $s1 => $t0 = ?sltu $t1, $s0, $s1 => $t1 = ?
opcode rs rt immediate
Branches: Instruction Format Use I-format:
opcode specifies beq v. bne Rs and Rt specify registers to compare
What can immediate specify? Immediate is only 16 bits, but PC is 32-bit
=> immediate cannot specify entire address Loops are generally small: < 50 instructions
Though we want to branch to anywhere in memory, a single branch only need to change PC by a small amount
We can use PC-relative addressing 16-bit immediate as a signed two’s complement
integer to be added to the PC if branch taken Now we can branch +/- 215 bytes from the PC
Branches: Instruction Format Note: Instructions are word aligned (byte
address is always a multiple of 4, i.e., it ends with 00 in binary) So the number of bytes to add to the PC will
always be a multiple of 4 So specify the immediate in words (<= confusing?)
Now, we can branch +/- 215 words from the PC (or +/- 217 bytes), so we can handle loops 4 times as large
Final Calculation: If branch not taken: PC = PC + 4 If branch taken: PC = (PC+4) + (immediate*4) Immediate field specifies the number of words to
jump, which is simply the number of instructions to jump.
Immediate field can be positive or negative. Due to hardware, add immediate to (PC+4), not to
PC
Branch Example MIPS Code:
Loop: beq $9,$0,Endadd $8,$8,$10
addi $9,$9,-1j Loop
End: Branch is I-Format:
opcode = 4 (look up in table)rs = 9 (first operand)rt = 0 (second operand)immediate = ??? Number of instructions to add to (or subtract
from) the PC, starting at the instruction following the branch => immediate = 3
MIPS Code:Loop: beq $9,$0,End
addi $8,$8,$10addi $9,$9,-1
j LoopEnd:
decimal representation:
binary representation:
4 9 0 3
000100 01001 00000 0000000000000011
Branch Example
Compare and Branch: BEQ rs, rt, offset if R[rs] == R[rt] then PC-relative BNE rs, rt, offset !=
Compare to zero and Branch: BLEZ rs, offset if R[rs] <= 0 then PC-
relative BGTZ rs, offset > BLTT rs, offset < BGEZ rs, offset >= BLTZAL rs, offset if R[rs] < 0 then branch and link BGEZAL rs, offset >=
Almost all comparisons are against zero!
Branch Instructions: Summary
J-Format Instructions (1/3) For branches, we assumed that we won’t
want to branch too far, so we can specify change in PC.
For general jumps (j and jal), we may jump to anywhere in memory.
Ideally, we could specify a 32-bit memory address to jump to.
Unfortunately, we can’t fit both a 6-bit opcode and a 32-bit address into a single 32-bit word, so we compromise.
J-Format Instructions (2/3) Define “fields” of the following number of bits
each:
As usual, each field has a name:
Key concepts: Keep opcode field identical to R-format and I-format
for consistency Combine other fields to make room for target address
Optimization: Note that, just like with branches, jumps will only
jump to word aligned addresses, so last two bits are always 00 (in binary) => specify 28 bits of the 32-bit bit address
6 bits 26 bits
opcode target address
J-Format Instructions (3/3) Where do we get the other 4 bits?
By definition, take the 4 highest order bits from the PC
Technically, this means that we cannot jump to anywhere in memory, but it’s adequate 99.9999…% of the time, since programs aren’t that long
If we absolutely need to specify a 32-bit address, we can always put it in a register and use the jr instruction
Summary: New PC = PC[31..28] || target address (26 bits)
|| 00 Note: II means concatenation
4 bits || 26 bits || 2 bits = 32-bit address Understand where each part came from!
MIPS Jump, Branch, CompareInstruction Example Meaningbranch on equal beq $1,$2,25 if ($1 == $2) go to PC+4+100
Equal test; PC relative branchbranch on not eq. bne $1,$2,25 if ($1!= $2) go to PC+4+100
Not equal test; PC relative set on less than slt $1,$2,$3 if ($2 < $3) $1=1; else $1=0
Compare less than; 2’s comp. set less than imm. slti $1,$2,100 if ($2 < 100) $1=1; else $1=0
Compare < constant; 2’s comp.set less than uns. sltu $1,$2,$3 if ($2 < $3) $1=1; else $1=0
Compare less than; natural numbersset l. t. imm. uns. sltiu $1,$2,100 if ($2 < 100) $1=1; else $1=0
Compare < constant; 2’s comp.jump j 10000 go to 10000 26-bit+4-bit of PCjump register jr $31 go to $31
For switch, procedure returnjump and link jal 10000 $31 = PC + 4; go to 10000
For procedure call
Outline Instruction set architecture
(using MIPS ISA as an example) Operands and addressing
Register organization Memory transfer and addressing
Instruction format Operations
Arithmetic and logical Decision making and branches Jumps for procedures (Sec. 3.6)
Miscellaneous MIPS Instructions
break A breakpoint trap occurs,transfers control toexception handler
syscall A system trap occurs,transfers control to
exception handler coprocessor inst. Support for floating point TLB instructions Support for virtual
memory restore from exception Restores previous
interruptmask & kernel/user modebits into status register
load word left/right Supports misaligned wordloads
store word left/right Supports misaligned wordstores
Summary: Typical OperationsData Movement load (from memory)
store (to memory)memory-to-memory moveregister-to-register moveinput (from I/O device)output (to I/O device)push, pop (to/from stack)
Arithmetic integer (binary + decimal) or FPadd, subtract, multiply, divide
Shift shift left/right, rotate left/rightLogical not, and, or, set, clearControl (jump/branch) unconditional, conditionalSubroutine linkage call, returnInterrupt trap, returnSynchronization test & set (atomic r-m-w)String search, translate
Top 10 80x86 Instructions
Rank Instruction % of Total1 load 22%
2 conditional branch 20% 3 compare 16% 4 store 12% 5 add 8% 6 and 6% 7 sub 5% 8 move register-register 4% 9 call 1% 10 return 1%
Total 96% Help to identify common cases! Simple instructions dominate instruction
frequency
Summary: MIPS ISA (1/2) 32-bit fixed format instructions (3 formats) 32 32-bit GPR (R0 = zero), 32 FP registers, (and
HI LO) partitioned by software convention
3-address, reg-reg arithmetic instructions Memory is byte-addressable with a single
addressing mode: base+displacement 16-bit immediate plus LUI
Decision making with conditional branches: beq, bne Often compare against zero or two registers for = To help decisions with inequalities, use: “Set on
Less Than”called slt, slti, sltu, sltui Jump and link puts return address PC+4 into link
register (R31) Branches and Jumps were optimized to address
to words, for greater branch distance
Summary: MIPS ISA (2/2) Immediates are extended as follows:
logical immediate: zero-extended to 32 bits arithmetic immediate: sign-extended to 32 bits Data loaded by lb and lh are similarly
extended:lbu, lhu are zero extended; lb, lh are sign extended
Simplifying MIPS: Define instructions to be same size as data (one word), so they can use same memory
Stored Program Concept: Both data and actual code (instructions) are stored in the same memory
Instructions formats are kept as similar as possible
opcode rs rt rd functshamtopcode rs rt immediate
R
Iopcode target addressJ