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.
Top Related