Generación de código Índice (i) Marina de la Cruz Alfonso Ortega • Definición del tipo semántico • Ejemplo de uso del tipo semántico para la comprobación de tipos • Declaración de variables • Modificación de la gramática • Deducción del tipo y del número de referencias • Generación de código • • • • • • • • • Aclaraciones previas a la generación de código de sentencias Generación e código para desreferenciación Generación de código para expresiones Generación de código para sentencias de asignación Generación de código para entrada de datos Generación de código para salida de datos Generación de código para el principio del programa Generación de código para el final del programa Estructura de un programa en NASM 1 Prácticas de compiladores 2004-2005 Definición del tipo semántico (I) • El tipo semántico (tipo de los atributos de los símbolos en el árbol de análisis sintáctico) tiene que contemplar todos los casos posibles de valores semánticos de cualquier símbolo del lenguaje (terminal o no terminal). • El valor semántico de los terminales lo proporciona el analizador léxico: • los identificadores tiene como valor semántico su lexema • las constantes numéricas tienen como valor semántico su valor numérico • las constantes booleanas tienen como valor semántico 0 (false) o 1 (true). No olvidar modificar el fichero asple.l para las reglas de las constantes booleanas. • Para la comprobación de tipos se pueden utilizar los siguientes atributos semánticos: • tipo: que puede ser “int”, “bool” o “error”. • número de referencias: que puede ser 0, 1, 2, 3, … • Posteriormente se incorporarán nuevos atributos semánticos, por ejemplo etiquetas para generar código para las sentencias de control. Prácticas de compiladores 2004-2005 2 Definición del tipo semántico (II) • El tipo semántico se define con la declaración %union en el fichero de especificación de Yacc/Bison. • El tipo union sólo permite la utilización de uno de sus campos, pero hay símbolos con un valor semántico múltiple, por ejemplo, un identificador de una variable tiene un atributo para el lexema, otro para el tipo y otro para el número de referencias. Para permitir la multiplicidad de atributos, se puede definir en la declaración %union un sólo campo, atributos, cuyo tipo sea tipo_atributos, que es un tipo definido como struct con tantos campos como sea necesario para contemplar todos los casos posibles de valores semánticos de cualquier símbolo del lenguaje (terminal o no terminal). El tipo_atributos lo definimos en un .h aparte. 3 Prácticas de compiladores 2004-2005 Definición del tipo semántico (III) asple.y • En la declaración %union del fichero asple.y, se define un sólo campo, atributos, cuyo tipo sea tipo_atributos (definido en el fichero asple.h). • Se añaden las correspondientes declaraciones %token. ¿qué símbolos terminales tienen valor semántico? • Se añaden las correspondientes declaraciones %type. Todos los no terminales tienen valor semántico para permitir la propagación de atributos en el árbol de análisis. Prácticas de compiladores 2004-2005 %{ #include “asple.h” %} %union { tipo_atributos atributos; } %token %token %token %token <atributos> <atributos> <atributos> <atributos> TOK_ID TOK_NUM TOK_FALSE TOK_TRUE /* resto de los tokens sin valor */ /* semántico */ %type <atributos> program %type <atributos> dcl_train %type <atributos> sm_train /* resto de los no terminales */ ... %% ... %% ... 4 Definición del tipo semántico (IV) • En el fichero asple.h, se define el tipo asple.h tipo_atributos como un struct con los siguientes campos : #ifndef ASPLE_H #define ASPLE_H • valor semántico de los terminales: • lexemaID: para identificadores • valorNUM: para constantes enteras • valorBOOL: para constantes booleanas #define MAXID 255 #define TIPO_ERROR 0 #define TIPO_BOOL 1 #define TIPO_INT 2 • valor semántico para comprobación de tipos: • tipo: para comprobación de tipos básicos • nrefs: para comprobación de número de referencias. /* otros defines */ typedef struct { char lexemaID[MAXID+1]; int valorNUM; int valorBOOL; int tipo; int nrefs; }tipo_atributos; • En el fichero asple.h, se definen: • una constante que define la longitud máxima de los identificadores. • las constantes que definen los distintos tipos. #endif 5 Prácticas de compiladores 2004-2005 Ejemplo de uso del tipo semántico para la comprobación de tipos • • • 1 2 Supongamos la declaración int X; Supongamos la declaración bool B; Supongamos la expresión X+B COMPROBACIÓN DE TIPOS exp El analizador léxico identifica un TOK_ID y se lo comunica al analizador sintáctico tipo TIPO_INT devolviendo TOK_ID y cargando su valor nrefs 1 semántico en el campo correspondiente yylval.atributos.lexemaID El analizador sintáctico apila en su pila de análisis el TOK_ID y su valor semántico. tipo: TIPO_INT nrefs: 1 El analizador sintáctico reduce la producción exp::=TOK_ID. Busca en la tabla de símbolos y obtiene el tipo y el número de referencias del identificador Y calcula el tipo y el número de referencias X del símbolo de la parte izquierda como Tabla de copia de los mismos atributos del símbolos símbolo de la parte derecha. 3 exp + Prácticas de compiladores 2004-2005 tipo TIPO_BOOL nrefs 1 tipo: TIPO_BOOL nrefs: 1 2 2 TOK_ID 3 Se dispone de los atributos necesarios ($1 y $3) para realizar la comprobación de tipos. exp B Tabla de símbolos TOK_ID 1 1 X + B 6 Declaración de variables (I) Modificación de la gramática (i) • Una lista de identificadores de variables en ASPLE tiene la forma: idlist: TOK_ID | TOK_ID ‘,’ idlist • Cada vez que se declara una variable hay que guardarla en la tabla de símbolos, por lo tanto dicha acción hay que realizarla en las posiciones marcadas con el símbolo : idlist: TOK_ID | TOK_ID ‘,’ idlist • Como las acciones semánticas se ejecutan cuando se reduce la regla, para poder guardar una variable en la tabla de símbolos cuando se declara, es necesario modificar la gramática de la siguiente manera: idlist: idv | idv ‘,’ idlist idv: TOK_ID 7 Prácticas de compiladores 2004-2005 Declaración de variables (II) Modificación de la gramática (ii) idv: TOK_ID Se modificará cuando se implementen las funciones Comprobación semántica Buscar en la tabla de símbolos el identificador $1.lexemaID NO Insertar el identificador en la tabla de símbolos clave = $1.lexemaID clase = CLASE_VARIABLE tipo = el que tenga número de referencias = las que tenga número de parámetros = 0 posición del parámetro = 0 número de variables locales = 0 posición de variable local = 0 Prácticas de compiladores 2004-2005 ¿ Existe ? SI Mostrar mensaje de error semántico “Identificador $1.lexemaID duplicado” Incrementar el número de errores semánticos 8 Declaración de variables (III) Deducción del tipo y del número de referencias (i) • Una declaración de variables en ASPLE puede ser, por ejemplo: int X, Y, Z; La producción de la gramática: declaration: mode idlist ‘;’ indica que lo primero que se identifica es el modo, en este caso int, y después la lista de variables, en este caso X, Y, Z. Hay que encontrar un mecanismo para recordar que el tipo int afecta a las tres variables X, Y, Z. • Sugerencia: • declarar una variable global global_tipo que se actualice con el valor correspondiente cada vez que se reduzca una de las dos primeras producciones de mode. • declarar una variable global global_nrefs que se inicialice a 1 cada vez que se reduzca una de las dos primeras producciones de mode y se incremente en 1 cada vez que se reduzca la última producción de mode. 9 Prácticas de compiladores 2004-2005 Declaración de variables (IV) Deducción del tipo y del número de referencias (ii) • La declaración: int X, Y, Z; es analizada de la siguiente forma: dcl_train declaration mode idlist idv : | : : | | : | : declaration declaration dcl_train mode idlist ‘;’ TOK_BOOL TOK_INT TOK_REF mode idv idv ‘,’ idlist TOK_ID 1 mode int dcl_train 9 declaration 8 idlist 2 idv X 7 ; , idlist 3 idv Y global_tipo = TIPO_INT; global_nrefs = 1; Prácticas de compiladores 2004-2005 , 6 idlist 5 4 idv Z 10 Declaración de variables (V) Deducción del tipo y del número de referencias (iii) • La declaración: ref ref ref bool A,B; es analizada así: dcl_train declaration mode idlist idv : | : : | | : | : declaration declaration dcl_train mode idlist ‘;’ TOK_BOOL TOK_INT TOK_REF mode idv idv ‘,’ idlist TOK_ID global_nrefs ++; /*4*/ dcl_train 11 declaration 10 idlist 9 4 mode ref global_nrefs ++; /*3*/ ref global_nrefs ++; /*2*/ ref 3 mode 5 idv 2 mode A , ; idlist 8 idlist 7 idv 6 1 mode B global_tipo = TIPO_BOOL; global_nrefs = 1; bool 11 Prácticas de compiladores 2004-2005 Declaración de variables (VI) Generación de código (i) • Cuando termina la sección de declaraciones de un programa APSLE, 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. program: TOK_BEGIN dcl_train subroutines stm_train TOK_END • Como las acciones semánticas se ejecutan cuando se reduce la regla, para poder generar código cuando se termina la sección de declaraciones, es necesario modificar la gramática de la siguiente manera: program: declaraciones subroutines stm_train TOK_END declaraciones: TOK_BEGIN dcl_train NOTA: MÁS ADELANTE VEREMOS QUE LA REDUCCIÓN DE LA PRODUCCIÓN DE declaraciones LA VAMOS A APROVECHAR PARA ESCRIBIR LA CABECERA DEL PROGRAMA Y LA CABECERA DEL SEGMENTO DE DATOS. Prácticas de compiladores 2004-2005 12 Declaración de variables (VII) Generación de código (ii) begin X int; A bool; ref ref int Y; ... PROGRAMAR UNA FUNCIÓN ... _X 0 _A 0 _Y _ _Y __Y _ _ _Y Programa ASPLE clave tipo nrefs ... X TIPO_INT 1 ... A TIPO_BOOL 1 ... Y TIPO_INT 3 ... ... ... ... ... Tabla de símbolos segment .data _X dd 0 _A dd 0 _ _ _Y dd 0 _ _ Y dd _ _ _Y _ Y dd _ _ Y ... ___Y Programa NASM 0 MEMORIA Prácticas de compiladores 2004-2005 13 Aclaraciones previas a la generación de código de sentencias • El código que vamos a generar es para una máquina a pila. • Cuando se apile un variable, se apilará siempre su dirección, no su contenido (en NASM es el nombre de la variable) • Cuando se apile una constante, se apilará su valor. • Es necesario un mecanismo que asegure que las etiquetas que se utilicen el en programa ensamblador sean únicas. • Un posible mecanismo es utilizar una variable entera global, etiqueta, que se incremente cada vez que se utiliza. Prácticas de compiladores 2004-2005 14 Generación de código para desreferenciación (I) dereference : TOK_DEREF TOK_ID Comprobación semántica Buscar en la tabla de símbolos el elemento con clave $2.lexemaID Se modificará cuando se implementen las funciones SI NO SI ¿Nº refs <2 ? Cálculo de atributos $$.tipo = elemento.tipo; $$.nrefs = elemento.nrefs-1; NO ¿existe ? Mostrar mensaje de error “Error de indirección” Mostrar mensaje de error “Identificador no declarado” Incrementar el número de errores semánticos Incrementar el número de errores semánticos Generación de código push dword [_elemento.clave] 15 Prácticas de compiladores 2004-2005 Generación de código para desreferenciación (II) dereference : NO Cálculo de atributos $$.tipo = $2.tipo; $$.nrefs = $2.nrefs-1; TOK_DEREF dereference ¿ $2.Nº refs <2 ? SI Mostrar mensaje de error “Error de indirección” Incrementar el número de errores semánticos Generación de código pop dword eax push dword [eax] Prácticas de compiladores 2004-2005 16 Generación de código para expresiones (I) • Las ideas básicas son: • Hacer comprobación de tipos (en los puntos adecuados), por ejemplo, no se pueden sumar variables de distinto tipo. • Hacer otras comprobaciones semánticas si es necesario, por ejemplo, comprobar si una variable ha sido declarada. • Generar código haciendo uso de la pila, desapilando los operadores y apilando los resultados de las operaciones. 17 Prácticas de compiladores 2004-2005 Generación de código para expresiones (II) exp : | | | | | | | | compare : | | constant : | bool_constant : | int_constant : Prácticas de compiladores 2004-2005 exp + exp exp - exp exp * exp - exp TOK_ID constant ( exp ) ( compare ) dereference exp = exp exp <= exp exp > exp bool_constant int_constant TOK_TRUE TOK_FALSE TOK_NUM 18 Generación de código para expresiones (III) int_constant : TOK_NUM CÁCULO DE ATRIBUTOS $$ atributos GENERACIÓN DE CÓDIGO $1 atributos lexemaID lexemaID valorNUM valorNUM valorBOOL valorBOOL tipo nrefs 88 88 tipo TIPO_INT nrefs 0 PILA Fichero ensamblador push dword $1.valorNUM $$.tipo = TIPO_INT; $$.nrefs = 0; 19 Prácticas de compiladores 2004-2005 Generación de código para expresiones (IV) bool_constant : TOK_FALSE CÁCULO DE ATRIBUTOS $$ atributos GENERACIÓN DE CÓDIGO $1 atributos lexemaID lexemaID valorNUM valorNUM valorBOOL valorBOOL tipo nrefs TIPO_BOOL 0 0 tipo 0 nrefs PILA $$.tipo = TIPO_BOOL; $$.nrefs = 0; Prácticas de compiladores 2004-2005 Fichero ensamblador push dword $1.valorBOOL 20 Generación de código para expresiones (V) bool_constant : TOK_TRUE CÁCULO DE ATRIBUTOS $$ GENERACIÓN DE CÓDIGO atributos $1 atributos lexemaID lexemaID valorNUM valorNUM valorBOOL valorBOOL tipo 1 tipo TIPO_BOOL nrefs 1 nrefs 0 PILA Fichero ensamblador push dword $1.valorBOOL $$.tipo = TIPO_BOOL; $$.nrefs = 0; 21 Prácticas de compiladores 2004-2005 Generación de código para expresiones (VI) constant : int_constant CÁCULO DE ATRIBUTOS atributos $$ $1 atributos lexemaID lexemaID valorNUM valorNUM valorBOOL valorBOOL tipo nrefs TIPO_INT tipo nrefs 0 TIPO_INT 0 $$.tipo = $1.tipo; $$.nrefs = $1.nrefs; Prácticas de compiladores 2004-2005 22 Generación de código para expresiones (VII) constant : bool_constant CÁCULO DE ATRIBUTOS atributos $$ atributos $1 lexemaID lexemaID valorNUM valorNUM valorBOOL valorBOOL tipo nrefs tipo TIPO_BOOL nrefs 0 TIPO_BOOL 0 $$.tipo = $1.tipo; $$.nrefs = $1.nrefs; 23 Prácticas de compiladores 2004-2005 Generación de código para expresiones (VIII) exp : constant CÁCULO DE ATRIBUTOS atributos $$ $1 atributos lexemaID lexemaID valorNUM valorNUM valorBOOL valorBOOL tipo El tipo tipo El tipo nrefs Nº refs nrefs Nº refs $$.tipo = $1.tipo; $$.nrefs = $1.nrefs; Prácticas de compiladores 2004-2005 24 Generación de código para expresiones (IX) exp : ‘(’ exp ‘)’ CÁCULO DE ATRIBUTOS atributos $$ atributos $1 lexemaID lexemaID valorNUM valorNUM valorBOOL valorBOOL tipo El tipo tipo El tipo nrefs Nº refs nrefs Nº refs $$.tipo = $2.tipo; $$.nrefs = $2.nrefs; 25 Prácticas de compiladores 2004-2005 Generación de código para expresiones (X) exp : ‘(’ compare ‘)’ CÁCULO DE ATRIBUTOS atributos $$ $1 atributos lexemaID lexemaID valorNUM valorNUM valorBOOL valorBOOL tipo El tipo tipo El tipo nrefs Nº refs nrefs Nº refs $$.tipo = $2.tipo; $$.nrefs = $2.nrefs; Prácticas de compiladores 2004-2005 26 Generación de código para expresiones (XI) exp : dereference CÁCULO DE ATRIBUTOS atributos $$ atributos $1 lexemaID lexemaID valorNUM valorNUM valorBOOL valorBOOL tipo El tipo tipo El tipo nrefs Nº refs nrefs Nº refs $$.tipo = $1.tipo; $$.nrefs = $1.nrefs; 27 Prácticas de compiladores 2004-2005 Generación de código para expresiones (XII) exp : TOK_ID Se modificará cuando se implementen las funciones Comprobación semántica Buscar en la tabla de símbolos el elemento con clave $1.lexemaID SI NO ¿es función? Cálculo de atributos ¿existe? NO SI Mostrar mensaje de error “Identificador no declarado” Mostrar mensaje de error “llamada a función sin parámetros” $$.tipo = elemento.tipo; $$.nrefs = elemento.nrefs; Incrementar el número de errores semánticos Incrementar el número de errores semánticos Cálculo de atributos Generación de código push dword _elemento.clave Prácticas de compiladores 2004-2005 Cálculo de atributos $$.tipo = TIPO_ERROR; $$.nrefs = 0; $$.tipo = TIPO_ERROR; $$.nrefs = 0; 28 Generación de código para expresiones (XIII) exp : exp1 + exp2 Comprobación de tipos SI exp2 exp1 ¿Tipos OK ? NO Comprobación de Nº de referencias PILA SI ¿Nº refs OK ? Generación de código pop dword edx SI $3.nrefs>0 mov edx , [edx] pop dword eax SI $1.nrefs>0 mov eax , [eax] add eax,edx | or eax,edx push dword eax NO Mostrar mensaje de error “La aritmética de punteros no está permitida ” Mostrar mensaje de error “La suma/OR requiere tipos básicos iguales” Incrementar el número de errores semánticos Incrementar el número de errores semánticos 29 Prácticas de compiladores 2004-2005 Generación de código para expresiones (XIV) exp : exp1 - exp2 exp : exp1 * exp2 • La generación de código para las producciones de resta y multiplicación de expresiones es similar a la generación de código para la suma de expresiones descrita en la transparencia anterior. Prácticas de compiladores 2004-2005 30 Generación de código para expresiones (XV) exp : ‘-’ exp %prec MENOSU Comprobación de Nº de referencias SI exp Cálculo de atributos $$.tipo = $2.tipo; $$.nrefs = 0; PILA Mostrar mensaje de error “La aritmética de punteros no está permitida ” Generación de código pop dword eax SI $2.nrefs>0 mov eax , [eax] TIPO_BOOL ¿Tipo? Incrementar el número de errores semánticos TIPO_INT Generación de código not_false#: fin_not# NO ¿OK ? Generación de código neg eax or eax,eax jz near not_false# mov dword eax,0 jmp near fin_not# mov dword eax,1 No olvidar incrementar el número de etiqueta Generación de código push dword eax 31 Prácticas de compiladores 2004-2005 Generación de código para expresiones (XVI) compare : exp1 ‘=’ exp2 Comprobación de tipos SI ¿Tipos OK ? NO Comprobación de Nº de referencias exp2 exp1 SI PILA ¿Nº refs OK ? NO Mostrar mensaje de error “La comparación de igualdad requiere tipos básicos int” Incrementar el número de errores semánticos Generación de código pop dword edx SI $3.nrefs>0 mov edx , [edx] pop dword eax SI $1.nrefs>0 mov eax , [eax] cmp eax, edx jne near no_igual# push dword 1 jmp near fin_igual# no_igual#: push dword 0 fin_igual#: Prácticas de compiladores 2004-2005 Mostrar mensaje de error “La comparación de punteros no está permitida ” Incrementar el número de errores semánticos No olvidar incrementar el número de etiqueta 32 Generación de código para expresiones (XVII) compare : exp1 ‘<=’ exp2 compare : exp1 ‘>’ exp2 • La generación de código para las producciones de comparación “<=“ y “>” de expresiones es similar a la generación de código para la comparación de igualdad de expresiones descrita en la transparencia anterior. 33 Prácticas de compiladores 2004-2005 Generación de código para sentencias de asignación (I) asgt_stm : TOK_ID ‘:=’ exp Comprobación semántica Buscar en la tabla de símbolos el elemento con clave $1.lexemaID SI ¿existe ? NO Comprobación de tipos SI ¿Tipos OK ? Comprobación de Nº de referencias SI ¿Nº refs OK ? Generación de código DISTIGUIR CASOS (ver página siguiente) NO Mostrar mensaje de error “Identificador no declarado” Incrementar el número de errores semánticos NO Mostrar mensaje de error “Asignación incompatible por el número de referencias” Mostrar mensaje de error “Asignación incompatible por los tipos” Incrementar el número de errores semánticos Incrementar el número de errores semánticos Prácticas de compiladores 2004-2005 34 Generación de código para sentencias de asignación (II) asgt_stm : TOK_ID ‘:=’ exp exp PILA CASO 2 Parte izquierda y parte derecha con el mismo número de referencias CASO 1 Parte izquierda 1 referencia y parte derecha 0 referencias Generación de código Generación de código pop dword eax mov dword eax, [eax] mov dword [_elemento.clave],eax pop dword eax mov dword [_elemento.clave],eax Se modificará cuando se implementen las funciones 35 Prácticas de compiladores 2004-2005 Generación de código para sentencias de asignación (III) asgt_stm: dereference ‘:=’ exp Comprobación de tipos SI ¿Tipos OK ? NO Comprobación de Nº de referencias SI ¿Nº refs OK ? Generación de código DISTIGUIR CASOS (ver página siguiente) Prácticas de compiladores 2004-2005 NO Mostrar mensaje de error “Asignación incompatible por el número de referencias” Mostrar mensaje de error “Asignación incompatible por los tipos” Incrementar el número de errores semánticos Incrementar el número de errores semánticos 36 Generación de código para sentencias de asignación (IV) asgt_stm: dereference ‘:=’ exp CASO 2 Parte izquierda y parte derecha con el mismo número de referencias CASO 1 Parte izquierda 1 referencia y parte derecha 0 referencias Generación de código Generación de código pop pop mov mov pop dword eax pop dword edx mov dword [edx],eax dword dword dword dword eax edx eax, [eax] [edx],eax exp dereference PILA 37 Prácticas de compiladores 2004-2005 Generación de código para entrada de datos (I) transput_stm : TOK_INPUT TOK_ID Comprobación semántica Buscar en la tabla de símbolos el elemento con clave $2.lexemaID SI NO NO ¿nrefs>1 ? Generación de código DISTIGUIR DOS CASOS POR TIPOS (ver página siguiente) Prácticas de compiladores 2004-2005 ¿Clase función? ¿existe ? Se modificará cuando se implementen las funciones NO SI SI Mostrar mensaje de error “No se admiten referencias en sentencia input ” Mostrar mensaje de error “Identificador erróneo en sentencia input ” Incrementar el número de errores semánticos Incrementar el número de errores semánticos Mostrar mensaje de error “Identificador no declarado” Incrementar el número de errores semánticos 38 Generación de código para entrada de datos (II) transput_stm : TOK_INPUT TOK_ID Se modificará cuando se implementen las funciones Apilar la dirección donde guardar la lectura (requerimiento io) Generación de código push dword _elemento.clave TIPO_BOOL TIPO_INT ¿Tipo? Generación de código Generación de código call lee_booleano add esp, 4 call lee_entero add esp, 4 39 Prácticas de compiladores 2004-2005 Generación de código para salida de datos transput_stm : TOK_OUTPUT exp NO SI SI ¿nrefs>1 ? ¿nrefs=1? Mostrar mensaje de error “No se admiten referencias en sentencia output ” Generación de código pop eax mov eax, [eax] push dword eax Incrementar el número de errores semánticos TIPO_BOOL $2.tipo TIPO_INT Generación de código Generación de código call imprime_booleano call imprime_entero Generación de código add esp, 4 call imprime_fin_linea Prácticas de compiladores 2004-2005 40 Generación de código para el principio del programa (I) • Un programa nasm comienza con una cabecera para portabilidad. Esta cabecera se puede escribir “aprovechando” la producción declaraciones en la que se escribe la tabla de símbolos ya que, a continuación de la cabecera se sitúa el segmento de datos. • A continuación del segmento de datos viene el de código, que contiene: • una sección constante que también se puede escribir “aprovechando” la producción declaraciones. • Las funciones • El programa principal • La producción declaraciones quedaría: declaraciones: TOK_BEGIN dcl_train { escribirCabecera(ficheroEnsamblador); escribirTablaSimbolos(ficheroEnsamblador,&tablaSimbolos); escribirSegmentoCodigo(ficheroEnsamblador); } 41 Prácticas de compiladores 2004-2005 Generación de código para el principio del programa (II) • La sección de funciones del programa ASPLE genera para cada función el código ensamblador correspondiente. Pero una vez que terminan las funciones en el programa ensamblador, comienza la rutina main, y para escribir su etiqueta de comienzo se puede utilizar la producción lambda del no terminal subroutines, ya que esta es la última producción que se reduce antes del comienzo de la sección de sentencias del programa ASPLE. La producción subroutines quedaría: subroutines: subroutine subroutines {} | { escribirInicioMain(ficheroEnsamblador); } ; void escribirInicioMain(FILE* fpasm) { fprintf(fpasm, "; -----------------------\n"); fprintf(fpasm, "; PROCEDIMIENTO PRINCIPAL\n"); fprintf(fpasm, "; -----------------------\n"); fprintf(fpasm, "main:\n"); } Prácticas de compiladores 2004-2005 42 Generación de código para el final del programa • Un programa nasm termina con la sentencia ret. Esta sentencia se puede escribir cuando se reduzca la producción del axioma, invocando a la función escribirFin del módulo de soporte generacion.c. (La producción del axioma se modificó previamente para escribir la tabla de símbolos al terminar la sección declarativa). program: declaraciones subroutines stm_train TOK_END { escribirFin(ficheroEnsamblador); } ; void escribirFin(FILE* fpasm) { fprintf(fpasm, "\n"); fprintf(fpasm, "\tret"); } 43 Prácticas de compiladores 2004-2005 Estructura de un programa en NASM %ifdef TIPO_MSVC %define main _main %endif segment .data ... ... segment .text global main extern lee_entero, imprime_entero ... _fun1: ... ... ret _fun2: ... ... ret main: ... ... ret Prácticas de compiladores 2004-2005 Cabecera para portabilidad Segmento de datos Segmento de código 44