100% encontró este documento útil (1 voto)
145 vistas44 páginas

Clase 12 Codigo Intermedio

El documento describe el código intermedio, que es una representación de un programa durante el proceso de compilación que se encuentra entre el análisis y la generación de código de máquina. Explica que el código de tres direcciones es una forma común de código intermedio que representa instrucciones como x = y op z. También cubre temas como cuádruplos, tripletes, acceso a arrays y bucles mientras proporciona ejemplos para ilustrarlos.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PPT, PDF, TXT o lee en línea desde Scribd
100% encontró este documento útil (1 voto)
145 vistas44 páginas

Clase 12 Codigo Intermedio

El documento describe el código intermedio, que es una representación de un programa durante el proceso de compilación que se encuentra entre el análisis y la generación de código de máquina. Explica que el código de tres direcciones es una forma común de código intermedio que representa instrucciones como x = y op z. También cubre temas como cuádruplos, tripletes, acceso a arrays y bucles mientras proporciona ejemplos para ilustrarlos.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PPT, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 44

Facultad de Ingeniería de Sistemas e

Informática

CODIGO INTERMEDIO

Profesor: Carlos A. Ruiz De La Cruz Melo


Correo: [email protected]
ETAPAS DE UN COMPILADOR
1. Etapa de inicial o de análisis
 Lexico
 Sintáctico
 Semántico

2. Etapa final o de síntesis


 Código intermedio
 Optimizador de código intermedio
 Generación de código
CODIGO INTERMEDIO
 Una representación intermedia es una
estructura de datos que representa al
programa fuente durante el proceso de la
traducción a código objeto.

 Es necesario generar una nueva forma


de representación intermedia. A esta
representación intermedia, que se
parece al código objeto pero que sigue
siendo independiente de la máquina, se
le llama código intermedio.
VENTAJAS DEL CODIGO
INTERMEDIO
 Permite abstraer la máquina,
separar operaciones de alto nivel de
su implementación a bajo nivel.

 Permite la reutilización de los front-


ends y backends.

 Permite optimizaciones generales.


DESVENTAJAS DEL CODIGO
INTERMEDIO
 Implica una pasada más para el
compilador (no se puede utilizar el
modelo de una pasada,
conceptualmente simple). –

 Dificulta llevar a cabo optimizaciones


específicas de la arquitectura
destino.

 Suele ser ortogonal a la máquina


destino, la traducción a una
arquitectura específica será más
larga e ineficiente
CODIGO DE 3-DIRECCIONES

 El código de tres direcciones se basa


en dos conceptos: direcciones e
instrucciones. En términos de
orientación a objetos, estos
conceptos corresponden a las clases
y los diversos tipos de direcciones e
instrucciones corresponden a
subclases apropiadas.

 De manera alternativa, podemos


implementar el código de tres
direcciones usando registros con
campos para las direcciones.
DIRECCION

Una dirección puede ser una de las


siguientes opciones:

 Un nombre para los


identificadores
 Una constante (diferentes tipos
de constantes)
 Un valor temporal generado por
el compilador
CODIGO DE 3-DIRECCIONES
 El código de tres direcciones es
una secuencia de proposiciones
de la forma general

x = y op z

 Donde op representa cualquier


operador; x, y, z representan
variables definidas por el
programador o variables
temporales generadas por el
compilador.
CODIGO DE 3-DIRECCIONES
 y,z también pueden representar
constantes o literales. op x = y op z
representa cualquier operador:
un operador aritmético de punto
fijo o flotante, o un operador
lógico sobre datos booleanos.

 No se permite ninguna expresión


aritmética compuesta, pues sólo
hay un operador en el lado
derecho.
CODIGO DE 3-DIRECCIONES

 Asi , x+y*z se traduce a una


secuencia, donde t1 , t2 son
variables temporales x+ y*z
generadas por el compilador.

 Las expresiones compuestas


