Você está na página 1de 20

4o Ingenier Informtica a a

II26 Procesadores de lenguaje


Generacin de cdigo o o Esquema del tema
1. Introduccin o 2. Cdigo intermedio o 3. Generacin de cdigo para expresiones o o 4. Generacin de cdigo para las estructuras de control o o 5. Generacin de cdigo mquina o o a 6. Resumen

1.

Introduccin o

Una vez ha terminado el anlisis y se ha obtenido un AST decorado, comienza la generacin a o de cdigo. Esta fase suele dividirse en dos partes: o Generacin de cdigo intermedio. o o Generacin de cdigo de mquina. o o a El primero es cdigo para una mquina virtual. Estas mquinas se denen con dos objetivos: o a a Ser lo sucientemente simples como para poder generar cdigo para ellas de manera sencilla, o pero con la suciente riqueza para poder expresar las construcciones del lenguaje fuente. Estar lo sucientemente prximas a los procesadores reales para que el paso del cdigo o o intermedio al cdigo de mquina sea prcticamente directo. o a a Para conseguirlo, las mquinas virtuales tienen una serie de caracter a sticas simplicadoras. Por ejemplo: Tienen pocos modos de direccionamiento. Pueden tener un nmero ilimitado de registros (que pueden estar organizados en forma de u pila). Tienen un juego de instrucciones relativamente simple. En principio, el cdigo intermedio es independiente del lenguaje de programacin fuente. En la o o prctica, es habitual que se denan cdigos intermedios que faciliten la representacin de detera o o minadas caracter sticas del lenguaje fuente. As el P-code est pensado para traducir Pascal y el , a Java-bytecode es muy bueno para el Java. Otra de las ventajas del cdigo intermedio es que facilita la escritura de compiladores para o distintas mquinas. La traduccin del lenguaje a cdigo intermedio ser idntica en todas y lo a o o a e unico que cambiar ser el traductor de cdigo intermedio a cdigo de mquina. Esta idea de a a o o a independencia del cdigo intermedio de la mquina puede llevarse un paso ms all, como se hace o a a a en diversos lenguajes, de los cuales Java es quiz el ms representativo. La idea es compilar el a a lenguaje a un cdigo intermedio que despus es interpretado. De esta manera, un mismo binario o e puede ejecutarse en mquinas con arquitecturas y sistemas operativos totalmente distintos. a Una vez generado el cdigo intermedio, se traduce ste a cdigo de mquina. La traduccin o e o a o tiene que hacerse de manera que se aprovechen adecuadamente los recursos de la mquina real. a

II26 Procesadores de lenguaje

Aunque en principio estas dos fases bastan para la generacin del cdigo nal, en la prctica o o a estn mezcladas con fases de optimizacin. Es habitual tener entonces un esquema similar al que a o vimos en el primer tema:
Programa fuente Anlisis a Anlisis a lxico e Anlisis a sintctico a Anlisis a semntico a

Arbol de sintaxis abstracta S ntesis Generacin de o cdigo intermedio o Optimizacin de o cdigo intermedio o Generacin de o cdigo objeto o Optimizacin de o cdigo objeto o

Programa objeto

Podr amos entonces decir que la generacin de cdigo tiene como objetivo generar el ejecutable o o que despus emplear el usuario. Sin embargo, es habitual que el producto del compilador no e a sea directamente un chero ejecutable. Es bastante ms comn que sea un chero en lenguaje a u ensamblador. De esta manera, se evitan problemas como tener que medir el tamao exacto de las n instrucciones o llevar la cuenta de sus direcciones. Adems, es muy probable que el cdigo generado a o tenga referencias a objetos externos como funciones de biblioteca. Estas referencias externas sern a resueltas por el enlazador o el cargador.

1.1.

Lo ms fcil: no generar cdigo a a o

Dado que la generacin de cdigo es una tarea compleja, especialmente si queremos que sea o o eciente y razonablemente compacto, es interesante buscar maneras de evitar esta fase. De hecho existe una manera sencilla de evitar la generacin de cdigo (al menos, la generacin de cdigo o o o o mquina) que permite obtener ejecutables de muy buena calidad: generar cdigo en otro lenguaje a o para el que haya un buen compilador. Hoy en d prcticamente cualquier sistema tiene compiladores de C de gran calidad. Es a, a posible, por tanto, traducir nuestro lenguaje a C y despus compilar el programa obtenido con un e buen compilador. Esto tiene varias ventajas: El tiempo de desarrollo de nuestro compilador se reduce considerablemente. En muchos sistemas, el compilador de C genera cdigo de muy alta calidad ya que de l o e dependen muchos programas cr ticos (entre ellos el propio sistema operativo). Nos permite tener un compilador para gran nmero de mquinas con un esfuerzo m u a nimo (el de adaptarse a las pequeas diferencias entre unos compiladores y otros). De hecho, n en algunos casos se habla del lenguaje C como de un ensamblador independiente de la mquina. a En el caso de lenguajes de muy alto nivel o de paradigmas como el funcional y el lgico, esta es o prcticamente la eleccin unnime. Hay incluso propuestas de lenguajes como el C--, que buscan a o a un lenguaje reducido y con ciertas caracter sticas de ms bajo nivel como lenguaje destino para a muchos compiladores. Si hemos construido el rbol mediante clases, la traduccin a C es prcticamente trivial para a o a muchas de las construcciones habituales en los lenguajes imperativos. Por ejemplo, para los nodos

