Está en la página 1de 42

Analizador sintctico

De Wikipedia, la enciclopedia libre Saltar a: navegacin, bsqueda Un analizador sintctico (en ingls parser) es una de las partes de un compilador que transforma su entrada en un rbol de derivacin. El anlisis sintctico convierte el texto de entrada en otras estructuras (comnmente rboles), que son ms tiles para el posterior anlisis y capturan la jerarqua implcita de la entrada. Un analizador lxico crea tokens de una secuencia de caracteres de entrada y son estos tokens los que son procesados por el analizador sintctico para construir la estructura de datos, por ejemplo un rbol de anlisis o rboles de sintaxis abstracta. El anlisis sintctico tambin es un estado inicial del anlisis de frases de lenguaje natural. Es usado para generar diagramas de lenguajes que usan flexin gramatical, como los idiomas romances o el latn. Los lenguajes habitualmente reconocidos por los analizadores sintcticos son los lenguajes libres de contexto. Cabe notar que existe una justificacin formal que establece que los lenguajes libres de contexto son aquellos reconocibles por un autmata de pila, de modo que todo analizador sintctico que reconozca un lenguaje libre de contexto es equivalente en capacidad computacional a un autmata de pila. Los analizadores sintcticos fueron extensivamente estudiados durante los aos 70 del siglo XX, detectndose numerosos patrones de funcionamiento en ellos, cosa que permiti la creacin de programas generadores de analizadores sintticos a partir de una especificacin de la sintaxis del lenguaje en forma Backus-Naur por ejemplo, tales como yacc, GNU bison y javaCC.

Contenido
[ocultar]

1 Lenguajes humanos 2 Lenguajes de programacin o 2.1 Visin general del proceso 3 Clasificacin 4 Vase tambin

[editar] Lenguajes humanos


En algunos sistemas de traduccin o procesamiento de lenguaje natural los lenguajes humanos son lxicamente analizados por programas informticos. Las frases no son fcilmente analizables debido a la carga de ambigedad que existe en la estructura del lenguaje humano. Para procesar el lenguaje humano los investigadores deben antes ponerse de acuerdo en la gramtica a utilizar y esta decisin est influenciada por criterios lingsticos y computacionales, por ejemplo algunos sistemas de anlisis usan

gramticas lxico-funcionales. Pero en general el anlisis de gramticas de este tipo es un NP-completo. El "Head-driven phrase structure grammar" es otro formalismo que ha sido popular en la comunidad, pero los esfuerzos en investigacin se han centrado en algoritmos menos complejos como el de Penn Treebank. El anlisis ligero "Shallow parsing" se encarga slo de encontrar los componentes principales de la frase como nombres o verbos. Otra estrategia popular para evitar la controversia lingstica es la gramtica de dependencias. La mayora de los analizadores modernos son al menos en parte estadsticos, esto quiere decir que se basan en unos datos de entrenamiento que han sido analizados a mano. Este enfoque permite al sistema reunir informacin sobre la frecuencia con que ocurren ciertas construcciones en un contexto especfico. Algunos de estos enfoques han incluido gramticas libres de contexto probabilsticas, sistemas de mxima entropa y redes neuronales. Los sistemas ms exitosos usan estadsticas lxicas, es decir obtienen la categora gramatical de las palabras, estos sistemas son vulnerables debido a que terminan por tener una cantidad excesiva de parmetros y finalmente requieren simplificaciones. Los algoritmos de anlisis de lenguaje natural no se pueden basar en gramticas que tengan unas buenas caractersticas como se hace con las gramticas diseadas, por ejemplo para los lenguajes de programacin. Algunos formalismos gramaticales son muy difciles de analizar computacionalmente, por lo que, en general se usa una aproximacin libre de contexto incluso si la estructura en s no es libre de contexto para obtener una primera simplificacin. Los algoritmos que usan gramticas libres de contexto se suelen basar en alguna variante del algoritmo Cocke-Younger-Kasami (CYK) y heurstica para la poda de anlisis infructuosos. En todo caso algunos enfoques sacrifican la velocidad por la precisin usando, por ejemplo, versiones lineales del algoritmo "shift-reduce". Enfoques recientemente desarrollados utilizan un algoritmo que genera de mltiples anlisis y otro que escoge la mejor opcin.

[editar] Lenguajes de programacin


El uso ms comn de los analizadores sintcticos es como parte de la fase de anlisis de los compiladores. De modo que tienen que analizar el cdigo fuente del lenguaje. Los lenguajes de programacin tienden a basarse en gramticas libres de contexto, debido a que se pueden escribir analizadores rpidos y eficientes para stas. Las gramticas libres de contexto tienen una expresividad limitada y slo pueden expresar un conjunto limitado de lenguajes. Informalmente la razn de esto es que la memoria de un lenguaje de este tipo es limitada, la gramtica no puede recordar la presencia de una construccin en una entrada arbitrariamente larga y esto es necesario en un lenguaje en el que por ejemplo una variable debe ser declarada antes de que pueda ser referenciada. Las gramticas ms complejas no pueden ser analizadas de forma eficiente. Por estas razones es comn crear un analizador permisivo para una gramtica libre de contexto que acepta un superconjunto del lenguaje (acepta algunas

construcciones invlidas), despus del anlisis inicial las construcciones incorrectas pueden ser filtradas. Normalmente es fcil definir una gramtica libre de contexto que acepte todas las construcciones de un lenguaje pero por el contrario es prcticamente imposible construir una gramtica libre de contexto que admita solo las construcciones deseadas. En cualquier caso la mayora de analizadores no son construidos a mano sino usando generadores automticos.

[editar] Visin general del proceso


El siguiente caso demuestra un caso comn de anlisis de un lenguaje de programacin con dos niveles de gramtica, lxica y sintctica. El primer estado es la generacin de tokens o anlisis lxico, en este proceso la cadena de entrada se parte en smbolos con significado definidos por una gramtica de expresiones regulares, por ejemplo un programa calculadora con la siguiente entrada: "12*(3+4)^2", la dividira en los siguientes tokens 12, *, (, 3, +, 4, ), ^ y 2, cada uno de estos smbolos tiene un significado en el contexto de la expresin aritmtica. El analizador contendr reglas para indicar que los smbolos *, +, ^, ( y ) indican el comienzo de un nuevo token, de modo que otros tokens que no tendran sentido como 12 o 13 no se generarn. El siguiente estado es el anlisis sintctico lo que significa comprobar que los tokens forman una expresin vlida, esto se hace usualmente usando una gramtica libre de contexto que define recursivamente componentes que pueden aparecer en una expresin y el orden en que estos deben aparecer. Las reglas que definen un lenguaje de programacin no siempre se pueden expresar usando nicamente una gramtica libre de contexto, por ejemplo la validacin de tipos y la declaracin correcta de identificadores. Estas reglas pueden expresarse formalmente usando gramticas de atributos. La fase final es el anlisis semntico, que trabaja en las implicaciones de la expresin ya validada y realiza las actuaciones pertinentes. En el caso de la calculadora, la accin es evaluar la expresin. Un compilador por el contrario generar cdigo. Las gramticas de atributos pueden ser usadas tambin para definir estas acciones.

[editar] Clasificacin
La tarea esencial de un analizador es determinar si una determinada entrada puede ser derivada desde el smbolo inicial, usando las reglas de una gramtica formal, y como hacer esto, existen esencialmente dos formas:

Analizador sintctico descendente (Top-Down-Parser): ..un analizador puede empezar con el smbolo inicial e intentar transformarlo en la entrada, intuitivamente esto sera ir dividiendo la entrada progresivamente en partes cada vez ms pequeas, de esta forma funcionan los analizadores LL, un ejemplo es el javaCC. Analizador sintctico ascendente (Bottom-Up-Parser): un analizador puede empezar con la entrada e intentar llegar hasta el smbolo inicial, intuitivamente

el analizador intenta encontrar los smbolos ms pequeos y progresivamente construir la jerarqua de smbolos hasta el inicial, los analizadores LR funcionan as y un ejemplo es el Yacc. Otros tipos de analizadores son:

Analizador sintctico descendente recursivo Chart parser Left corner parser Analizador sintctico LR

Captulo 5. Anlisis semntico


Se compone de un conjunto de rutinas independientes, llamadas por los analizadores morfolgico y sintctico. El anlisis semntico utiliza como entrada el rbol sintctico detectado por el anlisis sintctico para comprobar restricciones de tipo y otras limitaciones semnticas y preparar la generacin de cdigo. En compiladores de un solo paso, las llamadas a las rutinas semnticas se realizan directamente desde el analizador sintctico y son dichas rutinas las que llaman al generador de cdigo. El instrumento ms utilizado para conseguirlo es la gramtica de atributos. En compiladores de dos o ms pasos, el anlisis semntico se realiza independientemente de la generacin de cdigo, pasndose informacin a travs de un archivo intermedio, que normalmente contiene informacin sobre el rbol sintctico en forma linealizada (para facilitar su manejo y hacer posible su almacenamiento en memoria auxiliar). En cualquier caso, las rutinas semnticas suelen hacer uso de una pila (la pila semntica) que contiene la informacin semntica asociada a los operandos (y a veces a los operadores) en forma de registros semnticos.

Propagacin de atributos
Sea la expresin
int a,b,c; a/(b+c^2)

El rbol sintctico es:


/ --------| | a + --------| | b ^ --------| | c 2

De la instruccin declarativa, la tabla de smbolos y el analizador morfolgico obtenemos los atributos de los operandos:
/ --------| | a + int ---------

| b int

| ^ --------| | c 2 int int

Propagando los atributos obtenemos:


/ int --------| | a + int int --------| | b ^ int int --------| | c 2 int int

Si la expresin hubiera sido


a/(b+c^-2)

El rbol sintctico sera el mismo, sustituyendo 2 por -2. Sin embargo, la propagacin de atributos sera diferente:
/ real --------| | a + real int --------| | b ^ real int --------| | c -2 int int

