Generación de código -...

34
Prácticas de Procesadores de Lenguaje 2007-2008 1 Generación de código Notación Generación de código de la sección declarativa Ubicación de la acción Resultado esperado de la acción Esquema de la acción Contenido del fichero ensamblador antes de la traducción de la primera sentencia Generación de código para el final del programa Aclaraciones previas a la generación de código de sentencias Generación de código para las expresiones de tipo identificador Generación de código para las constantes Introducción Constantes lógicas Constantes enteras Índice Marina de la Cruz Alfonso Ortega Prácticas de Procesadores de Lenguaje 2007-2008 2 Generación de código Generación de código para las operaciones aritméticas Introducción Generación de código para la operación suma Generación de código para el resto de las operaciones aritméticas binarias Generación de código para la negación Generación de código para las operaciones lógicas Introducción Generación de código para la conjunción lógica Generación de código para la disyunción lógica Generación de código para la negación lógica Generación de código para las comparaciones Introducción Generación de código para la comparación “==“ Generación de código para la comparación “!=“ Generación de código para la comparación “<=“ Generación de código para la comparación “>=“ Generación de código para la comparación “<“ Generación de código para la comparación “>“ Índice

Transcript of Generación de código -...

Prácticas de Procesadores de Lenguaje 2007-2008 1

Generación de código

� Notación� Generación de código de la sección declarativa

� Ubicación de la acción� Resultado esperado de la acción� Esquema de la acción

� Contenido del fichero ensamblador antes de la traducción de la primera sentencia� Generación de código para el final del programa� Aclaraciones previas a la generación de código de sentencias� Generación de código para las expresiones de tipo identificador� Generación de código para las constantes

� Introducción� Constantes lógicas� Constantes enteras

Índice Marina de la Cruz

Alfonso Ortega

Prácticas de Procesadores de Lenguaje 2007-2008 2

Generación de código

� Generación de código para las operaciones aritméticas� Introducción� Generación de código para la operación suma� Generación de código para el resto de las operaciones aritméticas binarias� Generación de código para la negación

� Generación de código para las operaciones lógicas� Introducción� Generación de código para la conjunción lógica� Generación de código para la disyunción lógica� Generación de código para la negación lógica

� Generación de código para las comparaciones� Introducción� Generación de código para la comparación “==“� Generación de código para la comparación “!=“� Generación de código para la comparación “<=“� Generación de código para la comparación “>=“� Generación de código para la comparación “<“� Generación de código para la comparación “>“

Índice

Prácticas de Procesadores de Lenguaje 2007-2008 3

Generación de código

� Generación de código para indexación de vectores� Introducción� Vectores de una dimensión� Vectores de dos dimensiones

� Generación de código para operaciones de acceso� Introducción� Diseño de las acciones semánticas

� Generación de código para la liberación de memoria� Generación de código para sentencias de asignación

� Introducción� Asignación de una expresión a un identificador: esquema� Asignación de una expresión a un identificador: ejemplo� Asignación de una expresión al elemento de un vector� Asignación de una expresión a una operación de acceso� Reserva de memoria� Asignación de la dirección de un identificador a otro identificador

Índice

Prácticas de Procesadores de Lenguaje 2007-2008 4

Generación de código

� Generación de código para entrada de datos� Introducción� Lectura de un identificador� Lectura de un elemento de vector

� Generación de código para salida de datos� Introducción� Escritura de una expresión

� Generación de código para sentencias condicionales� Introducción� Condicionales simples� Condicionales compuestas

� Generación de código para sentencias iterativas� Introducción� Sentencia MIENTRAS� Sentencia DESDE_HASTA

� Generación de código para sentencias de selección

Índice

Prácticas de Procesadores de Lenguaje 2007-2008 5

Notación

� Para representar las producciones de la gramática de ALFA se utiliza la notación propia del generador de analizadores sintácticos Bison, y con las siguientes particularidades:

� Los símbolos no terminales se representan en minúsculas� Los símbolos terminales se representan con TOK_ …� Los símbolos terminales de un carácter se representan encerrados entre comillas simples

Prácticas de Procesadores de Lenguaje 2007-2008 6

Generación de código de la sección declarativa

� Cuando termina la sección de declaraciones de un programa ALFA, todas las variables declaradas están almacenadas en la tabla de símbolos. En ese punto es posible generar el código ensamblador correspondiente a la declaración de las variables (segmento de datos) Por lo tanto, el punto adecuado para escribir el segmento de datos del fichero ensamblador es el indicado por � :

programa: TOK_INICIO declaraciones � funciones sentencias TOK_FIN

Como las acciones semánticas se ejecutan cuando se reducen las reglas, para que se ejecute una acción semántica después del no terminal “declaraciones”, se modifica la gramática de la siguiente manera:

programa: tok_inicio_declaraciones funciones sentencias TOK_FINtok_inicio_declaraciones: TOK_INICIO declaraciones �

� Esta acción semántica se puede “aprovechar” para escribir la cabecera del segmento de código.

Ubicación de la acción

Prácticas de Procesadores de Lenguaje 2007-2008 7

Generación de código de la sección declarativa

Resultado esperado de la acción

INICIOENTERO x;LOGICO a;ENTERO ## ppe;VECTOR ENTERO DE 8 ve8;VECTOR LOGICO DE 3 POR 4 u34;…

Programa ALFA

...segment .bss

_x resd 1_a resd 1_ppe resd 1_ve8 resd 8_u34 resd 12

...

Programa NASM

Procesado de la sección declarativa

4321CLASE_VECTORTIPO_LOGICOu34

811CLASE_VECTORTIPO_ENTEROve8

3CLASE_PUNTEROTIPO_ENTEROppe

1CLASE_ESCALARTIPO_LOGICOa

1CLASE_ESCALARTIPO_ENTEROx

Nº columnasNº filasdimensiónnivel de indirección

clasetipoclave

Tabla de símbolos

Reducción de la nueva regla

Prácticas de Procesadores de Lenguaje 2007-2008 8

Generación de código de la sección declarativa

� Según lo visto anteriormente, la acción semántica asociada a la sección declarativa contendría:

� La escritura del segmento de datos� La escritura de la cabecera del segmento de código

� Por razones de estructuración, se puede escribir una librería que contenga funciones de apoyo a la generación de código. De momento esta librería contendría:

� Una función que a partir de la tabla de símbolos escriba el segmento de datos correspondiente� Una función que escriba la cabecera del segmento de código, que es como sigue:

segmentsegmentsegmentsegment ....texttexttexttext

global global global global mainmainmainmain

externexternexternextern lee_enterolee_enterolee_enterolee_entero, , , , imprime_enteroimprime_enteroimprime_enteroimprime_entero, , , , lee_booleanolee_booleanolee_booleanolee_booleano

externexternexternextern imprime_booleanoimprime_booleanoimprime_booleanoimprime_booleano, , , , imprime_cadenaimprime_cadenaimprime_cadenaimprime_cadena, , , , imprime_fin_lineaimprime_fin_lineaimprime_fin_lineaimprime_fin_linea

externexternexternextern reservar_memoriareservar_memoriareservar_memoriareservar_memoria, , , , liberar_memorialiberar_memorialiberar_memorialiberar_memoria