y las proposiciones de flujo
de control se han de
descomponer en
proposiciones de este tipo, t1 = y * z
definiendo un conjunto t2 = x + t1
suficientemente amplio de
operadores.
FORMA LINEALIZADA
 El código de tres direcciones es una
forma linealizada (de izquierda a x+ y*z
derecha) del árbol sintáctico en la que
los nombres temporales corresponden a
los nodos internos.

 Como estos nombres temporales se


representan en la memoria no se
especifica más información sobre ellos
en este tipo de código. t1 = y * z
t 2 = x + t1
 Normalmente se asignaran directamente
a registros o se almacenaran en la tabla
de símbolos.
EJEMPLO
Para 2*a+b-3
Código de 3-direcciones

t1 = 2 * a
t2 = b – 3
t3
t 3 = t1 + t2
+
t1 t2
-
*

2 a b 3
EJEMPLO
Para a=b+c*d El generador de código
intermedio, tratar de
dividir esta expresión en
sub-expresiones y, a
continuación, generar el
código correspondiente.

r1 = c * d
r2 = b + r1
r3 = r2 + r1
a = r3
EXPRESIVIDAD
Saltos condicionales
 El conjunto de proposiciones
(operadores) debe ser lo Saltos
suficientemente rico como
incondicionales
para poder implantar las
operaciones del lenguaje
fuente Llamadas a
funciones
 Las proposiciones de 3-
direcciones van a ser en bucles
cierta manera análogas al
código ensamblador.
TIPOS DE CODIGO
Un código de dirección tiene un
máximo de tres direcciones para
calcular la expresión. Un código
de dirección puede estar
representado en dos formas :
cuádruples y triples

cuadruplos

Código de tres
direcciones

tripletes
CUADRUPLOS
 Un cuádruplo es una estructura tipo
registro con cuatro campos que se
llaman (op, result, arg1, arg2). El campo
op contiene un código interno para el
operador.

 La proposición de tres direcciones x = y


+ z se representa como (+, x,y, z). Las
proposiciones con operadores unarios no
usan el arg2. Los campos que no se
usan se dejan vacíos o un valor NULL.
Como se necesitan cuatro campos se le
llama representación mediante
cuádruplos.
CUADRUPLOS

a = b + c * d;

OPERADOR Arg1 Arg2 RESULTADO


* c d r1
+ b r1 r2
+ r2 r1 r3
= r3 a
TRIPLETES

 Para evitar tener que introducir


nombres temporales en la tabla de
símbolos, se hace referencia a un
valor temporal según la posición de
la proposición que lo calcula.

 Las propias instrucciones


representan el valor del nombre
temporal. La implantación se hace
mediante registros de sólo tres
campos (op, arg1, arg2).
TRIPLETES
Cada instrucción en triples
a = b + c * d;
presentación tiene tres campos :
op, arg1, arg2.

Los resultados de las respectivas


sub-expresiones son indicados
por la posición de expresión.
OPERADOR Arg1 Arg2

0 * c d
1 + b (0)
2 + (1) (0)
3 = (2)
FORMAS UTILIZADAS

1. Proposiciones de la forma x = y op z
donde op es un operador binario
aritmético, lógico o relacional.

2. Instrucciones de la forma x = op y,
donde op es un operador unario
(operador negación lógico, menos
unario, operadores de
desplazamiento o conversión de
tipos).

3. Proposiciones de copia de la forma


x=y, donde el valor de y se asigna a
x.
FORMAS UTILIZADAS

4. Salto incondicional goto etiq.


La instrucción con etiqueta etiq
es la siguiente que se ejecutará.
5. Saltos condicionales como if x
goto etiq.
6. Asignaciones con índices de la
forma x=y[ i ], donde se asigna a
x el valor de la posición en i
unidades de memoria más allá
de la posición y. O también x[i] =
y.
EJEMPLO
Considere la siguiente instrucción
while (dato[i] != valor){
i= i+1;
}

Una solucion es la siguiente