En algn caso podra llegar a producirse error (p.e. si / representara slo la divisin entera). Si la expresin hubiera sido
int a,b,c,d; a/(b+c^d)

El rbol sintctico sera el mismo, sustituyendo 2 por d. Sin embargo, la propagacin de atributos sera incompleta:
/ {int,real} --------| | a + {int,real} int --------| |

b int

^ {int,real} --------| | c d int int

El analizador semntico podra reducir los tipos inseguros al tipo mximo (real) o utilizar un tipo interno nuevo (ej. arit={int,real}, una unin). Lo anterior es un ejemplo de propagacin bottom-up. La propagacin top-down tambin es posible: lo que se transmite son las restricciones y los tipos de las hojas sirven de comprobacin. Por ejemplo, si la divisin slo puede ser entera, transmitimos hacia abajo la restriccin de que sus operandos slo pueden ser enteros. Al llegar a d, esa restriccin se convierte en que d debe ser positiva. Si no lo es, error. La implantacin de todos los casos posibles de operacin con tipos mixtos podra ser excesivamente cara. En su lugar, se parte de operaciones relativamente simples (ej. int+int, real+real) y no se implementan las restantes (ej. int+real, real+int), aadiendo en su lugar operaciones mondicas de cambio de tipo (ej. int->real). Esta decisin puede introducir ambigedades. Por ejemplo, sea el programa
real a; int b,c; a:=b+c

El rbol sintctico es:


:= --------| | a + real --------| | b c int int

Existen dos conversiones posibles:


:= real --------| | a + real real --------| | b c int int := real --------| | a + int real --------| | b c int int

El problema es que no tenemos garanta de que los dos procedimientos sean equivalentes. El segundo puede dar overflow, el primero prdida de precisin. La definicin del lenguaje debe especificar estos casos. Las transformaciones posibles se pueden representar mediante un grafo cuyos nodos son los tipos de datos y cada arco indica una transformacin. Dado un operando de tipo A

que se desea convertir al tipo B, se trata de encontrar una cadena de arcos que pase de A a B en el grafo anterior. Podra haber varios grafos, cada uno de los cuales se aplicar en diferentes condiciones, por ejemplo, uno para las asignaciones, otro para las expresiones, etc.

Gramtica de atributos
Es una extensin de la notacin de Backus que consiste en introducir en las reglas sintcticas ciertos smbolos adicionales no sintcticos (smbolos de accin) que, en definitiva, se reducen a llamadas implcitas o explcitas a rutinas semnticas. Por ejemplo: sea la gramtica simplificada que analiza las instrucciones de asignacin:
<AsSt> ::= id #PId := <Exp> #RAs <Exp> ::= <Exp> + <T> #RS | <T> <T> ::= id #PId | Ct #PCt

Se observar que hemos hecho uso de cuatro smbolos de accin:


#PId: PUSH a la pila semntica el registro asociado al identificador. #PCt: PUSH a la pila semntica el registro asociado a la constante. #RS: Realizar suma: POP los dos registros superiores de la pila; comprobar que es posible sumarlos; realizar la suma (mediante una llamada al generador de cdigo) o generar representacin intermedia (si es una compilacin en dos o ms pasos); PUSH registro semntico del resultado en la pila semntica. #RAs: Realizar asignacin: POP los dos registros superiores de la pila; comprobar que es posible realizar la asignacin; realizarla (mediante una llamada al generador de cdigo) o generar representacin intermedia (si es una compilacin en dos o ms pasos).

En los analizadores sintcticos top-down basados en gramticas LL(1), la introduccin de los smbolos de accin en las rutinas correspondientes es trivial. En los analizadores bottom-up basados en gramticas SLR(1) es ms delicado, pues los estados del anlisis se mueven simultneamente sobre varias reglas. Slo en el momento de realizar una reduccin sabemos exactamente dnde estamos. Por ello, se suele introducir la restriccin de que los smbolos de accin deben estar situados nicamente al final de una regla. Cuando no se cumple esto (como en el ejemplo anterior) es trivial conseguirlo, introduciendo smbolos no terminales adicionales. Algunos generadores de analizadores sintcticos (como YACC) lo realizan automticamente. En nuestro ejemplo, quedara:
<AsSt> <ASPI> <Exp> <T> ::= ::= ::= ::= <ASPI> := <Exp> #RAs id #PId <Exp> + <T> #RS | <T> id #PId | Ct #PCt

Poner un ejemplo del anlisis sintctico-semntico bottom-up de la instruccin A := B + 1. Otro ejemplo: instrucciones condicionales con las reglas

<Instr> ::= If <Expr> #S2 then <Instr> #S1 | If <Expr> #S2 then <Instr> else #S3 <Instr> #S1

Ms adelante se ver cules son las tres acciones semnticas. Para que todas queden al final de una regla, basta cambiar estas reglas por:
<Instr> ::= If <Expr1> then <Instr> #S1 | If <Expr1> then <Instr> <Else> <Instr> #S1 <Expr1> ::= <Expr> #S2 <Else> ::= else #S3

Generacin de representaciones intermedias


Existen dos representaciones intermedias principales:

Notacin sufija Cudruplas

Los operadores didicos (o binarios) pueden especificarse mediante tres notaciones principales:

Prefija: el operador didico es analizado antes que sus operandos. Infija: el operador didico es analizado entre sus operandos. Sufija: el operador didico es analizado despus que sus operandos.

En los lenguajes de programacin clsicos, los operadores didicos se representan usualmente en notacin infija. La notacin prefija permite al operador influir sobre la manera en que se procesan sus operandos, pero a cambio suele exigir mucha ms memoria. La sufija no permite esa influencia, pero es ptima en proceso de memoria y permite eliminar el procesado de los parntesis. Los operadores mondicos slo pueden presentarse en notacin prefija o sufija. Adems, un rbol sintctico puede representarse en forma de tuplas de n elementos, de la forma (operador, operando-1, ..., operando-k, nombre). Las tuplas pueden tener longitud variable o fija (con operandos nulos). Las ms tpicas son las cudruplas, aunque stas pueden representarse tambin en forma de tripletes.

Notacin sufija
Llamada tambin postfija o polaca inversa, se usa para representar expresiones sin necesidad de parntesis. Ejemplos:
a*b a*(b+c/d) a*b+c*d ab* abcd/+* ab*cd*+

Los identificadores aparecen en el mismo orden. Los operadores en el de evaluacin (de izquierda a derecha).

Problema: operadores mondicos (unarios). O bien se transforman en didicos (binarios) o se cambia el smbolo. Ejemplo: -a se convierte en 0-a o en @a
a*(-b+c/d) ab@cd/+*

Existen dos problemas principales:


Construir la notacin sufija a partir de la infija. Analizar la notacin sufija en el segundo paso de la compilacin.

Rutina semntica para transformar de infijo a sufijo


Si el analizador sintctico es bottom-up, hacemos la siguiente suposicin: "Cuando aparece un no terminal V en el asidero, la cadena polaca correspondiente a la subcadena que se redujo a V ya ha sido generada". Se utiliza una pila donde se genera la salida, inicialmente vaca. Las acciones semnticas asociadas a las reglas son:
E E E T T T F F F ::= ::= ::= ::= ::= ::= ::= ::= ::= E + E T T * T / F i (E) - F T T F F Push + Push Push * Push / Push i Push @

Anlisis de la notacin sufija


La gramtica completa que permite analizar la notacin sufija es:
<Operando> ::= id | cte | <Operando> <Operando> <Operador didico> | <Operando> <Operador mondico> <Operador didico> ::= + | - | * | / | ... <Operador mondico> ::= @ | ...

Algoritmo de evaluacin de una expresin en notacin sufija que utiliza una pila:

Si el prximo smbolo es un identificador, se pasa a la pila. Corresponde a la aplicacin de la regla


<Operando> ::= id

Si el prximo smbolo es una constante, se pasa a la pila. Corresponde a la aplicacin de la regla


<Operando> ::= cte

Si el prximo smbolo es un operador didico, se aplica el operador a los dos operandos situados en lo alto de la pila y se sustituyen stos por el resultado de la operacin. Corresponde a la aplicacin de la regla

<Operando> ::= <Operando> <Operando> <Operador didico>

Si el prximo smbolo es un operador mondico, se aplica el operador al operando situado en lo alto de la pila y se sustituye ste por el resultado de la operacin. Corresponde a la aplicacin de la regla
<Operando> ::= <Operando> <Operador mondico>

Ejemplo: calcular ab@cd/+*.

Extensin de la notacin sufija a otros operadores

La asignacin, teniendo en cuenta que podemos no querer valor resultante. Adems, no interesa tener en la pila el valor del identificador izquierdo, sino su direccin.
a:=b*c+d GOTO L abc*d+:= L TR

La transferencia (GOTO). La instruccin condicional


if p then inst1 else inst2

se convierte en
p L1 TRZ inst1 L2 TR inst2 L1: L2:

Subndices:
a[exp1; exp2; ...; expn]

se convierte en
a exp1 exp2 ... expn SUBIN-n

Cudruplas
Una operacin didica se puede representar mediante la cudrupla
(<Operador>, <Operando1>, <Operando2>, <Resultado>)

Ejemplo:
(*,A,B,T)

Una expresin se puede representar mediante un conjunto de cudruplas. Ejemplo: la expresin a*b+c*d equivale a:
(*,a,b,t1) (*,c,d,t2) (+,t1,t2,t3)

Ejemplo: la expresin c:=a[i;b[j]] equivale a:


(*,i,d1,t1)

(+,t1,b[j],t2) (:=,a[t2],,c)

Tripletes
No se pone el resultado, se sustituye por referencias a tripletes. Por ejemplo: la expresin a*b+c*d equivale a:
(1) (*,a,b) (2) (*,c,d) (3) (+,(1),(2))

mientras que a*b+1 equivale a:


(1) (*,a,b) (2) (*,(1),1)