mainmainmainmain::::

Esquema de la acción

Prácticas de Procesadores de Lenguaje 2007-2008 9

Contenido del fichero ensamblador antes de la traducción de la primera sentencia

� Según lo visto anteriormente, el contenido del fichero ensamblador antes de la traducción de primera sentencia sería el siguiente:

segmentsegmentsegmentsegment ....bssbssbssbss

;

; declaración de variables según el contenido de la tabla de símbolos

;

segmentsegmentsegmentsegment ....texttexttexttext

global global global global mainmainmainmain

externexternexternextern lee_enterolee_enterolee_enterolee_entero, , , , imprime_enteroimprime_enteroimprime_enteroimprime_entero, , , , lee_booleanolee_booleanolee_booleanolee_booleano

externexternexternextern imprime_booleanoimprime_booleanoimprime_booleanoimprime_booleano, , , , imprime_cadenaimprime_cadenaimprime_cadenaimprime_cadena, , , , imprime_fin_lineaimprime_fin_lineaimprime_fin_lineaimprime_fin_linea

externexternexternextern reservar_memoriareservar_memoriareservar_memoriareservar_memoria, , , , liberar_memorialiberar_memorialiberar_memorialiberar_memoria

mainmainmainmain::::

Prácticas de Procesadores de Lenguaje 2007-2008 10

Generación de código para el final del programa

� Un programa escrito en NASM termina con la sentencia ret.

� Posteriormente se verá que es necesaria una etiqueta al final del programa, a la que se saltará en el caso en el que ocurra un error de ejecución “controlado”. Por ejemplo, pueden considerarse errores de ejecución “controlados” los siguientes:

� Si se controla que el indexado de vectores esté siempre dentro de los límites permitidos, cada vez que se realice una operación de indexado, si el/los índice(s) se sale(n) del rango permitido, se produce un salto a esta etiqueta.

� Si se quiere controlar que nunca ocurra una división por cero, cuando se compile una sentencia de división, si el denominador es cero, se produce un salto a esta etiqueta.

� Por razones de estructuración, se puede añadir a la librería que contiene las funciones de apoyo a la generación de código una función que escriba la etiqueta de final controlado y la sentencia ret de fin de programa.

� El punto adecuado para generar el código de fin de programa es la regla del axioma de la gramática, ya que es la última regla que se reduce.

Prácticas de Procesadores de Lenguaje 2007-2008 11

Aclaraciones previas a la generación de código de sentencias

� El código ensamblador que genera el compilador de ALFA es para una máquina a pila.

� Cuando sea necesario apilar una variable, se apilará siempre su dirección, no su contenido (en NASM, la dirección de una variable es el nombre de la misma)

� Cuando se apile una constante, se apilará su valor.

� La generación de código implica la creación automática de etiquetas (en sentencias condicionales, bucles de control, comparaciones, etc)

Por lo tanto, es necesario un mecanismo que asegure que las etiquetas que se utilicen en el programa ensamblador son únicas.

Un posible mecanismo es usar una variable entera global (etiqueta), inicializada a un valor concreto, por ejemplo 1, y que sea incrementada cada vez que se utilice en la generación de código.

Prácticas de Procesadores de Lenguaje 2007-2008 12

Generación de código para las expresiones de tipo identificador

� La producción de la gramática correspondiente a las expresiones de tipo identificador es la siguiente:

� exp : TOK_IDENTIFICADOR

� La generación de código que se realiza en esta producción consiste simplemente en apilar la dirección del identificador. Por lo tanto la evolución de la pila gráficamente sería:

� Es importante recordar que si el identificador corresponde a una variable de clase puntero, la dirección que se apila corresponde a la primera de todas las direcciones que ocupa el identificador en memoria.

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

dirección

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 13

Generación de código para las constantes

� El objetivo de la generación de código para las constantes es apilar el valor de la constante. Hay distintas alternativas para conseguir este objetivo.

� Las producciones de la gramática candidatas para insertar en su acción la generación de código correspondiente a las constantes, son aquellas en las que aparecen los símbolos no terminales constante, constante_entera y constante_logica, que son las siguientes por orden de aparición:

� clase_vector: TOK_VECTOR tipo TOK_DE constante_entera� clase_vector: TOK_VECTOR tipo TOK_DE constante_entera TOK_POR constante_entera� caso_estandar: TOK_CASO constante_entera ‘:’ sentencias� exp: constante� constante: constante_logica� constante: constante_entera� constante_logica: TOK_VERDADADERO� constante_logica: TOK_FALSO� constante_entera: TOK_NUMERO

Introducción

Prácticas de Procesadores de Lenguaje 2007-2008 14

Generación de código para las constantes

� 1ª alternativa para gestionar las constantes lógicas:

� El analizador léxico actualiza el atributo “valor_logico” a 1 cuando identifica en la entrada el tokenVERADERO y a 0 cuando reconoce el token FALSO, haciendo por ejemplo:

VERDADERO {...; yylval.atributos.valor_logico=1; ...}

FALSO {...; yylval.atributos.valor_logico=0; ...}

� El analizador semántico propaga el atributo “valor_logico” en las producciones:� constante_logica: TOK_VERADERO { ...; $$.valor_logico = $1.valor_logico; ...}

� constante_logica: TOK_FALSO { ...; $$.valor_logico = $1.valor_logico; ...}

� El generador de código genera la sentencia que apila el valor de la constante lógica en la siguiente producción:

constante: constante_logica{...; fprintf(fichero_asm, "\tpush dword %d\n", $1.valor_logico); ...}

Constantes lógicas

Prácticas de Procesadores de Lenguaje 2007-2008 15

Generación de código para las constantes

� 2ª alternativa para gestionar las constantes lógicas:

� El analizador léxico actualiza el atributo “valor_logico” a 1 cuando identifica en la entrada el tokenVERADERO y a 0 cuando reconoce el token FALSO, haciendo por ejemplo:

VERDADERO {...; yylval.atributos.valor_logico=1; ...}

FALSO {...; yylval.atributos.valor_logico=0; ...}

� El generador de código genera la sentencia que apila el valor de la constante lógica en las siguientes producciones:

� constante_logica: TOK_VERADERO{...; fprintf(fichero_asm,"\tpush dword %d\n", $1.valor_logico); ...}

� constante_logica: TOK_FALSO

{...; fprintf(fichero_asm,"\tpush dword %d\n", $1.valor_logico); ...}

Constantes lógicas

Prácticas de Procesadores de Lenguaje 2007-2008 16

Generación de código para las constantes

� 3ª alternativa para gestionar las constantes lógicas:

� El analizador léxico NO actualiza ningún atributo.

� El generador de código genera la sentencia que apila el valor de la constante lógica en las siguientes producciones:

� constante_logica: TOK_VERADERO{...; fprintf(fichero_asm,"\tpush dword 1\n"); ...}

� constante_logica: TOK_FALSO

{...; fprintf(fichero_asm,"\tpush dword 0\n"); ...}

Una de las ventajas de esta alternativa es que permite suprimir el atributo “valor_logico” de la gramática de atributos.

Constantes lógicas

