Generación de Código Intermedio Programación II Margarita Álvarez Generación de código intermedio Con la generación de código intermedio se inicia la tarea de síntesis. Aunque un programa fuente se puede traducir directamente a lenguaje objeto, algunas ventajas de utilizar una forma intermedia son: Aumentar la portabilidad del compilador de una máquina a otra: ¾ Se puede utilizar el mismo analizador para diferentes generadores. ¾ Se pueden utilizar optimizadores independientes de la máquina. ¾ Facilitar la división en fases del proyecto. Analizador Sintáctico Comprobación estática Gen. Código intermedio Código intermedio Generador de código Distinto formato de las instrucciones: z Notación polaca inversa o notación postfija z Árboles sintácticos z Código de tres direcciones 1 Notación postfijo La notación postfija de una expresión E se puede definir como: 1. Si E es una variable o una constante, entonces la notación postfija de E es también E. 2. Si E es una expresión de la forma E1opE2 donde op es cualquier operador binario, entonces la notación postfija de E es E1´E2´op donde E1´ y E2´ son las notaciones postfijas de E1 y E2 respectivamente. 3. Si E es una expresión de la forma (E1) entonces la notación postfija de E1 es también la notación postfija de E. Ejemplos 95-2+ (9-5)+2 9-(5+2) 952+abc-*bc-*+:= a:= b*-c+b*-c Notación postfijo - Ejemplo Definición dirigida por la sintaxis Producciones expr ⇒ expr + término expr ⇒ expr – término expr ⇒ término término ⇒ 0 … término ⇒ 9 Reglas semánticas expr.t ⇒ expr.t || término.t || + expr.t ⇒ expr.t || término.t || expr.t ⇒ término.t término.t ⇒ ´0´ … término.t ⇒ ´9´ expr.t 9-5+2 = 95-2+ expr.t = 95- + expr.t = 9 término.t = 9 término.t = 5 término.t =2 2 5 Operador de concatenación A cada no terminal está asociado un atributo t con un valor de la cadena que representa la notación postfija de la expresión generada por ese no terminal en un árbol de análisis sintáctico. Resultado: 95-2+ 9 2 Notación postfijo - Ejemplo Esquema de Traducción Acciones semánticas {print(´+´)} {print(´-´)} Producciones expr ⇒ expr + término expr ⇒ expr – término expr ⇒ término término ⇒ 0 … término ⇒ 9 {print(´0´)} … {print(´9´)} expr.t print(´+´) 9-5+2 expr.t expr.t - término.t término.t 9 término.t print(´-´) + 5 2 Resultado: print(´2´) 95-2+ print(5) print(9) Árbol sintáctico El uso de árboles sintácticos como representación intermedia permite que la traducción se separe del análisis sintáctico. Es una forma condensada de un árbol de análisis sintáctico. Útil para representar construcciones de lenguajes. Ejemplos S ⇒ if B then S1 else S2 Árbol de análisis sintáctico Árbol sintáctico 3 Grafo dirigidos acíclicos para expresiones (GDA) z Identifican las subexpresiones comunes de una expresión. Un nodo interior representa un operador y sus hijos los operandos. z Un nodo tiene más de un padre. z Ejemplo: a + a * (b - c) + (b - c) * d Código de tres direcciones Es una secuencia de proposición de la forma general: x:= y op z donde x,y y z son nombres, constantes o variables temporales, op representa cualquier operador. Ejemplos x+y*z t1 := y*z t2 := x + t1 = a := b*-c+b*-c t1 := -c t2 := b * t1 t3 := -c t4 := b * t3 t5 := t2 + t4 a := t5 a + * * b c b c 4 Código de tres direcciones Implementación de proposiciones de tres direcciones Una proposición de tres direcciones es una forma abstracta de código intermedio. En un compilador estas proposiciones se pueden implementar como registros con campos para el operador y los operandos. Las implementaciones pueden ser: ¾ Triplas ¾Cuádruplas ¾ Triplas Indirectas 5 Cuádruplas Se implanta en una secuencia de registros con 4 campos: o Operación o Argumento1 o Argumento2 o Resultado Ejemplo a:=b* -c+b* -c Los nombres t1, t2 y t3 pueden ser registros de la máquina o posiciones de memoria temporales, que desaparecen cuando se termina la compilación. Cuádruplas • • En general, las instrucciones que no requieren todos los campos dejan vacíos los que no utilizan, por ejemplo: las proposiciones con operadores unarios no utilizan arg2. Los saltos condicionales e incondicionales ponen la etiqueta (label) en Resultado. Los contenidos de los campos Argumento1, Argumento2 y Resultado son generalmente punteros a la Tabla de Símbolos. 6 Triplas z z Para evitar introducir nombres temporales en la Tabla de Símbolos, se hace referencia a un valor temporal mediante la posición de la proposición que lo calcula. En este caso los registros son de 3 campos. Triplas Indirectas z Se hace una lista de los punteros a triplas. 7 Comparación de las representaciones La diferencia en la implementación es cuestión de mayor o menor indirección: Las triplas necesitan menos espacio y el compilador no genera identificadores temporales, pero cambiar de sitio una proposición que defina un valor temporal exige modificar todas las referencias a él. Esto representa un inconveniente a la hora de optimizar el código, ya que en cada paso del proceso de optimización hay que cambiar constantemente las proposiciones de lugar. Este problema no lo presentan las triplas indirectas. Una proposición puede trasladarse reordenando únicamente la lista de proposiciones. En la práctica se utiliza la notación de cuádruplas como implementación del código de 3 direcciones. 8