etiqueta : t1= i*2
t2 = dato [t1]
if t2 == valor goto final
t3 = i + 1
i= t3
goto etiqueta
final:
EJEMPLO
a = 2 * x + 10; t1 = 2 * x
while (a <= p + 2) { t2 = t1 + 10
a := p[4+i*2]: a = t2
} w1: t1 = p + 2
a = a + 1; if a <= t1 goto w2
goto w3
w2: t1 = i * 2
t2 = 4 + t1
a = p[t2]
goto w1
w3: t1 = a + 1
a = t1
EJEMPLO
Considere la siguiente
instrucción

int a[10], b[10], dot_prod, i; dot_prod = 0;


for (i=0; i<10; i++) do_prod =0
dot_prod += a[i]*b[i]; i =0
L1: if (i >=10) goto L2
t1=i*2
t2=a[t1]
t3=i*2
t4= b[t3]
Una solución t5=t2*t4
t6=do_prod+t5
es la siguiente do_prod=t6
t7 =i+1
i=t7
goto L1
L2:
Acceso a elementos de array
Dada la declaracion de un array Tipo nombre[D1] [D2] [D3]
Acceso a posicion del array nombre[ i ][ j ] [ k ]

La direccion de memoria asociada es

t1 = 0
t2 = t1 x D1 + i
t3= t2 x D2 + j
t4 = t3 x D3 + k
t5 = dirbase [ t4 x tam(tipo) ]
Para una matriz entera MAT[3][5]

Considere la siguiente instrucción

b=MAT[2] [1]
MAT 0 1 2 3 4
t1=0 0 2 4 6 8
0
t2= t1x3+2 = 2 1 10 12 14 16 18
t3= t2x5 +1 =11 2
20 22

Multiplicando por el tamaño del tipo

tamaño=2 para un entero en C++

b= MAT[11*2]
FORMAS UTILIZADAS
Para los procedimientos y retornos
se implementan mediante el uso
de las siguientes instrucciones:
Para su uso se da
como una secuencia
func inicio inicio de función
de instrucciones
func fin fin de función

param x Para los parametros

param x1
param x2
call p, n Para las llamadas …..
y = call p, n a procedimientos y param xn
funciones p call p, n
return y

return
y representa a un valor
de retorno, es opcional
FORMAS UTILIZADAS
Asignaciones de direcciones a punteros
de la forma:

 x = &y (el valor de x es la dirección


de y )

 x = *y (el valor de x se iguala al


contenido de la dirección indicada
por el puntero y) ´

 *x = y (el objeto apuntado por x se


iguala al valor de y ).
EJEMPLO
int a[10], b[10], dot_prod, i; int* a1;
int* b1; dot_prod = 0; a1 = a; b1 = b; dot_prod=0
for (i=0; i<10; i++) a1=&a
dot_prod += *a1++ * *b1++; b1=&b
i=0
L1:if(i>=10) goto L2
t3=*a1
t4=a1+1
a1=t4
t5=*b1
t6=b1+1
b1=t6
Una solución t7=t3*t5
t8=dot_prod+t7
es la siguiente dot_prod=t8
t9=i+1
i=t9
goto L1
L2:
EJEMPLO
int dot_prod(int x[], int y[]){
func inicio dot_prod
int d, i; d = 0;
d=0
for (i=0; i<10; i++)
i=0
d += x[i]*y[i];
L1: if(i>=10) goto L2
return d;
t1=i*2
}
t2=x[t1]
t3=i*2
t4=y[t3]
t5=t2*t4
t6=d+t5
d= t6
t7=i+1
Una solución i=t7
es la siguiente goto L1
L2:
return d
func fin
EJEMPLO
func inicio main
main(){ param a
int p; int a[10], b[10]; param b
p = dot_prod(a,b); refparam result
} call dot_prod, 3
p = result
func end

func inicio main


