El documento describe los pasos en el proceso de generación de código máquina a partir de un programa fuente. Explica que el front-end genera un código intermedio que luego es traducido a código de ensamblador teniendo en cuenta la arquitectura de la máquina. Se dividen los bloques básicos y se genera código para cada uno asignando variables a registros y memoria de forma eficiente.
1. Generación de código. Procesadores de Lenguaje II
Generación de Código Máquina
front
end
generador
de código
Código
intermedio
Programa
fuente
Código
intermedio
Código
máquinaoptimización
2. Generación de código. Procesadores de Lenguaje II
Generación de Código Máquina
Traducción de la representación intermedia a código objeto
(normalmente ensamblador)
Hay que tener en cuenta la arquitectura de la máquina para
realizar una gestión eficiente de la memoria y de los registros
Primero se generan las directivas para reservar memoria para
las variables y datos
Luego se genera el resto del código. Por ejemplo, los árboles
de sintaxis abstracta se recorren para generar código
directamente
Hay que tener en cuenta:
Los lenguajes fuente y objeto
Gestión de memoria y registros
Juego de instrucciones
Orden de evaluación
3. Generación de código. Procesadores de Lenguaje II
Generación de Código
Se debe generar código correcto y de “alta calidad”
Asignación de recursos eficiente (optimizaciones dependientes de la
máquina)
Uso de registros (restricciones HW…)
Uso de memoria
Selección y orden de instrucciones (INC, ADD, …)
Asegurar que el código generado es el óptimo para cualquier entrada es
un problema “indecidible” (¿mejorar al programador de ensamblador?)
Enfoques:
Traducción directa de bloques de código intermedio
Uso de información de uso de variables en el bloque
Generación global de código
Análisis de flujo para asignación “óptima” de registros (cada bloque tiene
contexto)
4. Generación de código. Procesadores de Lenguaje II
Sistema Procesador de Lenguaje
skeletal source program
source program
target assembly program
relocatable machine code
absolute machine code
preprocessor
compiler
assembler
loader/link editor
5. Generación de código. Procesadores de Lenguaje II
Ensambladores
Código ensamblador: nemónicos para instrucciones,
y nombres para direcciones de memoria.
Ensamblador en dos pasos:
Primera pasada: los identificadores se asignan a
direcciones de momria (0-offset)
e.g. asignar 0 a a, 4 a b …
Segunda pasada: produce código máquina (reasignable) :
MOV a, R1
ADD #2, R1
MOV R1, b
0001 01 00 00000000 *
0011 01 10 00000010
0010 01 00 00000100 *
relocation
bit
6. Generación de código. Procesadores de Lenguaje II
Cargador y Editor de Enlace
Cargador(loader): toma código máquina
reasignable (relocatable) y mueve
direcciones para cargarlas en memoria.
Enlazador (linker): toma varios programas
reasignables, con referencias cruzadas, para
generar un solo fichero.
Necesita una correspondencia entre nombres de
variables y direcciones en cada instrucción.
7. Generación de código. Procesadores de Lenguaje II
Ejemplos Generación de Código (2)
Expresión Código
X::=Y Comienzo: MOV Y,X
X::=Y op Z Comienzo: MOV Y, R0
OP Z, R0
MOV R0,X
IF C THEN E1 ELSE E2 Comienzo: Código para C
IF-FALSE-GOTO et1
Código para E1
GOTO et2
et1: Código para E2
et2:
WHILE C DO S Comienzo: Código para C
IF-FALSE-GOTO et1
Código para S
GOTO Comienzo
et1:
8. Generación de código. Procesadores de Lenguaje II
Máquina Objeto
Máquina de registros R0 … RN-1
Instrucciones de dos direcciones: ins fuente, destino
MOV f,d (mover fuente a destino)
ADD f,d (sumar fuente a destino, resultado en destino)
SUB f,d (restar fuente a destino, resultado en destino)
MULT f,d (multiplicar, idem)
DIV f,d (dividir, idem)
GOTO d (salto hacia posición)
CJ< d (salto condicional según última operación a registro)
Modos de direccionamiento:
Modo Forma Dirección Coste extra
Absoluto M M 1
Constante #2 - 1
Registo Ri Ri 0
Registro ind *Ri contenido(Ri) 0
Indexado C(Ri) C+contendido(Ri) 1
Indexado reg *C(Ri) contenido(C+contendido(Ri)) 1
Con R’s
9. Generación de código. Procesadores de Lenguaje II
Coste de las instrucciones
Uno + uno por cada dirección de memoria o
literal en la instruicción
Se corresponde con la longitud total de la
instrucción
Cuesta más cargar una instrucción que ejecutarla
Ejemplos
MOV R0,R1 (coste 1)
MOV R2,M (coste 2)
11. Generación de código. Procesadores de Lenguaje II
Ejemplos Generación de Código y
coste
/* código intermedio */
temp2 := temp1 + 2
/*código ensamblador */
ADD R0, R1
MOV R1, temp2
(R1 tiene temp1,
R0 tiene #2)
(¡se pierde temp1!)
(coste: 3)
/*código máquina reasignable*/
1000 00 01
0010 01 00
0000 0100 *
/*código ensamblador */
ADD R0,*R1
MOV *R1, *R2
(R0 tiene #2,
*R1 tiene temp1,
*R2 tiene temp2)
(coste: 2)
/*código máquina reasignable*/
1101 00 01
0000 01 02
Variables ya en
registros
(por contexto)
12. Generación de código. Procesadores de Lenguaje II
Generación Simple de Código
Se divide el código intermedio en bloques básicos
Se genera código para cada bloque independiente del
resto
Más elaborado que un algoritmo “instrucción a
instrucción”:
d=a-b+c t1=a-b
t2=t1+c
d=t2
MOV a, R0
SUB b, R0
MOV R0, t1
MOV t1, R0
ADD c, R0
MOV R0, t2
MOV t2, d
Se pueden
quitar
Mejor: MOV R0,d
13. Generación de código. Procesadores de Lenguaje II
Grafos de Flujo y Bloques Básicos
Grafo de Flujo
Grafo dirigido que
representa el flujo de
control
Bloque Básico
Nodo de un grafo de flujo
Secuencia de sentencias
consecutivas. El flujo de
control lo recorre de
principio a fin
x := 1
i := 1
L:
x := x * x
i := i + 1
if i < 10 goto L
Método de división
Buscar sentencias “líder”
Primera sentencia
Sentencia origen de cualquier salto
Cualquier sentencia que sigue a un salto
Bloque básico: instruicciones entre sentencias “líder”
14. Generación de código. Procesadores de Lenguaje II
Tratamiento Bloque Básico
Sea el bloque básico
1. L:
2. t := 2 * x
3. w := t + x
4. if w > 0 goto L’
(3) nunca puede ser ejecutada sin haberse
ejecutado (2) antes
(3) podría cambiarse a w := 3 * x
Podría elminarse (2)?
15. Generación de código. Procesadores de Lenguaje II
Información de Uso Posterior
“x está viva a la salida de un bloque”
si x puede ser usada fuera del bloque
se supone que todas variables no
temporales están vivas a la salida (criterio
conservador)
“la sentencia j usa el valor
de x calculado en la
sentencia i”
si j toma a x como un
operando y el control puede
ir de i a j sin una asignación
a x
i: x := y op z
…
j: a:= b op x
…
x := y op z
…
…
a := b op x
…
16. Generación de código. Procesadores de Lenguaje II
Información de Uso Posterior
Método para determinar variables vivas
Recorrer las sentencias del bloque básico hacia atrás
Para cada instrucción i: x:=y op z marcar variables:
1. Asignar a la instrucción i la situación de “x”,”y”,”z” en la TS
2. Marcar en la TS “x” como “no viva” y “sin uso posterior”
3. Marcar en la TS “y” y “z” como “viva”, y el uso posterior fijarlo a i
Todas las variables se marcan como “viva” en la última
instrucción
Ej (d=a-b+c)
1. t1=a-b
2. t2=t1+c
3. d=t2
V, NP V, NP V,UP=2 V, NP V,UP=2 NV, NP
V, NP V, NP V, NP V, NP NV, NP V,UP=3
V, NP V, NP V, NP V, NP NV, NP NV, NP
a b c d t1 t2
1
2
3
17. Generación de código. Procesadores de Lenguaje II
Generación Simple de Código
Se efectúa asignación “local” de registros
Considera el uso posterior de cada variable sólo en el bloque
Al final del bloque se almacenan todas las variables en memoria
Se usan descriptores
Descriptores de registros: qué variables almacenan
Descriptores de variables: en qué sitios se encuentran
1. Determinar L, y’, z’
2. Generar MOV y’,L
3. Generar OP z’,L
4. Indicar que x está en L
5. Si los valores de y o z no tienen
uso posterior, liberar sus registros
L: Localización para guardar
resultado: obtenreg(x,y,z)
y’: una de las posiciones de y
z’: una de las posiciones de z
Ideal: y’, z’ en registros
Almacenar las variables vivas en memoria (según descriptores)
Para cada sentencia x:= y op z
18. Generación de código. Procesadores de Lenguaje II
Estrategia de almacenamiento
Debería L ser un registro o una posición de memoria?
Estrategia simple:
Mantener resultados calculados en registros lo máximo
posible
El número de registros puede agotarse
Al final del bloque básico salvar todos los registros en
memoria
Es el algoritmo más simple
Las técnicas globales gestionan el uso inter-bloque de los
registros mediante análisis de flujo de control
19. Generación de código. Procesadores de Lenguaje II
Obtención de registros
obtenreg(x:=y op z):
1. usar el registro de y si está en un registro que
no tiene otra variable, y además y no está
viva ni tiene uso posterior. Si no:
2. usar un registro vacío si hay. Si no:
3. usar un registro ocupado si op requiere que x
esté en un registro o si x tiene uso posterior.
Actualizar el descriptor de registro. Si no:
4. usar la posición de memoria de x
20. Generación de código. Procesadores de Lenguaje II
Ejemplo Generación Código
f=(a-b)*(c+d)+e t1=a-b
t2=c+d
t3=t1*t2
t4=t3+e
f=t4
MOV a, R0
SUB b, R0
MOV c, R1
ADD d, R1
MULT R0, R1
ADD e, R1
MOV R1, f
Distinguiendo
variables
temporales en
bloque básico
Descriptores Reg
R0 con t1
R1 con t2
R1 con t3
R1 con t4
CódigoInstrucciones
t1=a-b
t2=c+d
t3=t1*t2
t4=t3+e
f=t4
MOV a, R0
SUB b, R0
MOV c, R1
ADD d, R1
MOV R1, R2
MULT R0, R2
MOV R2, R3
ADD e, R3
MOV R3, f
//SALVAR R1-R4
Las variables
temporales no
se distinguen
Descriptores Reg
R0 con t1
R1 con t2
R2 con t3
R3 con t4
CódigoInstrucciones
21. Generación de código. Procesadores de Lenguaje II
Otras Instrucciones
Indexado y punteros
Saltos condicionales
a=b[i]
a[i]=b
a=*p
*p=a
MOV Mi, R
MOV b(R),R
MOV Mi, R
MOV b,a(R)
MOV Mp, R
MOV *R, R
MOV Mp,R
MOV a,*R
CódigoInstrucciones
if x<y goto L MOV x, R
SUB y, R
CJ< L
CódigoInstrucciones