Prácticas de Procesadores de Lenguaje 2007-2008 17

Generación de código para las constantes

� La gestión de las constantes enteras tiene la siguiente particularidad: el no terminal“constante_entera” requiere un tratamiento diferente dependiendo de si aparece en la sección declarativa o si lo hace en la sección de sentencias.

� En la sección de sentencias, la aparición del no terminal “constante_entera” implica la generación de código para apilar el valor de la constante. Esta acción se puede realizar en la regla del no terminal “constante_entera” o bien en la regla del no terminal “constante” (es simplemente una decisión de diseño)

� En la sección declarativa, la aparición del no terminal constante_entera no requiere generación de código. Las producciones que contienen el no terminal en esta sección son las siguientes:

� clase_vector: TOK_VECTOR tipo TOK_DE constante_entera� clase_vector: TOK_VECTOR tipo TOK_DE constante_entera TOK_POR constante_entera

� Para poder realizar un tratamiento correcto de las constantes enteras dependiendo de la sección en la que aparezcan, es necesario diseñar un mecanismo que lo permita. Hay múltiples alternativas, una de ellas es utilizar una variable global.

Constantes enteras

Prácticas de Procesadores de Lenguaje 2007-2008 18

Generación de código para las operaciones aritméticas

� Las producciones de la gramática correspondientes a las operaciones aritméticas son las siguientes:

� exp : exp ‘+’ exp� exp : exp ‘-’ exp� exp : exp ‘/’ exp� exp : exp ‘*’ exp� exp : ‘-’ exp

� Cuando se reduce una de las anteriores producciones los operandos están en la pila porque han sido reducidos previamente por alguna de las producciones del no terminal “exp”.

� El código ensamblador correspondiente a operaciones aritméticas usa la pila con la siguiente secuencia de acciones:

� Desapilar los operandos.� Realizar la operación aritmética correspondiente.� Apilar el resultado de la operación.

� En todas las producciones, la generación de código se puede hacer directamente desde la acción semántica de la regla o bien, para conseguir mayor nivel estructuración, programando una función en la librería de apoyo a la generación.

Introducción

Prácticas de Procesadores de Lenguaje 2007-2008 19

Generación de código para las operaciones aritméticas

Generación de código para la operación suma

GENERACIÓN DE CÓDIGO

; cargar el segundo operando en edx

pop dword edxSI $3.nivel_indireccion>0

mov edx , [edx]

; cargar el primer operando en eax

pop dword eaxSI $1.nivel_indireccion>0

mov eax , [eax]

; realizar la suma y dejar el resultado en eax

add eax,edx

; apilar el resultado

push dword eax

exp : exp1 ‘+’ exp2

EVOLUCIÓN DE LA PILA

exp2

exp1

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

exp1+exp2

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 20

Generación de código para las operaciones aritméticas

� Como se explicaba anteriormente, la generación de código se puede ubicar directamente en la acción semántica de la regla, o bien programando una función. En éste último caso la función tendría el siguiente aspecto:

void gc_suma(FILE* f, ni_op1, ni_op2)

{

fprintf(f, “; cargar el segundo operando en edx\n”);

fprintf(f, “pop dword edx\n”);

if (ni_op2 > 0)

fprintf(f, “mov edx , [edx]\n”);

fprintf(f, “; cargar el primer operando en eax\n”);

fprintf(f,”pop dword eax\n”);

if (ni_op1 > 0)

fprintf(f, “mov eax , [eax]\n”);

fprintf(f, “; realizar la suma y dejar el resultado en eax \n”);

fprintf(f, “add eax,edx\n);

fprintf(f, “; apilar el resultado\n”);

fprintf(f, “push dword eax\n”);

return;

}

Generación de código para la operación suma

Prácticas de Procesadores de Lenguaje 2007-2008 21

Generación de código para las operaciones aritméticas

� Las producciones correspondientes al resto de las operaciones aritméticas binarias son las siguientes:

� exp : exp ‘-’ exp� exp : exp ‘/’ exp� exp : exp ‘*’ exp

� El código ensamblador correspondiente a las producciones anteriores es similar al de la regla de la suma visto anteriormente. La única diferencia es la instrucción ensamblador que se utilice y las posibles peculiaridades de la misma.

� Para la resta se puede utilizar la instrucción ensamblador siguiente:

sub eax,edx

que resta al contenido del registro eax el conteido del registro edx y deja el resultado en eax.

Generación de código para el resto de las operaciones aritméticas binarias

Prácticas de Procesadores de Lenguaje 2007-2008 22

Generación de código para las operaciones aritméticas

� Para la división se puede utilizar la instrucción ensamblador de división entera idiv. Esta instrucción trabaja de la siguiente manera:

� Asume que el dividendo está en la composición de registros edx:eax

� El divisor es el registro que aparece en la instrucción.� El resultado de la división entera se ubica en el registro eax

� Para que el dividendo esté en la composición de registros edx:eax y realizar correctamente la división se tiene que hacer:

� Cargar el divisor en ecx� Cargar el dividendo en eax� Extender el dividendo a la composición de registros edx:eax (cdq)� Realizar la división (idiv ecx)

� Se puede comprobar que el divisor sea distinto de 0, y si es 0 realizar un salto al final del programa.

� Para el producto se puede utilizar la instrucción ensamblador de multiplicación entera imul. Esta instrucción asume que uno de los operandos está en el registro eax, y el otro operando está en el registro que aparece en la instrucción. El resultado se carga en eax.

Generación de código para el resto de las operaciones aritméticas binarias

Prácticas de Procesadores de Lenguaje 2007-2008 23

Generación de código para las operaciones aritméticas

Generación de código para la negación

GENERACIÓN DE CÓDIGO

; cargar el operando en eax

pop dword eaxSI $2.nivel_indireccion>0

mov eax , [eax]

; realizar la negación. El resultado en eax

neg eax

; apilar el resultado

push dword eax

exp : ‘-’ exp1

EVOLUCIÓN DE LA PILA

ANTES DE LA REDUCCIÓN

exp1

PILA

DESPUÉS DE LA REDUCCIÓN

-exp1

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 24

Generación de código para las operaciones lógicas

� Las producciones de la gramática correspondientes a las operaciones aritméticas son las siguientes:

� exp : exp ‘Y’ exp� exp : exp ‘O’ exp� exp : TOK_NO exp

� De la misma manera que en las operaciones aritméticas, cuando se reduce una de las anteriores producciones los operandos están en la pila porque han sido reducidos previamente por alguna de las producciones del no terminal “exp”.

� El código ensamblador correspondiente a operaciones lógicas usa la pila con la siguiente secuencia de acciones:

� Desapilar los operandos.� Realizar la operación aritmética correspondiente.� Apilar el resultado de la operación.

Introducción

Prácticas de Procesadores de Lenguaje 2007-2008 25

Generación de código para las operaciones lógicas

Generación de código para la conjunción lógica

GENERACIÓN DE CÓDIGO

; cargar el segundo operando en edx

pop dword edxSI $3.nivel_indireccion>0

mov edx , [edx]

; cargar el primer operando en eax

pop dword eaxSI $1.nivel_indireccion>0

mov eax , [eax]

