Generación de Código Objeto

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 19

Semana 4

LENGUAJES
AUTÓMATAS
Unidad 9
GENERACIÓN DE
CÓDIGO OBJETO
Unidad 10
OPTIMIZACIÓN
Material compilado con fines
académicos, se prohíbe su
reproducción total o parcial sin
la autorización de cada autor.
Temario
Unidad 9.
Generación de código objeto
9.1. Registros
9.2. Lenguaje ensamblador
9.3. Lenguaje máquina
9.4. Administración de memoria

Unidad 10.
Optimización
10.1. Tipos de optimización
10.1.1. Locales
10.1.2. Ciclos
10.1.3. Globales
10.1.4. De mirilla
10.2. Costos
10.2.1. Costo de ejecución. (memoria, registros, pilas)
10.2.2. Criterios para mejorar el código
10.2.3. Herramientas para el análisis del flujo de datos

2
UNIDAD 9
GENERACIÓN DE CÓDIGO OBJETO

Según la página It-brain (s.f.):

La generación de códigos puede considerarse como la fase final de la


compilación. A través de la generación de código postal, el proceso de
optimización se puede aplicar al código, pero eso se puede ver como parte
de la fase de generación de código. El código generado por el compilador
es un código objeto de algún lenguaje de programación de nivel inferior,
por ejemplo, el lenguaje ensamblador.

Asimismo, la página It-brain (s.f.) manifiesta que el código fuente escrito en un len-
guaje de nivel superior se transforma en un lenguaje de nivel inferior que origina un
código de objeto de nivel inferior, el cual debería presentar las siguientes propieda-
des mínimas:

• Debe llevar el significado exacto del código fuente.


• Debe ser eficiente en términos de uso de la CPU y administración de memo-
ria.

La figura 1 muestra un esquema de generación de código objeto, en el que pode-


mos observar que una vez que el programa ha sido revisado por los tres tipos de
analizadores se procede a la fase de generación del código intermedio hasta emitir
un código de máquina (código objeto) para una máquina objetivo (Vilar, 2010).

3
Figura 1. Esquema de generación de código objeto. Fuente: Vilar (2010, p. 2).

Finalmente, Sánchez y Rodríguez (2010) sostienen que la generación de código


puede funcionar de dos formas:

• Como una fase independiente que traduzca el código intermedio al código


objeto equivalente
• Integrado con el análisis semántico, generando código objeto a medida que
se encuentran construcciones semánticamente correctas.
• Opción más rápida, pero también más compleja.

9.1.
Registros

En la opinión de la Universitat Jaume I (2011):

Aunque cada máquina presenta una arquitectura distinta al principio, una


característica común en casi todas es disponer de un juego de registros
(unidades de memoria especiales mucho más rápidas que la memoria con-
vencional o que permiten ejecutar operaciones que no pueden hacerse
directamente con la memoria) (p. 24).

En otras palabras, básicamente los registros son la memoria principal de la compu-


tadora. Ahora bien, de manera general, los registros se pueden dividir en dos cate-
gorías (Puentes, 2013):

4
• Registros de propósito general (General Purpose Registers, GPRs): pueden
guardar tanto datos como direcciones y la mayor parte de las computadoras
modernas usa GPR. Además, son fundamentales en la arquitectura de von
Neumann.
• Registros de propósito específico: guardan información específica del estado
del sistema, como el puntero de pila o el registro de estado.

En resumen, operar sobre registros es más rápido y eficiente que operar sobre me-
moria, por lo que una correcta selección de registros reduce el número de instruc-
ciones y el número de referencias a la memoria.

9.2.
Lenguaje ensamblador

Bautista, Rojas y López (2015) expresan que el lenguaje ensamblador (assembly lan-
guage, ASM) traduce un código de bajo nivel a un código ejecutable directamente
por la máquina para la que se ha generado. El lenguaje ensamblador consiste en
un conjunto de mnemónicos que representan instrucciones básicas para las com-
putadoras, microprocesadores, microcontroladores y otros circuitos integrados pro-
gramables. Además, implementa una representación simbólica de los códigos de
máquina binarios y otras constantes necesarias para programar una arquitectura de
procesador y constituye la representación más directa del código máquina específico
para cada arquitectura legible por un programador.