Generacin de cdigo o o

correspondientes a las expresiones aritmticas, podemos utilizar algo parecido a: e Objeto NodoSuma: ... Mtodo traduceaC() e devuelve (i.traduceaC()+ "+" +d.traduceaC()); n traduceaC ... n NodoSuma donde i y d son los hijos izquierdo y derecho, respectivamente. Igual que vimos en el tema de anlisis semntico, tanto en este caso como en los que comentemos a a despus, no es necesario que utilicemos una clase por cada tipo de nodo. Tampoco es necesario e que utilicemos objetos y mtodos: podemos recorrer el rbol con ayuda de una pila o de funciones e a recursivas adecuadas.

2.

Cdigo intermedio o

Existen diversos tipos de cdigos intermedios que var en cuanto a su sencillez, lo prximos o an o que estn a las mquinas reales y lo fcil que es trabajar con ellos. Nosotros nos centraremos en a a a un tipo de cdigo que se parece bastante al lenguaje ensamblador. Existen otros tipos de cdigo o o intermedio que representan los programas como rboles o grafos. Tambin existen representaciones a e mixtas que combinan grafos o rboles y representaciones lineales. a El formato que usaremos para las operaciones binarias es: r1:= r2 op r3 donde r1, r2 y r3 son registros, de los que tenemos un nmero innito, u op es un operador. En caso de que el operador sea unario, la forma de la instruccin es r1:= op r2. Para acceder a o memoria utilizaremos asignaciones entre registros y memoria. Las asignaciones pueden ser directas de la forma r1:=&d o &d:= r2 y tambin relativas a un registro como en r1:= &r2[d] y en e &r1[d]:= r2. Por ejemplo, la sentencia a:= b*(-c), donde a es una variable local en la direccin 1 respecto o al FP y b y c son variables globales en las direcciones 1000 y 1001, se puede traducir como: r1:= &1000 r2:= &1001 r3:= - r2 r4:= r1 * r3 &FP[1]:= r4 Uno de los objetivos que suele perseguirse es reducir al m nimo el nmero de registros utilizados. u En nuestro caso, podemos emplear dos: r1:= &1000 r2:= &1001 r2:= - r2 r1:= r1 * r2 &FP[1]:= r1
c Universitat Jaume I 2006-2007

II26 Procesadores de lenguaje

Si las variables estuvieran en los registros1 r1, r2 y r3, podr amos incluso no utilizar ningn u registro auxiliar: r1:= - r3 r1:= r2 * r1 Tambin tendremos instrucciones para controlar el ujo de ejecucin, para gestionar entrae o da/salida, etc. A lo largo del tema iremos viendo esas instrucciones segn las vayamos necesitando. u

3.

Generacin de cdigo para expresiones o o

Comenzaremos por estudiar cmo generar cdigo para las expresiones. Asumiremos que para o o gestionar el cdigo disponemos de una funcin auxiliar, emite. Esta funcin recibe una cadena que o o o representa una l nea de cdigo intermedio y toma las medidas oportunas para que ese cdigo se o o utilice. Estas medidas pueden ser escribir la l nea en un chero adecuado, almacenar la instruccin o en una lista que despus se pasar a otros mdulos, o cualesquiera otras que necesitemos en nuestro e a o compilador.

3.1.

Expresiones aritmticas e

Comenzamos el estudio por las expresiones aritmticas. Lo que tendremos que hacer es crear e por cada tipo de nodo un mtodo que genere el cdigo para calcular la expresin y lo emita. Ese e o o cdigo dejar el resultado en un registro, cuyo nombre devolver el mtodo como resultado. o a a e Para reservar estos registros temporales, utilizaremos una funcin, reservaReg. En principio o bastar con que esta funcin devuelva un registro distinto cada vez que se la llame. a o Cada nodo generar el cdigo de la siguiente manera: a o Por cada uno de sus operandos, llamar al mtodo correspondiente para que se evale la a e u subexpresin. o Si es necesario, reservar un registro para guardar su resultado. a Emitir las instrucciones necesarias para realizar el clculo a partir de los operandos. a a Con este esquema, la generacin del cdigo para una suma ser: o o a Objeto NodoSuma: ... Mtodo generaCdigo() e o izda:= i.generaCdigo(); o dcha:= d.generaCdigo(); o r :=reservaReg(); emite(r:= izda + dcha); devuelve r; n generaCdigo o ... n NodoSuma En el caso de los operadores unarios, bastar con reservar un registro y hacer la correspondiente a operacin. o
Ejercicio 1

Escribe el mtodo de generacin de cdigo para el operador unario de cambio de signo. e o o

1 En principio ser raro que las variables globales residieran en registros pero no ser tan raro para la variable a a local.

Generacin de cdigo o o

El acceso a variables y constantes es directo: Objeto NodoAccesoVariable: ... Mtodo generaCdigo() e o r :=reservaReg(); if eslocal entonces emite(r:= &FP[dir]); si no emite(r:= &dir); n si devuelve r; n generaCdigo o ... n NodoVariableGlobal

Objeto NodoConstante: ... Mtodo generaCdigo() e o r :=reservaReg(); emite(r:= valor); devuelve r; n generaCdigo o ... n NodoConstante Observa que asumimos que las variables estn en memoria y no permanentemente en registros. a Esto no es ptimo, pero nos permite presentar un mtodo de generacin razonablemente sencillo. o e o Por ejemplo, vamos a traducir la expresin (a+2)*(b+c). Si suponemos que las variables estn o a en las direcciones 1000, 1001 y 1002, respectivamente, el cdigo que se generar es: o a

