1.3.7. Generador de Código Objeto

La fase final de un compilador es la generación de código objeto, que por lo general consiste en código de máquina relocalizable o código ensamblador. Las posiciones de memoria se seleccionan para cada una de las variables usadas por el programa. Después, cada una de las instrucciones intermedias se traduce a una secuencia de instrucciones de máquina que ejecuta la misma tarea. Un aspecto decisivo es la asignación de variables a registros.

 

El generador de código objeto puede considerarse como la penúltima fase de un compilador, la cual se encarga de tomar como entrada el código intermedio generado por el front-end, y producir código objeto de la arquitectura target para luego entrar en la fase de optimización de código.

 

Toma como  entrada de  representación intermedia  el programa  fuente y produce  como salida  un programa objeto equivalente.

 

Aspectos generales

Administración de memoria: la correspondencia entre los nombres del programa fuente con direcciones de objetos de datos en la memoria durante la ejecución la realiza la etapa inicial en cooperación con el generador de código. Las entradas en la Tabla de Símbolos se van creando conforme se examina las declaraciones de un procedimiento. El tipo en una declaración determina la cantidad de memoria necesaria para el nombre declarado.

Según la información de la Tabla de Símbolos se pueden determinar una dirección relativa para el nombre de un área de datos para el procedimiento.

Selección de instrucciones: es importante que el conjunto de instrucciones sea uniforme y completo. Las velocidades de las instrucciones es un factor importante. Si no se tiene en cuenta la eficiencia del programa objeto, la selección de instrucciones es sencilla. Para cada tipo de proposición de tres direcciones, se puede diseñar un esqueleto de código.

Ejemplo: código de tres direcciones de:

x := y + z

 

MOV y, R0 /* cargar y en el registro R0 */

ADD z, R0 /* suma z a R0 */

MOV R0, x /* almacenar R0 en x */

 

Asignación de registros: Las instrucciones que implican operandos en registros son generalmente más rápidas que las de los operandos en memoria. Por lo tanto, utilizar eficientemente los registros es fundamental para generar un buen código. El uso de registros se divide en dos subproblemas:

  • Durante la asignación de los registros: se selecciona el conjunto de variables que residirá en los registros en un momento del programa.
  • Durante la fase posterior de asignación a los registros, se escoge el registro específico en el que residirá una variable.

Elección del orden de evaluación: el orden en que se realizan los cálculos puede variar la eficiencia del código objeto. Algunos ordenamientos de los cálculos necesitan menos registros que otros para guardar resultados intermedios.

Elegir un orden mejor es un problema difícil, NP-completo.