Valverde (2017) menciona que los elementos del lenguaje ensamblador son los si-
guientes:

• Mnemónicos: representación simbólica del campo de operación (COP).


• Etiqueta: nombre que se le asigna a una posición de la memoria de programa,
la cual empieza en la primera columna y suele ir seguida de “:”.
• Operandos: representados por símbolos o constantes y que separan por
comas.
• Comentarios: comienzan con “;”

5
Figura 2. Elementos del lenguaje ensamblador. Fuente: Valverde (2017).

Pero, ¿cómo se genera el código objeto a través del lenguaje ensamblador? El pro-
grama lee un archivo escrito en lenguaje ensamblador y sustituye cada uno de los
códigos mnemotécnicos por su equivalente código máquina. La tabla 1 muestra un
ejemplo de diferentes mnemotécnicos y su respectiva sustitución a código máquina.

Tabla 1. Representación de mnemotécnico y su respectivo código máquina.

Olivares (2012b) e Infante (2013) sostienen que los ensambladores se pueden clasi-
ficar en dos tipos:

• Ensambladores básicos: son de muy bajo nivel y su tarea consiste bá-


sicamente en ofrecer nombres simbólicos a las distintas instrucciones,
parámetros y cosas tales como los modos de direccionamiento.
• Ensambladores modulares (macroensambladores): descendientes de
los ensambladores básicos, los cuales fueron muy populares en las dé-
cadas de 1950 y 1960, antes de la generalización de los lenguajes de
alto nivel. Una macroinstrucción es el equivalente a una función en un
lenguaje de alto nivel. Además, hacen todo lo que puede hacer un en-
samblador y proporcionan una serie de directivas para definir e invocar
macroinstrucciones (o simplemente, macros). Cuando invocamos a una
macro, esta se expande al cuerpo que se definió.

6
Finalmente, empleando las palabras del Instituto Tecnológico de Piedras Negras (s.f.):

Una de las principales ventajas del uso del ensamblador es que se encar-
ga de administrar la creación de memoria y el paso de parámetros para el
usuario de manera transparente. Además, permite acceder directamente a
los recursos de la máquina para un mejor desempeño.

Por tanto, “producir un ensamblador como código objeto hace más fácil el proceso
de generación, pues se pueden generar instrucciones simbólicas y llamadas a ma-
cros de ensamblador” (Sánchez y Valverde, 1989).

9.3.
Lenguaje máquina

Olivares (2012b) argumenta que “el lenguaje máquina sólo es entendible por las
computadoras. Se basa en una lógica binaria de 0 y 1, generalmente implementada
por mecanismos eléctricos”. Por su parte, como señalan Costa y Vivó (s.f.):

El lenguaje máquina es el único lenguaje que entiende directamente al or-


denador. Su estructura está completamente adaptada a los circuitos de la
máquina y muy alejada de la forma de expresión y análisis de los problemas
propios de los humanos. La programación en este lenguaje es complicada,
de manera que se requiere un profundo conocimiento de la arquitectura
física del ordenador.

7
Por otro lado, la Universidad Nacional Autónoma de México (s.f.) manifiesta que “el
lenguaje máquina es específico de cada máquina o arquitectura de la máquina, aun-
que el conjunto de instrucciones disponibles pueda ser similar entre ellas”.

Sánchez y Valverde (1989, p. 159) plantean que el código máquina puede producirse
desde dos enfoques diferentes:

