CAPITULO 6 COMP

Anuncio
6.1
Generación de código de referencias de estructuras de datos
Como se ha visto antes, se puede generar código intermedio para asignaciones
aritméticas simples, en esas ocasiones los valores básicos eran constantes o variables
simples. La traducción al código objeto, requiere que los nombres de variables o de
constantes se reemplacen por direcciones de memoria absoluta o bien de registros de
activación. En el momento que se genera el código intermedio se pueden insertar
estas direcciones.
Pero en situaciones donde se requiere de realizar cálculos de dirección para encontrar
la dirección real en cuestión, los cálculos deben ser expresados directamente incluso
tratándose del código intermedio. Los cálculos se presentan en subíndices de arreglos,
campos de registro y referencias de apuntador.
Como se puede entender, el dominio del lenguaje de programación es un requisito
indispensable para la correcta manipulación de direcciones y apuntadores.
6.2
Generación de código de sentencias de control y expresiones lógicas
Si supones que el generador de cogido genera una secuencia con nombres tales como
L1, L2, y así sucesivamente.
Para la sentencia:
If (E) S1 else S2
Se generé el siguiente patrón de código:
<code to evaluate E to t!>
If_else t1 goto L1
<code for S1>
Goto L2
Label L1
<code for S2>
Label L2
De la misma manera una sentencia while de la forma
while (E) S
provocaría que fuera generado el siguiente patrón de código de tres direcciones:
label L1
<codeto evaluate E to t1>
If_fase t1 goto L2
<code for S>
goto L1
label L2
El Código P para la sentencia de control sería del siguiente modo:
if ( E ) S1 else S2
se genera el siguiente patrón de código P:
- 64 -
<code to evaluate E>
Fjp L1
<code for S1>
Ujp L2
Lab L1
<code for S2>
Lab L2
Y para una sentencia como:
while ( E ) S
se genera el código P:
lab L1
<code to evaluate E>
Fjp L2
<code for S>
Ujp L1
Lab L2
Hace falta notar que todas estas secuencias de código finalizan en la declaración de
una etiqueta, a la que bien se le puede llamar etiqueta de salida. Muchos lenguajes de
programación proporcionan una construcción adecuada para salir de los ciclos, por
ejemplo, el lenguaje C proporciona la sentencia “breack”.
6.3
Generación de código de llamadas de procedimientos y funciones
Los requerimientos para las sentencias de representaciones de código de llamas de
funciones pueden describirse en términos generales como sigue:
En primer lugar, existen dos mecanismos que requieren describirse: la definición de
función/procedimiento, conocida también como “declaración” y la llamada de función /
procedimiento. Una definición crea un nombre de función, parámetros y código, pero
la función no se ejecuta en ese punto. Una llamada crea valores reales para los
parámetros o argumentos para la llamada, y realiza un salto un salto hacia el código
de la función, el cual se ejecuta entonces y regresa.
El ambiente de ejecución en el que ésta tiene lugar no se conoce cuando se crea el
código para la función, excepto en su estructura general. Este ambiente de ejecución
es construido en parte por el elemento que llama y en parte por el código de la
función llamada; esta responsabilidad y la división de ella, forma parte de la secuencia
de llamada.
El código intermedio para una definición debe incluir una instrucción que marque el
inicio o punto de entrada del código para la función y una instrucción que marque el
final o punto de retorno de la función.
Lo anterior se puede esquematizar de la siguiente forma:
instrucción de entrada
<código para el cuerpo de la función>
instrucción de retorno
De la misma manera, una llamada de función debe tener una instrucción que indique
el principio del cálculo de los argumentos, en preparación para la llamada, y luego una
instrucción de llamada real que indique el punto en que los argumentos han sido
construidos y el salto real hacia el código de la función puede tener lugar:
- 65 -
Instrucción de comienzo de cálculos de argumento
<código para calcular los argumentos>
Instrucción de llamada.
Para diferentes versiones de código intermedio proporcionan diferentes versiones de
las cuatro instrucciones agrupadas. En particular a la cantidad de información acerca
del ambiente, los parámetros y la función misma que es parte de cada instrucción.
6.4
Generación de código en compiladores comerciales: dos casos de
estudio.
Los casos de estudio a los que hace referencia el plan de estudios se refiere
específicamente al compilador para C de Borland, versión 3.9 para procesador Intel
80X86. El segundo es el compilador para C de Sun versión 2.0 para SparcStation.
Para ambos se intenta la salida de ensamblador para los mismos ejemplos de código
de tres direcciones y el código P.
Utilicemos el ejemplo de la expresión:
(x=x+3)+4
Supongamos que la variable X en esta expresión está almacenada localmente en el
marco de la pila.
El código ensamblador para esta expresión tal como se produce mediante el
compilador de Borland V.3.0 para la arquitectura 80X86 es de la siguiente manera:
MOV
ADD
MOV
ADD
ax , Word [prt bp-2]
ax, 3
word ptr [bp-2], ax
ax, 4
En el código, el registro de acumulador ax se utiliza como la ubicación temporal
principal para el calculo. La ubicación de la variable local x es bp-2, lo que refleja el
uso de registros bp (base pointer) como el apuntador de marco y el hecho de que las
variables enteras ocupan dos bytes en esta máquina.
Para el caso del compilador C de Sun 2.0 para la Sun SparcStations con referencia al
mismo ejemplo:
(x=x+3)+4
Suponemos que la variable x en esta versión es almacenada localmente en el marco
de la pila. El compilador C de Sun produce código ensamblador que es muy similar al
del código de Borland:
ld
add
st
ld
add
[%fp+-0x4],%o1
%o1,0x3,%o1]
%o1,[%fp+-0x4]
[%fp+-0x4], %o2
%o2,0x4,%o3
- 66 -
En este código, los nombres de registros comienzan con el símbolo de porcentaje y las
constantes comienzan con los caracteres 0x (x=hexadecimal), de manera que por
ejemplo, 0x4 es el hexadecimal 4 (lo mismo que el decimal 4). La primera instrucción
mueve el valor de x (en la localidad fp-4, porque los enteros son de cuatro bytes de
longitud) al registro o1. Hay que notar que las localidades fuente están a la izquierda
y las ubicaciones objetivo a la derecha, de modo opuesto a la conversión de la
arquitectura 80x86. La segunda instrucción agrega 3 a o1, mientras que la tercera
almacena o1 en la localidad de x. Finalmente, de vuelve a cargar el calor de x, esta
vez en el registro o2, y se le agrega 4. Colocando el resultado en el registro o3, donde
se deja como el valor final de la expresión.
- 67 -
Descargar