param a
Una solución param b
p=call dot_prod, 2
es la siguiente
func end
EJEMPLO
int fact(int n){
if (n==0) return 1;
else return (n*fact(n-1));
}
func inicio fact
if(n==0) goto l1
t1 = n-1
param t1
refparam result
Una solución call fact, 2
es la siguiente t3 = n*result
return t3
L1: return 1
func fin
EJEMPLO: PARA EL FACTORIAL
read x;
if ( 0 < x) {
fact := 1; read x
do{ t1 = 0 < x
fact := fact*x; if t1 goto L1
x := x – 1; fact = 1
}while x!=0; L2:
write fact; t2 = fact * x
} fact = t2
t3 = x – 1
x = t3
t4=x≠0
if t4 goto L2
write fact
L1:
EJEMPLO : PARA EL FACTORIAL
EN CUADRUPLOS
read x;
if ( 0 < x) {
fact := 1;
do{
fact := fact*x; read x 1.(read, x, - , - )
x := x – 1; t1 = 0 < x 2.(< , t1, 0 , x)
}while x!=0; if t1 goto L1 3.(if , t1, L1, -)
write fact; fact = 1 4.(= , fact, 1, -)
} 5.(L2 , -, - , -)
L2:
t2 = fact * x 6.(* , t2, fact, x)
fact = t2 7.(= , fact, t2, -)
t3 = x – 1 8.( sub , t3, x, 1)
x = t3 9.(= , x , t3, -)
t4=x≠0 10.(≠ , t4, x, 0)
if t4 goto L2 11.(if , t4, L2, -)
write fact 12.(write, fact, - , -)
L1: 13.(L1 , -, - , - )
EJEMPLO : PARA EL FACTORIAL
EN TRIPLETES
read x;
if ( 0 < x) {
fact := 1;
do{
fact := fact*x; read x
x := x – 1; t1 = 0 < x
}while x!=0; if t1 goto L1 (0) ( read, x, -)
write fact; fact = 1 (1) ( <, 0, x)
} (2) ( if , (1), (11))
L2:
t2 = fact * x (3) ( = , fact, 1)
fact = t2 (4) ( * , fact, x)
t3 = x – 1 (5) ( =, fact, (4) )
x = t3 (6) ( sub, x, 1)
t4=x≠0 (7) ( = , x, (6))
if t4 goto L2 (8) (≠, x, 0)
write fact (9) ( if , (8), (4))
L1: (10) ( write, fact, -)
ARBOLES SINTACTICOS

 Los nodos en un árbol


sintáctico representan +
construcciones en el programa
fuente.
+ *
 Los hijos de un nodo
representan los componentes
significativos de una
construcción. * d

a -

a+a*(b-c)+(b-c)*d
b c
ARBOLES SINTACTICOS
Pasos para construir el árbol dirigido

+
P1=hoja(id, entrada a)
P2=hoja(id, entrada a)=p1 + *
P3=hoja(id, entrada b)
P4=hoja(id, entrada c)
P5=nodo(-, p3,p4) * d
P6=nodo(*, p1,p5)
P7=nodo(+, p1,p6)
P8=hoja(id, entrada b)=p3
P9=hoja(id, entrada c)=p4 a -
P10=nodo(-, p3, p4)=p5
P11=hoja(id, entrada)
P12=nodo(*, p5, p11)
P13=nodo(+, p7,p12)
b c
ARBOLES SINTACTICOS
a+a*(b-c)+(b-c)*d
p13
+
p7 p12
P1=hoja(id, entrada a)
P2=hoja(id, entrada a)=p1 + *
P3=hoja(id, entrada b)
P4=hoja(id, entrada c) p6 p11
P5=nodo(-, p3,p4) * d
P6=nodo(*, p1,p5)
P7=nodo(+, p1,p6)
P8=hoja(id, entrada b)=p3
P9=hoja(id, entrada c)=p4 a - p5=p10
P10=nodo(-, p3, p4)=p5 P1 = p2
P11=hoja(id, entrada)
P12=nodo(*, p5, p11)
P13=nodo(+, p7,p12)
b c
p3=p8 p4=p9
USO

 El código intermedio es visto como