• Código máquina absoluto. Este código tiene la ventaja de poder cargar y eje-
cutar el programa inmediatamente. En aplicaciones comerciales no se suele
usar esta opción, aunque sí se utiliza en trabajos experimentales y especiales.
• Código máquina reubicable. Este código, también llamado módulo objeto,
es la solución adoptada por la mayoría de los compiladores comerciales, ade-
más permite tener módulos que se compilan por separado, aunque este tipo
precisa de un enlazador (linker) y de un cargador (ver figura 3 en el subtema
9.4. Administración de memoria).

9.4.
Administración de memoria

Silva (s.f.) revela que la administración de memoria consiste en determinar la posición


de memoria en la que los diferentes símbolos del programa almacenan la informa-
ción. Por su parte, Candela, García, Quesada, Santana y Santos (2007):

La memoria es el recurso donde residen el código y los datos de los proce-


sos que se ejecutan en el computador. Se trata de un recurso escaso, así
que el sistema operativo debe repartirlo entre los diferentes procesos que
solicitan ejecutarse, asegurando, además, cierto nivel de protección entre
las zonas ocupadas por los distintos programas (p. 164).

Ahora bien, empleando las palabras de Gálvez y Mora (2005):

8
Para que un programa se ejecute sobre un sistema operativo, es necesaria
la existencia de un cargador que suministra al programa un bloque contiguo
de memoria sobre el cual ha de ejecutarse. Por tanto, el código del pro-
grama resultante de la compilación debe organizarse de forma que haga
uso de este bloque, por lo que el compilador debe incorporar al programa
objeto el código necesario para ello (p. 293).

En otras palabras, desde el punto de vista de la memoria, un programa atraviesa una


serie de fases, desde que el programador lo escribe en un lenguaje de alto nivel has-
ta que se ejecuta en la memoria en forma de código máquina. En la figura 3 puedes
observar su ciclo de vida.

Figura 3. Ciclo de vida de la memoria de un programa.

9
Las técnicas de administración de memoria durante la ejecución del programa
difieren de unos lenguajes a otros, e incluso de unos compiladores a otros. En el caso
de lenguajes de bajo nivel, la figura 4 muestra de manera general cómo los registros
(PC, SP, etc.) son cargados en la memoria.

Figura 4. Interacción de los registros y la memoria.

Finalmente, desde el punto de vista de Magaña (2014):

La administración de memoria se refiere a los distintos métodos y ope-


raciones que se encargan de obtener la máxima utilidad de la memoria,
organizando los procesos y programas que se ejecutan de manera tal que
se aproveche de la mejor manera posible el espacio disponible.

10
UNIDAD 10
OPTIMIZACIÓN

En sentido general, un problema de optimización consiste en encontrar la mejor so-


lución entre todas las opciones posibles. De acuerdo con Olivares (2012a):

Las optimizaciones pueden realizarse de diferentes formas y se realizan


con base en el alcance ofrecido por el compilador. En otras palabras, la
optimización dependerá del lenguaje de programación y es directamente
proporcional al tiempo de compilación; es decir, entre más optimización
mayor tiempo de compilación.

Asimismo, según Olivares (2012a):

Como el tiempo de optimización es gran consumidor de tiempo (dado que


tiene que recorrer todo el árbol de posibles soluciones para el proceso de
optimización), la optimización se deja hasta la fase final […] De manera ge-
neral, la optimización se realiza reestructurando el código de tal forma que
el nuevo código tenga mayores beneficios.

10.1.
Tipos de optimización

Los diferentes tipos de optimización se denominan tomando en cuenta la


ubicación donde se realizará dicha reestructuración. A continuación, siguiendo a
Olivares (2012), describiremos algunos tipos de optimización: locales, ciclos, globales
y de mirilla,

11
10.1.1.
Locales

Dicho con palabras de Olivares (2012a):

La optimización local se realiza sobre módulos del programa. En la mayoría


de las ocasiones a través de funciones, métodos, procedimientos, clases,
etc. La característica de las optimizaciones locales es que sólo se ven re-
flejados en dichas secciones. La optimización local sirve cuando un bloque
de programa o sección es crítico; por ejemplo: la E/S (entrada/salida), la
concurrencia, la rapidez y la confiabilidad de un conjunto de instrucciones.

