Introducción • En el modelo de análisis y síntesis de un compilador, la etapa inicial traduce un programa fuente a una representación intermedia a partir de la cual la etapa final genera el código objeto. Los detalles del lenguaje objeto se confinan en la etapa final, si esto es posible. • Al generar el código intermedio facilito la independencia de la máquina, además es más fácil la optimización del código intermedio. a la entrada para b id 1 • Los códigos intermedios pueden ser: a la entrada para c id 2 asigna – Árbol sintáctico – Notación postfija – Código de 3 direcciones a + Ejemplo: a:=b*-c+b*-c * asigna b a 3 4 5 6 7 8 menosu + * b Código para el árbol César Ignacio García Osorio. Universidad de Burgos. t1:=-c t2:=b*t1 t5:=t2+t2 a:=t5 a la entrada para a a la entrada para a + * c id a la entrada para b a la entrada para c PL. Generación de código Código de tres direcciones • Es una especie de código máquina de la forma general: x:=y op z donde x, y y z son nombres, constantes o variables temporales generadas por el compilador, op representan cualquier operador. • No se permite ninguna expresión aritmética compuesta, pues sólo hay un operador en el lado derecho de una proposición. • El uso de nombres para los valores intermedios calculados por un programa permite que el código de tres direcciones se reorganice fácilmente – a diferencia de la notación postfija –. • Es una representación linealizada de un árbol sintáctico o un GDA en la que los nombres explícitos corresponden a los nodos interiores del grafo. t1:=-c t2:=b*c t3:=-c t4:=b*t3 t5:=t2+t4 a:=t5 5 id menosu César Ignacio García Osorio. Universidad de Burgos. Ejemplos 6 menosu a b c menosu * b c menosu * + asigna (1) 3 4 id * menosu b 1 1 4 asigna c c Código de tres direcciones - * + id := 2 (2) • Tipos de proposiciones de tres direcciones 1. Asignación de operadores binarios: • x:=y op z 2. Asignación de operadores unarios: • x:=op y 3. Proposiciones de copia: • x:=y 4. Salto incondicional: • goto E 5. Saltos condicionales: • if x oprel y goto E Código para el GDA 6. Llamadas a procedimientos: • param x • call p, n • return y PL. Generación de código 3 César Ignacio García Osorio. Universidad de Burgos. PL. Generación de código 4 Código de tres direcciones (3) Código de tres direcciones • D.D.S. para producir código de tres direcciones 7. Asignaciones con índices: • x:=y[i] • x[i]:=y PRODUCCIÓN S → id := E E → E1 + E2 8. Asignaciones de direcciones y apuntadores: • x:=&y • x:=*y • *x:=y E → E1 * E2 E → - E1 E → ( E1 ) E → id • El conjunto de operadores debe ser lo bastante rico como para implantar las operaciones del lenguaje fuente. PL. Generación de código Código de tres direcciones 5 (5) • Esquema de traducción para producir código de tres direcciones id:= E {emite(id.lugar ':=' E.lugar) } E1+E2 {E.lugar:= tempnuevo; emite(E.lugar ':=' E1.lugar '+' E2.lugar) } E1*E2 {E.lugar:= tempnuevo; emite(E.lugar ':=' E1.lugar '*' E2.lugar) } -E1 {E.lugar:=tempnuevo; emite(E.lugar ':=' 'menosu' E1.lugar) } {E.lugar:=E1.lugar } (E1) {E.lugar:=id.lugar } id while {S.comienzo:=etiqnueva; S.después:=etiqnueva; emite(S.comienzo ':') } E do {emite('if' E.lugar '=' '0' 'goto' S.despues)} {emite('goto' S.comienzo); emite(S.despues ':') } S1 {I.falso:=etiqnueva;} S→ if E then {emite('if' E.lugar '=' '0' 'goto' I.falso); } S1 I I→ endif {emite(I.falso ':') } {I.después:=etiqnueva; emite('goto' I.después); emite(I.falso ':') } I→ else S endif {emite(I.después ':') } PL. Generación de código E.lugar:= E1.lugar; E.lugar:= id.lugar; E.código:= E1.código E.código:= ' ' César Ignacio García Osorio. Universidad de Burgos. PL. Generación de código Código de tres direcciones S→ E→ E→ E→ E→ E→ S→ César Ignacio García Osorio. Universidad de Burgos. REGLAS SEMÁNTICAS S.código:= E.código || gen(id.lugar ':=' E.lugar) E.lugar:= tempnuevo; E.código:= E1.código||E2.código||gen(E.lugar ':=' E1.lugar '+' E2.lugar) /* similar al operador de suma de la regla anterior */ E.lugar:= tempnuevo; E.código:= E1.código || gen(E.lugar ':=' 'menosu' E1.lugar) S → while E do S1 S.comienzo:= etiqnueva; S.después:= etiqnueva; S.código:= gen(S.comienzo ':') || E.código || gen('if' E.lugar '=' '0' 'goto' S.despues) || S1.código || gen('goto' S.comienzo) || gen(S.despues ':') – Aunque un conjunto de operadores pequeño es más fácil de implantar, un conjunto de instrucciones limitado puede obligar a la etapa inicial a generar largas secuencias de proposiciones para algunas operaciones del lenguaje fuente lo que obliga a trabajar más al optimizador y al generador de código para producir buen código. César Ignacio García Osorio. Universidad de Burgos. (4) 7 6 (5) • Implantación de proposiciones de tres direcciones – Cuádruplos: Es una estructura tipo registro con cuatro campos, que se llamarán op, arg1, arg2 y resultado. El campo op contiene un código interno para el operador. La proposición de tres direcciones x:=y op z se representan poniendo y en arg1, z en arg2 y x en resultado. – Los contenidos de los campos arg1, arg2 y resultado son generalmente apuntadores a las entradas de la tabla de símbolos. Ejemplo op arg1 arg2 resultado a:=b*-c+b*-c (0) menosu c (1) b * t1 t1 t2 (2) menosu c (3) * b t3 t4 (4) + t2 t4 t5 (5) := t5 César Ignacio García Osorio. Universidad de Burgos. t3 a PL. Generación de código 8 Código de tres direcciones (6) Código de tres direcciones – Triples: Estructura tipo registro con sólo tres campos: op, arg1 y arg2. – Para evitar introducir nombres temporales en la TS, se hace referencia a un valor temporal según la posición de la proposición que lo calcula. – Los campos arg1 y arg2 son apuntadores a la tabla de símbolos o apuntadores dentro de la estructura de triple. Ejemplos op a:=b*-c+b*-c arg1 (0) menosu c (1) b * arg2 (0) (2) menosu c (3) * b (2) (4) + (1) (3) a (4) (5) asigna César Ignacio García Osorio. Universidad de Burgos. (0) arg1 arg2 []= x i (0) (14) (14) menosu c (0) y (1) (15) (15) b (2) (16) (16) menosu c x[i]:=y arg2 (3) (17) (17) * b (16) =[] y i (4) (18) (18) + (1) (17) x (0) (5) (19) (19) := a (18) (1) asigna x:=y[i] PL. Generación de código 9 (1) César Ignacio García Osorio. Universidad de Burgos. 10 (2) • Proceso de declaraciones en procedimientos anidados { desplazamiento:=0 } { introduce(id.nombre, T.tipo, desplazamiento); desplazamiento := desplazamiento + T.ancho } { T.tipo:=integer; T.ancho:=4 } { T.tipo:=real; T.ancho:=8 } { T.tipo:=array(núm.val, T1.tipo);T.ancho:= núm.val×T1.ancho } { T.tipo:=pointer(T1.tipo); T.ancho:= 4} • introduce(nombre, tipo, desplazamiento) crea una entrada en la TS para nombre, le da el tipo tipo y la dirección relativa desplazamiento en su área de datos. • El atributo tipo representa una expresión de tipos construida a partir de los tipos básicos integer y real aplicando los constructores de tipos pointer y array. Si las expresiones de tipos se representan por medio de grafos, entonces el atributo tipo puede ser un apuntador al nodo que representa una expresión de tipo. PL. Generación de código PL. Generación de código Declaraciones P→MD César Ignacio García Osorio. Universidad de Burgos. (14) arg1 • Cálculo de los tipos y direcciones relativas de nombres declarados T → integer T → real T → array [ núm ] of T1 T → ^T1 * op Declaraciones P→MD M→ ε D → D1 ; D2 D → id : T – Triples indirectos: Se utiliza una lista de los apuntadores a triples, en lugar de hacer una lista de los triples mismos. – Esta implantación tiene la ventaja de que en un compilador optimizador no sería necesario cambiar los triples (y por tanto el índice mediante el que son referenciados) sino únicamente el índice en la matriz proposición. Ejemplo op arg1 arg2 proposición op (1) asigna (0) (7) 11 { añadeancho(tope(tblpn), tope(desplazamiento)); saca(tblapn); saca(desplazamiento) } { t:=creatabla(nil); mete(t, tblapn); mete(0, desplazamiento) } M→ ε D → D1 ; D2 D → proc id ; N D1 ; S { t:=tope(tblapn); añadeancho(t, tope(desplazamiento)); saca(tblapn); saca(desplazamiento); introduceproc(tope(tblapn), id.nombre, t); } { introduce(tope(tblapn), id.nombre, T.tipo, tope(desplazamiento)); D → id : T tope(desplazamiento):= tope(desplazamiento) + T.ancho } { t:=creatabla(tope(tblapn)); N→ ε mete(t, tblapn); mete(0, desplazamiento) } { T.tipo:=record(tope(tblapn)); T.ancho:=tope(desplazamiento)); T → record L D end saca(tblapn); saca(desplazamiento) } { t:=creatabla(tope(tblapn)); L→ ε mete(t, tblapn); mete(0, desplazamiento) } César Ignacio García Osorio. Universidad de Burgos. PL. Generación de código 12 Proposiciones de asignación (1) Almacenamiento en memoria de una matriz A de dimensiones (n1=2)×(n2=3)×(n3=4) if p≠nil then emite(p ':=' E.lugar) E → E1 * E2 E → - E1 E → ( E1 ) E → id base else error } { E.lugar:=tempnuevo; emite(E.lugar ':=' E1.lugar '+' E2.lugar) } { E.lugar:=tempnuevo; emite(E.lugar ':=' E1.lugar '*' E2.lugar) } { E.lugar:=tempnuevo; emite(E.lugar ':=' 'menosu' E1.lugar) } A[inf1, inf2, inf3+3] E→(E1) E→L A[inf1+1,*,*] PL. Generación de código 13 (3) base+(i3−inf3+n3·(i2−inf2+n2·(i1−inf1)))·a { E.lugar:= E1.lugar } { if L.desplazamiento=null then E.lugar:=L.lugar else begin E.lugar:=tempnuevo; emite(E.lugar ':=' L.lugar '[ ' L.desplazamiento ']'); end} { L.lugar:=tempnuevo; L.desplazamiento:=tempnuevo; emite(L.lugar ':=' c(listaE.matriz)); emite(L.desplazamiento ':=' listaE.lugar '*' ancho(listaE.matriz)); } { L.lugar:=id.lugar; L.desplazamiento:=null } L→id listaE→listaE1,E { t:=tempnuevo; m:= listaE1.ndim+1 ; emite(t ':=' límite(listaE1.matriz,m) '*' listaE1.lugar); emite(t ':=' E.lugar '+' t); listaE.matriz:=listaE1.matriz; listaE.lugar:=t; listaE.ndim:=m } { listaE.matriz:=id.lugar; listaE.lugar:=E.lugar; listaE.ndim:=1 } listaE→ id[E L→listaE] PL. Generación de código [base−(inf3+n3·(inf2+n2·inf1)))·a]+(i3+n3·(i2+n2·i1)))·a Esta fórmula se puede generalizar a una matriz de k dimensiones n1×n2×... ×nk con nm=supm−infm+1 para la que los elementos que dependen de los índices se pueden calcular utilizando la recurrencia: César Ignacio García Osorio. Universidad de Burgos. e1=i1 em=im+nm·em-1 PL. Generación de código Proposiciones de asignación 14 (4) • Conversión de tipo dentro de asignaciones • En la práctica, hay muchos tipos diferentes de variables y constantes, así que el compilador debe rechazar algunas operaciones de tipos mixtos o bien generar las instrucciones de coerción (conversión de tipos) apropiadas. • Por ejemplo, para la producción E→E1+E2 se podría tener la siguiente acción semántica. E.lugar:=tempnuevo if E1.tipo=integer and E1.tipo=integer then begin emite(E.lugar ':=' E1.lugar '+ent' E2.lugar); E.tipo=integer end else if E1.tipo=real and E2.tipo=real then begin emite(E.lugar ':=' E1.lugar '+real' E2.lugar); E.tipo=real end else if E1.tipo=integer and E2.tipo=real then begin u:=tempnuevo; emite(u ':=' 'entareal' E1.lugar); E.tipo=real emite(E.lugar ':=' u '+real' E2.lugar); end else if E1.tipo=real and E2.tipo=integer then begin u:=tempnuevo; emite(u ':=' 'entareal' E2.lugar); E.tipo=real emite(E.lugar ':=' E1.lugar '+real' u); end else E.tipo=error_tipo; { if L.desplazamiento=null then emite(L.lugar ':=' E.lugar); else emite(L.lugar '[ ' L.desplazamiento ']' ':=' E.lugar); } { E.lugar:=tempnuevo; emite(E.lugar ':=' E1.lugar '+' E2.lugar) } César Ignacio García Osorio. Universidad de Burgos. base+ n3·a base+n3·n2·a • Esquema de traducción para acceder a elementos de matrices E→E1+E2 base+(n3·n2·(i1−inf1)+n3·(i2−inf2) +(i3−inf3))·a Factorizando queda: A[inf1, inf2+2, *] Proposiciones de asignación S→L:=E La posición del elemento A[i1,i2,i3] en esta matriz viene dada por la fórmula: Separando los elementos que dependen exclusivamente de la estructura de la matriz: { E.lugar:= E1.lugar } { p:=busca(id.nombre); if p≠nil then E.lugar:= p else error } César Ignacio García Osorio. Universidad de Burgos. (2) • Acceso a elementos de matrices • Esquema de traducción para producir cód. de 3 direcciones para las asignaciones { p:=busca(id.nombre); S → id := E E → E1 + E2 Proposiciones de asignación 15 César Ignacio García Osorio. Universidad de Burgos. PL. Generación de código 16 Expresiones booleanas • Esq. traduc. utilizando una representación numérica para los valores booleanos E → E1 or E2 { E.lugar:=tempnuevo; emite(E.lugar ':=' E1.lugar 'or' E2.lugar) } E → E1 and E2 { E.lugar:=tempnuevo; emite(E.lugar ':=' E1.lugar 'and' E2.lugar) } E → not E1 { E.lugar:=tempnuevo; emite(E.lugar ':=' 'not' E1.lugar) } E → ( E1 ) E → id1 oprel id2 { E.lugar:= E1.lugar } { E.lugar:=tempnuevo; después:=etiqnueva; cierto:=etiqnueva; emite('if' id1.lugar oprel.op id2.lugar 'goto' cierto); emite(E.lugar ':=' '0'); emite('goto' después); emite(cierto ':'); emite(E.lugar ':=' '1'); emite(después ':'); E → true E → false } { E.lugar:=tempnuevo; { E.lugar:=tempnuevo; César Ignacio García Osorio. Universidad de Burgos. emite(E.lugar ':=' '1'); emite(E.lugar ':=' '0'); } } PL. Generación de código 17