r1:= r2:= r3:= r4:= r5:= r6:= r7:=

&1000 2 r1 + r2 &1001 &1002 r4 + r5 r3 * r6

Cuando llamemos a una funcin, habr que guardar todos los registros activos en el registro o a de activacin. Por eso, ser bueno intentar reutilizar los registros y tener el m o a nimo nmero de u ellos ocupados. Si vamos a tener una fase de optimizacin, este mtodo no deber suponer mayor o e a problema. Si no tenemos un optimizador, podemos utilizar una estrategia sencilla que reduce bastante el nmero de registros empleados. La idea es tener tambin una funcin liberaReg que u e o marca un registro como disponible para una nueva llamada de reservaReg. Podemos tener una lista de registros utilizados y otra de registros libres. Si la generacin de o cdigo se hace con cuidado, los registros se comportarn como una pila, con lo que bastar con o a a que reservaReg y liberaReg acten sobre un contador que mantenga el tope de dicha pila. Por u ejemplo, cuando generemos cdigo para operaciones binarias, utilizaremos el registro del operando o izquierdo y liberaremos el del derecho.
c Universitat Jaume I 2006-2007

II26 Procesadores de lenguaje

Siguiendo esta idea, el nodo de la suma ser a: Objeto NodoSuma: ... Mtodo generaCdigo() e o izda:= i.generaCdigo(); o dcha:= d.generaCdigo(); o emite(izda:= izda + dcha); liberaReg(dcha); devuelve izda; n generaCdigo o ... n NodoSuma Para (a+2)*(b+c) se generar a: r1:= r2:= r1:= r2:= r3:= r2:= r1:= &1000 2 r1 + r2 &1001 &1002 r2 + r3 r1 * r2

En el caso de los unarios, directamente reutilizaremos el registro correspondiente al operando.


Ejercicio 2

Escribe el mtodo de generacin de cdigo para el operador unario de cambio de signo reutilie o o zando registros. Necesitas tener una clase por cada operador del lenguaje? La respuesta depender principala mente de cmo te sientas ms cmodo. En principio, tienes un rango de posibilidades, desde una o a o clase por operador a una unica clase que tenga como atributo el operador concreto. Un compromiso es tener clases que agrupen operadores con propiedades similares, como por ejemplo los lgicos por o un lado y los aritmticos por otro. Lo unico que hay que tener en cuenta en caso de que haya ms e a de un operador representado en una clase es que pueden existir sutilezas que los distingan. Por ejemplo, en Pascal, el operador / siempre devuelve un real, mientras que el tipo del operador + depende de los de sus operandos.

3.2.

Coercin de tipos o

Antes hemos supuesto impl citamente que slo ten o amos un tipo de datos con el que opera bamos. En la prctica es habitual tener, al menos, un tipo de datos entero y otro otante. En a este caso, es necesario generar las operaciones del tipo adecuado y, si es necesario, convertir los operandos. Supondremos que nuestro cdigo intermedio tiene un operador unario, a_real, para o transformar su operando entero en un nmero real. Adems, contamos con las versiones reales de u a los operadores, que se distinguen por tener una r delante. En este caso, el nodo de la suma ser similar al de la gura 1. a Al aumentar el nmero de tipos disponibles (por ejemplo al aadir enteros de distintos tau n maos), el cdigo se complica rpidamente. Otra fuente de dicultades es que existan registros n o a distintos para tipos distintos. Suele ser preferible aadir la conversin de tipos de forma expl n o cita en el AST. De este modo tendr amos nodos que se encargan de realizar la conversin de tipos. o
Ejercicio 3

Escribe el mtodo de generacin de cdigo para la suma suponiendo que hay nodos expl e o o citos para la conversin de tipos. o

Generacin de cdigo o o

Mtodo generaCdigo() e o izda:= i.generaCdigo(); o si i.tipo()= tipo() entonces emite(izda:= a_real izda) n si Objeto NodoSuma: dcha:=d.generaCdigo(); o ... si d.tipo()= tipo() entonces Mtodo tipo() e emite(dcha:= a_real dcha) si i.tipo()= real o d.tipo()=real entonces n si devuelve real si tipo()=real entonces si no emite(izda:=izda r+ dcha) devuelve entero si no n si emite(izda:=izda + dcha) n tipo n si ... liberaReg(dcha); devuelve izda; n generaCdigo o ... n NodoSuma

Figura 1: Algoritmo para la generacin de cdigo para las sumas. o o

3.3.

Asignaciones

En algunos lenguajes de programacin, la asignacin es un operador ms, mientras que en otros o o a se la considera una sentencia. El tratamiento en ambos casos es muy similar. La diferencia es que en el primer caso se devuelve un valor, generalmente el mismo que se asigna a la parte izquierda, mientras que en el segundo caso lo importante de la asignacin es su efecto secundario. o Si la parte izquierda de la asignacin puede ser unicamente una variable simple, basta con o generar el cdigo de modo que se calcule la expresin de la parte derecha y se almacene el resultado o o en la direccin de la variable: o Objeto NodoAsignacin: o ... Mtodo generaCdigo() e o dcha:= d.generaCdigo(); o si eslocal entonces emite(&FP[dir]:= dcha); si no emite(&dir:= dcha); n si devuelve dcha n generaCdigo o ... n NodoAsignacin o Hemos asumido que hay nodos para la conversin de tipos. Si consideramos la asignacin como o o sentencia, quitamos la devolucin de dcha y lo sustituimos por un liberaReg. o Si podemos tener en la parte izquierda variables estructuradas tenemos dos alternativas: podemos tener un nodo por cada posible estructura (NodoAsignaVector, NodoAsignaRegistro, etc.) o mantener un unico nodo. Para hacerlo, aadiremos a aquellos nodos que puedan aparecer en una n parte izquierda un nuevo mtodo, generaDir, que genera la direccin del objeto correspondiente y e o
c Universitat Jaume I 2006-2007