; realizar la conjunción y dejar el resultado en eax

and eax,edx

; apilar el resultado

push dword eax

exp : exp1 ‘Y’ exp2

EVOLUCIÓN DE LA PILA

exp2

exp1

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

exp1 Y exp2

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 26

Generación de código para las operaciones lógicas

Generación de código para la disyunción lógica

GENERACIÓN DE CÓDIGO

; cargar el segundo operando en edx

pop dword edxSI $3.nivel_indireccion>0

mov edx , [edx]

; cargar el primer operando en eax

pop dword eaxSI $1.nivel_indireccion>0

mov eax , [eax]

; realizar la conjunción y dejar el resultado en eax

or eax,edx

; apilar el resultado

push dword eax

exp : exp1 ‘O’ exp2

EVOLUCIÓN DE LA PILA

exp2

exp1

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

exp1 O exp2

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 27

Generación de código para las operaciones lógicas

Generación de código para la negación lógica

GENERACIÓN DE CÓDIGO

; cargar el operando en eax

pop dword eaxSI $2.nivel_indireccion>0

mov eax , [eax]

; ver si eax es 0 y en ese caso saltar a negar_falsoor eax,eaxjz near negar_falso#

; cargar 0 en eax (negación de verdadero) y saltar al final

mov dword eax,0jmp near fin_negacion#

; cargar 1 en eax (negación de falso)

negar_falso#: mov dword eax,1

; apilar eax

fin_negacion#: push dword eax

exp : TOK_NO exp1

EVOLUCIÓN DE LA PILA

ANTES DE LA REDUCCIÓN

exp1

PILA

DESPUÉS DE LA REDUCCIÓN

NO exp1

PILA

UNICIDAD DE ETIQUETAS

etiqueta ++

Prácticas de Procesadores de Lenguaje 2007-2008 28

Generación de código para las comparaciones

� De la misma manera que en las expresiones aritmético-lógicas, el código ensamblador relativo a las comparaciones usa la pila como se describe a continuación:

� Desapilar los elementos que se van a comparar� Realizar la comparación correspondiente� Apilar el resultado de la comparación

� La idea básica de la generación de código para las comparaciones es generar en ensamblador el código que realiza la comparación y deja en la cima de la pila el resultado de la misma (1 si la comparación termina con éxito y 0 en caso contrario).

� Las producciones de la gramática correspondientes a las comparaciones son las siguientes:� comparacion: exp TOK_IGUAL exp� comparacion: exp TOK_DISTINTO exp� comparacion: exp TOK_MENORIGUAL exp� comparacion: exp TOK_MAYORIGUAL exp� comparacion: exp TOK_MENOR exp� comparacion: exp TOK_MAYOR exp

Introducción

Prácticas de Procesadores de Lenguaje 2007-2008 29

Generación de código para las comparaciones

Generación de código para la comparación “==“

GENERACIÓN DE CÓDIGO

; cargar la segunda expresión en edx

pop dword edxSI $3.nivel_indireccion>0

mov edx , [edx]

; cargar la primera expresión en eax

pop dword eaxSI $1.nivel_indireccion>0

mov eax , [eax]

; comparar y apilar el resultado

cmp eax, edxje near igual#push dword 0jmp near fin_igual#

igual#: push dword 1fin_igual#:

comparacion : exp1 TOK_IGUAL exp2

EVOLUCIÓN DE LA PILA

UNICIDAD DE ETIQUETAS

etiqueta ++

exp2

exp1

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

0 ó 1

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 30

Generación de código para las comparaciones

Generación de código para la comparación “!=“

GENERACIÓN DE CÓDIGO

; cargar la segunda expresión en edx

pop dword edxSI $3.nivel_indireccion>0

mov edx , [edx]

; cargar la primera expresión en eax

pop dword eaxSI $1.nivel_indireccion>0

mov eax , [eax]

; comparar y apilar el resultado

cmp eax, edxjne near distinto#push dword 0jmp near fin_distinto#

distinto#: push dword 1fin_igual#:

comparacion : exp1 TOK_DISTINTO exp2

EVOLUCIÓN DE LA PILA

UNICIDAD DE ETIQUETAS

etiqueta ++

exp2

exp1

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

0 ó 1

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 31

Generación de código para las comparaciones

Generación de código para la comparación “<=“

GENERACIÓN DE CÓDIGO

; cargar la segunda expresión en edx

pop dword edxSI $3.nivel_indireccion>0

mov edx , [edx]

; cargar la primera expresión en eax

pop dword eaxSI $1.nivel_indireccion>0

mov eax , [eax]

; comparar y apilar el resultado

cmp eax, edxjle near menorigual#push dword 0jmp near fin_menorigual#

menorigual#: push dword 1fin_menorigual#:

comparacion : exp1 TOK_MENORIGUAL exp2

EVOLUCIÓN DE LA PILA

UNICIDAD DE ETIQUETAS

etiqueta ++

exp2

exp1

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

0 ó 1

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 32

Generación de código para las comparaciones

Generación de código para la comparación “>=“

GENERACIÓN DE CÓDIGO

; cargar la segunda expresión en edx

pop dword edxSI $3.nivel_indireccion>0

mov edx , [edx]

; cargar la primera expresión en eax

pop dword eaxSI $1.nivel_indireccion>0

mov eax , [eax]

; comparar y apilar el resultado

cmp eax, edxjge near mayorigual#push dword 0jmp near fin_mayorigual#

mayorigual#: push dword 1fin_mayorigual#:

comparacion : exp1 TOK_MAYORIGUAL exp2

EVOLUCIÓN DE LA PILA

UNICIDAD DE ETIQUETAS

etiqueta ++

exp2

exp1

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

0 ó 1

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 33

Generación de código para las comparaciones

Generación de código para la comparación “<“

GENERACIÓN DE CÓDIGO

; cargar la segunda expresión en edx

pop dword edxSI $3.nivel_indireccion>0

mov edx , [edx]

; cargar la primera expresión en eax

pop dword eaxSI $1.nivel_indireccion>0

mov eax , [eax]

; comparar y apilar el resultado

cmp eax, edxjl near menor#push dword 0jmp near fin_menor#

menor#: push dword 1fin_menor#:

comparacion : exp1 ‘<‘ exp2

EVOLUCIÓN DE LA PILA

UNICIDAD DE ETIQUETAS

etiqueta ++

exp2

exp1

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

0 ó 1

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 34

Generación de código para las comparaciones

Generación de código para la comparación “>“

GENERACIÓN DE CÓDIGO

; cargar la segunda expresión en edx

pop dword edxSI $3.nivel_indireccion>0

mov edx , [edx]

; cargar la primera expresión en eax

pop dword eaxSI $1.nivel_indireccion>0

mov eax , [eax]

; comparar y apilar el resultado

cmp eax, edxjg near mayor#push dword 0jmp near fin_mayor#

mayor#: push dword 1fin_mayor#:

comparacion : exp1 ‘>‘ exp2

EVOLUCIÓN DE LA PILA

UNICIDAD DE ETIQUETAS

etiqueta ++

exp2

exp1

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

0 ó 1

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 35

Generación de código para indexación de vectores

