Cod intermedio

50 %
50 %
Information about Cod intermedio

Published on November 10, 2012

Author: osced

Source: slideshare.net

Cap´ ıtulo 7Generaci´n de c´digo o ointermedio. Optimizaci´n o Bibliograf´ ıa: Aho, A.V., Sethi, R., Ullman, J.D. (1990), Compiladores: principios, t´cnicas y herramientas, Tema 8, 9, 10 (pag. 478- e 666). Louden, K.C. (1997), Compiler Construction: Principles and Practice, Tema 8, p´ginas: 398-481. a1. Introducci´n. o2. Tipos de representaciones intermedias: C´digo de 3-direcciones. o3. C´digo intermedio como un atributo sintetizado. o4. Generaci´n de c´digo para expresiones y sentencias de con- o o trol: a) Proposiciones de asignaci´n. o b) Expresiones aritm´ticas. e c) Expresiones booleanas. d ) Sentencias de control. e) Funciones.5. Optimizaci´n de c´digo: o o a) Bloques b´sicos y optimizaci´n local. a o 215

216CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION b) Eliminaci´n de subexpresiones comunes. o c) Eliminaci´n de c´digo muerto. o o d ) Transformaciones aritm´ticas. e e) Empaquetamiento de variables temporales. f ) Mejoras en lazos.7.1. Introducci´n oComo se coment´ en el primer cap´ o ıtulo el proceso de la com-pilaci´n se desglosa en dos partes: la parte que depende s´lo del o olenguaje fuente (etapa inicial o front-end ) y la parte que dependes´lo del lenguaje objeto (etapa final o back-end). o Etapa inicial: corresponde con la parte de an´lisis (l´xico, a e sint´ctico y sem´ntico). a a Etapa final: corresponde con la parte de s´ ıntesis (generaci´n o de c´digo). oLa etapa inicial traduce un programa fuente a una representaci´n ointermedia a partir de la cual la etapa final genera el c´digo ob- ojeto.De esta forma, los detalles que tienen que ver con las caracter´ ısti-cas del lenguaje objeto (c´digo ensamblador, c´digo m´quina ab- o o asoluto o relocalizable, . . . ), la arquitectura de la m´quina (n´mero a ude registros, modos de direccionamiento, tama˜o de los tipos de ndatos, memoria cache, ...), el entorno de ejecuci´n (estructura de oregistros y memoria de la m´quina donde se va a ejecutar el pro- agrama . . . ) y el sistema operativo se engloban en la etapa final yse aislan del resto.La generaci´n de c´digo es la tarea m´s complicada de un com- o o apilador. Las ventajas de utilizar esta representaci´n intermedia, oindependiente de la m´quina en la que se va a ejecutar el progra- ama, son: Se puede crear un compilador para una nueva m´quina dis- a tinta uniendo la etapa final de la nueva m´quina a una etapa a inicial ya existente. Se facilita la redestinaci´n. o

´7.2. TIPOS DE REPRESENTACIONES INTERMEDIAS: EL CODIGO DE 3-DIRECCIONES217 Se puede aplicar, a la representaci´n intermedia, un opti- o mador de c´digo independiente de la m´quina. o aLa figura 7.1 muestra las dos etapas y como se relacionan entres´ a trav´s de la representaci´n intermedia. ı e o Programa ETAPA fuente INICIAL ETAPA FINAL Analizador Léxico Generador de Componentes código máquina léxicos Código Analizador Intermedio Sintáctico Optimizador de Código máquina Arbol Sintáctico Analizador Semántico Código máquina Optimizador de código intermedio Figura 7.1: Etapa inicial y final de un compiladorEn este cap´ıtulo veremos c´mo traducir las construcciones de los olenguajes de programaci´n como: las declaraciones, asignaciones oy proposiciones de flujo de control a una representaci´n interme- odia. La mayor parte de las traducciones de estas proposicionesse pueden implantar durante el an´lisis sint´ctico utilizando las a at´cnicas de traducci´n vistas en en el dise˜o de esquemas de tra- e o nducci´n dirigidos por la sintaxis (ETDS). o7.2. Tipos de representaciones intermedias: el c´digo de 3-direcciones oUna representaci´n intermedia es una estructura de datos que orepresenta al programa fuente durante el proceso de la traduc-ci´n a c´digo objeto. Hasta ahora hemos usado el ´rbol de an´li- o o a a

218CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACIONsis sint´ctico como representaci´n intermedia, junto con la tabla a ode s´ımbolos que conten´ informaci´n sobre los nombres (vari- ıa oables, constantes, tipos y funciones) que aparec´ en el programa ıanfuente.Aunque el ´rbol de an´lisis sint´ctico es una representaci´n v´li- a a a o ada, no se parece ni remotamente al c´digo objeto, en el que s´lo o ose emplean saltos a direcciones en memoria en vez de construc-ciones de alto nivel, como sentencias if-then-else. Es nece-sario generar una nueva forma de representaci´n intermedia. A oesta representaci´n intermedia, que se parece al c´digo objeto o opero que sigue siendo independiente de la m´quina, se le llama ac´digo intermedio. oEl c´digo intermedio puede tomar muchas formas. Todas ellas se oconsideran como una forma de linearizaci´n del ´rbol sint´cti- o a aco, es decir, una representaci´n del ´rbol sint´ctico de forma se- o a acuencial. El c´digo intermedio m´s habitual es el c´digo de 3- o a odirecciones.El c´digo de tres direcciones es una secuencia de proposiciones ode la forma general x = y op zdonde op representa cualquier operador; x,y,z representan vari-ables definidas por el programador o variables temporales gener-adas por el compilador. y,z tambi´n pueden representar con- estantes o literales. op representa cualquier operador: un operadoraritm´tico de punto fijo o flotante, o un operador l´gico sobre e odatos booleanos.No se permite ninguna expresi´n aritm´tica compuesta, pues s´lo o e ohay un operador en el lado derecho. Por ejemplo, x+y*z se debetraducir a una secuencia, donde t1 , t2 son variables temporalesgeneradas por el compilador.t1 = y ∗ zt2 = x + t1Las expresiones compuestas y las proposiciones de flujo de controlse han de descomponer en proposiciones de este tipo, definiendoun conjunto suficientemente amplio de operadores. Se le llamac´digo de 3-direcciones porque cada proposici´n contiene, en el o o