La figura 5 muestra un ejemplo de cómo puede ser la optimización local de una fun-
ción. En este caso, la función de ejemplo recibe un entero al que le debe sumar 10
unidades y regresar el resultado. En el código de la parte izquierda de la figura se
puede observar que se han declarado dos variables extras (y, z), las cuales sirven,
para almacenar el valor de 10 y el valor final. Por otro lado, en la parte derecha de la
figura se puede visualizar que basta con regresar el mismo valor aumentado en 10,
con lo cual, el código se reduce significativamente.

Figura 5. Ejemplo de optimización local.

Finalmente, una ventaja de este tipo de optimización es que la optimización local


es más rápida debido a que el espacio de soluciones es más pequeño.

12
10.1.2.
Ciclos

De acuerdo con Olivares (2012a):

Los ciclos o bucles son una de las partes más esenciales en el rendimiento
de un programa dado que realizan acciones repetitivas, y si dichas acciones
están mal realizadas, el problema se hace N veces más grandes. La mayoría
de las optimizaciones sobre ciclos tratan de encontrar elementos que no
deben repetirse en un ciclo.

Por ejemplo, en el fragmento de código mostrado en la figura 6 se puede observar


que la declaración c=5 es independiente del ciclo while, por tanto, lo ideal es situar
esta instrucción fuera del ciclo.

Figura 6. Ejemplo de optimización en ciclos.

Finalmente, Olivares (2012a) argumenta que el problema de la optimización en ciclos


es que es muy difícil saber el uso exacto de algunas instrucciones, así que no todo
código de proceso puede ser optimizado.

10.1.3.
Globales

Citando a Olivares (2012a), “en algunos casos es mejor mantener variables globales
para agilizar los procesos (el proceso de declarar variables y eliminarlas toma su
tiempo) pero consume más memoria. Algunas optimizaciones incluyen utilizar como
variables registros del CPU, emplear instrucciones en ensamblador, etc.”.

13
10.1.4.
De mirilla

Como dice Olivares (2012a):

La optimización de mirilla trata de estructurar de manera eficiente el flujo


del programa, sobre todo en instrucciones de bifurcación como son las de-
cisiones, los ciclos y los saltos de rutinas. La idea es tener los saltos lo más
cerca de las llamadas, siendo el salto lo más pequeño posible.

En conclusión, Olivares (2012a) afirma que “la optimización es un proceso que busca
minimizar o maximizar alguna variable de rendimiento, generalmente tiempo, espacio,
procesador, etc.”. Para lograrlo se pueden llevar a cabo diferentes tipos de optimiza-
ción, los cuales dependerán del lugar (en el código) donde se realizará.

10.2.
Costos

Dicho con palabras de Olivares (2012a):

Los costos son el factor más importante a tomar en cuenta a la hora de op-
timizar, ya que en ocasiones la mejora obtenida puede no verse reflejada
en el programa final, pero sí ser perjudicial para el equipo de desarrollo. La
optimización de una pequeña mejora tal vez tenga una pequeña ganancia
en tiempo o en espacio, pero se invierte mucho tiempo al generarla.

14
10.2.1.
Costo de ejecución (memoria, registros, pilas)

Desde la posición de Olivares (2012a):

Los costos de ejecución son aquellos que vienen implícitos al ejecutar el


programa. En algunos casos se tiene un mínimo para ejecutar el programa,
por lo que el espacio y la velocidad del microprocesador son elementos
que se deben optimizar para tener un mercado potencial más amplio.

Por otra parte, Badía, Martínez, Morales y Sanchiz (2012, p. 9) manifiestan que “la
ejecución de un algoritmo en una máquina supone el uso de una serie de recursos:
espacio y tiempo”. Badía et al. (2012, p. 9) expresan que el coste espacial hace refe-
rencia a la cantidad de memoria consumida, mientras que el coste temporal alude
al tiempo que se necesita para ejecutar el programa, por ejemplo, el tiempo en que
tarda en ejecutar ciertas operaciones.