� Hay dos producciones de la gramática relativas a la operación de indexación de vectores, una para vectores de una dimensión y otra para vectores de dos dimensiones:

� elemento_vector: TOK_IDENTIFICADOR ‘[‘ exp ‘]’� elemento_vector: TOK_IDENTIFICADOR ‘[‘ exp ‘,’ exp ‘]’

� Los objetivos básicos de la generación de código de las producciones anteriores son:� Generar código para comprobar, en tiempo de ejecución, que los índices están dentro de los

rangos permitidos según el tamaño del vector.� Generar código para dejar en la cima de la pila la dirección del elemento indexado.

Introducción

Prácticas de Procesadores de Lenguaje 2007-2008 36

Generación de código para indexación de vectores

Vectores de una dimensión

GENERACIÓN DE CÓDIGO

; Carga del valor del índice en eax

pop dword eaxSI ($3.nivel_indireccion>0)

mov eax , [eax]

; Si el índice es menor que 0 se termina el programa

cmp eax,0jl near fin

; Si el índice es mayor de lo permitido se termina el programa

...

...

elemento_vector: TOK_IDENTIFICADOR ‘[’ exp ‘]’

EVOLUCIÓN DE LA PILA

ANTES DE LA REDUCCIÓN

exp

PILA

PUNTO INTERMEDIOEN LA REDUCCIÓN

PILA

la etiqueta fin está ubicada al final del programa ensamblador justo antes de la última sentencia ret.

� El primer paso consiste en comprobar que el índice está dentro del límite permitido, es decir, entre 0 y el tamaño del vector menos 1 (ambos inclusive)

Prácticas de Procesadores de Lenguaje 2007-2008 37

Generación de código para indexación de vectores

� El segundo paso consiste en generar código para dejar en la cima de la pila la dirección del elemento indexado. Para ello hay que tener en cuenta las siguientes consideraciones:

� En el segmento de datos .bss se reserva un bloque de memoria para almacenar todos los elementos del vector, y por lo tanto es necesario definir la disposición de dichos elementos dentro del bloque. Lo más razonable es ubicar los elementos por orden creciente de índice, es decir, primero el elemento 0, después el 1, y así sucesivamente hasta el elemento (tamaño-1).

� Una vez que se ha elegido una disposición concreta, las operaciones de indexado deben de ser coherentes con la elección.

� Cada elemento ocupa 4 bytes (porque así se ha hecho la reserva de memoria)

� La dirección de inicio del vector es accesible mediante el nombre del vector.

� Si se ubican los elementos por orden creciente de índice, la dirección del elemento [i] sería la siguiente:

dirección elemento [i] = dirección_inicio_vector + i*4

Vectores de una dimensión

Prácticas de Procesadores de Lenguaje 2007-2008 38

Generación de código para indexación de vectores

� Por ejemplo, la siguiente declaración de un vector de 5 posiciones en un programa ALFA:

VECTOR ENTERO DE 5 v;

se traduce en la declaración ensamblador siguiente:

_v resd 5

que reserva en memoria un bloque contiguo de 20 bytes, que se puede interpretar como 5 posiciones de 4 bytes cada una de ellas. Y si se considera que los elementos del vector se disponen dentro del bloque por orden creciente de índice, la memoria tendría el siguiente contenido:

Vectores de una dimensión

v[0]_v

MEMORIA

v[1]_v +4

v[2]

v[3]

_v +8

_v +12

v[4]_v +16

Prácticas de Procesadores de Lenguaje 2007-2008 39

Generación de código para indexación de vectores

Vectores de una dimensión

GENERACIÓN DE CÓDIGO

OPCIÓN 1 OPCIÓN 2

; Cargar en edx la dirección de inicio del vector ; Cargar 4 en edx (nº de bytes de cada elemento del vector)

mov dword edx _$1.lexema_identificador mov dword edx, 4; Cargar en eax la dirección del elemento indexado ; eax = eax*edx, es decir, eax = eax*4

lea eax, [edx + eax*4] imul edx; Apilar la dirección del elemento indexado ; Cargar en edx la dirección de inicio del vector

push dword eax mov dword edx _$1.lexema_identificador; Cargar en eax la dirección del elemento indexado

add eax, edx; Apilar la dirección del elemento indexado

push dword eax

EVOLUCIÓN DE LA PILA

PUNTO INTERMEDIOEN LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

dirección

PILAEl registro eax contiene el valor del índice como consecuencia de la

comprobación de la validez del valor del índice.

Prácticas de Procesadores de Lenguaje 2007-2008 40

Generación de código para indexación de vectores

Vectores de dos dimensiones

elemento_vector: TOK_IDENTIFICADOR ‘[’ exp1 ‘,’ exp2 ‘]’

� La generación de código para la indexación de vectores de dos dimensiones es similar a la correspondiente a vectores de una dimensión.

� En primer lugar se genera código para comprobar que los índices están dentro de los rangos permitidos. En el caso de vectores de una dimensión sólo es necesario comprobar la validez de un índice mientras que si el vector es de dos dimensiones, la comprobación se extiende a los dos índices.

� La idea básica del segundo paso, generación de código para dejar en la cima de la pila la dirección del elemento indexado, es la siguiente: en el segmento de datos .bss se reserva un bloque de memoria para almacenar todos los elementos del vector, y por lo tanto es necesario definir la disposición de dichos elementos dentro del bloque para que las operaciones de indexación sean correctas. Las dos disposiciones más generales son:

� Disposición por filas: en el bloque se sitúan primero los elementos de la primera fila, después los de la segunda, y así sucesivamente.

� Disposición por columnas: dentro del bloque, se almacenan en primer lugar los elementos de la primera columna, después los de la segunda, y así sucesivamente.

Una vez que se ha elegido una disposición concreta, las operaciones de indexado deben de ser coherentes con la elección.

Prácticas de Procesadores de Lenguaje 2007-2008 41

Generación de código para indexación de vectores

� Por ejemplo, si se utiliza una disposición por filas, el elemento [f,j] donde f es la fila, y c la columna, está en la siguiente dirección de memoria:

Dirección elemento [f,c] = dirección_inicio_vector+

(f * numero_columnas + c) * 4

� Si se utiliza una disposición por columnas, el elemento [f,j] donde f es la fila, y c la columna, está en la siguiente dirección de memoria:

Dirección elemento [f,c] = dirección_inicio_vector+

(c* numero_filas + f) * 4

Vectores de dos dimensiones

Prácticas de Procesadores de Lenguaje 2007-2008 42

Generación de código para indexación de vectores

� Por ejemplo, la siguiente declaración de un vector de dos dimensiones, de 3 filas y 4 columnas en un programa ALFA:

VECTOR ENTERO DE 3 POR 4 z;

se traduce en la declaración ensamblador siguiente:

_z resd 12

que reserva en memoria un bloque contiguo de 48 bytes, que se puede interpretar como 12 posiciones de 4 bytes cada una de ellas. Si se considera una disposición por filas de los elementos del vector, la memoria tendría el contenido que se muestra en la figura adjunta.

Vectores de dos dimensiones

v[0,0]_v

MEMORIA

v[0,1]_v +4

v[0,2]

v[0,3]

_v +8

