La generación de código para expresiones
Enviado por jessenc • 23 de Mayo de 2014 • Tutorial • 5.453 Palabras (22 Páginas) • 172 Visitas
4o Ingenier´ıa Inform´atica
II26 Procesadores de lenguaje
Generaci´on de c´odigo
Esquema del tema
1. Introducci´on
2. C´odigo intermedio
3. Generaci´on de c´odigo para expresiones
4. Generaci´on de c´odigo para las estructuras de control
5. Generaci´on de c´odigo m´aquina
6. Resumen
1. Introducci´on
Una vez ha terminado el an´alisis y se ha obtenido un AST decorado, comienza la generaci´on
de c´odigo. Esta fase suele dividirse en dos partes:
Generaci´on de c´odigo intermedio.
Generaci´on de c´odigo de m´aquina.
El primero es c´odigo para una m´aquina virtual. Estas m´aquinas se definen con dos objetivos:
Ser lo suficientemente simples como para poder generar c´odigo para ellas de manera sencilla,
pero con la suficiente riqueza para poder expresar las construcciones del lenguaje fuente.
Estar lo suficientemente pr´oximas a los procesadores reales para que el paso del c´odigo
intermedio al c´odigo de m´aquina sea pr´acticamente directo.
Para conseguirlo, las m´aquinas virtuales tienen una serie de caracter´ısticas simplificadoras. Por
ejemplo:
Tienen pocos modos de direccionamiento.
Pueden tener un n´umero ilimitado de registros (que pueden estar organizados en forma de
pila).
Tienen un juego de instrucciones relativamente simple.
En principio, el c´odigo intermedio es independiente del lenguaje de programaci´on fuente. En la
pr´actica, es habitual que se definan c´odigos intermedios que faciliten la representaci´on de deter-
minadas caracter´ısticas del lenguaje fuente. As´ı, el P-code est´a pensado para traducir Pascal y el
Java-bytecode es muy bueno para el Java.
Otra de las ventajas del c´odigo intermedio es que facilita la escritura de compiladores para
distintas m´aquinas. La traducci´on del lenguaje a c´odigo intermedio ser´ıa id´entica en todas y lo
unico que cambiar´ıa ser´ıa el traductor de c´odigo intermedio a c´odigo de m´aquina. Esta idea de
independencia del c´odigo intermedio de la m´aquina puede llevarse un paso m´as all´a, como se hace
en diversos lenguajes, de los cuales Java es quiz´a el m´as representativo. La idea es compilar el
lenguaje a un c´odigo intermedio que despu´es es interpretado. De esta manera, un mismo “binario”
puede ejecutarse en m´aquinas con arquitecturas y sistemas operativos totalmente distintos.
Una vez generado el c´odigo intermedio, se traduce ´este a c´odigo de m´aquina. La traducci´on
tiene que hacerse de manera que se aprovechen adecuadamente los recursos de la m´aquina real.
2 II26 Procesadores de lenguaje
Aunque en principio estas dos fases bastan para la generaci´on del c´odigo final, en la pr´actica
est´an mezcladas con fases de optimizaci´on. Es habitual tener entonces un esquema similar al que
vimos en el primer tema:
Programa fuente
An´alisis
An´alisis
l´exico
´
An´alisis
sint´actico
An´alisis
sem´antico
S´ıntesis
Generaci´on de
c´odigo intermedio
Optimizaci´on de
c´odigo intermedio
Generaci´on de
c´odigo objeto
Optimizaci´on de
c´odigo objeto
Programa objeto
Podr´ıamos entonces decir que la generaci´on de c´odigo tiene como objetivo generar el ejecutable
que despu´es emplear´a el usuario. Sin embargo, es habitual que el producto del compilador no
sea directamente un fichero ejecutable. Es bastante m´as com´un que sea un fichero en lenguaje
ensamblador. De esta manera, se evitan problemas como tener que medir el tamano exacto de las
instrucciones o llevar la cuenta de sus direcciones. Adem´as, es muy probable que el c´odigo generado
tenga referencias a objetos externos como funciones de biblioteca. Estas referencias externas ser´an
resueltas por el enlazador o el cargador.
1.1. Lo m´as f´acil: no generar c´odigo
Dado que la generaci´on de c´odigo es una tarea compleja, especialmente si queremos que sea
eficiente y razonablemente compacto, es interesante buscar maneras de evitar esta fase. De hecho
existe una manera sencilla de evitar la generaci´on de c´odigo (al menos, la generaci´on de c´odigo
m´aquina) que permite obtener ejecutables de muy buena calidad: generar c´odigo en otro lenguaje
para el que haya un buen compilador.
Hoy en d´ıa, pr´acticamente cualquier sistema tiene compiladores de C de gran calidad. Es
posible, por tanto, traducir nuestro lenguaje a C y despu´es compilar el programa obtenido con un
buen compilador. Esto tiene varias ventajas:
El tiempo de desarrollo de nuestro compilador se reduce considerablemente.
En muchos sistemas, el compilador de C genera c´odigo de muy alta calidad ya que de ´el
dependen muchos programas cr´ıticos (entre ellos el propio sistema operativo).
Nos permite tener un compilador para gran n´umero de m´aquinas con un esfuerzo m´ınimo
(el de adaptarse a las pequenas diferencias entre unos compiladores y otros). De hecho,
en algunos casos se habla del lenguaje C como de un “ensamblador independiente de la
m´aquina”.
En el caso de lenguajes de muy alto nivel o de paradigmas como el funcional y el l´ogico, esta es
pr´acticamente la elecci´on un´anime. Hay incluso propuestas de lenguajes como el C--, que buscan
un lenguaje reducido y con ciertas caracter´ısticas de m´as bajo nivel como lenguaje destino para
muchos compiladores.
Si hemos construido el ´arbol mediante clases, la traducci´on a C es pr´acticamente trivial para
muchas de las construcciones habituales en los lenguajes imperativos. Por ejemplo, para los nodos
Generaci´on de c´odigo 3
correspondientes a las expresiones aritm´eticas, podemos utilizar algo parecido a:
Objeto NodoSuma:
...
M´etodo traduceaC()
devuelve (i.traduceaC()+ "+" +d.traduceaC());
fin traduceaC
...
fin NodoSuma
donde
...