Badía et al. (2012) declaran que existen dos métodos que se utilizan para realizar un
análisis de costos:

• A posteriori (o experimental): se estudia el comportamiento de una imple-


mentación en un ordenador. Ejemplos:
• Coste espacial: 320Kbytes
• Coste temporal: 12,3seg
• A priori (o teórico): se determina matemáticamente el coste antes de imple-
mentar o ejecutar el algoritmo. Ejemplos:
• Coste espacial: 3n−2
• Coste temporal: 2logn

10.2.2.
Criterios para mejorar el código

Como lo hace notar Olivares (2012a), “la mejor manera de optimizar el código es
hacer ver a los programadores que optimicen su código desde el inicio”, utilizando

15
algunos de los tipos de optimización que se presentaron en la sección 10.1 Tipos de
optimización. Sin embargo, Olivares (2012a) revela que “el problema, como se men-
cionó anteriormente, radica en que el costo podría ser muy grande, debido a que
tendría que codificar más o hacer su código más legible”.

Finalmente, a juicio de Olivares (2012a):

Los criterios de optimización siempre están definidos por el compilador.


Muchos de estos criterios pueden modificarse con directivas del compilador
desde el código o de manera externa. Además, este proceso lo realizan
algunas herramientas del sistema como los ofuscadores para código móvil
y código para dispositivos móviles.

10.2.3.
Herramientas para el análisis del flujo de datos

Olivares (2012a) plantea que los depuradores y los desambladores son algunas de
las herramientas que permiten analizar los flujos de datos. A continuación, los des-
cribimos brevemente (Ruiz, 2019):

• Depuradores. Un depurador es una aplicación que permite correr otros pro-


gramas, lo cual ayuda al usuario a ejercer cierto control sobre ellos a medida
que se ejecutan, y examinar el estado del sistema (variables, registros, bande-
ras, etc.) en el momento en que se presente algún problema. El propósito final
de un depurador consiste en que el usuario pueda observar y comprender lo
que ocurre “dentro” de un programa mientras es ejecutado.
• Desambladores. Un desamblador es un programa de computador que tra-
duce el lenguaje de máquina a lenguaje ensamblador, la operación contraria
a que hace el ensamblador. La salida de un desensamblador es a menudo
formateada para la legibilidad humana en vez de ser adecuada para la entra-
da a un ensamblador, haciendo que este sea una herramienta de ingeniería
inversa.

16
En conclusión, la optimización y los costos son dos tareas que están relacionadas.
Por un lado, la optimización, teóricamente, nos ayudará a tener un código “limpio” en
cuanto al manejo de instrucciones, ciclos, etc., lo cual generará un mejor rendimiento
y aprovechamiento de los recursos (memoria, por ejemplo) de la maquina destino.
Sin embargo, en la práctica puede ocurrir que la optimización genere un costo mayor
a que si no estuviera optimizado. Por lo tanto, es importante realizar un análisis de
costo para cuantificar las mejoras obtenidas a partir de la optimización.

17
Referencias.
Badía, J., Martínez Salvador, B., Morales Escrig, A. y Sanchiz Martí, J. M. (01 de
junio de 2012). Tema 3. Análisis de costes. Recuperado de: http://repositori.uji.es/
xmlui/bitstream/handle/10234/119885/tema3.pdf?sequence=1&isAllowed=y

Bautista, J., Rojas, C. y López, A. (2015). Metodología para la enseñanza de sistemas digitales
mediante lenguaje ensamblador. Revista de Sistemas y Gestión Educativa, 2 (5), 1003-
1009. Recuperado de: https://www.ecorfan.org/bolivia/researchjournals/Sistemas_y_
Gestion_Educativa/vol2num5/Sistemas_y_Gestion_Educativa_Vol2_N5_4.pdf