_v +12

v[1,0]_v +16

v[1,1]_v +20

v[1,2]_v +24

v[1,3]_v +28

v[2,0]_v +32

v[2,1]_v +36

v[2,2]_v +40

v[2,3]_v +44

1ª fila

2ª fila

3ª fila

Prácticas de Procesadores de Lenguaje 2007-2008 43

Generación de código para operaciones de acceso

� Una operación de acceso significa acceder a una dirección de memoria “apuntada”.

� En términos de generación de código, “acceder” al contenido de una dirección de memoria significa apilar dicha dirección.

� El operador de acceso de ALFA es “->”.

� Sólo es correcto aplicar el operador “->” a variables que tengan un nivel de indirección superior a 1.

� El usuario de ALFA es el responsable de realizar la reserva de memoria para las variables declaradas con nivel de indirección mayor que 1, utilizando la palabra reservada RESERVAR.

� El compilador de ALFA no tiene que controlar los casos en los que se declare una variable con nivel de indirecciónmayor que 1 y se le aplique el operador de acceso sin haber reservado previamente la memoria.

Introducción

4

pe dir

dir

MEMORIA

INICIOENTERO # pe;

pe = RESERVAR;->pe = 4;…

Prácticas de Procesadores de Lenguaje 2007-2008 44

Generación de código para operaciones de acceso

� Ya que el objetivo en términos de generación de código es apilar la dirección de memoria a la que se realiza el acceso, en esta producción, la dirección que se apila es la que estáalmacenada en la dirección de memoria que ocupa la variable representada por TOK_IDENTIFICADOR. Por lo tanto la instrucción NASM sería:

push dword [_$2.lexema_identificador]

Diseño de las acciones semánticas

acceso: TOK_ACCESO TOK_IDENTIFICADOR

_$2.lexema_identificador dir

dir

MEMORIA

ANTES DE LA REDUCCIÓN

PILA

DESPUÉS DE LA REDUCCIÓN

dir

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 45

Generación de código para operaciones de acceso

� En la producción relativa a la definición recursiva de la operación de acceso, hay que tener en cuanta que en la cima de la pila se encuentra la dirección correspondiente a la reducción del no terminal acceso2. Por lo tanto la generación de código sería:

pop dword eax

push dword [eax]

Diseño de las acciones semánticas

acceso: TOK_ACCESO accesod

Prácticas de Procesadores de Lenguaje 2007-2008 46

Generación de código para la liberación de memoria

� La generación de código correspondiente a la liberación de memoria requiere la invocación de la función liberar_memoria de la librería mem.o de la siguiente manera:

� Apilar la dirección de memoria que se quiere liberar, es decir, apilar el contenido de la variable representada por TOK_IDENTIFICADOR.

push dword [_$2.lexema_identificador]

� Invocar a la función.

call liberar_memoria

� Restaurar el puntero de pila.

add esp, 4

liberacion: TOK_LIBERAR TOK_IDENTIFICADOR

Prácticas de Procesadores de Lenguaje 2007-2008 47

Generación de código para sentencias de asignación

� Las producciones de las sentencias de asignación son: � asignacion : TOK_IDENTIFICADOR ‘=’ exp� asignacion : elemento_vector ‘=’ exp� asignacion : acceso ‘=’ exp� asignacion : TOK_IDENTIFICADOR ‘=’ TOK_RESERVAR� asignacion : TOK_IDENTIFICADOR ‘=’ TOK_DIRECCION TOK_IDENTIFICADOR

� Básicamente, la generación de código de las producciones anteriores tiene como objetivo escribir el código ensamblador que hace efectiva la asignación. Es decir, si por ejemplo la sentencia es una asignación de una constante a un identificador, la generación de código deberá producir las instrucciones en ensamblador que cargan el valor de la constante en la posición de memoria que ocupa el identificador.

� Las características de las partes izquierda y derecha de la asignación, determinan el conjunto de instrucciones en ensamblador que realizan de manera efectiva la asignación.

Introducción

Prácticas de Procesadores de Lenguaje 2007-2008 48

Generación de código para sentencias de asignación

Asignación de una expresión a un identificador: esquema

GENERACIÓN DE CÓDIGO

; Cargar en eax la parte derecha de la asignación

pop dword eax

; Nivel de indirección igual a 1 en la parte izquierda y

; 0 en la parte derecha

mov dword [_$1.lexema_identificador] , eax

; Nivel de indirección igual a ambos lados de la

; asignación

mov dword eax, [eax]mov dword [_$1.lexema_identificador] , eax

asignacion: TOK_IDENTIFICADOR ‘=’ exp

EVOLUCIÓN DE LA PILA

ANTES DE LA REDUCCIÓN

exp

PILA

DESPUÉS DE LA REDUCCIÓN

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 49

Generación de código para sentencias de asignación

Asignación de una expresión a un identificador: ejemplo

...ENTERO ## ppe1, ppe2;...ppe1 = ppe2;...

Valor entero

dir2 dir3

dir3

dir2ppe1

MEMORIA

Valor entero

dir4 dir5

dir5

dir4ppe2

ANTES DE LA REDUCCIÓN

ppe2

PILA

eax

eax

ppe2

eax

dir4

Valor entero

dir2 dir3

dir3

dir4ppe1

MEMORIA

Valor entero

dir4 dir5

dir5

dir4ppe2

1

2

3

PROCESO DE REDUCCIÓN EN 3 PASOS

1. pop eax2. mov dword eax, [eax]3. mov dword [_$1.lexema_identificador] , eax

Prácticas de Procesadores de Lenguaje 2007-2008 50

Generación de código para sentencias de asignación

Asignación de una expresión al elemento de un vector

GENERACIÓN DE CÓDIGO

; Cargar en eax la parte derecha de la asignación

pop dword eax

; Cargar en edx la parte izquierda de la asignación

pop dword edx

SI ($3.nivel_indireccion == 1)mov dword eax, [eax]

mov dword [edx] , eax

asignacion: elemento_vector ‘=’ exp

EVOLUCIÓN DE LA PILA

DESPUÉS DE LA REDUCCIÓN

PILA

ANTES DE LA REDUCCIÓN

elem_vect

PILA

exp

� En este tipo de asignación, la parte izquierda siempre tiene nivel de indirección igual a 1 (recordar que en ALFA no están permitidos vectores de punteros)

Prácticas de Procesadores de Lenguaje 2007-2008 51

Generación de código para sentencias de asignación

Asignación de una expresión a una operación de acceso

GENERACIÓN DE CÓDIGO

; Cargar en eax la parte derecha de la asignación

pop dword eax

; Cargar en edx la parte izquierda de la asignación

pop dword edx

SI ($3.nivel_indireccion >0)mov dword eax, [eax]

mov dword [edx] , eax

asignacion: acceso ‘=’ exp

EVOLUCIÓN DE LA PILA

DESPUÉS DE LA REDUCCIÓN

PILA

ANTES DE LA REDUCCIÓN

dir_acceso

PILA

exp

� Se distinguen dos casos en función de los niveles de indirección de las partes izquierda y derecha de la asignación.

Prácticas de Procesadores de Lenguaje 2007-2008 52

