Generación de Código

Descripción

En esta fase se tiene ya un programa válido. Por tanto, se puede proceder a generar el lenguaje de salida.

De nuevo, para simplificar esta tarea, se divide en dos etapas: Gestión de Memoria y Selección de Instrucciones.

Gestión de Memoria

En los lenguajes de alto nivel no hay que preocuparse de dónde almacenar cada variable; se asume que el compilador se hará cargo de ello. Por ejemplo, en el siguiente fragmento se está indicando al compilador que busque un lugar de la memoria libre para guardar un 100. Nos es indiferente qué lugar de la memoria sea éste.

int data;
data = 100;

Pero, a bajo nivel, se deberá generar un código que escriba en una dirección de memoria concreta.

move [8], 100

Por tanto, la fase de Gestión de Memoria tiene como misión:

  • Determinar dónde se va a ubicar cada variable y, una vez determinado esto, asignarla su dirección.
  • Determinar qué estructura interna tiene cada variable (tamaño y qué se guardará en cada byte).

Esta fase recorre el árbol centrándose en un tipo de nodo: las definiciones de variables (nodos defVar). Y a dichos nodos les añade un nuevo atributo: la dirección que les corresponda (atributo dir o dirección).

En el ejemplo siguiente, dado que sólo hay una variable y además es global, bastaría con poner la dirección cero en su nodo defVar.

Sin embargo, como se verá en el tema correspondiente, en un programa más completo habrá que tener en cuenta más factores a la hora de asignar direcciones en función de:

  • El ámbito de la variable (por ejemplo si es un parámetro o una variable local).
  • El tipo de la misma (por ejemplo, si es un array o estructura).

Selección de Instrucciones

Una vez que se tienen todas las variables ubicadas en memoria, ya se puede generar el código.

La misión de la etapa de Selección de Instrucciones es, como su nombre indica, determinar qué instrucciones se generarán por cada estructura del lenguaje (nodo del árbol).

Para ello, se asocia a cada nodo una plantilla de código (en realidad, una función de código, pero estos detalles se verán en el tema correspondiente). Una plantilla de código es, básicamente, un conjunto reducido de instrucciones que hay que generar al recorrer dicho nodo. La idea es que, si cada plantilla es correcta, el programa total generado mediante la combinación de dichas plantillas será también correcto.

Para obtener el código de salida, basta con situarse en el nodo raíz y seguir las instrucciones de su plantilla.

Una plantilla tendrá principalmente dos tipos de elementos:

  • Llamadas a las plantillas de sus hijos (en el ejemplo anterior, sería code⟦sentencias⟧ ). En este caso, habrá que visitar a dichos hijos y seguir sus plantillas antes de seguir con la del padre.
  • Instrucciones del lenguaje de salida (en negrita en la imagen anterior). En este caso, simplemente hay que mandar dicha instrucción a la salida como parte del programa a generar.

Siguiendo el método anterior, el programa que saldría sería el siguiente.

push 0
load
push 2
add
out

Ejemplo

✏️ En este punto se recomienda ver el ejemplo 110.