´7.2. TIPOS DE REPRESENTACIONES INTERMEDIAS: EL CODIGO DE 3-DIRECCIONES219caso general, tres direcciones, dos para los operandos y una parael resultado. (Aunque aparece el nombre de la variable, realmentecorresponde al puntero a la entrada de la tabla de s´ ımbolos dedicho nombre).El c´digo de tres direcciones es una representaci´n linealizada o o(de izquierda a derecha) del ´rbol sint´ctico en la que los nom- a abres temporales corresponden a los nodos internos. Como estosnombres temporales se representan en la memoria no se especificam´s informaci´n sobre ellos en este tipo de c´digo. Normalmente a o ose asignar´n directamente a registros o se almacenar´n en la tabla a ade s´ımbolos. 2*a+b-3 t3 + t1 t2 * - 2 a b 3C´digo de 3-direcciones: ot1 = 2 * at2 = b - 3t3 = t1 + t27.2.1. Tipos de proposiciones de 3-direccionesLa forma de c´digo de 3-direcciones que hemos visto hasta aho- ora es insuficiente para representar todas las construcciones de unlenguaje de programaci´n (saltos condicionales, saltos incondi- ocionales, llamadas a funciones, bucles, etc), por tanto es nece-sario introducir nuevos operadores. El conjunto de proposiciones(operadores) debe ser lo suficientemente rico como para poderimplantar las operaciones del lenguaje fuente.Las proposiciones de 3-direcciones van a ser en cierta maneraan´logas al c´digo ensamblador. Las proposiciones pueden tener a oetiquetas simb´licas y existen instrucciones para el flujo de con- otrol (goto). Una etiqueta simb´lica representa el ´ o ındice de unaproposici´n de 3-direcciones en la lista de instrucciones. o

220CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACIONLas proposiciones de 3-direcciones m´s comunes que utilizaremos: a 1. Proposiciones de la forma x = y op z donde op es un op- erador binario aritm´tico, l´gico o relacional. e o 2. Instrucciones de la forma x = op y, donde op es un oper- ador unario (operador negaci´n l´gico, menos unario, oper- o o adores de desplazamiento o conversi´n de tipos). o 3. Proposiciones de copia de la forma x = y, donde el valor de y se asigna a x. 4. Salto incondicional goto etiq. La instrucci´n con etiqueta o etiq es la siguiente que se ejecutar´. a 5. Saltos condicionales como if false x goto etiq. 6. param x y call f para apilar los par´metros y llamadas a a funciones (los procedimientos se consideran funciones que no devuelven valores). Tambi´n return y, que es opcional, e para devolver valores. C´digo generado como parte de una o llamada al procedimiento p(x 1,x 2,...,x n). param x1 param x2 ... param xn call p,n 7. 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 o m´s all´ de la posici´n y. O tambi´n x[i] = y . a a o e 8. 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 o iguala al contenido de la direcci´n indicada por el puntero o y) ´ *x = y (el objeto apuntado por x se iguala al valor de o y).Ejemplo. Consideremos el c´digo que calcula el factorial de un on´mero. La tabla 7.1 muestra el c´digo fuente y el c´digo de 3- u o odirecciones. Existe un salto condicional if false que se usa para

´7.2. TIPOS DE REPRESENTACIONES INTERMEDIAS: EL CODIGO DE 3-DIRECCIONES221traducir las sentencias de control if-then, repeat-untilque contiene dos direcciones: el valor condicional de la expresi´n oy la direcci´n de salto. La proposici´n label s´lo tiene una di- o o orecci´n. Las operaciones de lectura y escritura, read, write, ocon una sola direcci´n. Y una instrucci´n de parada halt que no o otiene direcciones. read x; read x if 0<x then t1 = 0 < x fact:=1; if false t1 goto L1 repeat fact=1 fact:=fact*x; label L2 x:=x-1; t2=fact * x until x=0; fact=t2 write fact; t3=x-1 end; x=t3 t4=x==0 if false t4 goto L2 write fact label L1 haltCuadro 7.1: C´digo fuente y c´digo de 3-direcciones para el c´lculo del factorial o o a

222CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION7.2.2. Implementaci´n de c´digo de tres direcciones o oUna proposici´n de c´digo de 3-direcciones se puede implantar o ocomo una estructura tipo registro con campos para el operador,los operandos y el resultado. La representaci´n final ser´ entonces o auna lista enlazada o un vector de proposiciones.Implementaci´n mediante cu´druplos o aUn cu´druplo es una estructura tipo registro con cuatro campos aque se llaman (op, result, arg1, arg2). El campo opcontiene un c´digo interno para el operador. oPor ejemplo, la proposici´n de tres direcciones x = y + z se orepresenta mediante el cu´druplo (ADD, x,y, z). Las proposi- aciones con operadores unarios no usan el arg2. Los campos queno se usan se dejan vac´ o un valor NULL. Como se necesitan ıoscuatro campos se le llama representaci´n mediante cu´druplos. o aUna posible implantaci´n del programa que calcula el factorial omediante cu´druplos se muestra ahora en la parte izquierda de la atabla 7.2.