Generación de código para sentencias de asignación

Reserva de memoria

asignacion: TOK_IDENTIFICADOR ‘=’ TOK_RESERVAR

� Una vez realizadas las comprobaciones semánticas oportunas, que consisten en verificar que el identificador está declarado y que es de clase puntero, la generación de código de esta sentencia de asignación consiste básicamente en:

� Realizar la reserva de memoria invocando a la función reservar_memoria de la librería mem.o. Esta función no tiene parámetros de entrada y devuelve en “eax” la dirección de memoria reservada.

call reservar_memoria

� Hacer efectiva la asignación moviendo a la posición de memoria del identificador la nueva dirección reservada (contenido de eax)

mov dword [_$1.lexema_identificador] , eax

� El contenido de la pila es irrelevante para el planteamiento de la generación de código de esta producción de asignación.

Prácticas de Procesadores de Lenguaje 2007-2008 53

Generación de código para sentencias de asignación

Asignación de la dirección de un identificador a otro identificador

asignacion: TOK_IDENTIFICADOR ‘=’ TOK_DIRECCION TOK_IDENTIFICADOR

� Una vez realizadas las comprobaciones semánticas oportunas, que básicamente consiste en comprobar que los identificadores que aparecen a ambos lados de la asignación corresponden a variables declaradas con las siguientes características:

� Son del mismo tipo.� Ninguna de las dos es de clase vector o clase procedimiento.� El nivel de indirección del identificador de la parte izquierda es mayor que 1 y además es igual a 1

más el nivel de indirección del identificador de la parte derecha

� La generación de código consiste en hacer efectiva la asignación moviendo a la posición de memoria del identificador de la parte izquierda la dirección del identificador de la parte derecha.

mov dword [_$1.lexema_identificador] , _$4.lexema_identificador

� El contenido de la pila es irrelevante para el planteamiento de la generación de código de esta producción de asignación.

Prácticas de Procesadores de Lenguaje 2007-2008 54

Generación de código para entrada de datos

� El lenguaje de programación ALFA tiene dos sentencias para la entrada de datos. Las producciones de la gramática para dichas sentencias son:

� lectura_escritura: TOK_LEER TOK_IDENTIFICADOR� lectura_escritura: TOK_LEER elemento_vector

� En las acciones semánticas de las anteriores producciones, además de realizar las comprobaciones semánticas oportunas, se hace uso de la librería io.o de la siguiente manera:

� Si la lectura corresponde a un dato de tipo entero, se invoca a la función lee_entero.� Si la lectura corresponde a un dato de tipo lógico, se invoca a la función lee_booleano.� Las funciones lee_entero y lee_booleano tienen como parámetro de entrada la dirección de

memoria que será destino del dato leído, por lo tanto, antes de invocar a una de ellas es necesario apilar la dirección adecuada.

� Después de la invocación se restaura la pila (manipulando el puntero de pila o desapilando el parámetro apilado previamente)

Introducción

Prácticas de Procesadores de Lenguaje 2007-2008 55

Generación de código para entrada de datos

Lectura de un identificador

lectura_escritura: TOK_LEER TOK_IDENTIFICADOR

� En primer lugar, se prepara la pila para la llamada a la función correspondiente de la librería io.o, apilando la dirección de memoria que será destino del dato leído:

push dword _$2.lexema_identificador

� El segundo paso consiste en invocar a la rutina de lectura adecuada al tipo del identificador:

call lee_entero ; si el identificador es de tipo entero

call lee_booleano ; si el identificador es de tipo lógico

� Por último se restaura el puntero de pila:

add esp, 4

Prácticas de Procesadores de Lenguaje 2007-2008 56

Generación de código para entrada de datos

Lectura de un elemento de vector

lectura_escritura: TOK_LEER <elemento_vector>

� La única diferencia entre la generación de código de esta producción y la anterior es que cuando se reduce esta regla, en la cima de la pila se encuentra la dirección del elemento del vector, y por lo tanto, no es necesario apilarla.

� La invocación de la rutina de lectura adecuada al tipo del vector y la restauración de la pila después de la invocación, se realiza de manera similar a la producción anterior.

Prácticas de Procesadores de Lenguaje 2007-2008 57

Generación de código para salida de datos

� El lenguaje de programación ALFA tiene sólo una sentencia para salida de datos. La producción correspondiente es:

� lectura_escritura: TOK_ESCRIBIR exp

� En la acción semántica de la producción, además de comprobar que la expresión tiene nivel de indirección 0 ó 1, para la generación de código, se hace uso de la librería io.o de la siguiente manera:

� Si la expresión es de tipo entero, se invoca a la función imprime_entero.� Si la expresión es de tipo lógico, se invoca a la función imprime_booleano.� Las funciones imprime_entero e imprime_booleano tienen un único parámetro que corresponde al

VALOR de salida, por lo tanto, antes de invocar a una de ellas es necesario apilar dicho valor.� Después de la invocación se restaura la pila (manipulando el puntero de pila o desapilando el

parámetro apilado previamente)� Una vez escrito el valor de salida, es conveniente realizar un salto de línea. La librería io.o también

proporciona la función imprime_fin_linea que escribe un salto de línea en la salida estándar. Esta función no tiene parámetros de entrada.

� Antes de reducir la producción, en la cima de la pila se encuentra el resultado de la reducción del no terminal exp.

Introducción

Prácticas de Procesadores de Lenguaje 2007-2008 58

Generación de código para salida de datos

Escritura de una expresión

lectura_escritura: TOK_ESCRIBIR exp

GENERACIÓN DE CÓDIGO

; Acceso al valor de exp si es distinto de constante

SI $2.nivel_indireccion == 1pop dword eaxmov eax , [eax]push dword eax

; Si la expresión es de tipo entero ; Si la expresión es de tipo lógico

call imprime_entero call imprime_booleano

; Restauración del puntero de pila

add esp, 4

; Salto de línea

call imprime_fin_linea

EVOLUCIÓN DE LA PILA

ANTES DE LA REDUCCIÓN

exp

PILA

DESPUÉS DE LA REDUCCIÓN

PILA

Prácticas de Procesadores de Lenguaje 2007-2008 59

Generación de código para sentencias condicionales

� El lenguaje de programación ALFA tiene dos formatos de sentencia condicional descritos en las siguientes producciones:

� condicional: TOK_SI exp TOK_ENTONCES sentencias TOK_FIN� condicional: TOK_SI exp TOK_ENTONCES sentencias TOK_SINO sentencias TOK_FIN

� La generación de código de las sentencias condicionales tiene una característica particular que hasta ahora no se ha presentado en el desarrollo del compilador. La bifurcación del flujo de ejecución en función del valor de la condición requiere “recordar” las etiquetas de salto. En concreto, en la primera producción, una vez procesado el no terminal “exp”, si su valor es VERDADERO, el flujo de ejecución continúa por las instrucciones correspondientes a la traducción del no terminal “sentencias”, pero si su valor es FALSO, el flujo debe saltar a una etiqueta inmediatamente posterior a dicho bloque de instrucciones. El punto adecuado para escribir en ensamblador la instrucción de salto se encuentra antes “sentencias”, pero la escritura de la propia etiqueta a la que se salta sólo se puede hacer después de “sentencias”. Por lo tanto, es necesario un mecanismo para asegurar que se usa el mismo nombre de etiqueta en los dos puntos en los que se hace uso de ella.