II26 Procesadores de lenguaje

la devuelve en un registro. De esta manera, lo que tiene que hacer la asignacin es generar cdigo o o para la expresin, generar la direccin de la parte izquierda y hacer la asignacin: o o o Objeto NodoAsignacin: o ... Mtodo generaCdigo() e o dcha:= d.generaCdigo(); o izda:= i.generaDir(); emite(&izda[0]:= dcha); liberaReg(izda); devuelve dcha; n generaCdigo o ... n NodoAsignacin o
Ejercicio 4

Escribe el mtodo de generacin de direccin para las variables simples. Ten en cuenta que las e o o variables pueden ser locales o globales.
Ejercicio 5

Qu cdigo se generar para la sentencia a:= b*d/(e+f)? Supn que a y b son variables e o a o globales de tipo entero con direccin 100 y 200, respectivamente, y el resto locales con desplazao mientos 1, 2 y 3.

3.4.

Vectores

Para acceder a vectores, asumiremos que sus elementos ocupan posiciones consecutivas en la memoria. Esto quiere decir que un vector como, por ejemplo, a: ARRAY [2..5] OF integer se almacena en memoria de la siguiente forma: a[2] a[3] a[4] a[5]

Supongamos que el vector comienza en la direccin base b y cada nodo tiene un tamao t. o n Llamemos l al l mite inferior (en nuestro caso l = 2). Para acceder al elemento i, podemos utilizar la frmula: o d = b + (i l) t

Esta direccin se obtiene simplemente sumando a la direccin base el nmero de elementos delante o o u del que buscamos (il) multiplicado por el tamao de cada elemento (t). En general, el compilador n deber emitir cdigo para hacer ese clculo en tiempo de ejecucin. Sin embargo, para ahorrar a o a o tiempo de ejecucin, podemos descomponer la frmula anterior como sigue: o o d = i t + (b l t) Esta expresin se puede interpretar como la suma del desplazamiento correspondiente a i elementos o (i t) con la direccin del elemento cero del vector, que es b l t y se puede calcular en tiempo o de compilacin. o

Generacin de cdigo o o

Si llamamos base virtual a la direccin del elemento cero, la generacin de la direccin queda: o o o Objeto NodoAccesoVector: ... Mtodo generaDir() e d := exp.generaCdigo(); // Desplazamiento o emite(d:=d*t); emite(d := d+base virtual); devuelve d; n generaCdigo o ... n NodoAccesoVector
Ejercicio 6

Escribe el cdigo generado para la sentencia a[i]:= 5;. Supn que a es de tipo ARRAY[1..5] o o OF integer, que los enteros ocupan cuatro bytes y que a[1] est en la direccin 1000 e i en a o la 1100.
Ejercicio 7

Escribe el mtodo de lectura de un elemento de un vector. Puedes aprovechar el mtodo e e generaDir. Si el vector es n-dimensional, resulta cmodo interpretarlo como un vector unidimensional de o vectores n 1 dimensionales y representar los accesos al vector como anidamientos de accesos unidimensionales. Podemos, por ejemplo, representar a[3][5] como: NodoAccesoVector NodoAccesoVector NodoVariable NodoEntero
dir: 1000 valor: 3

NodoEntero
valor: 5

Esto supone cambiar NodoAccesoVector para tener en cuenta que la base virtual debe incluir los l mites inferiores de cada dimensin. Para simplicar la exposicin, asumiremos que, como o o en C, el l mite inferior es cero. En ese caso, el clculo de la direccin queda: a o Objeto NodoAccesoVector: ... Mtodo generaDir() e r :=izda.generaDir(); // Base del vector d :=exp.generaCdigo(); // Desplazamiento o emite(d:=d*t); emite(r:=r+d); liberaReg(d); devuelve r; n generaCdigo o ... n NodoAccesoVector
Ejercicio 8

Escribe el cdigo generado para la sentencia C a[3][5]=25; suponiendo que se ha declarado a o de tipo int a[10][10], que est en la direccin 1000 y que los enteros ocupan cuatro bytes. a o

c Universitat Jaume I 2006-2007

10

II26 Procesadores de lenguaje

Ejercicio* 9

Escribe el cdigo para el acceso a vectores suponiendo que cada dimensin puede tener un o o l mite inferior. Intenta reducir los clculos en tiempo de ejecucin. a o

3.5.

Expresiones lgicas o

A la hora de trabajar con expresiones de tipo lgico, hay dos opciones: o

Codicar numricamente los valores lgicos, por ejemplo 1 para cierto y 0 para falso y tratar e o las expresiones normalmente con operadores booleanos (x:= y O z, x:= y Y z, x:= NO y). Representar el valor de la expresin mediante el ujo de ejecucin del programa. Si la exo o presin es cierta, el programa llegar a un sitio y, si es falsa, a otro. o a