Tripletes indirectos: se numeran arbitrariamente los tripletes y se da el orden de ejecucin. Ejemplo, sean las instrucciones:
a := b*c b := b*c

Equivalen a los tripletes


(1) (*,b,c) (2) (:=,(1),a) (3) (:=,(1),b)

y el orden de ejecucin es (1),(2),(1),(3). Esta forma es til para preparar la optimizacin de cdigo. Si hay que alterar el orden de las operaciones o eliminar alguna, es ms fcil hacerlo ah.

Generacin automtica de cudruplas


En un anlisis bottom-up, asociamos a cada smbolo no terminal una informacin semntica, y a cada regla de produccin una accin semntica. Ejemplo, sea la gramtica
E E E T T T F F F ::= ::= ::= ::= ::= ::= ::= ::= ::= E + E T T * T / F i (E) -F T T F F

La regla F::=i asocia a F como informacin semntica el identificador concreto. La regla F::=(E) asocia a F como informacin semntica la informacin semntica asociada a E. La regla U::=V asocia a U como informacin semntica la informacin semntica asociada a V. La regla U::=VoW analiza la compatibilidad de los operandos, crea la cudrupla (o,Sem(V),Sem(W),Ti) y asocia a U la informacin semntica Ti.

La regla U::=oV crea la cudrupla (o,Sem(V),,Ti) y asocia a U la informacin semntica Ti. La informacin semntica se suele almacenar en otra pila paralela. Ejemplo: anlisis de a*(b+c)
Pila ----------| |a |F(a) |T(a) |T(a)* |T(a)*( |T(a)*(b |T(a)*(F(b) |T(a)*(T(b) |T(a)*(E(b) |T(a)*(E(b)+ |T(a)*(E(b)+c |T(a)*(E(b)+F(c) |T(a)*(E(b)+T(c) |T(a)*(E(b)+T(c) |T(a)*(E(T1) |T(a)*(E(T1)) |T(a)*F(T1) |T(T2) |E(T2) Entrada ------------a*(b+c)| *(b+c)| *(b+c)| *(b+c)| (b+c)| b+c)| +c)| +c)| +c)| +c)| c)| )| )| )| )| )| | | | | Regla Cudrupla ------------ --------F::=i T::=F

F::=i T::=F E::=T F::=i T::=F E::=E+T E::=E+T F::=(E) T::=T*F E::=T

(+,b,c,T1) (*,a,T1,T2)

Ejemplo de generacin de cudruplas en anlisis top-down


unsigned int E (char *cadena, unsigned int i) { if (i<0) return i; switch (cadena[i]) { case 'i': push (id); i++; i = V (cadena, i); break; case '(': i++; i = E (cadena, i); i = C (cadena, i); i = V (cadena, i); break; default: return -1; } return i; } unsigned int V (char *cadena, unsigned int i) { unsigned int j; if (i<0) return i; switch (cadena[i]) { case '*': case '/':

j = i; i++; i = T (cadena, i); cuad (cadena[j], pop(), pop(), gen(Ti)); push (Ti); i = X (cadena, i); break; case '+': case '-': j = i; i++; i = E (cadena, i); cuad (cadena[j], pop(), pop(), gen(Ti)); push (Ti); break; } return i; } unsigned int X (char *cadena, unsigned int i) { unsigned int j; if (i<0) return i; switch (cadena[i]) { case '+': case '-': j = i; i++; i = E (cadena, i); cuad (cadena[j], pop(), pop(), gen(Ti)); push (Ti); break; } return i; } unsigned int T (char *cadena, unsigned int i) { if (i<0) return i; switch (cadena[i]) { case 'i': push (id); i++; i = U (cadena, i); break; case '(': i++; i = E (cadena, i); i = C (cadena, i); i = U (cadena, i); break; default: return -2; } return i; } unsigned int U (char *cadena, unsigned int i) { if (i<0) return i; unsigned int j; switch (cadena[i]) {

case '*': case '/': j = i; i++; i = T (cadena, i); cuad (cadena[j], pop(), pop(), gen(Ti)); push (Ti); break; } return i; } unsigned int F (char *cadena, unsigned int i) { if (i<0) return i; switch (cadena[i]) { case 'i': push (id); i++; break; case '(': i++; i = E (cadena, i); i = C (cadena, i); break; default: return -3; } return i; } unsigned int C (char *cadena, unsigned int i) { if (i<0) return i; switch (cadena[i]) { case ')': i++; break; default: return -4; } return i; }

Semntica de instrucciones condicionales


Sean las reglas
<Instr> ::= If <Expr> S2 then <Instr> S1 | If <Expr> S2 then <Instr> else S3 <Instr> S1

Una secuencia de cudruplas equivalente a "If E1 then I1 else I2"


(p-1) (p) (q) (q+1) (r) (?,?,?,t1) (TRZ,(q+1),t1,) ... (TR,(r),,) ... Anlisis de E1 S2: Crear cudrupla Anlisis de I1 S3: Crear cudrupla Poner (cl.sig.) Push (q) Anlisis de I2 S1: Poner (cl.sig.) (p) | Push p (q) en top | Pop en top | Pop

Una secuencia de cudruplas equivalente a "If E1 then I1"


(p-1) (p) (r) (?,?,?,t1) (TRZ,(r),t1,) ... Anlisis de E1 S2: Crear cudrupla (p) | Push p Anlisis de I1 S1: Poner (cl.sig.) en top | Pop

Al generar la cudrupla (p) no conocemos el valor de (q). Guardamos en una pila el nmero de la cudrupla asociada y lo rellenamos ms tarde, como indican los ejemplos.

Semntica de etiquetas y GOTO


Suponemos que las etiquetas aparecen en la tabla de smbolos con tres valores asociados: (tipo=etiqueta, bit=declarada/no declarada, nmero de cudrupla). Sea la regla
<Instr> ::= id : <Instr>

Semntica asociada:
{ Buscar id en la tabla de smbolos; if (no est) Insertar id,valor=(etiqueta, declarada, cudrupla siguiente); else { if (tipo==etiqueta && bit==no declarada) { i=nmero de cudrupla; while (i) { j=cudrupla[i][2]; cudrupla[i][2]=cudrupla siguiente; i=j; } Cambiar valor a (etiqueta, declarada, cudrupla siguiente); } else error(); } }

Sea la regla
<Instr> ::= GOTO id

Semntica asociada:
{ Buscar id en la tabla de smbolos; if (no est) { Insertar id,valor=(etiqueta, no declarada, cudr.siguiente); Generar cudrupla (TR,,,); } else { if (tipo==etiqueta) { if (bit==declarada) Generar cudrupla (TR,nmero de cudrupla,,); else if (bit==no declarada) {

i=nmero de cudrupla; Cambiar valor a (etiqueta, no declarada, cudr.siguiente); Generar cudrupla (TR,i,,); } } else error(); } }

Si se permiten etiquetas locales a bloques, podemos encontrar el siguiente caso:


