LENGUAJES Y AUTOMATAS 2
I.S.C
UNIDAD 4
Generación de código objeto.
Gustavo Ramirez Bello 20321169
Docente: Betzi Yenia Pastrana Evaristo
Fecha de entrega: 10/01/2025
Hora: 1:00 pm- 2:00 pm
GENERACIÓN DE CÓDIGO OBJETO
La fase final de un compilador es la generación de código objeto, que por lo general
consiste en código de máquina relocalizable o código ensamblador. Las posiciones
de memoria se seleccionan para cada una de las variables usadas por el programa.
Después, cada una de las instrucciones intermedias se traduce a una secuencia de
instrucciones de máquina que ejecuta la misma tarea. Un aspecto decisivo es la
asignación de variables a registros.
El generador de código objeto puede considerarse como la penúltima fase de un
compilador, la cual se encarga de tomar como entrada el código intermedio generado
por el front-end, y producir código objeto de la arquitectura target para luego entrar en
la fase de optimización de código.
Toma como entrada de representación intermedia el programa fuente y produce
como salida un programa objeto equivalente.
4.1 Registros.
Una de las consideraciones a tener para el generador de código
consiste en la asignación de registros. Los registros son los
elementos más valiosos y escasos en la fase de generación de
código, puesto que el CPU solamente puede procesar datos que
se encuentren en registros
Los registros son la memoria principal de la computadora. Existen
diversos registros de propósito general y otros de uso exclusivo.
Algunos registros de propósito general son utilizados para cierto
tipo de funciones. Existen registros acumuladores, puntero de
instrucción, de pila,
etc.
Además, las instruccio
nes que implican oper
andos en registros so
n más cortas y
rápidas que las de ope
randos en memoria. E
l uso de registros se di
vide generalmente en
dos problemas:
Durante la asignación de registros, se selecciona el conjunto de variables
y/o constantes que residirán en los registros en un momento del programa.
Durante una fase posterior a la anterior, se escoge el registro específico en
el que residirá una variable.
Los registros se pueden clasificar de la siguiente forma:
Registros de datos:
o AX: Registro acumulador. Es el principal empleado en las
operaciones aritméticas.
o BX: Registro base. Se usa para indicar un desplazamiento.
o CX: Registro contador. Se usa como contador en los bucles.
o DX: Registro de datos. También se usa en las operaciones
aritméticas.
Estos registros son de uso general y también pueden ser utilizados
como registros de 8 bits, para utilizarlos como tales es necesario
referirse a ellos como por ejemplo: AH y AL, que son los bytes alto
(high) y bajo (low) del registro AX. Esta nomenclatura es aplicable
también a los registros BX, CX y DX.
Registros de segmentos:
o CS: Registro de segmento de código. Contiene la dirección de las
instrucciones del programa.
o DS: Registro segmento de datos. Contiene la dirección del área de
memoria donde se encuentran los datos del programa.
o SS: Registro segmento de pila. Contiene la dirección del segmento
de pila. La pila es un espacio de memoria temporal que se usa para
almacenar valores de 16 bits (palabras).
o ES: Registro segmento extra. Contiene la dirección del segmento
extra. Se trata de un segmento de datos adicional que se utiliza para
superar la limitación de los 64Kb del segmento de datos y para hacer
transferencias de datos entre segmentos.
Registros punteros de pila:
o SP: Puntero de la pila. Contiene la dirección relativa al segmento de
la pila.
o BP: Puntero base. Se utiliza para fijar el puntero de pila y así poder
acceder a los elementos de la pila.
Registros índices:
o SI: Índice fuente.
o DI: Índice destino.
Puntero de instrucciones:
o IP: Registro puntero de instrucción o contador de programa (PC).
Contiene el desplazamiento de la siguiente instrucción a ejecutar
respecto al segmento de código en ejecución. Por lo tanto, la
dirección completa de la siguiente instrucción sería CS:IP. La única
forma de influir en este registro es de forma indirecta mediante
instrucciones de bifurcación.
Registro de banderas (flags): Cada bandera es un bit y se usa para
registrar la información de estado y de control de las operaciones del
microprocesador.
Hay nueve banderas (los 7 bits restantes no se utilizan):
Banderas de estado: Registran el estado del procesador, normalmente
asociado a una comparación o a una instrucción aritmética.
o CF: Bandera de acareo.
o OF: Bandera de desbordamiento (aritmético).
o ZF: Bandera de resultado 0 o comparación igual.
o SF: Bandera de resultado o comparación negativa.
o PF: Bandera de paridad (número par de bits).
o AF: Bandera auxiliar. Indica si hay necesidad de ajuste en las
operaciones aritméticas con números BCD. Banderas de control:
o DF: Bandera de dirección. Controla la dirección de las operaciones
con cadenas de caracteres incrementando o decremento
automáticamente los registros índices (SI y DI).
o IF: Bandera de interrupciones. Indica si están permitidas o no las
interrupciones de los dispositivos externos.
o Bandera de atrape. Controla la operación de modo paso a paso
(usada por el programa DEBUG).
Las instrucciones que implican operandos en registros son
generalmente más rápidas que las de los operandos en memoria.
Por lo que, utilizar eficientemente los registros es fundamental para
generar un buen código. El uso de registros se divide en dos
formas:
Durante la asignación de los registros: se selecciona el conjunto de
variables que residirá en los registros en un momento del programa.
Durante la fase posterior de asignación a los registros: se
escoge el registro específico en el que residirá una variable.
4.2 Lenguaje ensamblador.
El lenguaje ensamblador, o assembler (assembly language en
inglés), es un lenguaje de programación de bajo nivel para los
computadores, microprocesadores, microcontroladores y otros
circuitos integrados programables. Implementa una representación
simbólica de los códigos de máquina binarios y otras constantes
necesarias para programar una arquitectura dada de CPU y
constituye la representación más directa del código máquina
específico para cada arquitectura legible por un programador.
Esta representación es usualmente definida por el fabricante de
hardware, y está basada en los mnemónicos que simbolizan los
pasos de procesamiento (las instrucciones), los registros del
procesador, las posiciones de memoria y otras características del
lenguaje. Un lenguaje ensamblador es por lo tanto específico de
cierta arquitectura de computador física (o virtual). Esto está en
contraste con la mayoría de los lenguajes de programación de alto
nivel, que idealmente son portátiles.
El programa escrito en lenguaje ensamblador se denomina código
fuente (*.asm). El programa ensamblador proporciona a partir de
este fichero el correspondiente código máquina, que suele tener la
extensión *.hex.
I. Características
El código escrito en lenguaje ensamblador posee una cierta dificultad de ser
entendido ya que su estructura se acerca al lenguaje máquina, es decir, es
un lenguaje de bajo nivel.
El lenguaje ensamblador es difícilmente portable, es decir, un código escrito
para un microprocesador, puede necesitar ser modificado, para poder ser
usado en otra máquina distinta. Al cambiar a una máquina con arquitectura
diferente, generalmente es necesario reescribirlo completamente.
Los programas hechos por un programador experto en lenguaje
ensamblador son generalmente
mucho más rápidos y consumen
menos recursos del sistema (memoria
RAM y ROM) que el programa
equivalente compilado desde un
lenguaje de alto nivel. Al programar
cuidadosamente en lenguaje
ensamblador se pueden crear
programas que se ejecutan más
rápidamente y ocupan menos espacio que con lenguajes de alto nivel.
Con el lenguaje ensamblador se tiene un control muy preciso de las tareas
realizadas por un microprocesador por lo que se pueden crear segmentos
de código difíciles y/o muy ineficientes de programar en un lenguaje de alto
nivel, ya que, entre otras cosas, en el lenguaje ensamblador se dispone de
instrucciones del CPU que generalmente no están disponibles en los
lenguajes de alto nivel.
También se puede controlar el tiempo en que tarda una rutina en
ejecutarse, e impedir que se interrumpa durante su ejecución.
Clasificaciones.
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, o macro ensambladores: Descendientes de
los ensambladores básicos, fueron muy populares en las décadas de los 50
y los 60, fueron 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.
ETIQUETA: Nombre que se le asigna a una posición de la
memoria de programa. Empieza en la 1ª columna y suele ir
seguida de ':‘.
MNEMÓNICO: Representación simbólica del campo de operación
(COP).
OPERANDOS: Representados por símbolos o constantes,
separados por comas.
COMENTARIOS: Comienzan con ';‘.
Ventajas
• Da a un programador la capacidad de realizar tareas
muy técnicas que serían difíciles.
• Mínimo tiempo de ejecución.
• Requiere menos memoria que un programa escrito en
lenguaje de alto nivel.
• Acceso al cualquier recurso de la computadora.
Desventajas
• Tiempo de programación.
• Programas fuentes grandes.
• Peligro de afectar recursos inesperadamente.
• Falta de portabilidad.
• Podría dificultar que un usuario sea experto en ello.
• Dificulta escribir programas grandes y complejos.
4.3 Lenguaje máquina.
Es el que proporciona poca o ninguna abstracción del
microprocesador de un ordenador. El lenguaje máquina solo es
entendible por las computadoras. Se basa en una lógica binaria de
0 y 1, generalmente implementada por mecanismos eléctricos.
Un lenguaje de programación de bajo nivel es el que proporciona
poca o ninguna abstracción del microprocesador de un ordenador.
Consecuentemente es fácilmente trasladado a lenguaje de
máquina. La palabra “bajo” no implica que el lenguaje sea inferior a
un lenguaje de alto nivel; se refiere a la reducida abstracción entre
el lenguaje y el hardware.
En general el lenguaje maquina es difícil de entender para los
humanos por este motivo hacemos uso de lenguajes más
parecidos a los lenguajes naturales.
Características
El lenguaje máquina realiza un conjunto de operaciones
predeterminadas llamadas micro operaciones.
Las micro operaciones sólo realizan operaciones del tipo aritmética
(+,- ,*,/), lógicas (AND, OR, NOT) y de control (secuencial, de
control y repetitiva).
El lenguaje maquina es dependiente del tipo de arquitectura. Así un
programa máquina para una arquitectura Intel x86 no sé ejecutara
en una arquitectura Power PC de IBM (al menos de manera
nativa).
Algunos microprocesadores implementan más funcionalidades
llamado CISC, pero son más lentos que los RISC ya que estos
tienen registros más grandes.
I. Ventajas
Mayor adaptación al equipo.
Posibilidad de obtener la máxima velocidad con mínimo uso de memoria.
II. Inconvenientes
Imposibilidad de escribir código independiente de la máquina.
Mayor dificultad en la programación y en la comprensión de los programas.
El programador debe conocer más de un centenar de instrucciones.
Es necesario conocer en detalle la arquitectura de la máquina.
III. Direccionamiento de lenguaje maquina
Es la forma en cómo se accede a la memoria. Recordar que un
programa no puede ejecutarse sino se encuentra en memoria
principal. La forma de acceder a la memoria depende del
microprocesador, pero en general existen dos tipos de
direccionamiento: directo e indirecto.
El direccionamiento directo también recibe el nombre de direccionamiento
absoluto y el acceso a las direcciones se hace de manera directa.
El direccionamiento indirecto también recibe el nombre de direccionamiento
relativo y se basa a partir de una dirección genérica, generalmente el inicio
del programa.
Para acceder a una dirección relativa se suma a la dirección
base el número de espacios de memorias necesarias.
El direccionamiento relativo hace a los programas relocalizables
e independientes. Si la dirección base es el inicio de la memoria
fija el direccionamiento pasa a ser un variante de
direccionamiento absoluto.
ADMINISTRACIÓN DE LA MEMORIA
I. Definición
La administración de la memoria es un proceso hoy en día muy importante de tal
modo que su mal o buen uso tiene una acción directa sobre el desempeño de
memoria.
En general un ensamblador tiene un administrador de memoria más limitado que un
compilador. En la mayoría de los lenguajes de programación el uso de punteros no
estaba vigilado por lo que se tienen muchos problemas con el uso de memoria.
Los lenguajes más recientes controlan el uso de los punteros y tienen un programa
denominado recolector de basura que se encarga de limpiar la memoria no utilizada
mejorando el desempeño.
II. Propósitos de la administración
Se distinguen los siguientes propósitos del sistema de administración de memoria:
Protección.
Si varios programas comparten la memoria principal, se debería asegurar que el
programa no sea capaz de cambiar las ubicaciones no pertenecientica él. Aunque una acción
de escritura puede tener efectos más graves que una
de lectura, esta última tampoco debería estar permitida,
para proporcionar algo de privacidad al programa.
Compartimiento.
Este objetivo parece contradecir al anterior, sin embargo a
veces es necesario para los usuarios poder compartir y actualizar información (por ejemplo,
en una base de datos) y, si se organiza la tarea de entrada a la misma, se puede evitar el
tener varias copias de la rutina.
Reubicación.
La técnica de multiprogramación requiere que varios programas ocupen la memoria al mismo
tiempo.
Sin embargo no se sabe con anticipación donde será cargado cada programa por
lo que no es práctico usar direccionamiento absoluto de memoria.
Organización física.
Debido al costo de una memoria principal rápida, éste se usa en conjunto con una
memoria secundaria mucho más lenta (y por consiguiente, barata) a fines de extender su
capacidad.
Organización lógica.
Aunque la mayor parte de las memorias son organizadas linealmente con un
direccionamiento secuencial, esto difícilmente concuerda con el camino seguido por el
programa, debido al uso de procedimientos, funciones, subrutinas, arreglos, etc.