El primer mtodo es relativamente sencillo si disponemos de instrucciones que permitan realizar e las operaciones booleanas directamente. Por ejemplo, podemos tener algo parecido a la instruccin o r1:= r2<r3 que guarde en r1 un cero o un uno segn los valores de r2 y r3. En la prctica, es u a habitual que las instrucciones que tengamos sean del tipo: si r1<r2 salta Etq. En este caso, es ms aconsejable utilizar la generacin mediante control de ujo. a o
Ejercicio 10

Escribe la traduccin de a<b O NO a<c. Supn que las variables son globales en 100, 110 y 120, o o respectivamente y utiliza las instrucciones siguientes: r1:= r2 O r3, r1:= NO r2, r1:= r2<r3.

Otra razn por la que la representacin mediante control de ujo es especialmente interesante es o o el empleo de cortocircuitos. En muchos lenguajes de programacin, la evaluacin de una expresin o o o se detiene tan pronto como el valor es conocido. Esto quiere decir que si se sabe que el primer operando de un y-lgico es falso, no se evala el segundo operando. Anlogamente, no se evala o u a u el segundo operando de un o-lgico si el primero es cierto. Esta manera de generar el cdigo es o o especialmente util para las estructuras de control.

3.6.

Generacin de cdigo para expresiones mediante control de ujo o o

La generacin del cdigo se puede hacer aadiendo un mtodo, cdigoControl, a cada nodo. o o n e o Este mtodo recibir dos parmetros: el primero, cierto, ser la etiqueta donde debe saltarse e a a a en caso de que la expresin sea cierta; el segundo, falso, ser el destino en caso de falsedad. o a Usaremos el convenio de pasar un valor especial (por ejemplo, None) en caso de que queramos que la ejecucin vaya a la instruccin siguiente. Asumiremos que los dos parmetros no son None o o a simultneamente. a El cdigo para una comparacin como menor que es: o o

Generacin de cdigo o o

11

Objeto NodoMenor: ... Mtodo cdigoControl(cierto, falso) e o izda:= i.generaCdigo(); o dcha:= d.generaCdigo(); o si cierto= None entonces emite(si izda < dcha salta cierto); si no emite(si izda >= dcha salta falso); n si si cierto=None y falso=None entonces emite(salta falso) n si liberaReg(izda); liberaReg(dcha); n cdigoControl o ... n NodoMenor Para el caso en que la ejecucin deba pasar a la instruccin siguiente si el resultado es cierto, o o nos ha bastado con invertir la condicin. Habitualmente, todas las posibles comprobaciones estn o a implementadas en el procesador y esto no supone mayor problema.
Ejercicio 11

Escribe el cdigo generado para la expresin a<100 para las llamadas siguientes: o o nodo.cdigoControl("et1", "et2") o nodo.cdigoControl("et1", None) o nodo.cdigoControl(None, "et2") o Supn que a es una variable local con desplazamiento 4 respecto al FP. o Como en el caso de las operaciones aritmticas, tendremos que tener cuidado con los tipos. Es e habitual que tengamos instrucciones separadas para comparar reales y enteros. En ese caso, podemos aplicar las mismas estrategias que entonces. Tambin como con las operaciones aritmticas, e e no es necesario tener exactamente una clase por cada posible comparacin; puede bastar con un o atributo adicional para indicar el tipo de comparacin que se va a hacer. o La generacin de cdigo para el y-lgico es tambin sencilla. Primero, generaremos cdigo o o o e o para la parte izquierda. Este cdigo tendr como destino en caso de cierto el cdigo de la parte o a o derecha, que le seguir. En caso de falso, el control deber ir al falso del Y. Podemos representarlo a a grcamente de la siguiente manera: a

Y i

V F

V F

Al generar cdigo, tendremos que tener en cuenta que el falso de Y puede ser None. Para ese o caso, generaremos una nueva etiqueta donde saltar si la parte izquierda resulta ser falsa. El mtodo e
c Universitat Jaume I 2006-2007

12

II26 Procesadores de lenguaje

cdigoControl queda entonces: o Objeto NodoY: ... Mtodo cdigoControl(cierto, falso) e o si falso= None entonces aux:=nuevaEtiqueta(); si no aux:=falso; n si i.cdigoControl(None, aux); o d.cdigoControl(cierto, falso); o si falso= None entonces emite(aux:); n si n cdigoControl o ... n NodoY El caso del o-lgico es anlogo. o a
Ejercicio 12

Escribe el mtodo de generacin de cdigo para el o-lgico. e o o o La generacin de cdigo para el no-lgico es extremadamente sencilla, basta con cambiar cierto o o o por falso: Objeto NodoNo: ... Mtodo cdigoControl(cierto, falso) e o e.cdigoControl(falso,cierto) o n cdigoControl o ... n NodoNo Por ultimo, nos queda el problema de cmo generar cdigo para los objetos elementales de tipo o o lgico. Las constantes cierto y falso son sencillas, basta con un salto incondicional a la etiqueta o correspondiente (o nada si es None). Para las variables de tipo booleano, hay que decidir una codicacin y hacer que el cdigo correspondiente sea la lectura de la direccin de memoria seguida o o o de un salto condicional y, posiblemente, uno incondicional. La asignacin a una variable lgica se o o har generando el equivalente a dos sentencias, una para asignarle cierto y otra para asignarle el a valor falso. El cdigo completo evaluar la expresin, saltando a la instruccin correspondiente o a o o segn sea necesario. u
Ejercicio 13