� Un posible mecanismo para “recordar” etiquetas consiste en:� Modificar la gramática de manera adecuada� Añadir un nuevo atributo semántico que permita propagar etiquetas

Introducción

Prácticas de Procesadores de Lenguaje 2007-2008 60

Generación de código para sentencias condicionales

Condicionales simples

exp

PILA

pop eaxmov eax, [eax] ; si nivel de indirección exp>0cmp eax, 0je near fin_si#

;; exp;

;; sentencias;

fin_si#:

Estos dos bloques de código ensamblador se generan en momentos diferentes pero hacen referencia a una misma etiqueta (fin_si#)

condicional : TOK_SI exp TOK_ENTONCES sentencias TOK_FIN

Prácticas de Procesadores de Lenguaje 2007-2008 61

MODIFICACIÓN DE LA GRAMÁTICA

si_exp_entonces : TOK_SI exp TOK_ENTONCES

condicional: si_exp_entonces sentencias TOK_FIN

Generación de código para sentencias condicionales

Condicionales simples

Generación de código

fin_si#: ; # es $1.etiqueta

Comprobaciones semánticasComprobar que $2.tipo == TIPO_LOGICOComprobar que $2.nivel_indireccion == 0/1

Reserva de etiqueta (#)$$.etiqueta = etiqueta++

Generación de códigopop eaxmov eax , [eax] ; Si nivel de indirección de exp == 1

cmp eax, 0je near fin_si# ; # es $$.etiqueta

condicional : TOK_SI exp TOK_ENTONCES sentencias TOK_FIN

Prácticas de Procesadores de Lenguaje 2007-2008 62

pop eaxmov eax, [eax] ; si nivel de indirección exp>0cmp eax, 0je near fin_si#

;; exp;

;; sentencias;

jmp near fin_sino#fin_si#:

fin_sino#:

;; sentencias;

condicional: TOK_SI exp TOK_ENTONCES sentencias TOK_SINO sentencias TOK_FIN

Generación de código para sentencias condicionales

Condicionales compuestas

exp

PILA

Estos tres bloques de código ensamblador se generan en momentos diferentes pero hacen referencia a las mismas etiquetas (fin_si# y fin_sino#)

Prácticas de Procesadores de Lenguaje 2007-2008 63

Generación de código para sentencias condicionales

condicional: TOK_SI exp TOK_ENTONCES sentencias TOK_SINO sentencias TOK_FIN

Condicionales compuestas

Propagación de atributos$$.etiqueta = $1.etiqueta

Generación de código

jmp near fin_sino# ; # es $1.etiqueta

fin_si#: ; # es $1.etiqueta

Comprobaciones semánticasComprobar que $2.tipo == TIPO_LOGICOComprobar que $2.nivel_indireccion == 0/1

Reserva de etiqueta (#)$$.etiqueta = etiqueta++

Generación de códigopop eaxmov eax , [eax] ; Si nivel de indirección de exp == 1

cmp eax, 0je near fin_si# ; # es $$.etiqueta

MODIFICACIÓN DE LA GRAMÁTICA

si_exp_entonces : TOK_SI exp TOK_ENTONCES

si_exp_entonces_sentencias : si_exp_entonces sentencias

condicional : si_exp_entonces_sentencias TOK_SINO sentencias TOK_FIN

Generación de código

fin_sino#: ; # es $1.etiqueta

Prácticas de Procesadores de Lenguaje 2007-2008 64

Generación de código para sentencias iterativas

� El lenguaje de programación ALFA tiene dos sentencias iterativas cuya sintaxis se describe en las siguientes producciones:

� bucle: TOK_MIENTRAS exp TOK_HACER sentencias TOK_FIN� bucle: TOK_DESDE TOK_IDENTIFICADOR '=' exp TOK_HASTA exp TOK_HACER sentencias

TOK_FIN

� La traducción a ensamblador de las sentencias iterativas tiene características similares a la traducción de sentencias condicionales.La idea básica es la necesidad de implementar un mecanismo que permita “recordar” etiquetas.

Introducción

Prácticas de Procesadores de Lenguaje 2007-2008 65

bucle: TOK_MIENTRAS exp TOK_HACER sentencias TOK_FIN

Generación de código para sentencias iterativas

Sentencia MIENTRAS

exp

PILA

Estos tres bloques de código ensamblador se generan en momentos diferentes pero hacen referencia a las mismas etiquetas (inicio_mientras# y fin_mientras#)

pop eaxmov eax, [eax] ; si nivel de indirección exp>0cmp eax, 0je near fin_mientras#

;; exp;

;; sentencias;

jmp near inicio_mientras#fin_mientras#:

inicio_mientras#:

Prácticas de Procesadores de Lenguaje 2007-2008 66

Generación de código para sentencias iterativas

Sentencia MIENTRAS

Reserva de etiqueta (#)$$.etiqueta = etiqueta++

Generación de código

inicio_mientras#: ; # es $$.etiqueta

Comprobaciones semánticasComprobar que $2.tipo == TIPO_LOGICOComprobar que $2.nivel_indireccion == 0/1

Propagación de atributos$$.etiqueta = $1.etiqueta

Generación de códigopop eaxmov eax , [eax] ; Si nivel de indirección de exp == 1cmp eax, 0je near fin_mientras# ; # es $$.etiqueta

MODIFICACIÓN DE LA GRAMÁTICA

mientras : TOK_MIENTRAS

mientras_exp : mientras exp

bucle : mientras_exp TOK_HACER sentencias TOK_FIN

Generación de código

jmp near inicio_mientras# ; # es $1.etiquetafin_mientras#: ; # es $1.etiqueta

bucle: TOK_MIENTRAS exp TOK_HACER sentencias TOK_FIN

Prácticas de Procesadores de Lenguaje 2007-2008 67

Generación de código para sentencias iterativas

Sentencia DESDE-HASTA

� La generación de código de esta sentencia se propone como trabajo del alumno.

� En el enunciado de la última práctica se describe detalladamente el funcionamiento de la sentencia. Es imprescindible conocer dicho funcionamiento para implementar de manera correcta la generación de código correspondiente.

Prácticas de Procesadores de Lenguaje 2007-2008 68

Generación de código para sentencias de selección

� El lenguaje de programación ALFA tiene una sentencia de selección cuya sintaxis se describe en las siguientes producciones:

� seleccion: TOK_SELECCIONAR ‘(’ exp ‘)’ casos_seleccion TOK_FIN� casos_seleccion: casos_estandar caso_defecto� casos_estandar: caso_estandar� casos_estandar: casos_estandar caso_estandar� caso_estandar: TOK_CASO constante_entera ‘:’ sentencias� caso_defecto: TOK_DEFECTO sentencias

� La generación de código de esta sentencia se propone como trabajo del alumno.

� En el enunciado de la última práctica se describe detalladamente el funcionamiento de la sentencia. Es imprescindible conocer dicho funcionamiento para implementar de manera correcta la generación de código correspondiente.