Beruflich Dokumente
Kultur Dokumente
Bibliografa:
Aho, A.V., Sethi, R., Ullman, J.D. (1990), Compiladores: principios, t cnicas y herramientas, Tema 8, 9, 10 (pag. 478-666). e Louden, K.C. (1997), Compiler Construction: Principles and Practice, Tema 8, p ginas: 398-481. a
1 Introducci n. o 2 Tipos de representaciones intermedias: C digo de 3-direcciones. o 3 C digo intermedio como un atributo sintetizado. o 4 Generaci n de c digo para expresiones y sentencias de control: o o 4.1 Proposiciones de asignaci n. o 4.2 Expresiones aritm ticas. e 4.3 Expresiones booleanas. 4.4 Sentencias de control. 5 Optimizaci n de c digo: o o 5.1 Bloques b sicos y optimizaci n local. a o 5.2 Eliminaci n de subexpresiones comunes. o 5.3 Eliminaci n de c digo muerto. o o
206
7.1. INTRODUCCION
5.4 Transformaciones aritm ticas. e 5.5 Empaquetamiento de variables temporales. 5.6 Mejoras en lazos.
7.1
INTRODUCCION
Como se coment en el primer captulo el proceso de la compilaci n se o o desglosa en dos partes: la parte que depende s lo del lenguaje fuente (etapa o inicial o front-end ) y la parte que depende s lo del lenguaje objeto (etapa o nal o back-end).
Etapa inicial: corresponde con la parte de an lisis (l xico, sint ctico a e a y sem ntico). a Etapa nal: corresponde con la parte de sntesis (generaci n de c digo). o o
La etapa inicial traduce un programa fuente a una representaci n intermeo dia a partir de la cual la etapa nal genera el c digo objeto. o De esta forma, los detalles que tienen que ver con las caractersticas del lenguaje objeto (c digo ensamblador, c digo m quina absoluto o relocalo o a izable, . . . ), la arquitectura de la m quina (n mero de registros, modos de a u direccionamiento, tama o de los tipos de datos, memoria cache, ...), el enn torno de ejecuci n (estructura de registros y memoria de la m quina donde o a se va a ejecutar el programa . . . ) y el sistema operativo se engloban en la etapa nal y se aislan del resto. La generaci n de c digo es la tarea m s complicada de un compilador. o o a Las ventajas de utilizar esta representaci n intermedia, independiente de o la m quina en la que se va a ejecutar el programa, son: a Se puede crear un compilador para una nueva m quina distinta uniena do la etapa nal de la nueva m quina a una etapa inicial ya existente. a Se facilita la redestinaci n. o
207
La gura 7.1 muestra las dos etapas y como se relacionan entre s a trav s e de la representaci n intermedia. o
Programa fuente ETAPA INICIAL ETAPA FINAL Analizador Lxico Componentes lxicos Analizador Sintctico Arbol Sintctico Analizador Semntico Cdigo Intermedio Optimizador de Cdigo mquina Generador de cdigo mquina
En este captulo veremos c mo traducir las construcciones de los lenguajes o de programaci n como: las declaraciones, asignaciones y proposiciones o de ujo de control a una representaci n intermedia. La mayor parte de las o traducciones de estas proposiciones se pueden implantar durante el an lisis a sint ctico utilizando las t cnicas de traducci n vistas en en el dise o de a e o n esquemas de traducci n dirigidos por la sintaxis (ETDS). o
Cdigo mquina
208
7.2
Una representaci n intermedia es una estructura de datos que representa al o programa fuente durante el proceso de la traducci n a c digo objeto. Haso o ta ahora hemos usado el arbol de an lisis sint ctico como representaci n a a o intermedia, junto con la tabla de smbolos que contena informaci n sobre o los nombres (variables, constantes, tipos y funciones) que aparecan en el programa fuente. Aunque el arbol de an lisis sint ctico es una representaci n v lida, no se a a o a parece ni remotamente al c digo objeto, en el que s lo se emplean saltos o o a direcciones en memoria en vez de construcciones de alto nivel, como sentencias if-then-else. Es necesario generar una nueva forma de representaci n intermedia. A esta representaci n intermedia, que se parece o o al c digo objeto pero que sigue siendo independiente de la m quina, se le o a llama c digo intermedio. o El c digo intermedio puede tomar muchas formas. Todas ellas se consido a eran como una forma de linearizaci n del arbol sint ctico, es decir, una o representaci n del arbol sint ctico de forma secuencial. El c digo intero a o medio m s habitual es el c digo de 3-direcciones. a o El c digo de tres direcciones es una secuencia de proposiciones de la o forma general x = y op z donde op representa cualquier operador; x,y,z representan variables denidas por el programador o variables temporales generadas por el compilador. y,z tambi n pueden representar constantes o literales. op repree senta cualquier operador: un operador aritm tico de punto jo o otante, o e un operador l gico sobre datos booleanos. o
209
No se permite ninguna expresi n aritm tica compuesta, pues s lo hay un o e o operador en el lado derecho. Por ejemplo, x+y*z se debe traducir a una secuencia, donde son variables temporales generadas por el compilador.
Las expresiones compuestas y las proposiciones de ujo de control se han de descomponer en proposiciones de este tipo, deniendo un conjunto sucientemente amplio de operadores. Se le llama c digo de 3-direcciones o porque cada proposici n contiene, en el caso general, tres direcciones, dos o para los operandos y una para el resultado. (Aunque aparece el nombre de la variable, realmente corresponde al puntero a la entrada de la tabla de smbolos de dicho nombre). El c digo de tres direcciones es una representaci n linealizada (de izquiero o da a derecha) del arbol sint ctico en la que los nombres temporales correa sponden a los nodos internos. Como estos nombres temporales se representan en la memoria no se especica m s informaci n sobre ellos en este a o tipo de c digo. Normalmente se asignar n directamente a registros o se o a almacenar n en la tabla de smbolos. a 2*a+b-3
Cdigo de 3-direcciones: o
= 2 * a = b - 3 = +
% &"
+
t1
t3
* 2 a b
t2
210
7.2.1
La forma de c digo de 3-direcciones que hemos visto hasta ahora es insuo ciente para representar todas las construcciones de un lenguaje de programaci n (saltos condicionales, saltos incondicionales, llamadas a funciones, o bucles, etc), por tanto es necesario introducir nuevos operadores. El conjunto de proposiciones (operadores) debe ser lo sucientemente rico como para poder implantar las operaciones del lenguaje fuente. Las proposiciones de 3-direcciones van a ser en cierta manera an logas al a c digo ensamblador. Las proposiciones pueden tener etiquetas simb licas o o y existen instrucciones para el ujo de control (goto). Una etiqueta simb lica o representa el ndice de una proposici n de 3-direcciones en la lista de in o strucciones. Las proposiciones de 3-direcciones m s comunes que utilizaremos: a 1 Proposiciones de la forma x = y op z donde op es un operador binario aritm tico, l gico o relacional. e o 2 Instrucciones de la forma x = op y, donde op es un operador unario (operador negaci n l gico, menos unario, operadores de desplazao o miento o conversi n de tipos). o 3 Proposiciones de copia de la forma x = y, donde el valor de y se asigna a x. 4 Salto incondicional goto etiq. La instrucci n con etiqueta etiq o es la siguiente que se ejecutar . a 5 Saltos condicionales como if false x goto etiq. 6 param x y call f para apilar los par metros y llamadas a funa ciones (los procedimientos se consideran funciones que no devuelven valores). Tambi n return y, que es opcional, para devolver vale ores. C digo generado como parte de una llamada al procedimiento o p(x 1,x 2,...,x n).
211
param param ... param call p,n 7 Asignaciones con ndices de la forma x = y[i], donde se asigna a x el valor de la posici n en i unidades de memoria m s all de la o a a posici n y. O tambi n x[i] = y . o e 8 Asignaciones de direcciones a punteros de la forma x = &y (el valor de es la direcci n de ), x = *y (el valor de se iguala al cono tenido de la direcci n indicada por el puntero ) o *x = y (el objeto o apuntado por se iguala al valor de ). Ejemplo. Consideremos el c digo que calcula el factorial de un n mero. o u La tabla 7.1 muestra el c digo fuente y el c digo de 3-direcciones. Existe o o un salto condicional if false que se usa para traducir las sentencias de control if-then, repeat-until que contiene dos direcciones: el valor condicional de la expresi n y la direcci n de salto. La proposici n o o o label s lo tiene una direcci n. Las operaciones de lectura y escrituo o ra, read, write, con una sola direcci n. Y una instrucci n de parada o o halt que no tiene direcciones.
) 0
(
212
Tabla 7.1: C digo fuente y c digo de 3-direcciones para el c lculo del factorial o o a
7.2.2
Una proposici n de c digo de 3-direcciones se puede implantar como una o o estructura tipo registro con campos para el operador, los operandos y el resultado. La representaci n nal ser entonces una lista enlazada o un o a vector de proposiciones. Implementaci n mediante cu druplos o a
Un cu druplo es una estructura tipo registro con cuatro campos que se llaa man (op, result, arg1, arg2). El campo op contiene un c digo o interno para el operador. Por ejemplo, la proposici n de tres direcciones x = y + z se representa o mediante el cu druplo (ADD, x,y, z). Las proposiciones con operadores a . Los campos que no se usan se dejan vacos o un unarios no usan el valor NULL. Como se necesitan cuatro campos se le llama representaci n o mediante cu druplos. a
7 5 3 8642
read x; if 0 x then fact:=1; repeat fact:=fact*x; x:=x-1; until x=0; write fact; end;
1
read x t1 = 0 x if false t1 goto L1 fact=1 label L2 t2=fact * x fact=t2 t3=x-1 x=t3 t4=x==0 if false t4 goto L2 write fact label L1 halt
213
Una posible implantaci n del programa que calcula el factorial mediante o cu druplos se muestra ahora en la parte izquierda de la tabla 7.2. a
(a) Cu druplos a (read,x,,) (isbigger,t1,x,0) (if false,t1,L1,) (assign,fact,1,) (label,L2,,) (mult,t2,fact,x) (assign,fact,t2,) (sub,t3,x,1) (assign,x,t3,) (isequal,t4,x,0) (if false,t4,L2,) (write,fact,,) (label,L1,,) (halt,,,) (b) Tripletes (0) (read,x,) (1) (isbigger,x,0) (2) (if false, (1), (11)) (3) (assign, fact,1) (4) (mult, fact,x) (5) (assign, (4), fact) (6) (sub, x,1) (7) (assign, (6), x) (8) (isequal, x, 0) (9) (if false, (8),(4)) (10) (write,fact,) (11) (halt,,)
214
Implementaci n mediante tripletes o Para evitar tener que introducir nombres temporales en la tabla de smbolos, se hace referencia a un valor temporal seg n la posici n de la proposici n u o o que lo calcula. Las propias instrucciones representan el valor del nombre temporal. La implantaci n se hace mediante registros de s lo tres campos o o (op, arg1, arg2). La parte derecha de la tabla 7.2 muestra la implantaci n mediante tripletes o del c lculo del factorial. Los n meros entre par ntesis representan puna u e teros dentro de la lista de tripletes, en tanto que los punteros a la tabla de smbolos se representan mediante los nombres mismos. En la notaci n de tripletes se necesita menor espacio y el compilador no o necesita generar los nombre temporales. Sin embargo, en esta notaci n, o trasladar una proposici n que dena un valor temporal exige que se modo iquen todas las referencias a esa proposici n. Lo cual supone un incono veniente a la hora de optimizar el c digo, pues a menudo es necesario o cambiar proposiciones de lugar. A partir de ahora nos vamos a centrar en la notaci n de cu druplos, que es o a la que se implantar en la pr ctica 3. La gura 7.2 a continuaci n muesa a o tra en c digo C como se podra implantar la estructura de datos para los o cu druplos: a
typedef enum assign, add, mult,if false, goto, label, read, write, isequal, . . . OpKind; typedef struct int val;// para valores char *name;// para identicadores de variables Address; typedef struct OpKind op; Address result, arg1, arg2; Quad;
@ 9 9 9 @ @
215
Por simplicidad se podrian considerar los campos result, arg1, arg2 como punteros a caracter. En esta implantaci n s lo se permite que un aro o gumento represente una constante entera o una cadena ( la cadena representa el nombre de un temporal o una variable de la tabla de smbolos). Una alternativa a almacenar nombres en el cu druplo es almacenar punteros a a la tabla de smbolos, con lo que se evita tener que hacer operaciones de b squeda en un procesado posterior. u
7.3
El c digo intermedio puede ser considerado como un atributo sintetizao do. El c digo intermedio es visto como una cadena de caracteres y se o puede dise ar un esquema de traducci n dirigido por la sintaxis (ETDS) n o que genere dicho c digo al recorrer el arbol de an lisis sint ctico en orden o a a postjo. Consideremos como ejemplo la siguiente gram tica simplicada que gena se calcula el era expresiones aritm ticas. Dada la expresi n e o valor del no-terminal en un nombre temporal . Por el momento, se crea un nuevo nombre cada vez que se necesita. M s tarde veremos un sencillo a m todo para reutilizar los nombres temporales. e Cada no-terminal tiene dos atributos:
A A C A B A A
,(
Ambos atributos son sintetizados. La funci n nuevotemp() genera nomo bres distintos , , ... cada vez que es llamada. Las llaves indican
E E E GGFD
).
216
una instrucci n de c digo de 3-direcciones. El smbolo // representa la o o concatenaci n de los trozos de c digo. o o
Producci n o
V V
+ 4
Veamos el ejemplo x=x+3+4. La gura 7.3 muestra el arbol sint ctico. a El c digo de 3-direcciones correspondiente es: o
#"
= x + 3 =
x =
S
cod=t1=x+3 E lugar=t2 t2=t1+4
id (x)
cod=t1=x+3 E lugar=t1
cod= E lugar=4
cod=
E lugar=x + id (x)
@ w DFkq
X V ij
w F6q
X V gWT
w DF6q
X fV 9
g g
v k uxusq y w v r t r
Q iRP
w G6q
y w v r t r uxusq
QbgaXghVegaV ` T Q b `X w G6q X V
X V mhT
oopegaV T Q b `X w G6q X V
oopegaV T Q b `X w G6q X V
V TS Q WURP lhV v u Q nP y
I H
217
Reutilizaci n de los nombres temporales o Hasta ahora se ha supuesto que la funci n nuevotemp() genera un nomo bre temporal cada vez que se necesita un temporal . Sin embargo, los nombres temporales utilizados para guardar valores intermedios de los c lculos a de expresiones tienen que ser introducidos en la tabla de smbolos para guardar sus valores con el consiguiente aumento de espacio. Los nombres temporales se pueden reutilizar mediante un sencillo m todo e que consiste en llevar una cuenta , iniciada a cero, de variables temporales. Cuando se utilice un nombre temporal como operando hay que decrementar el valor de en . Siempre que se genere un nuevo nombre temporal se usa el valor del contador (sprintf(simbolo,t%d,c)) y se incrementa el valor de en . Consideremos el ejemplo:
Proposici n o t0 = a * b t1 = c * d t0 = t0 + t1 t1 = e * f t0 = t0 - t1 x = t0
= id (x) * a b c + * d e * f
Valor de c 0 1 2 1 2 1 0
z x w u y(v
t4 s
218
7.4
La tabla 7.4 a continuaci n muestra como se puede realizar la traducci n o o de expresiones aritm ticas m s complejas. La funci n e a o gen cuad(op, result, arg1, arg2) genera el correspondiente c digo de 3-direcciones en notaci n de cu druplos. o o a Cada vez que es llamada genera una lista de c digo con s lo un cu druplo. o o a
Producci n o
V V V V V h V y V TS Q U|nP I V I V I V I V I {H
En la pr ctica las proposiciones de 3-direcciones se pueden escribir de a forma consecutiva a un archivo de salida en lugar de construir la lista de proposiciones en el atributo cod, lo que facilita la implementaci n. o Veamos ahora como se puede implantar una funci n recursiva que genera o el c digo de 3-direcciones, que llamaremos genera cdigo(). Al n o o y al cabo se trata de un atributo sintetizado y podemos recorrer el arbol en modo postjo. Supongamos que la funci n devuelve un registro llamado o TAC (Three-Address-Code), que contiene dos campos: lugar (nombre temporal donde se almacena el valor de una expre-
X V gh
w DF6q
X V h
w F6&q X V c
w F6&q
X V
w G6q
0$y X V YH
~ &y
QP I RV
219
si n) o
220
NodeKind kind; int nchilds; // nmero de hijos u struct streenode childs[MAXNUMCHILDS]; // se puede usar una lista con primer hijo que apunta a hermanos int val; // para constantes numricas e char *strval; // para identificadores, deberia ser puntero a la TS STreeNode;
= E
id
n_id
case n assign:
// childs[0]=id, childs[1]=E
typedef struct
n_mas
E 1
n_num
typedef enum
NodeKind;
221
aux1=genera codigo(childs[0]); aux2=genera codigo(childs[1]); datos.lugar=nuevotemp(); cod=gen cuad(add, datos.lugar,aux1.lugar,aux2.lugar); datos.cod=concatena codigo(aux1.cod,aux2.cod,cod); break;
aux1=genera codigo(childs[0]); aux2=genera codigo(childs[1]); datos.lugar=nuevotemp(); cod=gen cuad(mult, datos.lugar,aux1.lugar,aux2.lugar); datos.cod=concatena codigo(aux1.cod,aux2.cod,cod); break;
// no haria falta crear este tipo de nodo datos=genera codigo(childs[1]); break; case n id: datos.lugar=lexema(id); datos.cod=NULL;
return(datos);
break;
break;
case n parentesis:
// childs[1]=
case n mult:
// childs[0]=
, childs[1]=
case n add:
// childs[0]=
, childs[1]=
222
7.5
Las expresiones booleanas se utilizan principalmente como parte de las proposiciones condicionales que alteran el ujo de control del programa, if-then, if-then-else, while-do. Las expresiones booleanas se componen de los operadores booleanos and,or,not aplicados a variables booleanas o expresiones relacionales. A su vez, las expresiones relacionales son de la forma oprel , donde y son expresiones aritm ticas y oprel es cualquier operador e relacional <, >, <=, >=,.... Consideremos expresiones booleanas generadas por la gram tica: a
Uno de los m todos para traducir expresiones booleanas a c digo de 3e o direcciones consiste en codicar num ricamente los valores true y false e y evaluar una expresi n booleana como una expresi n aritm tica, siguieno o e do unas prioridades. A menudo se utiliza para indicar true y 0 para indicar false. Las expresiones booleanas se eval an de manera similar a una expresi n u o aritm tica de izquierda a derecha. Supongamos el ejemplo: a or b and e not c. La secuencia de c digo de 3-direcciones correspondiente es: o
r
= a or b
w F6q
= not c = and
Producci n o
La funci n para generar c digo se podra ampliar a adiendo nuevos casos o o n a la sentencia switch correspondientes a los tipos de nodos asociados a los operadores l gicos. Por ejemplo, para el nodo correspondiente al o operador l gico and, nodo tipo n and, se insertara el siguiente trozo de o c digo: o
w Fkq
La siguiente gram tica en la tabla 7.5 muestra el ETDS para producir a c digo de 3-direcciones para las expresiones booleanas. o
Tabla 7.5: ETDS para expresiones booleanas utilizando una representaci n num rica o e
case n and: // childs[0]= , childs[1]= aux1=genera codigo(childs[0]); aux2=genera codigo(childs[1]); datos.lugar=nuevotemp(); cod=gen cuad(and, datos.lugar,aux1.lugar,aux2.lugar
A A Q RP r q $Dw r u q r 6e I V I V I V I V y I V I V I V I V o o T Q b `X ppegaV y w v r t r xu&q w G6q Q iRP T X V w F6q di$y u w w r F G X V fY P Q ` T Q b `X egaV y e v r u r w G6q g g db T X V wF6q di$y u w w r F p XfY P V Q ` T Q b `X egaV y e v r u r w G6q gg db T X V ywxvrut&q r y w r t xvur&q wF6&q q6e y u r w r F cRP Q mlGRP Q XV b Q ` T Q b `X egaV y e v r u r w G6q db T X V g g Q b `X V T Q b `X gaghegaV w G6q w G6q X V ghT X V wG6q DwF6q y u w r F X V XfVm Q b ` h h Q b `X V T Q b `X pigaghegaV y e v r u r w G6q db T X V X V m w DF6q X V Q f6 w $y Q w u ` r F hphiQdcaipigaghegaV b `X V h h Q b `X V T Q b `X y e v r u r w G6q db T X V hphiQdcaipigaghegaV b `X V h h Q b `X V T Q b `X y e v r u r w G6q db T X V Q tnP b Q fGRP lV V b hf V Q iv V w hV b fhV X V i w Fkq X V i w DF6q X V h w F6&q y u w r F X V b Q `
224
225
7.6
Pasemos a considerar ahora la traducci n de proposiciones de control ifo then, if-then-else, while-do generadas por la siguiente gram tica: a
S if E then
7.6.1
Proposici n if-then o
A B
Supongamos una sentencia if-then de la forma if then , ver diagrama de ujo en gura 7.6. Para generar el c digo correspondio ente a esta proposici n habra que a adir a la funci n genera cdigo() o n o o un nuevo caso para la sentencia switch que contemple este tipo de nodo en el arbol sint ctico, nodo n ifthen. a
CODIGO CALCULO E
(a)
Figura 7.6: (a) Diagrama de ujo para la proposici n if-then y (b) tipo de nodo o
TAC datos; TAC aux1, aux2; lista codigo *cod; datos.cod=NULL; direcciones dir; //usamos direccin al salto, en vez de etiquetas o
case n ifthen:
// childs[0]=E, childs[1]=
| while E do
| if E then
else
%
n_ifthen E S
(b)
226
// an no se sabe la direc. u
de salto
Supondremos que tenemos una funci n, sigtedirlibre(), que guaro da el ndice de la siguiente instrucci n libre (la funci n gen cuad() in o o crementa ese contador). En la implantaci n se ha optado por utilizar direco ciones directamente a las instrucciones en vez de usar etiquetas.
7.6.2
Proposici n if-then-else o
A B p
Supongamos una sentencia if-then-else de la forma if then else , cuyo diagrama de ujo tiene la forma representada en la gura 7.7. Para generar el c digo correspondiente a esta proposici n o o o habra que a adir a la funci n genera cdigo() un nuevo caso para la n o sentencia switch que contemple este tipo de nodo en el arbol sint ctico, a nodo n ifthenelse. Este fragmento de c digo se podra implantar de o forma similar a como hemos hecho en la secci n anterior. Ver ejercicios. o
227
CODIGO CALCULO E
n_ifthen_else
S1
S2
(a=
(b)
Figura 7.7: (a)Diagrama de ujo para la proposici n if-then-else y (b) tipo de nodo o
7.6.3
Proposici n while-do o
while do , cuyo Una sentencia while -do tiene la forma diagrama de ujo viene representado en la gura 7.8. Para generar el c digo correspondiente a esta proposici n habra que a adir a la funci n o o n o genera cdigo() un nuevo caso para la sentencia switch que contemo ple este tipo de nodo en el arbol sint ctico. a
Etiqueta 1 CODIGO CALCULO E
n_while
SALTO INCONDICIONAL Etiqueta 2
(a=
(b)
Figura 7.8: (a)Diagrama de ujo para la proposici n while do y (b) tipo de nodo o
Supondremos que tenemos una funci n, sigtedirlibre(), que guarda el o ndice de la siguiente instrucci n libre (se asume que la funci n gen cuad() o o
228
dir1=sgtedirlibre(); aux1=genera codigo(childs[0]); cod1=gen cuad(if false,--, aux1.lugar, dir?); aux2=genera codigo(childs[1]); cod2=gen cuad(goto,--, dir1,--);// salto incondicional a dir1 dir2=sigtedirlibre(); // rellena argumento de cod1, direccion salto condicional rellena(cod1,arg3,dir2) datos.cod=concatena codigo(aux1.cod,cod1,aux2.cod,cod2); break;
7.7
El t rmino optimizaci n de c digo es inadecuado ya que no se garantiza e o o el obtener, en el sentido matem tico, el mejor c digo posible atendiendo a o a maximizar o minimizar una funci n objetivo (tiempo de ejecuci n y eso o
case n while:
// childs[0]=E, childs[1]=
229
pacio). El t rmino de mejora de c digo sera m s apropiado que el de e o a optimizaci n. o Nos concentraremos b sicamente en la optimizaci n de c digo de tresa o o direcciones, puesto que son siempre transportables a cualquier etapa nal, a son optimizaciones independientes de la m quina. Las optimizaciones a nivel de la m quina, como la asignaci n de registros y la utilizaci n de a o o instrucciones especcas de la m quina, se salen del contexto de esta asig a natura. La mayora de los programas emplean el del tiempo de ejecuci n en el o de su c digo. Lo m s adecuado es identicar las partes del programa o a que se ejecutan m s frecuentemente y tratar de que se ejecuten lo m s a a ecientemente posible. En la pr ctica, son los lazos internos los mejores a candidatos para realizar las transformaciones. Adem s de la optimizaci n a nivel de c digo intermedio, se puede reducir a o o el tiempo de ejecuci n de un programa actuando a otros niveles: a nivel de o c digo fuente y a nivel de c digo objeto. o o
Codigo fuente Etapa Inicial Codigo Intermedio Codigo Objeto
el compilador puede usar registros seleccionar instrucciones
Figura 7.9: Niveles en los que el programador y el compilador pueden mejorar el c digo o
Generador de Codigo
230
Un bloque b sico es una unidad fundamental de c digo. Es una secuencia a o de proposiciones donde el ujo de control entra en el principio del bloque y sale al nal del bloque. Los bloques b sicos pueden recibir el control desde a m s de un punto en el programa (se puede llegar desde varios sitios a una a etiqueta) y el control puede salir desde m s de una proposici n (se podra a o ir a una etiqueta o seguir con la siguiente instrucci n). Cuando aplicamos o optimizaci n dentro de un bloque b sico s lo nos tenemos que preocupar o a o sobre los efectos de la optimizaci n en los valores de las variables a la o entrada del bloque y los valores que tienen a la salida del bloque, que han de ser los mismos que en el c digo original sin transformar. o El algoritmo para particionar un programa en bloques se describe a continuaci n: o
1 Encontrar todas las proposiciones que comienzan el principio de un bloque bsico: a
2 Para cualquier proposicin que comienza un bloque bsico, el bloque o a consiste de esa proposicin y de todas las siguientes hasta el prino cipio del siguiente bloque o el final del programa.
El ujo de control de un programa puede visualizarse como un grafo dirigido de bloques b sicos. A este grafo se le llama grafo de ujo. Como a ejemplo consideremos este trozo de c digo escrito en pseudoC que suma o los diez primeros n meros que se muestra en la tabla 7.6 (a): u
La primera sentencia del programa. Cualquier proposicin del programa que es el objetivo de un salto. o Cualquier proposicin que sigue a una bifurcacin. o o
231
t0 = i 0 do
while i
s=s+i; i=i-1;
(a)
La gura 7.10 muestra el diagrama de ujo para el ejemplo anterior. Aparecen 4 bloques b sicos. a
i = 10 s=0
label l2 ...
232
Dada una proposici n de 3-direcciones de la forma a = b op c decimos o que la proposici n referencia b y c y que dene a. o
Se dice que un nombre en un bloque b sico vive al nal del bloque si a su valor es referenciado en otro bloque b sico en el programa. a
Se presentan algunas de las transformaciones m s utiles para mejorar el a c digo. Son transformaciones locales, se pueden realizar observando s lo o o las proposiciones de un bloque b sico. a
7.7.1
Si una expresi n se calcula m s de una vez, se puede remplazar el c lculo o a a de la segunda por el valor de la primera expresi n. Consideremos el siguo iente ejemplo del c digo 7.7 (a). Vemos que la expresi n o o se calcula dos veces, por tanto podemos escribir el c digo de la gura 7.7 (b): o
t1 = 4 - 2 t2 = t1 / 2 t3 = a * t2 t4 = t3 * t1 t5 = t4 + b t6 = t3 * t1 t7 = t6 + b c = t5 * t7 (a) t1 = 4 - 2 t2 = t1 / 2 t3 = a * t2 t4 = t3 * t1 t5 = t4 + b t6 = t4 t7 = t6 + b c = t5 * t5 (b)
|
Esto s lo es posible si los operandos que est n implicados en el c lculo de o a a la expresi n no han modicado su valor en las proposiciones intermedias. o
233
7.7.2
Propagaci n de copias o
. La propagaci n de copias considera las proposiciones de la forma o Despu s de esta sentencia sabemos que y tienen el mismo valor, por e tanto, podemos remplazar cada vez que aparezca por , con la esperanza de que podamos remplazar todas las ocurrencias de hasta que se convierta en un nombre muerto y se pueda entonces eliminar la proposici n de copia. o A partir del c digo anterior podemos eliminar la proposici n de copia o o . Sustituimos por . Vease gura 7.8 (a). Ahora se puede ver que hay de nuevo una subexpresi n com n que puede ser eliminada, obteniendo o u el c digo que se muestra en 7.8 (b). Y nalmente realizando otra vez o propagaci n de copias, obtenemos 7.8 (c): o
t1 = 4 - 2 t2 = t1 / 2 t3 = a * t2 t4 = t3 * t1 t5 = t4 + b t6 = t4 t7 = t4 + b c = t5 * t7 (a) t1 = 4 - 2 t2 = t1 / 2 t3 = a * t2 t4 = t3 * t1 t5 = t4 + b t6 = t4 t7 = t5 c = t5 * t7 (b) t1 = 4 - 2 t2 = t1 / 2 t3 = a * t2 t4 = t3 * t1 t5 = t4 + b t6 = t4 t7 = t5 c = t5 * t5 (c)
{ c c
Vemos que el haber hecho una optimizaci n puede dar lugar a que sea o posible aplicar nuevas optimizaciones.
234
7.7.3
Podemos tener proposiciones que denen un nombre que nunca m s es a referenciado, est muerto. Estas proposiciones pueden entonces ser elima inadas. Dada la proposici n a= b op c, se dice que es c digo muerto o o o o inactivo si no es referenciada. En general, el c digo muerto aparece como consecuencia de la propagaci n de copias y es esto lo que hace que la o t cnica de propagaci n de copias sea tan util. Veamos como aplicar esta e o t cnica al ejemplo anterior en la gura 7.8 (c). e Vemos que y no tienen ninguna referencia a partir de su denici n, o por tanto pueden ser eliminadas. Obtenemos el c digo que aparece en o la gura 7.9. Se ha supuesto que todas las variables no-temporales est n a vivas, se hace referencia a ellas en el resto del programa.
d
Aunque es poco probable que el programador introduzca c digo muerto o o inactivo, este puede aparecer como resultado de transformaciones anteriores.
235
7.7.4
Se pueden hacer uso de transformaciones algebraicas simples para reducir la cantidad de computaci n transformando operaciones m s costosas o a por otras menos costosas. Existen tres tipos de transformaciones algebraicas b sicas. a C lculo previo de constantes a Se trata de calcular a nivel de compilaci n el valor previo de constantes o en vez de hacerlo en tiempo de ejecuci n que retardara la ejecuci n de un o o programa. A esta optimizaci n se le llama c lculo previo de constantes o a (en ingl s constant folding). El c digo de la gura 7.9 puede ser mejorado e o dando lugar al c digo de la gura 7.10 (a). Haciendo una propagaci n de o o copias y eliminaci n de c digo muerto tenemos el c digo de la gura 7.10 o o o (b). Realizando de nuevo un c lculo previo de constantes obtenemos 7.10 a (c). Y nalmente podemos hacer de nuevo la propagaci n de copias y la o eliminaci n de c digo muerto, obteniendo el c digo de la gura 7.10 (d). o o o
t1 =2 t2 = t1 / 2 t3 = a * t2 t4 = t3 * t1 t5 = t4 + b c = t5 * t5 (a) (b) (c) (d) t2 = 2 / 2 t3 = a * t2 t4 = t3 * 2 t5 = t4 + b c = t5 * t5 t2 = 1 t3 = a * t2 t4 = t3 * 2 t5 = t4 + b c = t5 * t5 t3 = a * 1 t4 = t3 * 2 t5 = t4 + b c = t5 * t5
Hemos pasado de seis proposiciones a tener cuatro, eliminado una substracci n y una divisi n en el proceso. o o
236
Transformaciones algebraicas Podemos hacer uso de identidades algebraicas para simplicar el c digo. o Las principales identidades son:
Partiendo de la gura 7.10 (d) podemos obtener el c digo de la gura 7.11 o (a). De nuevo si usamos propagaci n de copias y eliminaci n de c digo o o o muerto obtenemos el c digo de la gura 7.11 (b) : o
t3 = a t4 = t3 * 2 t5 = t4 + b c = t5 * t5 (a) (b) t4 = a * 2 t5 = t4 + b c = t5 * t5
Reducci n de intensidad o En la mayora de las m quinas las operaciones de multiplicaci n y divisi n a o o son substancialmente m s costosas que las operaciones de suma y resta. Y a a su vez, las potencias son m s costosas que las multiplicaciones y dia visiones. Por tanto, siempre que sea posible es conveniente sustituir un operador m s costoso por otro menos costoso. A esto se le conoce como a reducci n de la intensidad. Las identidades m s comunes son: o a
!
Otra transformaci n tpica es usar desplazamientos de bits cuando se divio da o se multiplique por potencias de dos.
237
7.7.5
Empaquetamiento de temporales
Se trata de reusar los temporales con el n de ahorrar espacio. Despu s e de haber optimizado el c digo es normal pensar que se puedan usar menos o temporales y sera conveniente entonces renombrar estos temporales. Supongamos que tenemos una tabla de temporales disponibles (por ejemplo de a ), de manera que marcamos si un temporal est vivo en un a cierto punto del bloque b sico. En general, es posible remplazar dos tema porales por uno unico si no existe ning n punto donde los dos temporales u est n vivos a la vez. Para cada temporal lo remplazamos por el primer tema poral en la tabla que est muerto en todos los lugares en el que el temporal a bajo consideraci n est vivo. o a Veamos nuestro ejemplo de la gura 7.12. se dene en la primera proposici n y est vivo en la segunda, entonces lo remplazamos por el o a o primer terminal muerto de la tabla, , obteniendo el c digo de la gura 7.13 (a). Por otro lado, es denido en la segunda proposici n y vivo en o la proposici n 3. Esto no interacciona con el temporal , que esta vivo o s lo en la segunda proposici n, por tanto o o puede ser sustituido por . Obteniendo el c digo de la gura 7.13 (b). o
t1 = a + a t5 = t1 + b c = t5 * t5 (a) t1 = a + a t1 = t1 + b c = t1 * t1 (b)
c d d
Comparando este c digo con el original que tena ocho proposiciones, o siete temporales, dos adiciones, una substracci n, cuatro multiplicaciones o
238
y una divisi n, lo hemos reducido a tres proposiciones que implican dos o adiciones y una multiplicaci n. o
7.7.6
Traslado de c digo o Una modicaci n importante que disminuye la cantidad de c digo en o o un lazo es el traslado de c digo. Esta transformaci n toma una expresi n o o o que produce el mismo resultado independientemente del n mero de veces u que se ejecute un lazo y la coloca antes del lazo. Por ejemplo:
while (i<=limite-2) ...
239
Variables de inducci n o Se trata de identicar las variables que permanecen ligadas entre s en la ejecuci n, est n relacionadas entre s de forma que conocido el valor de o a una se puede obtener el de la otra. Supongamos el siguiente fragmento de c digo donde se ha supuesto que los elementos de la matriz ocupan 4 bytes o y ya se ha realizado cierta optimaci n. o
suma=0; j=0; while j 10 suma = suma + a[j]; j=j+1; ...
suma=0; j=0; label l1 t1= j 10 iffalse t1 goto l2 t2=4*j t3=a[t2] suma=suma+t3 j=j+1 goto l1 label l2 ...
en la tabla 7.14 (b) signica la direcci n de m s un deo a donde splazamiento . Sabemos que no cambia en ning n momento salvo en u , por tanto justo despu s de la proposici n e o se cumple que , y se puede sustituir la asignaci n o por , debiendo inicializar el valor de . Siempre que haya variables de inducci n se pueden hacer sustituciones de o este tipo y eliminar todas menos una.
y U
240
7.8. EJERCICIOS
7.8
EJERCICIOS
1 (0.25 ptos) Escribe el pseudoc digo correspondiente, usando la noo taci n que hemos visto a lo largo del captulo, para la implementaci n o o de la generaci n de c digo de tres direcciones medinate cuadr plos o o u para la construccion if-then-else. 2 (0.25 ptos) Escribe el pseudoc digo correspondiente, usando la noo taci n que hemos visto a lo largo del captulo, para la implementaci n o o de la generaci n de c digo de tres direcciones medinate cuadr plos o o u para la construcci n repeat until. o 3 (0.25 ptos) Escribe el pseudoc digo correspondiente, usando la noo taci n que hemos visto a lo largo del captulo, para la implementaci n o o de la generaci n de c digo de tres direcciones medinate cuadr plos o o u para la construcci n switch. o 4 (0.3 ptos) Supongamos una nueva construcci n de los lenguajes de o programaci n que llamaremos do-while-do (hacer-mientras-hacer). o Esta construcci n nace de forma natural, como las construcciones o while-do, do-while que ya conoc is. Esta construcci n surge para e o implementar la idea en que hay casos en los que no se desea salir de la iteraci n al principio, ni al nal de la iteraci n, sino a la mitad, o o despu s de que se ha hecho cierto procesamiento. e Por ejemplo para implementar el c digo: o
dowhiledo read(X); while (no final fichero) process(X); end dowhiledo
hhhf
hlhh
241
Dibujar el diagrama de ujo de esta construcci n. o Dibujar la forma del arbol abstracto de an lisis sint ctico que a a usaras para su traducci n a c digo de 3-direcciones. o o Escribir el pseudoc digo de la funci n generar cdigo para trao o o ducir este tipo de sentencias a una lista de cu druplos. a Desafortunadamente ning n lenguaje com n de programaci n u u o implementa esta construcci n. A qu crees que se debe esto?. o e Qu construcci n(es) usa el lenguaje C para implementar esta e o idea?
242
7.9
Supongamos una peque a calculadora que realizar operaciones aritm ticas n e sencillas seg n la gram tica: u a
Se pide implementar un traductor que traduzca las expresiones aritm ticas e a c digo de 3-direcciones, implementado mediante cu duplos de la foro a ma (operador, resultado, arg1, arg2). Los tipos de operadores son:
(ASSIGN, result, arg1,NULL) (ADD, result, arg1,arg2) (SUB, result, arg1,arg2) (MULT, result, arg1,arg2) (DIV, result, arg1,arg2) (NEG, result, arg1,NULL) (HALT,NULL, NULL, NULL) asigna arg1 a result suma arg1, arg2 y lo alamacena en result resta arg1, arg2 y lo alamacena en result multiplica arg1, arg2 y lo alamacena en result divide arg1, argg2 y lo alamacena en result mult. por (-1) arg1, y lo alamacena en result nal de programa
(ecuacion)+ id = expresion ; termino ( + termino - termino )* factor ( * factor / factor)* ( expresion ) dato num id - num - id
243
Se ha implementado la clase cuadruplo (Cuad.h). Se ha implementado la clase TAC (Three-Address-Code), a partir de una lista de cuaduplos y se ha introducido el lugar como un dato protegido adicional. Se ha implementado el m todo generar c digo en la clase AST. Fichero con la especie o cacion de la gram tica p3.g. a Para la entrada: a=3+2; b=a*2; c=a+b+2*6; d=-1+a;
Obtenemos la salida: ( ENTRADA ( = a ( + 3 2 ) ) ( = b ( * a 2 ) ) ( = c ( + ( + a b ) ( * 2 6 ) ) ) ( = d ( + ( UMINUS 1 ) a ) ) ) Numero de lineas analizadas 6 (ADD,t0,3,2) (ASSIGN,a,t0,NULL) (MULT,t1,a,2) (ASSIGN,b,t1,NULL) (ADD,t2,a,b) (MULT,t3,2,6) (ADD,t4,t2,t3) (ASSIGN,c,t4,NULL) (NEG,t5,1,NULL) (ADD,t6,t5,a) (ASSIGN,d,t6,NULL) (HALT,NULL,NULL,NULL)
Numero de cuadruplos 12
244
06 may 03 18:43
Cuad.h
class Cuad { protected: OpKind op; char *opname; char *res; char *arg1; char *arg2;
public: Cuad(); Cuad( Cuad &); Cuad(OpKind, const char *, const char*, const char *, const char *); ~Cuad();
friend ostream &operator << (ostream &f, const Cuad & c); Cuad & operator = (const Cuad &); bool operator== (const Cuad &) const;
};
#endif
Cuad.h
1/1
06 may 03 19:03
Cuad.cpp
06 may 03 19:03
Cuad.cpp
Pgina 2/4
#include "Cuad.h"
Cuad::Cuad(){ op=EMPTY;
opname = new char [strlen("NULL")+1]; if (opname==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(opname, "NULL");
res=new char [strlen("NULL")+1]; if (res==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(res,"NULL");
arg1=new char [strlen("NULL")+1]; if (arg1==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(arg1,"NULL");
arg2=new char [strlen("NULL")+1]; if (arg2==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(arg2,"NULL"); }
Cuad::Cuad(OpKind ope, const char *name, const char *r, const char *a1, const char *a2){ op=ope; char * Cuad::GetArg1() { return arg1; }
void Cuad::PutRes(char *s) { delete(res); res = new char [strlen(s)+1]; if (res==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(res,s); }
opname=new char [strlen(name)+1]; if (opname==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(opname, name);
res=new char [strlen(r)+1]; if (res==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(res,r);
arg1=new char [strlen(a1)+1]; if (arg1==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(arg1,a1);
void Cuad::PutArg1(char *s) { delete arg1 ; arg1 = new char [strlen(s)+1]; if (arg1==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(arg1,s); } char * Cuad::GetArg2() { return arg2; }
arg2=new char[strlen(a2)+1]; if (arg2==NULL) cerr << "ERROR reservando memoria" << endl; strcpy(arg2,a2);
void Cuad::PutArg2(char *s) { delete arg2; arg2 = new char [strlen(s)+1]; if (arg2==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(arg2,s); } ostream & operator << (ostream &f, const Cuad & c) { f<< "(" << c.opname << "," << c.res << "," << c.arg1 << "," << c.arg2 << ")" << endl; return f; }
245
Cuad.cpp
1/1
246
06 may 03 19:03
Cuad.cpp
06 may 03 19:03 Pgina 4/4
Cuad.cpp
if (this!=&c){ char *auxopname; auxopname = new char [strlen(c.opname)+1]; if (auxopname==NULL) cerr << "ERROR reservando memoria" << endl; else strcpy(auxopname,c.opname); delete [] opname; opname=auxopname;
char *auxres; auxres = new char [strlen(c.res)+1]; if (auxres==NULL) cerr << "ERROR reservando memoria" << endl; else strcpy(auxres,c.res); delete [] res; res=auxres;
char *auxarg1; auxarg1 = new char [strlen(c.arg1)+1]; if (auxarg1==NULL) cerr << "ERROR reservando memoria" << endl; else strcpy(auxarg1,c.arg1); delete [] arg1; arg1=auxarg1;
char *auxarg2; auxarg2 = new char [strlen(c.arg2)+1]; if (auxarg2==NULL) cerr << "ERROR reservando memoria" << endl; else strcpy(auxarg2,c.arg2); delete [] arg2; arg2=auxarg2;
op=c.op; } return(*this);
opname = new char [strlen("NULL")+1]; if (opname==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(opname, "NULL");
res=new char [strlen("NULL")+1]; if (res==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(res,"NULL");
arg1=new char [strlen("NULL")+1]; if (arg1==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(arg1,"NULL");
arg2=new char [strlen("NULL")+1]; if (arg2==NULL) cerr << "ERROR reservando memoria"<< endl; strcpy(arg2,"NULL");
(*this)=c;
Cuad.cpp
1/1
05 may 03 18:08
TAC.h
class TAC: public dllista<Cuad> { //OJO mas que derivar sera una lista de cuadruplos
public:
};
#endif
247
TAC.h
1/1
248
07 may 03 13:01
tree = (AST *) tree>right(); } } void genera_codigo(TAC & tac); };
AST.h
Pgina 1/4 07 may 03 13:01 Pgina 2/4
AST.h
#include #include #include #include #include void AST:: genera_codigo(TAC & tac) { AST *down,*right; AST *tree=this;
class AST : public ASTBase { protected: ANTLRTokenPtr token; public: AST(ANTLRTokenType tok, char *s) { token = new ANTLRToken(tok, s); } AST(ANTLRTokenPtr t) { token = t; } void preorder_action() { cout << token>getText() << " " ; } void postorder_action() { cout << token>getText() << " "; } virtual void preorder_before_action() { cout << " (" ; } virtual void preorder_after_action() { cout << " )" ; } virtual void postorder_before_action() { cout << " ("; } virtual void postorder_after_action() { cout << " )"; }
while ( tree!= NULL ) { down=(AST *)tree>down(); right=(AST *)tree>right(); if ( down != NULL ) tree>postorder_before_action(); if ( down!=NULL ) down>postorder(); tree>postorder_action(); if ( down!=NULL ) tree>postorder_after_action(); tree = (AST *)right; }
void preorder( ){ AST *tree = this; while ( tree!= NULL ) { if ( tree>down() != NULL ) { tree>preorder_before_action(); }; tree>preorder_action(); if ( tree>down()!=NULL ) { AST *d; d= (AST *) tree>down(); d>preorder(); tree>preorder_after_action(); }
down=(AST *)tree>down(); right=(AST *)tree>right(); switch (tree>type()) { case TKN_ENTRADA: { TAC aux1; down>genera_codigo(aux1); tac.concatenar(aux1); right=(AST *) down>right(); while (right!=NULL) { TAC aux; right>genera_codigo(aux); tac.concatenar(aux); right=(AST *)right>right(); } Cuad c(HALT,"HALT","NULL", "NULL", "NULL"); TAC cuad; if (cuad.Insertar(c)==false) cerr << "Error al insertar" << endl; tac.concatenar(cuad); break; } case TKN_ASIGN:{ TAC aux1, aux2; down>genera_codigo(aux1); ((AST *) down>right())>genera_codigo(aux2); Cuad c(ASSIGN,"ASSIGN",aux1.GetLugar(),aux2.GetLugar(),"NULL"); TAC cuad; if (cuad.Insertar(c)==false) cerr << "Error al insertar" << endl; aux1.concatenar(aux2); aux1.concatenar(cuad); tac.concatenar(aux1); break; } case TKN_MAS: { TAC aux1, aux2; char *temp; down>genera_codigo(aux1); ((AST *)down>right())>genera_codigo(aux2); temp = newtemp(); tac.PutLugar(temp); free(temp); Cuad c(ADD,"ADD",tac.GetLugar(),aux1.GetLugar(),aux2.GetLugar()); TAC cuad; if (cuad.Insertar(c)==false) cerr << "Error al insertar" << endl; aux1.concatenar(aux2); aux1.concatenar(cuad); tac.concatenar(aux1); break; }
AST.h
1/1
07 may 03 13:01
AST.h
07 may 03 13:01
AST.h
Pgina 4/4
249
case TKN_MULT: { TAC aux1, aux2; char *temp; down>genera_codigo(aux1); ((AST *)down>right())>genera_codigo(aux2); temp = newtemp(); tac.PutLugar(temp); free(temp); Cuad c(MULT,"MULT",tac.GetLugar(),aux1.GetLugar(),aux2.GetLugar()); TAC cuad; if (cuad.Insertar(c)==false) cerr << "Error al insertar" << endl; aux1.concatenar(aux2); aux1.concatenar(cuad); tac.concatenar(aux1); break; } case TKN_MENOS: { TAC aux1, aux2; char *temp; down>genera_codigo(aux1); ((AST *)down>right())>genera_codigo(aux2); temp = newtemp(); tac.PutLugar(temp); free(temp); Cuad c(SUB,"SUB",tac.GetLugar(),aux1.GetLugar(),aux2.GetLugar()); TAC cuad; if (cuad.Insertar(c)==false) cerr << "Error al insertar" << endl; aux1.concatenar(aux2); aux1.concatenar(cuad); tac.concatenar(aux1); break; } case TKN_DIV: { TAC aux1, aux2; char *temp; down>genera_codigo(aux1); ((AST *)down>right())>genera_codigo(aux2); temp = newtemp(); tac.PutLugar(temp); free(temp); Cuad c(DIV,"DIV",tac.GetLugar(),aux1.GetLugar(),aux2.GetLugar()); TAC cuad; if (cuad.Insertar(c)==false) cerr << "Error al insertar" << endl; aux1.concatenar(aux2); aux1.concatenar(cuad); tac.concatenar(aux1); break; } case TKN_UMINUS: { TAC aux1; char *temp; down>genera_codigo(aux1); temp = newtemp(); tac.PutLugar(temp); free(temp); Cuad c(NEG,"NEG",tac.GetLugar(),aux1.GetLugar(),"NULL"); TAC cuad; if (cuad.Insertar(c)==false) cerr << "Error al insertar" << endl; aux1.concatenar(cuad); tac.concatenar(aux1); break; } case TKN_NUM:{ tac.PutLugar(tree>getText()); break; } case TKN_ID: { tac.PutLugar(tree>getText());
AST.h
1/1
250
07 may 03 12:59
p3.g
07 may 03 12:59 Pgina 2/2
p3.g
#header<< #include <iostream.h> #include <math.h> #include "AToken.h" typedef ANTLRCommonToken ANTLRToken; #include "MiParser.h" #include "Cuad.h" #include "TAC.h" extern int nlineas;
>>
#token BEGIN_COMMENT "/\*" << for (; ;){ advance(); while ((ch!=*) && (ch!=EOF)) advance(); if (ch==*) { advance(); while (ch==*) advance(); if (ch==/) break; } if (ch==EOF){ errstd( "Found EOF inside a comment" ); break; } } advance(); skip(); >> class MiParser { entrada : << #0=#(#[TKN_ENTRADA,"ENTRADA"], NULL); >> (c:ecuacion )+ << AST *raiz; raiz= (AST *)#0; raiz>preorder(); raiz>genera_codigo(lista_cod);>>; ecuacion : TKN_ID TKN_ASIGN^ exp TKN_PTOCOMA!; exp : term ( TKN_MAS^ term | TKN_MENOS^ term)* ; factor)* ; dato ;
char * newtemp() { char *res=NULL; res=new char [20]; sprintf(res,"t%d",ntemp); ntemp++; return(res); }
void ascendente(TAC & l) { Iter_dllista<Cuad> j; int cont =0; for (j=l.Inicio(); j.Valido(); j++) {cout << j.Dato(); cont++;} cout << "Numero de cuadruplos " << cont; }
p.parser()>entrada(&root); cout << endl << "Numero de lineas analizadas " << nlineas << endl; ascendente(lista_cod);
} >>
// Operadores especiales
TKN_PA "\(" TKN_PC "\)" TKN_MAS "\+" TKN_MENOS "\" TKN_MULT "\*" TKN_DIV "/"
p3.g
1/1