Escribe los esquemas correspondientes a las constantes lgicas, el uso de una variable de tipo o lgico y la asignacin a una variable de tipo lgico. o o o Mencin aparte merecen los lenguajes, como C, que no tienen un tipo lgico independiente o o sino que utilizan los enteros. En este caso, las expresiones aritmticas tienen que tener los dos e mtodos generaCdigo y cdigoControl. El segundo llamar al primero y despus generar una e o o a e a instruccin para saltar segn el resultado. o u
Ejercicio 14

Escribe el mtodo cdigoControl para la clase NodoSuma en un lenguaje que interprete el e o cero como falso y cualquier otra cosa como cierto.

Generacin de cdigo o o

13

Alternativamente, podemos tener un nodo que transforme el valor entero en el control de ujo adecuado. Por ejemplo, siguiendo el convenio de C: Objeto NodoEnteroALgico o ... Mtodo cdigoControl(cierto, falso) e o r := e.generaCdigo() o si cierto= None entonces emite(si r!=0 salta cierto) si no emite(si r=0 salta falso) n si si cierto=None y falso=None entonces emite(salta falso) n si liberaReg(r); n cdigoControl o ... n NodoEnteroALgico o

4.

Generacin de cdigo para las estructuras de control o o

Una vez sabemos cmo generar cdigo para las expresiones y asignaciones, vamos a ver cmo o o o podemos generar el cdigo de las estructuras de control. o Hay dos estructuras que ya hemos visto impl citamente. Por un lado, la estructura de control ms simple, la secuencia, consiste simplemente en escribir las distintas sentencias una detrs de a a otra. En cuanto a las subrutinas, bastar con crear el correspondiente prlogo y ep a o logo segn u vimos en el tema anterior. Las sentencias de su cuerpo no tienen nada especial. A continuacin veremos algunas de las estructuras ms comunes de los lenguajes de programao a cin imperativos. Otras estructuras se pueden escribir de forma similar. o

4.1.

Instrucciones condicionales

Comenzamos por la instruccin condicional. Tenemos dos versiones distintas segn si existe o o u no la parte else. Si slo tenemos la parte si, o si condicin entonces o sentencias n si tendremos que generar una estructura similar a esta: condicin o sentencias siguiente: donde siguiente es una etiqueta que tendr que generar el compilador. El bloque de condicin a o saltar a esa etiqueta si la condicin es falsa y a las instrucciones del bloque si es cierta. Podemos a o
c Universitat Jaume I 2006-2007
V F

14

II26 Procesadores de lenguaje

hacer esto en el mtodo de generacin de cdigo de la siguiente manera: e o o Objeto NodoSi: ... Mtodo generaCdigo() e o siguiente:=nuevaEtiqueta(); condicin.cdigoControl(None, siguiente); o o para s en sentencias hacer s.generaCdigo(); o n para emite(siguiente:); n generaCdigo o ... n NodoSi Si tenemos la sentencia completa: si condicin entonces o sentenciassi si no sentenciassino n si el esquema es el siguiente: condicin o sentenciassi salta siguiente sino: sentenciassino siguiente:
Ejercicio 15
V F

Escribe el mtodo de generacin de cdigo para la sentencia condicional completa. e o o


Ejercicio 16

Escribe el resultado de generar cdigo para la sentencia: o if x+3 && y then x:= x+2 else y:= y+1 fi Supn que x e y tienen tipo entero y que estn en las direcciones 1000 y 1004, respectivamente. o a
Ejercicio 17

Escribe el mtodo de generacin de cdigo para la sentencia condicional completa utilizando el e o o mtodo generaCdigo para la expresin. (Este es el que habr que utilizar si no tenemos control e o o a de ujo.)

Generacin de cdigo o o

15

4.2.

Iteracin o
mientras condicin hacer o sentencias n mientras

Veremos primero el bucle mientras. Si tenemos una estructura como:

podemos pensar en un esquema similar a: comienzo: condicin o


V F

sentencias salta comienzo siguiente: Sin embargo, puede ser ms eciente una estructura como la siguiente: a salta condicin o comienzo: sentencias condicin: o condicin o
V F

Ejercicio 18

Por qu es ms eciente el segundo esquema? e a Pista: cuntas veces se ejecuta el salto incondicional en cada caso? a
Ejercicio 19

Escribe el esquema de generacin de cdigo para la sentencia mientras. o o


Ejercicio 20

Cmo podemos implementar instrucciones break y continue similares a las de C? o Vamos a ver ahora la compilacin de la sentencia para segn la sintaxis siguiente: o u para v:=expr1 hasta expr2 hacer sentencias n para Dado que sabemos compilar la sentencia mientras, es tentador transformarla de la siguiente manera: v:=expr1; mientras vexpr2 hacer sentencias v:=v+1; n mientras
c Universitat Jaume I 2006-2007

16

II26 Procesadores de lenguaje