Candela Solá, S. C., García Rodríguez, R., Quesada Arencibia, A., Santana Pérez,
F. J., Santos Espino, J. M. (2007). Fundamentos de sistemas operativos: teoría
y ejercicios resueltos. España: Editorial Paraninfo. Recuperado de: https://books.
google.com.mx/books?id=fRK3lbTrNy4C&printsec=frontcover#v=onepage&q=la%20
memoria%20es%20el%20recurso&f=false

Costa, R. y Vivó, J. (s.f.). Lenguaje máquina, ensamblador y de alto nivel. Recuperado de:
http://cv.uoc.edu/moduls/XW02_79049_00373/web/main/m4/v2_3.html

Gálvez Rojas, S. y Mora Mata, M. Á. (2005). Java a tope: compiladores y traductores


con LEX/YACC, JFLEX/CUP Y JAVACC. España: Universidad de Málaga. Recuperado
de: http://www.lcc.uma.es/~galvez/ftp/libros/Compiladores.pdf

Infante Ventura, J. R. (2013). Lenguajes de interfaz. Recuperado de: https://


es.scribd.com/document/374076020/Materia-Lenguajes-de-Interfaz

It-brain (s.f.). Diseño del compilador: generación de código. Recuperado de: https://
es.it-brain.online/tutorial/compiler_design/compiler_design_code_generation/

18
Magaña, M. (03 de abril de 2014). Funciones de administración de memoria. Recuperado de:
https://es.slideshare.net/MiguelMagaa2/funciones-de-administracion-de-memoria

Olivares Rojas, J. C. (2012a). Unidad VII. Optimización. Recuperado de: http://


dsc.itmorelia.edu.mx/~jcolivares/courses/ps207a/ps2_u7.pdf

Olivares Rojas, J. C. (2012b). Unidad VIII. Generación de código intermedio. Recuperado


de: https://hopelchen.tecnm.mx/principal/sylabus/fpdb/recursos/r99907.PDF

Puentes García, J. M. (28 de agosto de 2013). Registros de la CPU. Recuperado


de: https://es.slideshare.net/jomapuga/registros-de-la-cpu

Ruiz, A. (21 de octubre de 2019). Lenguajes y autómatas II. Recuperado de: https://
alexisruizclemente1998.blogspot.com/2019/10/323-herramientas-para-el-analisis-del.html

Sánchez, S. y Rodríguez, D. (2010). Procesadores de lenguaje. Tema 8: Generación


de código y optimización. Recuperado de: http://www.cc.uah.es/ie/docencia/
ProcesadoresDeLenguaje/ProcesadoresDeLenguajeTema8_1xpagina.pdf

Sánchez Dueñas, G. y Valverde Andreu, J. A. (1989). Compiladores e intérpretes:


un enfoque pragmático. Madrid: Ediciones Díaz de Santos.

Silva, M. O. (s.f.). Sistemas operativos. Recuperado de: http://www1.frm.utn.edu.ar/soperativos/index.html

Universidad Nacional Autónoma de México (s.f.). Lenguajes de programación.


Recuperado de: https://programas.cuaed.unam.mx/repositorio/moodle/
pluginfile.php/1023/mod_resource/content/1/contenido/index.html

Universitat Jaume I (2011). Generación de código. Recuperado de: http://repositori.uji.es/xmlui/


bitstream/handle/10234/5916/codigo.apun_2010.pdf?sequence=5&isAllowed=y

Valverde Sosa, R. (2017). Tema 3: Lenguaje ensamblador. La primera abstracción de la máquina


para el programador. Recuperado de: https://docplayer.es/37913673-Tema-3-lenguaje-
ensamblador-la-primera-abstraccion-de-la-maquina-para-el-programador.html

Vilar Torres, J. M. (2010). Generación de código. Recuperado de: http://repositori.uji.es/xmlui/


bitstream/handle/10234/5916/codigo.apun_2010.pdf?sequence=5&isAllowed=y

19

También podría gustarte