21
Compiladores Generación de código

Generacion_de_Codigo Objeto.pdf

Embed Size (px)

Citation preview

Compiladores

Generación de código

Introducción

• La fase final de nuestro modelo de

compilador es el generador de código

• Toma por entrada una RI del fuente y

produce su equivalente en código objeto

• Las técnicas que veremos son

independientes de si se realiza o no

optimización

Introducción

• Los requerimientos que, tradicionalmente,

se imponen en un generador de código

son muy demandantes

• El código generado debe ser correcto y de

alta calidad (debe hacer uso efectivo de

los recursos de la máquina objeto)

• El generador, además, debe ser eficiente

Introducción

• El problema de generar código óptimo es

no decidible

– Son problemas NP completos

– No hay algoritmos polinomiales que resuelvan

el problema

• En la práctica, se utilizan heurísticas que

generan código bueno (aunque

probablemente no óptimo)

Aspectos generales

• Entrada al generador de código (RI)

• Programas objeto

• Administración de memoria

• Selección de instrucciones

• Adjudicación de registros

• Elección de orden de evaluación

Entrada al generador de código

• La entrada consiste en la RI del código fuente,

producida por el frontend, junto con la

información de la tabla de símbolos

• La TS se utiliza para determinar las direcciones

en tiempo de ejecución de los objetos de datos

denotados por los nombres en la RI

• Asumimos que la RI es de “suficiente bajo nivel”

con tipos de datos que se pueden mapear

razonablemente a la arquitectura objeto

Entrada al generador de código

• Asumimos que el chequeo de tipos ya fue

realizado, con la adecuada inserción de

operadores de cambio de tipos, y demás

• Asumimos que no hay errores semánticos

obvios (Ej., intentar indizar un arreglo con

un punto flotante)

Entrada al generador de código

• Esta fase se basa en la asunción de que

la RI no tiene errores (aunque en algunos

compiladores, el chequeo se realiza a la

vez que la generación de código)

Código objeto

• La salida del generador es el código o

programa objeto, el cual puede tomar

distintas formas:

– código máquina absoluto

– código máquina reubicable

– código assembler

Código objeto

• Producir código máquina absoluto como

salida conlleva la ventaja de que puede

ser ubicado en un lugar fijo de memoria y

ejecutado inmediatamente

• Código reubicable (también llamado

módulo objeto) permite tener módulos que

se compilan por separado. Luego se unen

(linking) y se cargan

Código objeto

• En este caso se gana mucha flexibilidad

en función del trabajo que conlleva la

unión y carga.

• Si la máquina objeto no se hace cargo de

la reubicación, el compilador debe

producir información explícita para el

linker

Código objeto

• Finalmente, producir código assembler como salida facilita el proceso de compilación. Podemos generar instrucciones simbólicas y usar las macros de assembler como ayudas.

• Finalmente, facilita la optimización “a mano” del código generado.

• El precio es que aparece una etapa final de ensamblado

Administración de memoria

• Consiste en determinar la posición de

memoria en la que los diferentes símbolos

del programa almacenan la información

• Depende de la estrategia utilizada para la

gestión de memoria, el mecanismo puede

variar

Selección de instrucciones

• La naturaleza del conjunto de instrucciones de

la máquina objeto, determina la dificultad de la

selección de instrucciones

• La uniformidad y completitud del conjunto de

instrucciones son factores muy importantes.

• Si la máquina objeto no soporta cada tipo de

datos de forma uniforme, entonces se necesitan

estrategias alternativas

Selección de instrucciones

• Si no nos interesa la eficiencia, para cada tipo

de sentencia en C3D podemos diseñar un

esqueleto de código que muestra el código

objeto a generar para esa construcción

• Ej.: x:=y+z

– MOV Y, R0

– ADD Z, R0

– MOV R0, X

• Esto genera código pobre

Selección de instrucciones

• Por ejemplo: – a := b + c

– d := a + e

• Se traduce a – MOV b, R0

– ADD c, R0

– MOV R0, a redundante?

– MOV a, RO redundante?

– ADD e, R0

– MOV R0, d

Selección de instrucciones

• La calidad del código generado se mide en

función de

– Tamaño

– Velocidad

• En función de la variedad de instrucciones uno

puede seleccionar la instrucción más

performante (ej. INC vs MOV - ADD - MOV).

• Para los casos no triviales, es un problema

muy complejo.

Asignación de registros

• Operar sobre registros es más rápido y

eficiente que operar sobre memoria

• Por ello, la adjudicación eficiente de

registros tiene un gran impacto en la

performance

Asignación de registros

• El uso de registros puede dividirse en

dos subproblemas:

– Durante la reserva de registros

(allocation), se seleccionan el conjunto de

variables que vivirá en registros en un punto

del programa.

– Durante la (posterior) asignación de

registros (assignation), se elige el registro

específico para cada variable.

Orden de evaluación

• El orden en que algunas computaciones

se llevan a cabo puede afectar la

eficiencia del código objeto

• Algunos ordenes requieren menos

registros para almacenar valores

intermedios

• Seleccionar el orden óptimo es también

NP-completo

Orden de evaluación

• El orden en que algunas computaciones

se llevan a cabo puede afectar la

eficiencia del código objeto

• Algunos ordenes requieren menos

registros para almacenar valores

intermedios

• Seleccionar el orden óptimo es también

NP-completo