Sin embargo, este esquema tiene un defecto importante: si expr2 alcanza el mximo valor de a su tipo, se producir un desbordamiento al llegar al nal. Para resolverlo, tenemos que cambiar el a esquema: v:=expr1; si v> expr2 saltar siguiente bucle: sentencias si v= expr2 saltar siguiente v:=v+1; saltar bucle siguiente: Observa que entre ambos esquemas hay una diferencia importante en cuanto al valor de la variable de control en la salida. Es habitual que los lenguajes de programacin no establezcan o restricciones acerca del valor de la variable, pero si lo hacen, podr ser necesario modicar el a esquema para tenerlo en cuenta. Finalmente, en muchos lenguajes de programacin, la semntica del bucle para establece que o a las expresiones de los l mites se evalan slo una vez. En ese caso, habr que crear una variable u o a temporal y almacenar en ella el valor de expr2.
Ejercicio 21

Disea el esquema de traduccin de una sentencia for similar a la de C. n o Cmo se implementar en l las instrucciones break y continue? o an e

4.3.

Seleccin mltiple o u

Terminaremos con la instruccin de seleccin mltiple. Esta estructura se puede implementar o o u de varias maneras con distintos grados de eciencia. La forma general de la estructura es: opcin expresin o o v1 : accin1 ; o v2 : accin2 ; o ... vn : accinn ; o otro: accind ; o n opcin o La implementacin ms directa es transformarla en una serie de sentencias si: o a t:=expresin; o si t=v1 entonces accin1 ; o si no si t=v2 entonces accin2 ; o ... si no si t=vn entonces accinn ; o si no accind ; o

Generacin de cdigo o o

17

donde t es una variable temporal. Sin embargo, es ligeramente ms eciente algo parecido a: a t:=expresin; o si t=v1 saltar et1; si t=v2 saltar et2; ... si t=vn saltar etn; accind ; o saltar siguiente; et1: accin1 ; o saltar siguiente; et2: accin2 ; o saltar siguiente; ... etn: accinn ; o siguiente: En cualquier caso, la bsqueda de la opcin correcta tiene un coste lineal con el nmero de u o u opciones. Esto es aceptable cuando hay pocas (digamos, diez o menos). Si el nmero de opciones es u elevado, se puede crear una tabla de pares valor-direccin y generar cdigo que busque en la tabla. o o En caso de que los valores posibles estn en un rango i1 . . . in , y haya prcticamente in i1 valores e a distintos, se puede hacer que la tabla tenga la forma de un array indexado de i1 a in con una direccin en cada celda. La sentencia case consistir entonces en saltar a la direccin almacenada o a o en la celda con ndice igual al valor de la expresin. Si el nmero de valores es reducido con respecto o u al tamao del rango, interesa generar cdigo que implemente bien una bsqueda en una tabla de n o u dispersin (tabla hash) o bien una bsqueda binaria. o u

5.

Generacin de cdigo mquina o o a

Esta fase recibe a la entrada un programa en un lenguaje intermedio (cdigo de pila, de tres o direcciones, estructurado como rbol, etc) y emite cdigo de mquina para una mquina objetivo a o a a (cdigo objeto). La generacin de cdigo de mquina con el correspondiente optimizador forman o o o a el back end del compilador. Debe escribirse un back end para cada mquina para la que deseamos a generar cdigo. o Usualmente, el cdigo de entrada ya ha sufrido una primera fase de optimizacin que permio o te eliminar cierta redundancia del cdigo, aprovechar mejor el espacio (eliminando las variables o temporales que no se necesitan), entre otras mejoras posibles.

5.1.

Traduccin de cdigo intermedio a intrucciones de mquina o o a

El proceso de traduccin directo es relativamente sencillo. Cada instruccin del cdigo intero o o medio puede traducirse por una secuencia de instrucciones de mquina. a As las instrucciones: r1:= &FP[-3] r2:= &FP[-2] r1:= r1+r2 &FP[-1]:= r1 pueden traducirse al ensamblador de Pentium por: movl addl addl movl -12(%ebp), %eax -8(%ebp), %edx %edx, %eax %eax, -4(%ebp)

c Universitat Jaume I 2006-2007

18

II26 Procesadores de lenguaje

Como ves, hemos tenido que emplear los registros disponibles en el procesador. Adems, hemos a ajustado los tamaos de modo que las variables que ocupaban una posicin de memoria ahora n o ocupan cuatro.

5.2.

Seleccin de instrucciones o

Aunque seguir el proceso mencionado antes producir cdigo ejecutable correcto, si se quiere a o hacer que el cdigo sea eciente hay que tener cuidado con cmo se hace la seleccin. En particular, o o o muchos lenguajes de mquina tienen instrucciones especializadas que permiten ahorrar espacio, a accesos a memoria y/o una ejecucin ms eciente. o a Por ejemplo, si tenemos r1:= &FP[-1] r2:= 1 r1:= r1 + r2 &FP[-1] := r1 es mejor emplear instrucciones de incremento: leal incl -4(%ebp), %eax (%eax)

Estas cuestiones estn en la frontera entre generacin de cdigo y optimizacin de cdigo. a o o o o

5.3.

Una pasada/mltiples pasadas u