L: ... { ... GOTO L; ...

Tenemos ambigedad: GOTO L puede ir a la etiqueta externa (ya definida o no) o a una etiqueta local al bloque posterior a la instruccin. Tenemos tres posibilidades:

Un compilador en dos pasos. Forzar declaraciones de etiquetas. Tratar L en el bloque como si fuera local. Si al final del bloque descubrimos que no ha sido declarada, tratarla como si fuera global. La lista de referencias debera fundirse con la de L global (si L global no ha sido definida an) o rellenarse con el valor de L (si ya ha sido definida). Si L global no existe, debe crearse, y pasarle la lista de referencias de L local.

Semntica de bloques
Sean las reglas
<Instr> ::= do <id> := <Expr> S1 , <Expr> S2 <CD1> <LInstr> end S5 <CD1> ::= , <Expr> S3 | ^ S4

Semntica asociada al anlisis de "do x:=n1,n2,n3; I1; I2; end":


S1:
Generar cudruplas asociadas a instruccin de asignacin x:=n1. Guardar i=nmero de cudrupla siguiente.

S2:
Guardar j=nmero de cudrupla siguiente. Generar cudrupla (TRG,,x,(n2)). Generar cudrupla (TR,,,).

S3:
Generar cudrupla (+,x,(n3),x). Generar cudrupla (TR,(i),,). Hacer cudrupla[j+1][2]=cudrupla siguiente.

S5:
Generar cudrupla (TR,(j+2),,). Hacer cudrupla[j][2]=cudrupla siguiente.

Adems, S4:
Generar cudrupla (:=,x,1,x). Generar cudrupla (TR,(i),,). Hacer cudrupla[j+1][2]=cudrupla siguiente.

Evaluacin ptima de las expresiones booleanas


Las operaciones booleanas usualmente se definen as:
O | T F --|----T | T T F | T F Y | T F --|----T | T F F | F F NO| T F --|----| F T

y la sintaxis adecuada para que la precedencia sea: NO, Y, O. Sin embargo, es posible simplificarlas considerablemente. Basta fijarse en la expresin
a Y (b O NO c)

Si a es falso, no hace falta calcular el parntesis, sabemos que el resultado es falso. Por tanto, redefiniremos la sintaxis y la semntica as:
<ZB> <EB> <TB> <FB> ::= ::= ::= ::= <EB> <TB> O <EB> | <TB> <FB> Y <TB> | <FB> i | ( <EB> ) | NO <FB>

a O b <=> if (a) TRUE else b; a Y b <=> if (a) b else FALSE; NO a <=> if (a) FALSE else TRUE;

Anlisis top-down
El programa queda:
void ZB (char *cadena) { int FL=0, TL=0, i; cuad (":=", "T", NULL, x); i = EB (cadena, 0, &FL, &TL); FILL (FL); cuad (":=", "F", 0, x); FILL (TL); } unsigned int EB (char *cadena, unsigned int i, int *FL, int *TL) { int tl=0; i = TB (cadena, i, FL, &tl); MERGE (TL, tl); if (i<strlen(cadena)) switch (cadena[i]) { case 'O': i++; FILL (*FL); *FL = 0; i = EB (cadena, i, FL, TL); break;

default: break; } return i; } unsigned int TB (char *cadena, unsigned int i, int *FL, int *TL) { int fl=0; i = FB (cadena, i, &fl, TL); MERGE (FL, fl); if (i<strlen(cadena)) switch (cadena[i]) { case 'Y': i++; FILL (*TL); *TL = 0; i = TB (cadena, i, FL, TL); break; default: break; } return i; } unsigned int FB (char *cadena, unsigned int i, int *FL, int *TL) { if (i<strlen(cadena)) switch (cadena[i]) { case 'i': i++; *TL = cudrupla siguiente; *FL = - cudrupla siguiente; cuad (TRB, id, 0, 0); break; case '(': i++; i = EB (cadena, i, FL, TL); i = C (cadena, i); break; case 'NO': i++; i = FB (cadena, i, FL, TL); *FL <-> *TL break; default: error (cadena, i); } else error (cadena, i); return i; } unsigned int C (char *cadena, unsigned int i) { if (i<strlen(cadena)) switch (cadena[i]) { case ')': i++; break; default: error (cadena, i); } else error (cadena, i); return i; }

void FILL (int lista) { int i,j,n; n = lista; while (n) { if (n>0) i=2; else i=3; j = abs (n); n = cuad[j][i]; cuad[j][i] = cudrupla siguiente; } } void MERGE (int *uno, int dos) { int i,k; if (*uno==0) *uno = dos; else { k = *uno; for (;;) { if (k>0) i=2; else i=3; k = abs (k); if (cuad[k][i]==0) break; k = quad[k][i]; } cuad[k][i] = dos; } }

Analicemos "a O b O NO c"


ZB ("a O b O NO c") cuad (":=", "T", NULL, "X"); i = EB ("a O b O NO c", 0, &FL, &TL); i = TB ("a O b O NO c", 0, FL, &tl); i = FB ("a O b O NO c", 0, &fl, TL); case id: i=1; *TL = 1; fl = -1; cuad (TRB, a, 0, 0); MERGE (FL, fl); FL = -1; MERGE (TL, tl); TL = 1; case 'O': i=2; FILL (FL); n = -1; while (n) { i = 3; j = 1; (abs(FL)) n = 0; (cuad[1][3]) cuad[1][3] = 2; } FL = 0; i = EB ("a O b O NO c", 2, FL, TL); i = TB ("a O b O NO c", 2, FL, &tl); i = FB ("a O b O NO c", 2, &fl, TL); case 'i': i=3; *TL = 2;

*fl = -2; cuad (TRB, b, 0, 0); MERGE (FL, fl); FL = -2; MERGE (TL, tl); k = 1; for (;;) { i = 2; k = 1; } cuad[1][2] = 2; case 'O': i=4; FILL (FL); n = -2; while (n) { i = 3; j = 2; (abs (n)) n = 0; (cuad[2][3]) cuad[2][3] = 3; } FL = 0; i = EB ("a O b O NO c", 4, FL, TL); i = TB ("a O b O NO c", 4, FL, &tl); i = FB ("a O b O NO c", 4, &fl, TL); case 'NO': i=5; i = FB ("a O b O NO c", 5, FL, TL); case 'i': i=6; *TL = 3; *FL = -3; cuad (TRB, c, 0, 0); FL <-> TL MERGE (FL, fl); FL = 3; MERGE (TL, tl); k = 1; for (;;) { i = 2; k = 1; (abs (k)) k = 2; (cuad[1][2]) } cuad[2][2] = -3; FILL (FL); cuad[3][2] = 4; cuad (":=", "F", 0, "X"); FILL (TL); cuad[1][2] = 5; cuad[2][2] = 5; cuad[3][3] = 5;

Cudruplas obtenidas:
0: 1: 2: 3: 4: 5: (":=", "T",, x); ("TRB", a, 5, 2); ("TRB", b, 5, 3); ("TRB", c, 4, 5); (":=", "F",, x);

Compilador
De Wikipedia, la enciclopedia libre Saltar a: navegacin, bsqueda Compilacin redirige aqu. Para otras acepciones, vase recopilacin.

Diagrama a bloques de la operacin de un buen compilador.

Un compilador es un programa informtico que traduce un programa escrito en un lenguaje de programacin a otro lenguaje de programacin, generando un programa equivalente que la mquina ser capaz de interpretar. Usualmente el segundo lenguaje es lenguaje de mquina, pero tambin puede ser un cdigo intermedio (bytecode), o simplemente texto. Este proceso de traduccin se conoce como compilacin.1 Un compilador es un programa que permite traducir el cdigo fuente de un programa en lenguaje de alto nivel, a otro lenguaje de nivel inferior (tpicamente lenguaje de mquina). De esta manera un programador puede disear un programa en un lenguaje mucho ms cercano a como piensa un ser humano, para luego compilarlo a un programa ms manejable por una computadora.

Contenido
[ocultar]

1 Partes de un compilador 2 Historia 3 Tipos de compiladores 4 Proceso de compilacin 5 Etapas del proceso o 5.1 Fase de anlisis 5.1.1 Anlisis lxico 5.1.2 Anlisis sintctico 5.1.3 Anlisis semntico o 5.2 Fase de sntesis 5.2.1 Generacin de cdigo intermedio o 5.3 Optimizacin de cdigo 6 Estructura de datos principales o 6.1 Componentes lxicos o tokens o 6.2 rbol sintctico o 6.3 Tabla de smbolos o 6.4 Tabla de literales o 6.5 Cdigo intermedio o 6.6 Archivos temporales 7 Vase tambin 8 Referencias 9 Enlaces externos

[editar] Partes de un compilador


La construccin de un compilador involucra la divisin del proceso en una serie de fases que variar con su complejidad. Generalmente estas fases se agrupan en dos tareas: el anlisis del programa fuente y la sntesis del programa objeto.

Anlisis: Se trata de la comprobacin de la correccin del programa fuente, e incluye las fases correspondientes al Anlisis Lxico (que consiste en la descomposicin del programa fuente en componentes lxicos), Anlisis Sintctico (agrupacin de los componentes lxicos en frases gramaticales ) y Anlisis Semntico (comprobacin de la validez semntica de las sentencias aceptadas en la fase de Anlisis Sintctico). Sntesis: Su objetivo es la generacin de la salida expresada en el lenguaje objeto y suele estar formado por una o varias combinaciones de fases de Generacin de Cdigo (normalmente se trata de cdigo intermedio o de cdigo objeto) y de Optimizacin de Cdigo (en las que se busca obtener un cdigo lo ms eficiente posible).

Alternativamente, las fases descritas para las tareas de anlisis y sntesis se pueden agrupar en Front-end y Back-end:

Front-end: es la parte que analiza el cdigo fuente, comprueba su validez, genera el rbol de derivacin y rellena los valores de la tabla de smbolos. Esta parte suele ser independiente de la plataforma o sistema para el cual se vaya a compilar, y est compuesta por las fases comprendidas entre el Anlisis Lxico y la Generacin de Cdigo Intermedio. Back-end: es la parte que genera el cdigo mquina, especfico de una plataforma, a partir de los resultados de la fase de anlisis, realizada por el Front End.

Esta divisin permite que el mismo Back End se utilice para generar el cdigo mquina de varios lenguajes de programacin distintos y que el mismo Front End que sirve para analizar el cdigo fuente de un lenguaje de programacin concreto sirva para generar cdigo mquina en varias plataformas distintas. Suele incluir la generacin y optimizacin del cdigo dependiente de la mquina. El cdigo que genera el Back End normalmente no se puede ejecutar directamente, sino que necesita ser enlazado por un programa enlazador (linker)

[editar] Historia
En 1946 se desarroll la primera computadora digital. En un principio, estas mquinas ejecutaban instrucciones consistentes en cdigos numricos que sealaban a los circuitos de la mquina los estados correspondientes a cada operacin, lo que se denomin lenguaje mquina. Pronto los primeros usuarios de estos ordenadores descubrieron la ventaja de escribir sus programas mediante claves ms fciles de recordar que esos cdigos; al final, todas esas claves juntas se traducan manualmente a lenguaje mquina. Estas claves constituyen los llamados lenguajes ensambladores. Pese a todo, el lenguaje ensamblador segua siendo el de una mquina, pero ms fcil de manejar. Los trabajos de investigacin se orientaron hacia la creacin de un lenguaje que expresara las distintas acciones a realizar de una manera lo ms sencilla posible para una persona. El primer compilador fue escrito por Grace Hopper, en 1952 para el lenguaje de programacin A-0. En 1950 John Backus dirigi una investigacin en IBM sobre un lenguaje algebraico. En 1954 se empez a desarrollar un lenguaje que permita escribir frmulas matemticas de manera traducible por un ordenador; le llamaron FORTRAN (FORmulae TRANslator). Fue el primer lenguaje de alto nivel y se introdujo en 1957 para el uso de la computadora IBM modelo 704. Surgi as por primera vez el concepto de un traductor como un programa que traduca un lenguaje a otro lenguaje. En el caso particular de que el lenguaje a traducir es un lenguaje de alto nivel y el lenguaje traducido de bajo nivel, se emplea el trmino compilador. La tarea de realizar un compilador no fue fcil. El primer compilador de FORTRAN tard 18 aos-persona en realizarse y era muy sencillo. Este desarrollo de FORTRAN estaba muy influenciado por la mquina objeto en la que iba a ser implementado. Como un ejemplo de ello tenemos el hecho de que los espacios en blanco fuesen ignorados, debido a que el perifrico que se utilizaba como entrada de programas (una lectora de tarjetas perforadas) no contaba correctamente los espacios en blanco. El primer compilador autocontenido, es decir, capaz de compilar su propio cdigo fuente fue el creado para Lisp por Hart y Levin en el MIT en 1962. Desde 1970 se ha convertido en una prctica comn escribir el compilador en el mismo lenguaje que este compila, aunque Pascal y C han sido alternativas muy usadas. Crear un compilador autocontenido genera un problema llamado bootstrapping, es decir el primer compilador creado para un lenguaje tiene que o bien ser compilado por un

compilador escrito en otro lenguaje o bien compilado al ejecutar el compilador en un intrprete.

[editar] Tipos de compiladores


Esta taxonoma de los tipos de compiladores no es excluyente, por lo que puede haber compiladores que se adscriban a varias categoras:

Compiladores cruzados: generan cdigo para un sistema distinto del que estn funcionando. Compiladores optimizadores: realizan cambios en el cdigo para mejorar su eficiencia, pero manteniendo la funcionalidad del programa original. Compiladores de una sola pasada: generan el cdigo mquina a partir de una nica lectura del cdigo fuente. Compiladores de varias pasadas: necesitan leer el cdigo fuente varias veces antes de poder producir el cdigo mquina. Compiladores JIT (Just In Time): forman parte de un intrprete y compilan partes del cdigo segn se necesitan.

Pauta de creacin de un compilador: En las primeras pocas de la informtica, el software de los compiladores era considerado como uno de los ms complejos existentes. Los primeros compiladores se realizaron programndolos directamente en lenguaje mquina o en ensamblador. Una vez que se dispone de un compilador, se pueden escribir nuevas versiones del compilador (u otros compiladores distintos) en el lenguaje que compila ese compilador. Actualmente existen herramientas que facilitan la tarea de escribir compiladores intrpretes informticos. Estas herramientas permiten generar el esqueleto del analizador sintctico a partir de una definicin formal del lenguaje de partida, especificada normalmente mediante una gramtica formal y barata, dejando nicamente al programador del compilador la tarea de programar las acciones semnticas asociadas.

[editar] Proceso de compilacin


Es el proceso por el cual se traducen las instrucciones escritas en un determinado lenguaje de programacin a lenguaje mquina. Adems de un traductor, se pueden necesitar otros programas para crear un programa objeto ejecutable. Un programa fuente se puede dividir en mdulos almacenados en archivos distintos. La tarea de reunir el programa fuente a menudo se confa a un programa distinto, llamado preprocesador. El preprocesador tambin puede expandir abreviaturas, llamadas a macros, a proposiciones del lenguaje fuente. Normalmente la creacin de un programa ejecutable (un tpico.exe para Microsoft Windows o DOS) conlleva dos pasos. El primer paso se llama compilacin (propiamente dicho) y traduce el cdigo fuente escrito en un lenguaje de programacin almacenado en un archivo a cdigo en bajo nivel (normalmente en cdigo objeto, no directamente a lenguaje mquina). El segundo paso se llama enlazado en el cual se

enlaza el cdigo de bajo nivel generado de todos los ficheros y subprogramas que se han mandado compilar y se aade el cdigo de las funciones que hay en las bibliotecas del compilador para que el ejecutable pueda comunicarse directamente con el sistema operativo, traduciendo as finalmente el cdigo objeto a cdigo mquina, y generando un mdulo ejecutable. Estos dos pasos se pueden hacer por separado, almacenando el resultado de la fase de compilacin en archivos objetos (un tpico.obj para Microsoft Windows, DOS o para Unix); para enlazarlos en fases posteriores, o crear directamente el ejecutable; con lo que la fase de compilacin se almacena slo temporalmente. Un programa podra tener partes escritas en varios lenguajes (por ejemplo C, C++ y Asm), que se podran compilar de forma independiente y luego enlazar juntas para formar un nico mdulo ejecutable.

[editar] Etapas del proceso


El proceso de traduccin se compone internamente de varias etapas o fases, que realizan distintas operaciones lgicas. Es til pensar en estas fases como en piezas separadas dentro del traductor, y pueden en realidad escribirse como operaciones codificadas separadamente aunque en la prctica a menudo se integren juntas.

[editar] Fase de anlisis


[editar] Anlisis lxico Artculo principal: Analizador lxico.

El anlisis lxico constituye la primera fase, aqu se lee el programa fuente de izquierda a derecha y se agrupa en componentes lxicos (tokens), que son secuencias de caracteres que tienen un significado. Adems, todos los espacios en blanco, lneas en blanco, comentarios y dems informacin innecesaria se elimina del programa fuente. Tambin se comprueba que los smbolos del lenguaje (palabras clave, operadores, etc.) se han escrito correctamente. Como la tarea que realiza el analizador lxico es un caso especial de coincidencia de patrones, se necesitan los mtodos de especificacin y reconocimiento de patrones, se usan principalmente los autmatas finitos que acepten expresiones regulares. Sin embargo, un analizador lxico tambin es la parte del traductor que maneja la entrada del cdigo fuente, y puesto que esta entrada a menudo involucra un importante gasto de tiempo, el analizador lxico debe funcionar de manera tan eficiente como sea posible.
[editar] Anlisis sintctico Artculo principal: Analizador sintctico.

En esta fase los caracteres o componentes lxicos se agrupan jerrquicamente en frases gramaticales que el compilador utiliza para sintetizar la salida. Se comprueba si lo obtenido de la fase anterior es sintcticamente correcto (obedece a la gramtica del lenguaje). Por lo general, las frases gramaticales del programa fuente se representan mediante un rbol de anlisis sintctico.

La estructura jerrquica de un programa normalmente se expresa utilizando reglas recursivas. Por ejemplo, se pueden dar las siguientes reglas como parte de la definicin de expresiones:
1. Cualquier identificador es una expresin. 2. Cualquier nmero es una expresin. 3. Si expresin1 y expresin2 son expresiones, entonces tambin lo son: o expresin1 + expresin2 o expresin1 * expresin2 o ( expresin1 )

Las reglas 1 y 2 son reglas bsicas (no recursivas), en tanto que la regla 3 define expresiones en funcin de operadores aplicados a otras expresiones. La divisin entre anlisis lxico y anlisis sintctico es algo arbitraria. Un factor para determinar la divisin es si una construccin del lenguaje fuente es inherentemente recursiva o no. Las construcciones lxicas no requieren recursin, mientras que las construcciones sintcticas suelen requerirla. No se requiere recursin para reconocer los identificadores, que suelen ser cadenas de letras y dgitos que comienzan con una letra. Normalmente, se reconocen los identificadores por el simple examen del flujo de entrada, esperando hasta encontrar un carcter que no sea ni letra ni dgito, y agrupando despus todas las letras y dgitos encontrados hasta ese punto en un componente lxico llamado identificador. Por otra parte, esta clase de anlisis no es suficientemente poderoso para analizar expresiones o proposiciones. Por ejemplo, no podemos emparejar de manera apropiada los parntesis de las expresiones, o las palabras begin y end en proposiciones sin imponer alguna clase de estructura jerrquica o de anidamiento a la entrada.
[editar] Anlisis semntico

La fase de anlisis semntico revisa el programa fuente para tratar de encontrar errores semnticos y rene la informacin sobre los tipos para la fase posterior de generacin de cdigo. En ella se utiliza la estructura jerrquica determinada por la fase de anlisis sintctico para identificar los operadores y operandos de expresiones y proposiciones. Un componente importante del anlisis semntico es la verificacin de tipos. Aqu, el compilador verifica si cada operador tiene operandos permitidos por la especificacin del lenguaje fuente. Por ejemplo, las definiciones de muchos lenguajes de programacin requieren que el compilador indique un error cada vez que se use un nmero real como ndice de una matriz. Sin embargo, la especificacin del lenguaje puede imponer restricciones a los operandos, por ejemplo, cuando un operador aritmtico binario se aplica a un nmero entero y a un nmero real. Revisa que los arreglos tengan definido el tamao correcto.

[editar] Fase de sntesis


Consiste en generar el cdigo objeto equivalente al programa fuente. Slo se genera cdigo objeto cuando el programa fuente est libre de errores de anlisis, lo cual no quiere decir que el programa se ejecute correctamente, ya que un programa puede tener errores de concepto o expresiones mal calculadas. Por lo general el cdigo objeto es

cdigo de mquina relocalizable o cdigo ensamblador. Las posiciones de memoria se seleccionan para cada una de las variables usadas por el programa. Despus, cada una de las instrucciones intermedias se traduce a una secuencia de instrucciones de mquina que ejecuta la misma tarea. Un aspecto decisivo es la asignacin de variables a registros.
[editar] Generacin de cdigo intermedio

Despus de los anlisis sintctico y semntico, algunos compiladores generan una representacin intermedia explcita del programa fuente. Se puede considerar esta representacin intermedia como un programa para una mquina abstracta. Esta representacin intermedia debe tener dos propiedades importantes; debe ser fcil de producir y fcil de traducir al programa objeto. La representacin intermedia puede tener diversas formas. Existe una forma intermedia llamada cdigo de tres direcciones que es como el lenguaje ensamblador de una mquina en la que cada posicin de memoria puede actuar como un registro. El cdigo de tres direcciones consiste en una secuencia de instrucciones, cada una de las cuales tiene como mximo tres operandos. Esta representacin intermedia tiene varias propiedades:

Primera.- Cada instruccin de tres direcciones tiene a lo sumo un operador, adems de la asignacin, por tanto, cuando se generan estas instrucciones, el traductor tiene que decidir el orden en que deben efectuarse las operaciones. Segunda.- El traductor debe generar un nombre temporal para guardar los valores calculados por cada instruccin. Tercera.- Algunas instrucciones de tres direcciones tienen menos de tres operandos, por ejemplo, la asignacin.

[editar] Optimizacin de cdigo


La fase de optimizacin de cdigo consiste en mejorar el cdigo intermedio, de modo que resulte un cdigo mquina ms rpido de ejecutar. Esta fase de la etapa de sntesis es posible sobre todo si el traductor es un compilador (difcilmente un interprete puede optimizar el cdigo objeto). Hay mucha variacin en la cantidad de optimizacin de cdigo que ejecutan los distintos compiladores. En los que hacen mucha optimizacin, llamados compiladores optimizadores, una parte significativa del tiempo del compilador se ocupa en esta fase. Sin embargo, hay optimizaciones sencillas que mejoran sensiblemente el tiempo de ejecucin del programa objeto sin retardar demasiado la compilacin.

[editar] Estructura de datos principales


La interaccin entre los algoritmos utilizados por las fases del compilador y las estructuras de datos que soportan estas fases es, naturalmente, muy fuerte. El escritor del compilador se esfuerza por implementar estos algoritmos de una manera tan eficaz como sea posible, sin aumentar demasiado la complejidad. De manera ideal, un compilador debera poder compilar un programa en un tiempo proporcional al tamao del mismo.

[editar] Componentes lxicos o tokens


Cuando un analizador lxico rene los caracteres en un token, generalmente representa el token de manera simblica, es decir, como un valor de un tipo de datos enumerado que representa el conjunto de tokens del lenguaje fuente. En ocasiones tambin es necesario mantener la cadena de caracteres misma u otra informacin derivada de ella, tal como el nombre asociado con un token identificador o el valor de un token de nmero. En la mayora de los lenguajes el analizador lxico slo necesita generar un token a la vez. En este caso se puede utilizar una variable global simple para mantener la informacin del token. En otros casos (cuyo ejemplo ms notable es FORTRAN), puede ser necesario un arreglo (o vector) de tokens.

[editar] rbol sintctico


Si el analizador sintctico genera un rbol sintctico, por lo regular se construye como una estructura estndar basada en un puntero que se asigna de manera dinmica a medida que se efecta el anlisis sintctico. El rbol entero puede entonces conservarse como una variable simple que apunta al nodo raz. Cada nodo en la estructura es un registro cuyos campos representan la informacin recolectada tanto por el analizador sintctico como, posteriormente, por el analizador semntico. Por ejemplo, el tipo de datos de una expresin puede conservarse como un campo en el nodo del rbol sintctico para la expresin. En ocasiones, para ahorrar espacio, estos campos se asignan de manera dinmica, o se almacenan en otras estructuras de datos, tales como la tabla de smbolos, que permiten una asignacin y desasignacin selectivas. En realidad, cada nodo del rbol sintctico por s mismo puede requerir de atributos diferentes para ser almacenado, de acuerdo con la clase de estructura del lenguaje que represente. En este caso, cada nodo en el rbol sintctico puede estar representado por un registro variable, con cada clase de nodo conteniendo solamente la informacin necesaria para ese caso.

[editar] Tabla de smbolos


Esta estructura de datos mantiene la informacin asociada con los identificadores: funciones, variables, constantes y tipos de datos. La tabla de smbolos interacta con casi todas las fases del compilador: el analizador lxico, el analizador sintctico o el analizador semntico pueden introducir identificadores dentro de la tabla; el analizador semntico agregar tipos de datos y otra informacin; y las fases de optimizacin y generacin de cdigo utilizarn la informacin proporcionada por la tabla de smbolos para efectuar selecciones apropiadas de cdigo objeto. Puesto que la tabla de smbolos tendr solicitudes de acceso con tanta frecuencia, las operaciones de insercin, eliminacin y acceso necesitan ser eficientes, preferiblemente operaciones de tiempo constante. Una estructura de datos estndar para este propsito es la tabla de dispersin o de clculo de direccin, aunque tambin se pueden utilizar diversas estructuras de rbol. En ocasiones se utilizan varias tablas y se mantienen en una lista o pila.

[editar] Tabla de literales


La bsqueda y la insercin rpida son esenciales tambin para la tabla de literales, la cual almacena constantes y cadenas utilizadas en el programa. Sin embargo, una tabla de literales necesita impedir las eliminaciones porque sus datos se aplican globalmente al programa y una constante o cadena aparecer slo una vez en esta tabla. La tabla de literales es importante en la reduccin del tamao de un programa en la memoria al permitir la reutilizacin de constantes y cadenas. Tambin es necesaria para que el generador de cdigo construya direcciones simblicas para las literales y para introducir definiciones de datos en el archivo de cdigo objeto.

[editar] Cdigo intermedio


De acuerdo con la clase de cdigo intermedio (por ejemplo, cdigo de tres direcciones o cdigo P) y de las clases de optimizaciones realizadas, este cdigo puede conservarse como un arreglo de cadenas de texto, un archivo de texto temporal o bien una lista de estructuras ligadas. En los compiladores que realizan optimizaciones complejas debe ponerse particular atencin a la seleccin de representaciones que permitan una fcil reorganizacin.
Generacin de cdigo intermedio

Despus de los anlisis sintctico y semntico, algunos compiladores generan una representacin intermedia explcita del programa fuente. Se puede considerar esta representacin intermedia como un programa para una mquina abstracta. Esta representacin intermedia debe tener dos propiedades importantes; debe ser fcil de producir y fcil de traducir al programa objeto. La representacin intermedia puede tener diversas formas. Existe una forma intermedia llamada cdigo de tres direcciones, que es como el lenguaje ensamblador para una mquina en la que cada posicin de memoria puede actuar como un registro. El cdigo de tres direcciones consiste en una secuencia de instrucciones, cada una de las cuales tiene como mximo tres operandos. El programa fuente de (1) puede aparecer en cdigo de tres direcciones como
temp1 := entarea1(60) temp2 := id3 * temp1 (2) temp3 := id2 + temp2 id1 := temp3

Esta representacin intermedia tiene varias propiedades. Primera, cada instruccin de tres direcciones tiene a lo sumo un operador, adems de la asignacin. Por tanto, cuando se generan esas instrucciones el compilador tiene que decidir el orden en que deben efectuarse, las operaciones; la multiplicacin precede a la adicin al programa fuente de. Segunda, el compilador debe generar un nombre temporal para guardar los valores calculados por cada instruccin. Tercera, algunas instrucciones de tres direcciones tienen menos de tres operadores, por ejemplo la primera y la ltima instrucciones de.
Optimizacin de Cdigo

La fase de optimizacin de cdigo trata de mejorar el cdigo intermedio de modo que resulte un cdigo de mquina ms rpido de ejecutar. Algunas optimizaciones son triviales. Por ejemplo, un algoritmo natural genera el cdigo intermedio (2) utilizando una instruccin para cada operador de la representacin del rbol despus del anlisis semntico, aunque hay una forma mejor de realizar los mismos clculos usando las dos instrucciones
temp1 := id3 * 60.0 (3) id1 := id2 + temp1

Este sencillo algoritmo no tiene nada de malo, puesto que el problema se puede solucionar en la fase de optimizacin de cdigo. Esto es, el compilador puede deducir que la conversin de 60 de entero a real se puede hacer de una vez por todas en el momento de la compilacin, de modo que la operacin entreal se puede eliminar. Adems, temp3 se usa slo una vez, para transmitir su valor a id1. Entonces resulta seguro sustituir a id1 por temp3, a partir de lo cual la ltima proposicin de (2) no se necesita y se obtiene el cdigo de (3). Hay muchas variaciones en la cantidad de optimizacin de cdigo que ejecutan los distintos compiladores. En lo que hacen mucha optimizacin llamados compiladores optimizadores, una parte significativa del tiempo del compilador se ocupa en esta fase. Sin embargo, hay optimizaciones sencillas que mejoran sensiblemente el tiempo de ejecucin del programa objeto sin retardar demasiado la compilacin.

[editar] Archivos temporales


Al principio las computadoras no tenan la suficiente memoria para guardar un programa completo durante la compilacin. Este problema se resolvi mediante el uso de archivos temporales para mantener los productos de los pasos intermedios durante la traduccin o bien al compilar al vuelo, es decir, manteniendo slo la informacin suficiente de las partes anteriores del programa fuente que permita proceder a la traduccin. Las limitaciones de memoria son ahora un problema mucho menor, y es posible requerir que una unidad de compilacin entera se mantenga en memoria, en especial si se dispone de la compilacin por separado en el lenguaje. Con todo, los compiladores ocasionalmente encuentran til generar archivos intermedios durante alguna de las etapas del procesamiento. Algo tpico de stos es la necesidad de direcciones de correccin hacia atrs durante la generacin de cdigo

Captulo 7. Optimizacin de cdigo


La optimizacin de cdigo puede realizarse durante la propia generacin o como paso adicional, ya sea intercalado entre el anlisis semntico y la generacin de cdigo (se optimizan las cudruplas) o situado despus de sta (se optimiza a posteriori el cdigo generado). Hay teoremas (Aho, 1970) que demuestran que la optimizacin perfecta es indecidible. Por tanto, las optimizaciones de cdigo en realidad proporcionan mejoras, pero no aseguran el xito total. Clasificacin de optimizaciones: 1. Dependientes de la mquina. o Asignacin de registros (ver captulo anterior). o Instrucciones especiales ("idioms"). o Reordenacin del cdigo. 2. Independientes de la mquina. o Ejecucin en tiempo de compilacin. o Eliminacin de redundancias. o Cambio de orden. o Reduccin de frecuencia de ejecucin (invariancias). o Reduccin de fuerza. Optimizacin y depuracin suelen ser incompatibles. Por ejemplo, si se elimina totalmente una instruccin, puede ser imposible poner una parada en ella para depuracin. Ejemplo:
x = x;

Instrucciones especiales ("idioms")


ALgunas mquinas tienen instrucciones especiales que permiten acelerar ciertos procesos. Por ejemplo:

TRT en IBM 390 y XLAT en INTEL permiten realizar una codificacin en una sola instruccin mquina. MOV en IBM 370 permite copiar bloques de memoria de hasta 255 caracteres. REP en INTEL permite copiar, llenar o comparar bloques de memoria utilizando como registros ndice SI y DI. TEST en INTEL permite realizar fcilmente varias comparaciones booleanas. Ej: if (x&4 || x&8) ... se puede representar:
TEST X,12 JZ L ... L:

Reordenacin del cdigo


En muchas mquinas, la multiplicacin en punto fijo de dos operandos de longitud 1 da un operando de longitud 2, mientras la divisin necesita un operando de longitud 2 y otro de longitud 1 para dar un cociente y un resto de longitud 1. Reordenar las operaciones puede optimizar. Por ejemplo: sea la expresin a=b/c*d;
MOV XOR DIV MUL MOV AX,B DX,DX AX,C AX,D A,AX

Si la reordenamos as: a=b*d/c;, aprovechando que la multiplicacin y la divisin son asociativas, tenemos:
MOV MUL DIV MOV AX,B AX,D AX,C A,AX

Ahorramos una instruccin. Veamos otro ejemplo:


a=b/c; d=b%c;

Los dos cdigos siguientes son equivalentes. Puede tratarse como un caso particular del manejo de registros. La realizacin de la primera divisin debera guardar constancia de que DX contiene el resultado del resto.
MOV XOR DIV MOV MOV XOR DIV MOV AX,B DX,DX AX,C A,AX AX,B DX,DX AX,C D,DX MOV XOR DIV MOV MOV AX,B DX,DX AX,C A,AX D,DX

Ejecucin en tiempo de compilacin


Ejemplo:
int i; float f; i = 2+3; i = 4; f = i+2.5;

(+,2,3,t1) (=,t1,,i) (=,4,,i) (CIF,i,,t2) (+,t2,2.5,t3) (=,t3,,f)

(=,5,,i) (=,4,,i) (=,6.5,,f)

La ejecucin se aplica principalmente a las operaciones aritmticas (+-*/) y a las conversiones de tipo.

La tabla de smbolos puede contener el valor conocido del identificador (ej., i=4), o bien podemos tener una subtabla T con pares (id, valor). Algoritmo para tratar la ejecucin en tiempo de compilacin:

Si la cudrupla tiene la forma (op, op1, op2, res), donde op1 es un identificador y (op1,v1) est en la tabla T, sustituimos en la cudrupla op1 por v1. Si la cudrupla tiene la forma (op, op1, op2, res), donde op2 es un identificador y (op2,v2) est en la tabla T, sustituimos en la cudrupla op2 por v2. Si la cudrupla tiene la forma (op, v1, v2, res), donde v1 y v2 son valores constantes o nulos, eliminamos la cudrupla, eliminamos de T el par (res, v), si existe, y aadimos a T el par (res, v1 op v2), a menos que v1 op v2 produzca un error, en cuyo caso daremos un aviso y dejaremos la cudrupla como est. Ejemplo:
if (false) f = 1/0;

Esta instruccin debe dar un aviso, pero no un error. De hecho, una optimizacin adicional de cdigo la eliminara totalmente.

Si la cudrupla tiene la forma (=, v1, , res), eliminamos de T el par (res, v), si existe. Si v1 es un valor constante, aadimos a T el par (res, v1).

En el ejemplo:
(+,2,3,t1) (=,t1,,i) (=,4,,i) (CIF,i,,t2) Elim, T = {(t1,5)} Sust por (=,5,,i), T = {(t1,5),(i,5)} T = {(t1,5),(i,4)} Sust por (CIF,4,,t2), Elim, T = {(t1,5),(i,4),(t2,4.0)} (+,t2,2.5,t3) Sust por (+,4.0,2.5,t3) Elim, T = {(t1,5),(i,4),(t2,4.0),(t3,6.5)} (=,t3,,f) Sust por (=,6.5,,f)

Y quedan las cudruplas optimizadas: (=,5,,i), (=,4,,i), (=,6.5,,f). En cuanto sea posible que los valores de las variables cambien, el compilador debe "olvidar" el valor de las variables (inicializar la tabla T, total o parcialmente). Esto puede ocurrir si aparece:

una etiqueta; una cudrupla objetivo de una transferencia; una llamada a una subrutina, si se pasan variables por referencia o variables globales; una instruccin de lectura externa.

Este proceso no exige la generacin de las cudruplas, puede realizarse directamente durante las rutinas semnticas asociadas al anlisis sintctico, especialmente si es Bottom-up.

Problema con la ejecucin en tiempo de compilacin: si tenemos un "cross-compiler", la precisin puede ser menor en el ordenador que compila que en el que ejecuta.

Eliminacin de redundancias
Ejemplo:
int a,b,c,d; a = a+b*c; d = a+b*c; b = a+b*c; (*,b,c,t1) (+,a,t1,t2) (=,t2,,a) (*,b,c,t3) (+,a,t3,t4) (=,t4,,d) (*,b,c,t5) (+,a,t5,t6) (=,t6,,b) (*,b,c,t1) (+,a,t1,t2) (=,t2,,a) (+,a,t1,t4) (=,t4,,d) (=,t4,,b)

Una solucin: el programador podra reescribir su programa as:


int a,b,c,d,e; e = b*c; (*,b,c,t1) (=,t1,,e) a = a+e; (+,a,e,t2) (=,t2,,a) d = a+e; (+,a,e,t3) (=,t3,,d) b = d; (=,d,,b)

Desventaja: esta forma de programar puede ser ms larga y menos legible. Adems, hay redundancias que el programador no puede eliminar. Por ejemplo:
array X[0:4, 0:9]; X[i,j]:=X[i,j]+1; (*,i,10,t1) (+,t1,j,t2) (+,X[t2],1,t3) (*,i,10,t4) (+,t4,j,t5) (:=,t3,,X[t5]) (*,i,10,t1) (+,t1,j,t2) (+,X[t2],1,t3) (:=,t3,,X[t2])

Algoritmo para eliminar redundancias:


A cada variable de la tabla de smbolos le asignamos la dependencia -1. Numeramos las cudruplas. for (i=0; i<nmero de cudruplas; i++) { o Para cada uno de los dos operandos de la cudrupla: si la cudrupla de la que depende es (SAME,j,0,0), se sustituye el operando por el resultado de la cudrupla (j). o A la cudrupla (i) le asignamos como dependencia 1 + el mximo de las dependencias de sus operandos. o Si la cudrupla (i) tiene como resultado el identificador id, asignamos a id la dependencia i. o Si la cudrupla (i) es idntica a la cudrupla (j), j<i, excepto por el resultado generado, y las dependencias de ambas son iguales, sustituimos

la cudrupla i por una nula (SAME,j,0,0), que no genera cdigo. En las asignaciones se exige tambin que el resultado sea el mismo. Prueba: si j<k<i, y la cudrupla k cambiara alguno de los operandos de la cudrupla i, entonces dep(i)>k. Pero dep(j)<=k, luego dep(i)>dep(j) y no se podra eliminar (i). Ejercicio: aplicar el algoritmo a los dos ejemplos anteriores. El uso de tripletes simplifica el proceso, y an ms si son indirectos.

Reordenacin de operaciones
Tener en cuenta la conmutatividad de algunas operaciones puede mejorar el proceso, pues las cudruplas (*,a,b,-) y (*,b,a,-) seran equivalentes. Para facilitar el reconocimiento, se puede adoptar un orden cannico para los operandos de las operaciones conmutativas. Por ejemplo: trminos que no son variables ni constantes, luego variables indexadas por orden alfabtico, luego variables sin indexar por orden alfabtico, finalmente constantes. Esto mejora tambin la ejecucin en tiempo de compilacin. Por ejemplo, si tenemos las instrucciones a=1+c+d+3; a=c+d+1+3; b=d+c+2; b=c+d+2; la reordenacin nos permite efectuar en tiempo de compilacin la operacin 1+3, y reconocer c+d como parte comn de las dos instrucciones. Esto no es completo, sin embargo, ya que a=1+c+d+3; a=c+d+1+3; b=d+c+c+d; b=c+c+d+d; la reordenacin no nos permite reconocer que c+d, evaluado en la primera instruccin, puede aplicarse a la segunda. Otra mejora podra ser la utilizacin de los operadores mondicos para aumentar el nmero de cudruplas equivalentes. Por ejemplo:
a = c-d; b = d-c; (-,c,d,t1) (=,t1,,a) (-,d,c,t2) (=,t2,,b) (-,c,d,t1) (=,t1,,a) (-,t1,,t2) (=,t2,,b)

que no disminuye el nmero de cudruplas, pero sustituye una operacin didica por una mondica, que usualmente son ms eficientes. Las variables intermedias para resultados parciales pueden reutilizarse para minimizar la memoria (aunque eso puede ir en contra de las optimizaciones anteriores). Por ejemplo: sea la expresin (a*b)+(c+d). Sus cudruplas equivalentes seran:
(*,a,b,t1) (+,c,d,t2) (+,t1,t2,t1)

En este caso, utilizamos dos variables auxiliares (t1, t2). Pero si aprovechamos la asociatividad de la suma para reordenar de esta manera:
(*,a,b,t1) (+,t1,c,t1)

(+,t1,d,t1)

necesitaremos slo una variable auxiliar. El nmero mnimo de variables auxiliares se puede calcular construyendo un grafo de la expresin y aplicando las siguientes reglas: 1. Marcar las hojas con 0. 2. Si (j,k) son las marcas de los hijos del nodo i, si j=k, asociar (k+1) al nodo i, en caso contrario asociarle max(j,k). Por ejemplo, el grafo de (a*b)+(c+d) es:
+(2) ----------------*(1) +(1) ----------------a(0) b(0) c(0) d(0)

Pero el grafo de ((a*b)+c)+d es:


+(1) ---------------+(1) d(0) ----------------*(1) c(0) --------a(0) b(0)

Tambin se puede aprovechar la conmutatividad, como en el ejemplo:


(a+b)+(c*d) +(2) ----------------+(1) *(1) ----------------a(0) b(0) c(0) d(0) a+(c*d)+b +(1) ----------------+(1) b(0) --------a(0) *(1) --------c(0) d(0)

Optimizacin de bucles
Una operacin es invariante respecto a un bucle, si ninguno de los operandos de los que depende cambia de valor durante la ejecucin del bucle. La optimizacin consiste en sacar la operacin fuera del bucle. Otra optimizacin es la reduccin de la fuerza de una operacin (sustituir una operacin fuerte por otra ms dbil, como la multiplicacin por la suma o la diferencia por el cambio de signo, como en el apartado anterior). Por ejemplo:
for (i=a; i<c; i+=b) {... d=i*k; ...}

donde b,k son invariantes respecto al bucle. (b podra ser una expresin, en cuyo caso todos sus operandos deben ser invariantes). Adems, i no se modifica dentro del bucle, excepto en la instruccin de cierre, i+=b, y d no se usa ni modifica antes de la

instruccin indicada y no se modifica despus. En este caso, podemos sustituir el cdigo generado por su equivalente:
d=a*k; t1=b*k; for (i=a; i<c; i+=b, d+=t1) {...}

con lo que hemos reducido la fuerza de una multiplicacin a una suma (dentro del bucle). Esto no se debe hacer si i o k son reales, pues podra perderse precisin al sumar i veces en vez de multiplicar una. Pero s se puede hacer si i,k son enteros. Otro ejemplo:
for (i=0; i<10; i++) {... a=(b+c*i)*d; ...} INIT: (=,0,,i) LOOP: ... (*,c,i,t1) (+,b,t1,t2) (*,t2,d,t3) (=,t3,,a) ... INCR: (+,i,1,i)

donde b,c,d son invariantes respecto al bucle, e i es la variable del bucle. Supongamos que se cumplen todas las condiciones. Podemos aplicar reduccin de fuerza a la primera cudrupla del bucle as:
INIT: (=,0,,i) (*,c,0,t1) (*,c,1,t4) LOOP: ... (+,b,t1,t2) (*,t2,d,t3) (=,t3,,a) ... INCR: (+,i,1,i) (+,t1,t4,t1)

Ahora t1 desempea el mismo papel que i. Se le asigna un valor inicial y en cada paso del bucle se le incrementa en t4. Por tanto, podemos aplicar reduccin de fuerza a la cudrupla siguiente:
INIT: (=,0,,i) (*,c,0,t1) (*,c,1,t4) (+,b,t1,t2) LOOP: ... (*,t2,d,t3) (=,t3,,a) ... INCR: (+,i,1,i) (+,t1,t4,t1) (+,t2,t4,t2)

Ahora pasa lo mismo con t2, luego podemos aplicar reduccin de fuerza a la siguiente cudrupla:
INIT: (=,0,,i)

(*,c,0,t1) (*,c,1,t4) (+,b,t1,t2) (*,t2,d,t3) (*,t4,d,t5) LOOP: ... (=,t3,,a) ... INCR: (+,i,1,i) (+,t1,t4,t1) (+,t2,t4,t2) (+,t3,t5,t3)

Todava podemos optimizar ms notando que ahora t1 y t2 no se emplean dentro del bucle, luego no es necesario incrementarlas:
INIT: (=,0,,i) (*,c,0,t1) (*,c,1,t4) (+,b,t1,t2) (*,t2,d,t3) (*,t4,d,t5) LOOP: ... (=,t3,,a) ... INCR: (+,i,1,i) (+,t3,t5,t3)

Si sacamos operaciones fuera de un bucle, pueden quedar dentro de otro bucle ms externo. El proceso podra repetirse. Si hay alguna llamada de subrutina dentro del bucle, es difcil saber si se cambia alguna de las variables (podran ser globales o pasarse como argumento por referencia). En tal caso, slo pueden aplicarse las optimizaciones si el compilador sabe qu variables se cambian. Esto suele ocurrir slo para ciertas funciones y subrutinas predefinidas. Para realizar las optimizaciones pueden hacer falta dos pasos: uno primero, en el que se analizan los bucles y se obtiene informacin sobre las variables que cambian, y otro segundo, en el que se realiza la optimizacin propiamente dicha. Pero tambin se puede fusionar el proceso con el analizador semntico y el generador de cdigo y hacerlo todo en un solo paso. Para esto, a veces hay que retrasar o cambiar de orden algunas de las operaciones del bucle. Por ejemplo, podramos generar un cdigo como el siguiente:
GOTO INIT LOOP: ... INCR: ... GOTO TEST INIT: ... TEST: IF (no fin de bucle) GOTO LOOP

con lo que INIT y INCR (que son los que cambian con la optimizacin) quedan al final. Hay que tener cuidado con estas optimizaciones. Si el bucle se ejecuta normalmente 0 (o 1) veces, y es muy raro que se entre en l, las optimizaciones anteriores degradarn (o dejarn invariante) la eficiencia.

Regiones
Supongamos que tenemos un programa dividido en bloques bsicos. con ellos podemos formar un grafo donde los nodos son los bloques, los arcos indican sucesin de ejecucin. Llamamos "regin fuertemente conexa" (o simplement regin) a un subgrafo del programa en el que existe un camino de cualquier nodo del subgrafo a otro nodo del subgrafo. Ejemplo:
--------------------| ------| v v | |v | 1 -> 2 -> 3 -> 5 -> 6 -> 7 -> 8 | ^ |--> 4 ---|

En la figura hay cinco regiones: (6), (3,5), (2,3,5,6,7), (2,3,4,6,7), (2,3,4,5,6,7). Llamamos "bloque de entrada" de una regin a un bloque al que entra un arco desde fuera de la regin. (3,5) tiene un bloque de entrada: 3. (2,3,5,6,7) tiene dos bloques de entrada: 2 y 6. Llamamos "predecesor" de una regin a un bloque situado fuera de la regin del que sale un arco que lleva a un bloque de entrada de la regin. (3,5) tiene un predecesor: 2. (2,3,5,6,7) tiene dos predecesores: 1 y 4. Construimos una lista R={R1,R2,...,Rn} de regiones tales que Ri!=Rj si i!=j, y i<j => Ri y Rj no tienen bloques en comn o bien Ri es un subconjunto de Rj. En el ejemplo, una lista vlida sera: (6),(3,5),(2,3,5,6,7),(2,3,4,5,6,7). Otra lista vlida sera: (6),(2,3,4,6,7),(2,3,4,5,6,7). (2,3,4,6,7) y (2,3,5,6,7) no pueden estar juntas en una lista vlida. Cul elegir? Conviene que estn los bucles, que tienen normalmente un solo nodo predecesor y un solo nodo de entrada. Cuando haya dos posibilidades, preferiremos las regiones con esta propiedad. Para cada bloque definiremos las siguientes variables booleanas:

R[i]=1 si la variable i es utilizada dentro del bloque. A[i]=1 si la variable i es asignada dentro del bloque. B[i]=1 si la variable i es usada dentro del bloque antes de asignarle algo.

El O lgico de R[i] de todos los bloques de una regin nos da R[i] para la regin. Lo mismo con A[i]. La optimizacin se aplica sucesivamente a cada regin de la lista, de la primera a la ltima. Al optimizar cada regin, se crean bloques nuevos de inicializacin. En el ejemplo:
---------------------------

| ------| v v | |v | 1 -> 2 -> 3 -> 5 -> I1 -> 6 -> 7 -> 8 | ^ |--> 4 -> I2 ---|

Los bloques I se aaden a las regiones correspondientes, para que entren en las nuevas optimizaciones. Todos los bloques de la regin recin tratada se sustituyen por uno solo (R1, en este caso), calculando las variables booleanas aplicables a la regin. Este bloque ya no debe ser optimizado. (R1), (3,5), (2,3,5,I1,R1,7), (2,3,4,5,I1,I2,R1,7). Pasamos a la regin R2 (3,5), optimizamos:
---------------------------------| ------| v v | |v | 1 -> 2 -> I3 -> 3 -> 5 -> I1 -> R1 -> 7 -> 8 | ^ |--> 4 -> I2 ---|

y sustituimos:
-----------------------------| -| v |v | 1 -> 2 -> I3 -> R2 -> I1 -> R1 -> 7 -> 8 | ^ |-> 4 -> I2 -|

Las regiones sern: (R1), (R2), (2,I3,R2,I1,R1,7), (2,I3,R2,4,I1,I2,R1,7). Etctera. En principio, slo hacen falta los vectores booleanos correspondientes a las variables que se utilizan dentro de la regin que se est optimizando. El algoritmo es como sigue: 1. i=1; 2. Seleccionar regin Ri. Preparar un bloque I vaco. 3. Ejecutar y eliminar redundancias dentro de cada bloque de la regin. Construir los vectores booleanos para cada bloque. 4. Sacar invariancias y reducir la fuerza dentro de la regin, llenando el bloque I. Eliminar asignaciones muertas (asignaciones que nunca se usan). Esto cambia los vectores booleanos y crea variables temporales. 5. Crear los tres vectores para la regin entera. Sustituir todos sus bloques por el bloque regin, que ya no debe ser optimizado. Insertar copias del bloque I entre todo predecesor y bloque de entrada de la regin. 6. i++; si i<=n, ir al paso 2. 7. Realizar optimizacin de cudruplas y eliminacin de redundancias en los bloques bsicos que no pertenecen a ninguna regin.

Asignaciones muertas
Surgen si el resultado de la asignacin no se utiliza posteriormente o si la asignacin es recursiva (ej., i++) y slo se usa en dichas definiciones recursivas. Todas esas asignaciones pueden eliminarse. Suelen surgir como consecuencia de la reduccin de fuerza y optimizaciones semejantes (como se vio). Para ver si una asignacin est muerta se puede utilizar el algoritmo:

1. Seguir las operaciones del bloque. Si aparece otra asignacin a la misma variable sin un uso intermedio, la asignacin est muerta. Si aparece un uso de esa variable, la asignacin no est muerta. En caso contrario, ir al paso 2. 2. Seguir todas las ramificaciones del programa a partir del bloque en que estamos y mirar las variables R, A, B de cada bloque por el que pasemos. Si encontramos B[i]=1 en un bloque, la asignacin no est muerta. Si B[i]=0, pero A[i]=1, abandonamos este camino. Si se nos acaban los caminos o entramos en bucles, la asignacin est muerta.

También podría gustarte