´7.2. TIPOS DE REPRESENTACIONES INTERMEDIAS: EL CODIGO DE 3-DIRECCIONES223 (a) Cu´druplos a (b) Tripletes (read,x,–,–) (0) (read,x,–) (isbigger,t1,x,0) (1) (isbigger,x,0) (if false,t1,L1,–) (2) (if false, (1), (11)) (assign,fact,1,–) (3) (assign, fact,1) (label,L2,–,–) (4) (mult, fact,x) (mult,t2,fact,x) (5) (assign, (4), fact) (assign,fact,t2,–) (6) (sub, x,1) (sub,t3,x,1) (7) (assign, (6), x) (assign,x,t3,–) (8) (isequal, x, 0) (isequal,t4,x,0) (9) (if false, (8),(4)) (if false,t4,L2,–) (10) (write,fact,–) (write,fact,–,–) (11) (halt,–,–) (label,L1,–,–) (halt,–,–,–)Cuadro 7.2: C´digo de 3-direcciones mediante: (a) cu´druplos y (b) tripletes o aImplementaci´n mediante tripletes oPara evitar tener que introducir nombres temporales en la tablade s´ ımbolos, se hace referencia a un valor temporal seg´n la posi- uci´n de la proposici´n que lo calcula. Las propias instrucciones o orepresentan el valor del nombre temporal. La implantaci´n se hace omediante registros de s´lo tres campos (op, arg1, arg2). oLa parte derecha de la tabla 7.2 muestra la implantaci´n mediante otripletes del c´lculo del factorial. Los n´meros entre par´ntesis a u erepresentan punteros dentro de la lista de tripletes, en tanto quelos punteros a la tabla de s´ımbolos se representan mediante losnombres mismos.En la notaci´n de tripletes se necesita menor espacio y el com- opilador no necesita generar los nombre temporales. Sin embargo,en esta notaci´n, trasladar una proposici´n que defina un val- o oor temporal exige que se modifiquen todas las referencias a esaproposici´n. Lo cual supone un inconveniente a la hora de opti- omizar el c´digo, pues a menudo es necesario cambiar proposiciones ode lugar.

224CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACIONA partir de ahora nos vamos a centrar en la notaci´n de cu´dru- o aplos, que es la que se implantar´ en la pr´ctica 3. La figura 7.2 a aa continuaci´n muestra en c´digo C como se podr´ implantar la o o ıaestructura de datos para los cu´druplos: atypedef enum {assign, add, mult,if false, goto, label, read, write, isequal, . . . }OpKind;typedef struct { int val;// para valores char *name;// para identificadores de variables} Address;typedef struct { OpKind op; Address result, arg1, arg2;} Quad; Figura 7.2: Posible implantaci´n en C de la estructura cu´druplo o aPor simplicidad se podrian considerar los campos result, arg1,arg2 como punteros a caracter. En esta implantaci´n s´lo se per- o omite que un argumento represente una constante entera o una ca-dena ( la cadena representa el nombre de un temporal o una vari-able de la tabla de s´ ımbolos). Una alternativa a almacenar nom-bres en el cu´druplo es almacenar punteros a la tabla de s´ a ımbolos,con lo que se evita tener que hacer operaciones de b´squeda en uun procesado posterior.7.3. C´digo intermedio como un atributo sin- o tetizadoEl c´digo intermedio puede ser considerado como un atributo sin- otetizado. El c´digo intermedio es visto como una cadena de car- oacteres y se puede dise˜ar un esquema de traducci´n dirigido por n ola sintaxis (ETDS) que genere dicho c´digo al recorrer el ´rbol de o aan´lisis sint´ctico en orden postfijo. a aConsideremos como ejemplo la siguiente gram´tica simplificada aque genera expresiones aritm´ticas. Dada la expresi´n E → E1 + e o

´7.3. CODIGO INTERMEDIO COMO UN ATRIBUTO SINTETIZADO 225E2 se calcula el valor del no-terminal E en un nombre temporalt. Por el momento, se crea un nuevo nombre cada vez que senecesita. M´s tarde veremos un sencillo m´todo para reutilizar a elos nombres temporales.Cada no-terminal tiene dos atributos: E.lugar, nombre temporal que contendr´ el valor de E, a (t1 , t2 , . . .). E.cod, serie de todas las proposiciones de c´digo de 3-direcciones o que calculan E.Ambos atributos son sintetizados. La funci´n nuevotemp() gen- oera nombres distintos t1 , t2 , ... cada vez que es llamada.Las llaves indican una instrucci´n de c´digo de 3-direcciones. El o os´ ımbolo // representa la concatenaci´n de los trozos de c´digo. o oProducci´n o Regla Sem´ntica aS → id := E S.cod = E.cod//{lexema(id) = E.lugar}E → E1 + E2 E.lugar = nuevotemp(); E.cod = E1 .cod//E2 .cod//{E.lugar = E1 .lugar + E2 .lugar}E → (E1 ) E.lugar = E1 .lugar E.cod = E1 .codE → id E.lugar = lexema(id) E.cod = “ “E → num E.lugar = lexema(num); E.cod = “ “ Cuadro 7.3: ETDS para expresiones aritm´ticas eVeamos el ejemplo x=x+3+4. La figura 7.3 muestra el ´rbol sint´cti- a aco. El c´digo de 3-direcciones correspondiente es: ot1 = x + 3t2 = t1 + 4x = t2

226CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION cod=’t1=x+3 t2=t1+4 x=t2’ S cod=’t1=x+3 E lugar=t2 id t2=t1+4’ = (x) cod=’t1=x+3’ E lugar=t1 + cod=’ ’ E lugar=’4’ cod=’ ’ E lugar=’x’ + cod=’ ’E lugar=’3’ num (4) id num (x) (3) ´ Figura 7.3: Arbol sint´ctico para el ejemplo x=x+3+4 aReutilizaci´n de los nombres temporales oHasta ahora se ha supuesto que la funci´n nuevotemp() genera oun nombre temporal cada vez que se necesita un temporal . Sinembargo, los nombres temporales utilizados para guardar valoresintermedios de los c´lculos de expresiones tienen que ser intro- aducidos en la tabla de s´ ımbolos para guardar sus valores con elconsiguiente aumento de espacio.Los nombres temporales se pueden reutilizar mediante un sencillom´todo que consiste en llevar una cuenta c, iniciada a cero, de evariables temporales. Cuando se utilice un nombre temporal comooperando hay que decrementar el valor de c en 1. Siempre que segenere un nuevo nombre temporal se usa el valor del contador(sprintf(simbolo,‘‘t %d’’,c)) y se incrementa el valorde c en 1. Consideremos el ejemplo: x = a ∗ b + c ∗ d − e ∗ f = id - + * (x) * * e f a b c d ´ Figura 7.4: Arbol sint´ctico para el ejemplo x=a*b+c*d-e*f a

´7.3. CODIGO INTERMEDIO COMO UN ATRIBUTO SINTETIZADO 227 Proposici´n o Valor de c 0 t0 = a * b 1 t1 = c * d 2 t0 = t0 + t1 1 t1 = e * f 2 t0 = t0 - t1 1 x = t0 0Cuidado: este m´todo no es aplicable para temporales que se eutilizan m´s de una vez. Por ejemplo, al evaluar una expresi´n en a ouna proposici´n condicional. A estos valores hay que asignarles onombres temporales creados con un nombre propio.

228CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION 7.4. Traducci´n de proposiciones de asignaci´n o o y expresiones aritm´ticas e La tabla 7.4 a continuaci´n muestra como se puede realizar la o traducci´n de expresiones aritm´ticas m´s complejas. La funci´n o e a o gen cuad(op, result, arg1, arg2) genera el correspondiente c´digo de 3-direcciones en notaci´n o o de cu´druplos. Cada vez que es llamada genera una lista de c´digo a o con s´lo un cu´druplo. o aProducci´n o Reglas Sem´nticas aS → id := E S.cod = E.cod//gen cuad(ASSIGN, id.lugar, E.lugar, −−)E → E1 + E2 E.lugar = nuevotemp() E.cod = E1 .cod//E2 .cod//gen cuad(ADD, E.lugar, E1 .lugar, E2 .lugar)E → E1 ∗ E2 E.lugar = nuevotemp() E.cod = E1 .cod//E2 .cod//gen cuad(M U LT, E.lugar, E1 .lugar, E2 .lugar)E → −E1 E.lugar = nuevotemp() E.cod = E1 .cod//gen cuad(U M IN U S, E.lugar, E1 .lugar, −−)E → (E1 ) E.lugar = E1 .lugar E.cod = E1 .codE → id E.lugar = lexema(id) E.cod = “ “ Cuadro 7.4: ETDS para expresiones aritm´ticas e En la pr´ctica las proposiciones de 3-direcciones se pueden escribir a de forma consecutiva a un archivo de salida en lugar de construir la lista de proposiciones en el atributo cod, lo que facilita la implementaci´n.o Veamos ahora como se puede implantar una funci´n recursiva que o genera el c´digo de 3-direcciones, que llamaremos genera c´digo(). o o Al fin y al cabo se trata de un atributo sintetizado y podemos recorrer el ´rbol en modo postfijo. Supongamos que la funci´n a o devuelve un registro llamado TAC (Three-Address-Code), que contiene dos campos: lugar (nombre temporal donde se almacena el valor de una

´ ´ ´7.4. TRADUCCION DE PROPOSICIONES DE ASIGNACION Y EXPRESIONES ARITMETICAS2 expresi´n) o cod (puntero a la lista de cu´druplos que almacena el c´di- a o go).Se utiliza el lexema, pero ser´ en realidad un puntero a la tabla ıade s´ ımbolos.

230CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACIONtypedef enum {n assign, n mult, n add, n id, ...} NodeKind;typedef struct streenode { NodeKind kind; int nchilds; // n´mero de hijos u struct streenode childs[MAXNUMCHILDS]; // se puede usar una lista con primer hijo que apunta a hermanos int val; // para constantes num´ricas e char *strval; // para identificadores, deberia ser puntero a laTS } STreeNode;typedef STreeNode * SyntaxTreeRoot;typedef struct { char lugar[10]; lista codigo *cod; //puntero a lista de cu´druplos a } TAC; n_menos n_mult n_uminus = n_mas id E E1 E2 E1 E2 E1 E2 E n_id n_num Figura 7.5: Tipos de nodoTAC genera c´digo (STreeNode *nodo) { o TAC datos; TAC aux1, aux2; lista codigo *cod=NULL; datos.cod=NULL; switch (node->kind) { case n assign: // childs[0]=id, childs[1]=E aux1=genera codigo(childs[1]); cod=gen cuad(assign, lexema(id), aux1.lugar,--); datos.cod=concatena codigo(aux1.cod,cod); break; case n add: // childs[0]=E1 , childs[1]=E2 aux1=genera codigo(childs[0]);

´ ´ ´7.4. TRADUCCION DE PROPOSICIONES DE ASIGNACION Y EXPRESIONES ARITMETICAS2 aux2=genera codigo(childs[1]); datos.lugar=nuevotemp(); cod=gen cuad(add, datos.lugar,aux1.lugar,aux2.lugar); datos.cod=concatena codigo(aux1.cod,aux2.cod,cod); break; case n mult: // childs[0]=E1 , childs[1]=E2 aux1=genera codigo(childs[0]); aux2=genera codigo(childs[1]); datos.lugar=nuevotemp(); cod=gen cuad(mult, datos.lugar,aux1.lugar,aux2.lugar); datos.cod=concatena codigo(aux1.cod,aux2.cod,cod); break; case n parentesis: // childs[1]=E // no haria falta crear este tipo de nodo datos=genera codigo(childs[1]); break; case n id: datos.lugar=lexema(id); datos.cod=NULL; break; } case n num: datos.lugar=lexema(num); datos.cod=NULL; break; } return(datos);}

232CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION7.5. Traducci´n de expresiones booleanas oLas expresiones booleanas se utilizan principalmente como partede las proposiciones condicionales que alteran el flujo de controldel programa, if-then, if-then-else, while-do. Las ex-presiones booleanas se componen de los operadores booleanosand,or,not aplicados a variables booleanas o expresiones rela-cionales.A su vez, las expresiones relacionales son de la forma E1 oprelE2 , donde E1 y E2 son expresiones aritm´ticas y oprel es cualquier eoperador relacional <, >, <=, >=,....Consideremos expresiones booleanas generadas por la gram´tica: aE → E or E | E and E | not E | ( E ) | id oprel id | true | false | idUno de los m´todos para traducir expresiones booleanas a c´digo e ode 3-direcciones consiste en codificar num´ricamente los valores etrue y false y evaluar una expresi´n booleana como una expre- osi´n aritm´tica, siguiendo unas prioridades. A menudo se utiliza o e1 para indicar true y 0 para indicar false.Las expresiones booleanas se eval´an de manera similar a una ex- upresi´n aritm´tica de izquierda a derecha. Supongamos el ejemp- o elo: a or b and not c. La secuencia de c´digo de 3-direcciones ocorrespondiente es:t1 = a or bt2 = not ct3 = t1 and t2La siguiente gram´tica en la tabla 7.5 muestra el ETDS para aproducir c´digo de 3-direcciones para las expresiones booleanas. oLa funci´n para generar c´digo se podr´ ampliar a˜adiendo nuevos o o ıa ncasos a la sentencia switch correspondientes a los tipos de no-

´ 7.5. TRADUCCION DE EXPRESIONES BOOLEANAS 233Producci´n o Reglas Sem´nticas aE → E1 or E2 E.lugar = nuevotemp() E.cod = E1 .cod//E2 .cod//gen cuad(or, E.lugar, E1 .lugar, E2 .lugar)E → E1 and E2 E.lugar = nuevotemp() E.cod = E1 .cod//E2 .cod//gen cuad(and, E.lugar, E1 .lugar, E2 .lugar)E → not E1 E.lugar = nuevotemp() E.cod = E1 .cod//gen cuad(not, E.lugar, E1 .lugar, −−)E → (E1 ) E.lugar = E1 .lugar E.cod = E1 .codE → id1 oprel id1 E.lugar = nuevotemp(); E.cod = gen cuad(oprel, E.lugar, lexema(id1 ), lexema(id2 ))E → true E.lugar = nuevotemp(); E.cod = gen cuad(assign, E.lugar, 1, −−)E → f alse E.lugar = nuevotemp(); E.cod = gen cuad(assign, E.lugar, 0, −−)E → id E.lugar = lexema(id) E.cod = ““ Cuadro 7.5: ETDS para expresiones booleanas utilizando una representaci´n o num´rica e dos asociados a los operadores l´gicos. Por ejemplo, para el nodo o correspondiente al operador l´gico and, nodo tipo n and, se in- o sertar´ el siguiente trozo de c´digo: ıa o case n and: // childs[0]=E1 , childs[1]=E2 aux1=genera codigo(childs[0]); aux2=genera codigo(childs[1]); datos.lugar=nuevotemp(); cod=gen cuad(and, datos.lugar,aux1.lugar,aux2.lugar); datos.cod=concatena codigo(aux1.cod,aux2.cod,cod); break;

234CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION7.6. Traducci´n de proposiciones de control oPasemos a considerar ahora la traducci´n de proposiciones de con- otrol if-then, if-then-else, while-do generadas por lasiguiente gram´tica: aS → if E then S1 | if E then S1 else S2 | while E do S17.6.1. Proposici´n if-then oSupongamos una sentencia if-then de la forma S → if Ethen S1 , ver diagrama de flujo en figura 7.6. Para generar elc´digo correspondiente a esta proposici´n habr´ que a˜adir a o o ıa nla funci´n genera c´digo() un nuevo caso para la sentencia o oswitch que contemple este tipo de nodo en el ´rbol sint´ctico, a anodo n ifthen. CODIGO CALCULO E F SALTO CONDICIONAL V n_ifthen CODIGO S Etiqueta 1 E S (a) (b)Figura 7.6: (a) Diagrama de flujo para la proposici´n if-then y (b) tipo de onodo TAC datos; TAC aux1, aux2; lista codigo *cod; datos.cod=NULL; direcciones dir; //usamos direcci´n al salto, en vez de etiquetas o case n ifthen: // childs[0]=E, childs[1]=S1 aux1=genera codigo(childs[0]); cod=gen cuad(if false,--, aux1.lugar, dir?); // a´n no se sabe la direc. de salto u aux2=genera codigo(childs[1]);

´7.6. TRADUCCION DE PROPOSICIONES DE CONTROL 235 dir=sgtedirlibre(); //relleno de retroceso rellena(cod,arg3,dir) datos.cod=concatena codigo(aux1.cod,cod,aux2.cod); break;Supondremos que tenemos una funci´n, sigtedirlibre(), que oguarda el ´ındice de la siguiente instrucci´n libre (la funci´n gen cuad() o oincrementa ese contador). En la implantaci´n se ha optado por outilizar direcciones directamente a las instrucciones en vez de usaretiquetas.7.6.2. Proposici´n if-then-else o Supongamos una sentencia if-then-else de la forma S → ifE then S1 else S2 , cuyo diagrama de flujo tiene la formarepresentada en la figura 7.7. Para generar el c´digo correspondi- oente a esta proposici´n habr´ que a˜adir a la funci´n genera c´digo() o ıa n o oun nuevo caso para la sentencia switch que contemple este tipode nodo en el ´rbol sint´ctico, nodo n ifthenelse. Este frag- a amento de c´digo se podr´ implantar de forma similar a como o ıahemos hecho en la secci´n anterior. Ver ejercicios. o CODIGO CALCULO E ´ SALTO CONDICIONAL F V CODIGO S1 SALTO INCONDICIONAL Etiqueta 2 n_ifthen_else CODIGO S2 Etiqueta 1 E S1 S2 (a= (b)Figura 7.7: (a)Diagrama de flujo para la proposici´n if-then-else y (b) tipo ode nodo

236CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION7.6.3. Proposici´n while-do oUna sentencia while -do tiene la forma S → while E doS1 , cuyo diagrama de flujo viene representado en la figura 7.8.Para generar el c´digo correspondiente a esta proposici´n habr´ o o ıaque a˜adir a la funci´n genera c´digo() un nuevo caso para n o ola sentencia switch que contemple este tipo de nodo en el ´rbol asint´ctico. a Etiqueta 1 CODIGO CALCULO E ´ F SALTO CONDICIONAL V CODIGO S n_while SALTO INCONDICIONAL Etiqueta 2 E S (a= (b)Figura 7.8: (a)Diagrama de flujo para la proposici´n while do y (b) tipo de onodoSupondremos que tenemos una funci´n, sigtedirlibre(), que guar- oda el ´ındice de la siguiente instrucci´n libre (se asume que la ofunci´n gen cuad() incrementa ese contador). o TAC datos; datos.cod=NULL; TAC aux1, aux2; lista codigo *cod1=NULL, *cod2=NULL; direcciones dir1,dir2; case n while: // childs[0]=E, childs[1]=S1 dir1=sgtedirlibre(); aux1=genera codigo(childs[0]); cod1=gen cuad(if false,--, aux1.lugar, dir?); aux2=genera codigo(childs[1]); cod2=gen cuad(goto,--, dir1,--);// salto incondicionala dir1 dir2=sigtedirlibre(); // rellena argumento de cod1, direccion salto condicional

´7.6. TRADUCCION DE PROPOSICIONES DE CONTROL 237 rellena(cod1,arg3,dir2) datos.cod=concatena codigo(aux1.cod,cod1,aux2.cod,cod2); break; Se ha optado por utilizar direcciones directamente a las in-strucciones en vez de etiquetas.

238CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION7.7. Traducci´n de funciones oEn esta secci´n se describe en t´rminos generales el mecanismo o ede generaci´n de c´digo de 3-direcciones para funciones. La gen- o oeraci´n de c´digo para las funciones es la parte m´s complicada o o aporque depende de la m´quina objeto y de la organizaci´n del a oentorno de ejecuci´n. La traducci´n de funciones implica dos eta- o opas: la definici´n de la funci´n. Una definici´n de funci´n crea o o o o un nombre, par´metros y c´digo del cuerpo de la funci´n pero a o o no se ejecuta la funci´n en ese punto. o la llamada a la funci´n desde una parte del programa o (trataremos a los procedimientos como funciones que no de- vuelven valores). Una llamada crea los valores actuales para los par´metros y realiza un salto al c´digo de entrada de la a o funci´n que se ejecuta y retorna al punto de la llamada. oSupongamos que el sub´rbol sint´ctico correspondiente a una fun- a aci´n tiene como ra´ un nodo de tipo n call, llamada a funci´n, o ız ocon una serie de hijos que corresponden a los argumentos, que engeneral, ser´n expresiones E1 , . . . , En a evaluar para obtener el avalor actual de los n-argumentos.A continuaci´n se incluye una implantaci´n para generar el c´di- o o ogo de 3-direcciones correspondiente a la llamada de la funci´n en oprimer lugar. Posteriormente analizaremos el caso de la definici´n ode la funci´n. Supondremos que se han comprobado previamente olas condiciones sem´nticas de la llamada (n´mero y tipo de argu- a umentos).Ejemplo de llamada a la funci´n o La llamada a una funci´n de dos argumentos genera el siguiente oc´digo intermedio: oSe hace uso de una pila en donde se apilan los valores actuales delos par´metros de la funci´n para despu´s, como veremos en el a o e

´ 7.7. TRADUCCION DE FUNCIONES 239 Calculo de los param. de F ´ Apilar direccion de retorno de F Punto de entrada a F Apilar argumentos (paso de parametros) Desapilar valores parametros . Llamada CALL/ Salto a F . Cuerpo de F : Apilar valor de retorno Recoger valor de retorno Salto retorno Figura 7.9: Diagrama de flujo para la llamada a funci´n oASIGN C t0 147 // asigna la direcci´n de retorno 147 a t0 (la siguiente al CALL) oPUSH t0 – // apila la direcci´n de retorno oPUSH t1 – // apila el valor de t1, primer arg. de la funci´n oPUSH t2 – // apila el valor de t2, segundo arg. de la funci´n oCALL X – // salta a la direcci´n X, entrada a la funci´n o oPOP t3 – // direccion 147, se desapila el valor devuelto por la funci´n, se almacena en t3 o Cuadro 7.6: Ejemplo de c´digo de llamada a una funci´n o o siguiente apartado, en la implantaci´n del cuerpo de la funci´n, o o desapilarlos. Nota: Respecto a la direcci´n de la llamada a la funci´n (´ o o ındice de la instrucci´n del c´digo de entrada a la funci´n), se podr´ o o o ıa conocer en el caso de que el c´digo para la funci´n ya se hubiera o o generado antes de la llamada (por ejemplo, se podr´ haber al- ıa macenado en la entrada de la tabla de s´ ımbolos correspondiente a esa funci´n en un campo definido para ese prop´sito). o o

240CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACIONEn el caso de que a´n no se haya generado el c´digo para la defini- u oci´n de la funci´n tendr´ o o ıamos que hacer un relleno de retroceso entodas las llamadas a esa funci´n. Una forma ser´ para cada fun- o ıaci´n almacenar los ´ o ındices de las instrucciones donde se hace unallamada a dicha funci´n (por ejemplo, en un vector de ´ o ındices) yuna vez definida la funci´n que ya se conoce la direcci´n de entra- o oda recorremos el vector de ´ ındices y rellenamos con la direcci´n oadecuada en todas las proposiciones de llamadas a esa funci´n.o

´7.7. TRADUCCION DE FUNCIONES 241TAC datos;datos.cod=NULL;TAC aux[MAXNCHILDS];cuadruplos cod1, cod2, codpush[MAXNCHILDS], codcall, codpop;direcciones dir1,dir2; //direcciones a los saltoschar *t0, *t1; // varibales temporalescase n call: // c´lculo de los par´metros de la funci´n a a o for(i = 0; i<nchilds;i++) aux[i] = genera c´digo(childs[i]); o t0 = nuevotemp(); //asigna en t0 la direc. de retorno de la func., a´n no se sabe u cod1 = gen cuad(ASSIGN,t0,dir1?); cod2 = gen cuad(PUSH,–,t0,–); // apilamos la direc. de retorno // apilamos los argumentos for(i = 0; i<nchilds;i++) codpush[i] = gen cuad(PUSH,–, aux[i].lugar,–)); // llamada a la func., no se sabe a´n la direcc. de su cuerpo u codcall = gen cuad(CALL, –, dir2?,–); // la siguiente direcci´n libre es la de retorno o dir1 = sigtedirlibre(); rellena(cod1,arg3,dir1); t1 = nuevotemp(); // recogemos el valor devuelto por la funcion codpop = gen cuad(POP,t1,–,–) ; datos.cod=concatena(losaux[i].cod,cod1,cod2,loscodpush[i],codcall,codpop); datos.lugar = t1; //para relleno de retroceso de dir2, vease Nota return (datos); Figura 7.10: Implantaci´n de la llamada a funciones o

242CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION Cuerpo de la funci´n o Para traducir el cuerpo de la funci´n bastar´ recorrer el ´rbol o ıa a hasta encontrar un nodo de tipo n function a partir del cual colgar´ el bloque de sentencias del cuerpo de la funci´n. No har´ a o ıa falta generar la parte del ´rbol de los argumentos ya que ´stos a e estar´ almacenados en la tabla de s´ ıan ımbolos. A partir de cierta direcci´n de c´digo intermedio deber´ o o ıamos tener un instrucci´n o que ser´ la entrada a la funci´n y que contendr´ ıa o ıa:POP t10 // recoge el segundo argumentoPOP t11 // recoge el primer argumento... // operaciones de la funci´n que almacena el resultado en t20 (por ejemplo) oPOP t15 // recoge de la pila el valor de la direcc. de retorno (147) y lo pone en t15 (por ejem.)RETURN t15 t20 // salida de la funci´n: el entorno de ejecuci´n deber primero saltar a la instrucc. o o cuya direcci´n es el valor de t15 y apilar el valor devuelto que es el de t20 o Esto implica las siguientes tareas (ver parte derecha de la figura 7.9) : 1. Recoger la direcci´n libre actual y utilizarla como direcci´n o o para rellenar (relleno con retroceso) las llamadas a la funci´n o que estamos definiendo. 2. Generar c´digo para desapilar los argumentos de la funci´n o o y meterlos en las variables correspondientes. 3. Generar c´digo para el bloque de sentencias S. o 4. Desapilar la direccion de retorno. 5. Generar la salida de la funcion con RETURN. 6. Unir los trozos de c´digo para tenerlos en una unica lista de o ´ cu´druplos. a

´7.7. TRADUCCION DE FUNCIONES 243TAC datos;datos.cod=NULL;TAC aux[MAXNCHILDS];cuadruplos codpoparg[MAXNPARAM], codpopdirreturn, codreturn;direcciones dir; //direccion de retornochar *t; // varibale temporalcase n function: dir = sigtedirlibre(); rellena(todas llamadas a la funcion con esta dir); // desapilamos los argumentos for(i = 0; i < nparams ;i++) codpoparg[i] = gen cuad(POP, param[i].lugar, -, -)); //genera codigo para el cuerpo de la funci´n (k intrucciones) o //y calculamos el valor a devolver for(k = 0; k < nchilds; k++) aux[k] = genera c´digo(childs[k]); o t = nuevotemp(); //desapilamos la direccion de retorno de la funci´n o codpopdirreturn = gen cuad(POP, t, -, -); //salida de la funcion: el entorno de ejecuci´n salta a la o //direcc. de retorno en t y se apila el valor devuelto codreturn = gen cuad(RETURN, t, valor devuelto, -); datos.cod=concatena(loscodpoparg[i],losaux[k].cod,codpopdirreturn,codreturn); datos.lugar = valor devuelto; return (datos); Figura 7.11: Implantaci´n del cuerpo de las funciones o VUESTRA pr´ctica 3 a En la definici´n de la funci´n vais a tener que colgar los o o argumentos en el ´rbol para saber cuantos par´metros teneis a a que desapilar cuando implementeis el cuerpo de la funci´n, o pues no teneis la tabla de simbolos con esa informaci´n. o Para la llamada a las funciones tendre´ que crear a mano ıs un nodo n CALL, producci´n FunctionStm → id ( Arg ) . De o lo contrario, lo confundiria´ con el nodo n id. ıs

244CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION Para colgar los par´metros en la definici´n de la funci´n ten- a o o dre´ que crear un nodo a mano n PARAM, producci´n de ıs o Arg.Vamos a hacer el siguiente ejemplo. Se ha considerado que primerose genera el c´digo del programa principal y a continuaci´n el o oc´digo de las funciones. omodule Ejemplo ;var resultado : integer ;function Suma ( integer a, integer b) : integer ;var total: integer;begin total = a + b ; return(total) ;end Suma ;begin (* programa principal *)resultado = Suma ( 2, 4*8) ;write resultado ;end Ejemplo .Listado de cuadruplos00 (ASSIGN C, t1, 2, NULL)01 (MULT, t2, 4, 8)02 (ASSIGN C, t3, 7, NULL)03 (PUSH, t3, NULL, NULL)04 (PUSH, t1, NULL, NULL)05 (PUSH, t2, NULL, NULL)06 (CALL, 11, NULL, NULL)07 (POP, t4, NULL, NULL)08 (ASSIGN V, resultado, t4, NULL)09 (WRITE, resultado, NULL, NULL)10 (HALT, NULL, NULL, NULL) // final del programa11 (POP, b, NULL, NULL) // entrada a la funci´n o12 (POP, a, NULL, NULL)13 (ADD, t5, a, b)14 (ASSIGN V, total, t5, NULL)

´7.7. TRADUCCION DE FUNCIONES 24515 (POP, t6, NULL, NULL) // desapilo la direcci´n de retorno, la 07 o16 (RETURN, t6, total, NULL) // el entorno de ejecuci´n salta a la direccion o// en t6 (la 07) y apila el valor a devolver

246CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION7.8. Optimizaci´n de c´digo intermedio o oLa optimizaci´n de c´digo intermedio se puede realizar: o o a nivel local: s´lo utilizan la informaci´n de un bloque b´sico o o a para realizar la optimizaci´n.o a nivel global: que usan informaci´n de varios bloques b´sicos. o aEl t´rmino optimizaci´n de c´digo es inadecuado ya que no se e o ogarantiza el obtener, en el sentido matem´tico, el mejor c´digo a oposible atendiendo a maximizar o minimizar una funci´n objetivo o(tiempo de ejecuci´n y espacio). El t´rmino de mejora de c´digo o e oser´ m´s apropiado que el de optimizaci´n. ıa a oNos concentraremos b´sicamente en la optimizaci´n de c´digo de a o otres-direcciones, puesto que son siempre transportables a cualquieretapa final, son optimizaciones independientes de la m´quina. Las aoptimizaciones a nivel de la m´quina, como la asignaci´n de reg- a oistros y la utilizaci´n de instrucciones espec´ o ıficas de la m´quina, ase salen del contexto de esta asignatura.La mayor´ de los programas emplean el 90 % del tiempo de eje- ıacuci´n en el 10 % de su c´digo. Lo m´s adecuado es identificar las o o apartes del programa que se ejecutan m´s frecuentemente y tratar ade que se ejecuten lo m´s eficientemente posible. En la pr´cti- a aca, son los lazos internos los mejores candidatos para realizar lastransformaciones.Adem´s de la optimizaci´n a nivel de c´digo intermedio, se puede a o oreducir el tiempo de ejecuci´n de un programa actuando a otros oniveles: a nivel de c´digo fuente y a nivel de c´digo objeto. o o Codigo Codigo fuente Intermedio Codigo Etapa Generador Objeto Inicial de Codigo el programador puede el compilador puede el compilador puede modificar algoritmos mejorar los lazos usar registros transformar lazos seleccionar instruccionesFigura 7.12: Niveles en los que el programador y el compilador pueden mejorarel c´digo o

´ ´7.8. OPTIMIZACION DE CODIGO INTERMEDIO 247Un bloque b´sico es una unidad fundamental de c´digo. Es una a osecuencia de proposiciones donde el flujo de control entra en elprincipio del bloque y sale al final del bloque. Los bloques b´sicos apueden recibir el control desde m´s de un punto en el programa a(se puede llegar desde varios sitios a una etiqueta) y el controlpuede salir desde m´s de una proposici´n (se podr´ ir a una a o ıaetiqueta o seguir con la siguiente instrucci´n). Cuando aplicamos ooptimizaci´n dentro de un bloque b´sico s´lo nos tenemos que o a opreocupar sobre los efectos de la optimizaci´n en los valores de olas variables a la entrada del bloque y los valores que tienen ala salida del bloque, que han de ser los mismos que en el c´digo ooriginal sin transformar.El algoritmo para particionar un programa en bloques se describea continuaci´n: o 1. Encontrar todas las proposiciones que comienzan el principio de un bloque b´sico: a La primera sentencia del programa. Cualquier proposici´n del programa que es el objetivo de un o salto. Cualquier proposici´n que sigue a una bifurcaci´n. o o 2. Para cualquier proposici´n que comienza un bloque b´sico, el bloque o a consiste de esa proposici´n y de todas las siguientes hasta el o principio del siguiente bloque o el final del programa.El flujo de control de un programa puede visualizarse como ungrafo dirigido de bloques b´sicos. A este grafo se le llama grafo ade flujo. Como ejemplo consideremos este trozo de c´digo escrito oen pseudoC que suma los diez primeros n´meros que se muestra uen la tabla 7.7 (a):

248CAP´ ´ ´ ´ ITULO 7. GENERACION DE CODIGO INTERMEDIO. OPTIMIZACION void main() i = 10 { s=0 int i,s; label l1 i=10; t0 = i > 0 s=0; iffalse t0 goto l2 while i > 0 do s=s+i { i=i-1 s=s+i; goto l1 i=i-1; label l2 } ... } (a) (b) Cuadro 7.7: Ejemplo (a) c´digo fuente, (b) c´digo de 3-direcciones o o La figura 7.13 muestra el diagrama de flujo para el ejemploanterior. Aparecen 4 bloques b´sicos. a i = 10 s=0 label l1 t0 = i > 0 iffalse t0 goto l2 s= s+i i=i-1 goto l1 label l2 ... Figura 7.13: Diagrama de flujo

´ ´7.8. OPTIMIZACION DE CODIGO INTERMEDIO 249Algunas definiciones previas: Dada una proposici´n de 3-direcciones de la forma a = b op o c decimos que la proposici´n referencia b y c y que define a. o Se dice que un nombre en un bloque b´sico vive al final del a bloque si su valor es referenciado en otro bloque b´sico en el a programa. Se dice que un nombre est´ muerto si no es referenciado en a el resto del programa. Se presentan algunas de las transformaciones m´s utiles para a ´mejorar el c´digo. Son transformaciones locales, se pueden realizar oobservando s´lo las proposiciones de un bloque b´sico. o a7.8.1. Eliminaci´n de subexpresiones comunes oSi una expresi´n se calcula m´s de una vez, se puede remplazar o ael c´lculo de la segunda por el valor de la primera expresi´n. a oConsideremos el siguiente ejemplo del c´digo 7.8 (a). Vemos que

Add a comment

Related pages

Ernst Klett Verlag - ¡Adelante! Nivel intermedio Spanisch ...

Nivel intermedio Einstieg Konzeption Produktübersicht Lehrwerk-Online Stoffverteilung Einzeltitel ¡Adelante ... Adelante-Code mit den Audios, ...
Read more

COD. 216 escultural intermedio hiperflex by gema ...

Nice Demo Banner. It is a long established fact that a reader will be distracted by the readable content of page when looking at its layout. Read More
Read more

call of duty y el intermedio - YouTube

Call Of Duty BLACK OPS 3 RAP | KRONNO, ZARCORT, CYCLO & PITER G | ( Videoclip Oficial ) - Duration: 6:37. Kronno Zomber 3,619,681 views
Read more

Buscaminas intermedio - YouTube

Buscaminas intermedio Chaodo COD. Subscribe Subscribed Unsubscribe 8 8. Loading ... Chaodo COD 37 views. 6:12 Buscaminas tutorial - Duration: 3:35.
Read more

Mas With Connect Access Code: Espanol Intermedio: Amazon ...

Ana Maria - Mas With Connect Access Code: Espanol Intermedio jetzt kaufen. ISBN: 9780073522128, Fremdsprachige Bücher - Spanisch
Read more

Bytecode - Wikipedia, the free encyclopedia

Bytecode, also known as p-code (portable code), is a form of instruction set designed for efficient execution by a software interpreter. Unlike human ...
Read more

Intermedio

Knee Play is the musical outgrowth of the members of Intermedio -- an outlet to explore our interests in collaborative and experimental musical ...
Read more

¡Anda! Curso intermedio, 2nd Edition - MyPearsonStore

Curso intermedio, 2nd Edition Audrey L. Heining-Boynton, Jean W. LeLoup, Glynis S. Cowell; English Grammar Guide for !Anda! Curso elemental, 2nd Edition
Read more

¡Adelante!: Nivel intermedio, Trainingsheft - innerhalb ...

Nivel intermedio, Trainingsheft. zum Preis von: 12,50€ ... - Adelante-Code mit den Audios, den Lösungen und den Transkriptionen der Hörverstehensübungen.
Read more