Los generadores de cdigo de mquina pueden ser de una pasada o de mltiples pasadas, en o a u funcin del nmero de veces que debe leerse el cdigo intermedio. El principal problema lo plantean o u o los saltos hacia adelante. Cada vez que encontramos una denicin de etiqueta, recordamos la o direccin de memoria en la que se encuentra. As cuando encontramos un salto con posterioridad, o , sabremos sustituir la etiqueta por la direccin que le corresponde. Pero si encontramos un salto a o una etiqueta que an no est denida no podemos efectuar esa sustitucin. u a o Hay dos formas bsicas de resolver el problema de los saltos hacia adelante: a Backpatching: (una sola pasada) se mantiene una tabla de instrucciones que referencien etiquetas no denidas previamente. Cada instruccin con una referencia hacia adelante genera o una instruccin incompleta. Cada denicin de etiqueta se almacena en una tabla de etiqueo o tas con su direccin. Cuando hemos nalizado de leer el cdigo de entrada, el generador de o o cdigo puede visitar la tabla de instrucciones incompletas y completar los datos que faltan. o En generadores de mltiples pasadas, la primera de ellas averigua dnde se dene cada u o etiqueta. Una segunda pasada reemplaza todas las referencias a etiquetas por sus respectivos valores. Otra manera de resolver el problema es generar cdigo ensamblador y que un ensamblador o resuelva el problema. Lgicamente, el ensamblador se encontrar con el mismo problema y usar o a a alguna de las estrategias anteriores. Esta opcin es muy interesante por s misma, ya que este es slo o o uno de los mltiples problemas que tiene el paso de una representacin en lenguaje ensamblador u o a una representacin en lenguaje de mquina. o a

5.4.

Reserva de registros

Aunque cada mquina presenta una arquitectura en principio distinta, una caracter a stica comn u a casi todas es disponer de un juego de registros (unidades de memoria especiales mucho ms a rpidas que la memoria convencional y/o que permiten ejecutar operaciones que no pueden hacerse a directamente con la memoria).

Generacin de cdigo o o

19

Por ejemplo, hay mquinas (PDP-8) con un solo registro aritmtico (llamado acumulador) sobre a e el que se efectan todas las operaciones aritmticas. Otras (80x86) tienen un reducido conjunto u e de registros que no son de propsito general, es decir, sobre cada uno puede realizarse un conjunto o determinado de operaciones. (En estas mquinas no se reduce el problema de la reserva de registros: a muchas de las operaciones deben hacerse en registros particulares.) Las arquitecturas ms modernas tienen un nmero moderado o grande de propsito general. Por a u o ejemplo, M680x0 tiene 16 registros de propsito general y las arquitecturas RISC pueden tener ms o a de 500 registros (divididos en bancos de, por ejemplo, 32 registros utilizables simultneamente). a Existen varios problemas a la hora de decidir cmo se usan los registros. Si nuestro lenguaje o intermedio opera sobre pila, habr que transformar parte de las operaciones para que empleen a los registros del procesador. Por otro lado, los lenguajes intermedios que utilizan registros suelen tenerlos en un nmero ilimitado y los manejan de manera que todos los registros tienen las mismas u propiedades. Esto hace que la asignacin de registros a registros reales o direcciones de memoria no o sea trivial. Por otro lado, si el lenguaje intermedio opera slo sobre direcciones de memoria, habr o a que hacer que algunas variables puedan estar en registros, al menos en determinados momentos. De esta manera se incrementar la eciencia del cdigo generado. a o Una manera de reducir la complejidad es seguir una pol tica que determine qu variables e deben estar en registros en qu per e odos de tiempo. Esta pol tica puede venir dada por el sistema operativo, la arquitectura o por el autor del compilador. As se pueden reservar algunos registros , para variables intermedias, otros para paso de parmetros, etc. Estos conjuntos no tienen por a qu ser disjuntos, pero debe estar denido qu papel juega cada registro en un momento dado. e e Otro aspecto importante es decidir si es el llamador o el llamado el encargado de guardar aquellos registros que puedan variar en una subrutina. Podr amos pensar que la reserva es optimizacin de cdigo, pero (al menos parte) suele encono o trarse en la fase de generacin de cdigo. Una correcta seleccin de registros reduce el nmero de o o o u instrucciones y reduce el nmero de referencias a memoria. Esto ultimo es particularmente imporu tante en mquinas RISC, en las que se procura ejecutar una instruccin por ciclo de mquina. a o a

6.

Resumen
Dos fases en la generacin de cdigo: o o Cdigo intermedio. o Cdigo de mquina (o ensamblador). o a Mquinas virtuales: a Fcil generar cdigo para ellas. a o Pocas restricciones (p.ej. nmero innito de registros). u Pocas instrucciones. Tipos de cdigo intermedio: o Lineales: pila y tres direcciones. Arboles y grafos. Mixtos.

Cdigo para expresiones: o Evaluamos operandos. Si es necesario, se crea un temporal. Calculamos y guardamos el resultado. Coercin de tipos: o Impl cita en el nodo del operador.
c Universitat Jaume I 2006-2007

20

II26 Procesadores de lenguaje

Con nodos expl citos. Acceso a vectores: Desde una direccin base virtual. o Se genera cdigo para la expresin y se suma a la direccin base. o o o Expresiones lgicas: o Mediante valores expl citos. Mediante control de ujo. Estructuras de control: Interesante evaluar la condicin mediante control de ujo. o La evaluacin de los l o mites de la sentencia para puede ser delicada. Distintas posibilidades para la seleccin mltiple: o u Bsqueda lineal. u Tabla. Bsqueda binaria. u Generacin de cdigo mquina: o o a Se traduce cada instruccin o estructura del cdigo intermedio a instrucciones de mo o a quina. Es importante hacer bien la seleccin. o Puede necesitarse ms de una pasada (o dejrselo a un ensamblador). a a La reserva de registros es cr tica.

Você também pode gostar