una cadena de caracteres y se
Dada la G.L.C:
puede diseñar un esquema de
traducción dirigido por la sintaxis
que genere dicho código al recorrer G=(VN , VT , P, S)
el árbol de análisis sintáctico en VN ={ S, E }
orden postfijo. VT = { :=, id, (, ), num }

 La siguiente gramática genera P= { S  id := E


expresiones aritméticas. Dada la
E  E1 + E2
expresión E E1 + E2 se calcula el
valor del no-terminal E en un E  ( E1 )
nombre temporal t. Se crea un E  id
nuevo nombre cada vez que se E  num }
necesita
EJEMPLO
Se considera la siguiente G.L.C:

G=(VN , VT , P, S)
VN ={ S, E } VT = { :=, id, (, ), num }
P= { S  id := E ATRIBUTOS
E  E1 + E2
E  ( E1 ) Cada no-terminal tiene
E  id dos atributos:
E  num }
ATRIBUTO DESCRIPCION
E. dir nombre temporal que contendra el valor
de E, (t1 , t2 , …).

E.cod serie de todas las proposiciones de


codigo de 3-direcciones que calculan E.
EJEMPLO: REGLAS SEMANTICAS

G=(VN , VT , P, S) PRODUCCION REGLA SEMANTICA


VN ={ S, E } Sid:=E S.cod=E.cod //{ lexema(id)=E.dir
}
VT = { :=, id, (, ), num } E E1 + E2 E.dir = nuevotemp();
P= { S  id := E1 E.cod= E1.cod//. E2 cod//
{E.dir = E1.dir + E2.dir}
E1  E2 + E3
E (E1) E.dir = E1.dir
E  ( E1 ) E.cod = “ ”
E  id E id E.dir = lexema(id)
E  num } E.cod= “ ”
Enum E.dir = lexema(num);
E.cod = “ ”
EJEMPLO: DESCRIPCION DE LAS
REGLAS SEMANTICAS

 Ambos atributos son


sintetizados. PRODUCCION REGLA SEMANTICA
Sid:=E S.cod=E.cod //{ lexema(id)=E.dir }
 La función nuevotemp()
genera nombres distintos t1 ,
t2 , ... cada vez que es E E1 + E2 E.dir = nuevotemp();
llamada. E.cod= E1.cod//. E2 cod//
{E.dir = E1.dir + E2.dir}
 Las llaves indican una
instrucción de código de 3- E (E1) E.dir = E1.dir
direcciones. E.cod = “ ”

 El símbolo // representa la E id E.dir = lexema(id)


concatenación de los trozos E.cod= “ ”
de código.
Enum E.dir = lexema(num);
E.cod = “ ”
EJEMPLO: PARA x =x +3 + 4

CODIGO INTERMEDIO cod=‘t1=x+3


t1 = x + 3 t2 = t1 + 4’
x=t2
t2 = t1 + 4 S
x = t2 cod=‘t1=x+3
t2 = t1 + 4’
dir=t2
PRODUCCION REGLA SEMANTICA
= E
Sid:=E S.cod=E.cod //{ lexema(id)=E.dir }
id
(x)

E E1 + E2 E.dir = nuevotemp();
E.cod= E1.cod//. E2 cod//
cod=‘t1=x+3’ cod=‘ ‘
{E.dir = E1.dir + E2.dir}
dir=t1 E + E
dir=4
E (E1) E.dir = E1.dir
E.cod = “ ”

cod=‘ ‘ cod=‘ ‘
E id E.dir = lexema(id)
E.cod= “ ”
E + E dir=3 num
dir=x
(4)
Enum E.dir = lexema(num);
E.cod = “ ” id num
(x) (3)
EJERCICIO

Para el ejercicio de la clase anterior (análisis


semantico) del cual se muestra un ejemplo:

int a, b ;
float c;
inicio
a =2;
b= 5;
c= a * b + (a + a);
fin

Construya el analizador léxico, sintáctico,


semántico, y además implemente el generador
de código intermedio.

También podría gustarte