2.2.2 Código P.

2.2.2 Código P.

Generación de Código Intermedió

*Después de los análisis sintácticos y semánticos, algunos compiladores generan una representación intermedia explicita del programa fuente. Se puede considerar esta representación intermedia como un programa para una maquina abstracta. Esta representación intermedia debe tener dos propiedades importantes, debe ser fácil de producir y fácil de traducir al programa objeto.
*El código intermedió es particularmente utilizado cuando el objetivo de compilador es producir código muy eficiente, ya que para hacerlo así se requiere una cantidad importante del análisis de las propiedades del código objetivo, y esto se facilita mediante el uso del código intermedio.
*El código intermedio también puede ser útil al hacer que un compilador sea mas fácilmente re dirigible: si el código intermedio es hasta cierto punto independiente de la maquina objetivo, entonces genera código para una maquina objetivo diferente solo requiere volver a escribir el traductor de código intermedio a código objetivo, y por lo regular esto es mas fácil que volver a escribir todo un generador de código.
*La representación intermedia puede tener diversas formas, se trata una forma intermedia llamada "CÓDIGO DE TRES DIRRECCIONES" y el "CÓDIGO P"
*El código de tres direcciones consiste en una secuencia de instrucciones, cada una de las cuales tiene como máximo tres operadores.

            temp1 := entareal(60)
            temp2 := id3 * temp1
            temp3 := id2 + temp2
            id1 := temp3

Esta representación intermedia tiene varias propiedades.
- primera, cada instrucción de tres direcciones tiene a lo sumo un operador, además de la asignación. Por tanto, cuando genera esas instrucciones, el  compilador tienes de decidir el orden en que deben efectuarse las operaciones; la multiplicación precede a la adicción en el programa fuente.
- segunda, el compilador debe generar un nombre temporal para guardar los valores calculados por cada instrucción.
- tercera, algunas instrucciones de "tres direcciones" tiene menos de tres operadores, por ejemplo, la primera y la ultima instrucción.
                     EL CÓDIGO P
*El código P comenzó como un código ensamblador objetivo estándar producido por varios compiladores Pascal en la década de 1970 y principios de la de 1980. Fue diseñado para código real para una maquina de pila hipotética la idea era hacer que los compiladores de Pascal se transportaran fácilmente requiriendo solo que se volviera a escribir el interprete de la maquina P para una plataforma, el código P también a probado ser útil como código intermedio y sean utilizado varias extensiones y modificaciones del mismo en diverso compiladores de código nativo,
La mayor parte para lenguaje tipo Pascal.
*Como el código P fue diseñado para ser directamente ejecutable, contiene una descripción implícita de un ambiente de ejecución particular que incluye tamaños de datos, además de mucha información especifica para la maquina P, que debe conocer si se desea que un programa de código P se comprensible. La maquina P esta compuesta por una memoria de código, una memoria de datos no específica para variables nombre das y una pila para datos temporales, junto como cualquiera registro que sea necesario para mantener la pila y apoyar la ejecución.
        COMPARACIÓN
*El código P en muchos aspectos está más código de maquina real que al código de tres direcciones. Las instrucciones en código P también requiere menos de tres direcciones: tosas las instrucciones que hemos visto son instrucciones de "una dirección" o "cero direcciones". Por otra parte, el código P es menos compacto que el código de tres direcciones en términos de números de instrucciones, y el código P no esta "auto contenido" en el sentido que las instrucciones funciones implícitas en una pila (y las localidades de pila implícitas son de hecho las direcciones "perdidas"). La ventaja respecto a la pila es que contiene los valores temporales necesarios en cada punto del código, y el compilador no necesita asignar nombre a ninguno de ellos, como el código de tres direcciones.
            Ejemplo
·         Se puede considerar esta una representación intermedia como un programa para una maquina abstracta.
·         Traducción de una proposición  
            CÓDIGO DE TRES DIRECCIONES
Las reglas semánticas para generar código de tres direcciones  a partir de construcciones de lenguaje de programación comunes son similares a las reglas para construir arboles sintácticos o para notación posfija.
El código de tres direcciones es una secuencia de preposiciones  de la forma general

x := y op z

Donde 'x', 'y' y 'z' son nombres, constates o variables temporales generadas por el compilador, op representa cualquier valor, como un operador aritmético de punto fijo o flotante, o un operador lógico sobre datos con valores booleanos. Obsérvese que no se permite ninguna expresión aritmética compuesta, pues solo hay un operador en el lado derecho de una proposición. Por tanto, una expresión del lenguaje fuente como x+y*z  se puede traducir en una secuencia

t1  :=  y * z
t2 :=  x + t1

Donde t1 y t2 son nombres temporales generados por el compilador.
El código de tres direcciones  es una reprehensión linealizada de un árbol sintáctico o un GDA en la que lo nombres explícitos corresponde a los nodos interiores del grafo.
            t1 := -c                                                                       t1 := -c
            t2 := b * t1                                                                 t2 := b*t1
            t3 := -c                                                                       t5 := t2 + t2
            t4 := b * t3                                                                 a := t5
            t5 := t2 +t4
            a := t5
(a) Código para el árbol sintáctico                      (b) Código para el GDA

Las proposiciones de tres direcciones son análogas al código ensamblador. Las proposiciones pueden tener etiquetas simbólicas y existen proposiciones para el flujo del control. Una etiqueta simbólica representa el índice de una proposición  de tres direcciones en la matriz que contiene código intermedio. Los índices reales se pueden sustituir por las etiquetas ya sea realizado una pasada independiente o mediante "relleno en retroceso".
La notación gen(x ':='  y '+' z) en la figura (1) para representar la proposición de tres direcciones x := y + z
Se puede añadir proposiciones de flujo del control al lenguaje de asignaciones de la figura (1) mediante producciones y reglas semánticas como las de las proposiciones while de la figura (2).

1.) Definición dirigida por la sintaxis para producir código de tres direcciones para las asignaciones.

Producción
Reglas semántica
S à id := E
S. Código := E. código || gen (id. lugar ':=' E. lugar)
E à E1 +E2
E. lugar := tempnuevo;
E. código := E1. código || E2. código || gen(E. lugar ':=' E1. lugar '+' E2.lugar)
E à E1 * E2
E. lugar := tempnuevo;
E. código := E1. código || E2. código || gen(E. lugar ':=' E1. lugar '*' E2.lugar)
E à -E1
E. lugar := tempnuevo;
E. código := E1. código || gen('E. lugar ':='  'menosu' E1. lugar)
E à (E1)
E. lugar  := E1. lugar;
E. código := E1. código
E à id
E. lugar := id. lugar;
E. código  := ' ' 

2.) Reglas semánticas que genera código para una proposición while:
IMPLEMENTACIÓN DE CÓDIGO DE TRES DIRECCIONES 
·         Cuádruplas.
·         Estructura con 4 campos: operador, operando1, operando2, resultado.
·         Triplas.
·         Estructura con 3 campos: operador, operando1, operando2. Los resultados temporales se referecian por la posición en que fueron calculados.
EL CÓDIGO P 
El código P comenzó como un código ensamblador objetivo estándar producido por varios compiladores Pascal en la década de 1970 y principios de la de 1980. la descripción de diversas versiones de código P.
La máquina P está compuesta por una memoria de código, una memoria de datos no especificada  para variables nombradas y una pila para datos temporales, junto cualquier registro que sea necesario para mantener la pila y apoyar la ejecución.
Como primer ejemplo se considera la expresión:
·         La versión de código P para esta expresión es la que se muestra en seguida:
ldc 2   ; carga la constante 2
lod a   ; carga el valor de la variable a
mpi     ; multiplicación entera
lod b  ; carga el valor de la variable b
ldc 3   ; carga la constante 3
sbi      ; sustracción o resta entera
adi      ; adiciona de enteros

            Esta instrucción se ven  como si representaran las siguientes operaciones en una maquina P.
En primer lugar, ldc 2 inserta el valor 2 en la pila temporal. Luego, lod a inserta el valor de la variable a  en la pila. Las instrucción mpi extrae estos dos valores de la pila, los multiplica (en orden inverso) e inserta el resultado en la pial. Las siguientes dos instrucciones (lod b y ldc 3) inserta valor de b y la constante 3 en la pila (ahora tenemos tres valores en la pila). Posteriormente, la instrucción sbi extrae los dos valores superiores de la pila, resta el primero del segundo, e inserta el resultado. Finalmente, la instrucción adi extrae los dos valores restantes de la pila, los suma e inserta el resultado. El código final con un solo valor en la pila, que representa el resultado del cálculo.
IMPLEMENTACIÓN DEL CÓDIGO P
Históricamente, el código P ha sido en su mayor  parte generado como un archivo de texto, pero las  descripciones anteriores de las implementaciones de estructura de datos internas para el código de tres direcciones (cuádruples  y triples) también funcionara como una modificación propia para el código P.
GENERACION DEL CODIGO PARTIENDO DE LI. ALGORITMOS:
Se sabe que entre el parse y el lenguaje objeto resultado de la compilación, habrá una de estas dos cosas o ambas.
Un lenguaje intermedio LI.
Una generación de código.
Se supone la existencia de ambos componentes, nos hace falta ahora el regresar el código objeto para la maquina objeto deseada, que en el caso normal de no tratarse de un compilador cruzado, es el mismo código con el que está escrito el compilador.
Como ejemplo de las posibilidades existentes se mencionan las de la figura 5 para el lenguaje pascal.
Se emplea in código P para un maquina hipotética que opera con una pila. Este compilador es muy portable de una maquina a otra. Es el método usado en el USCD PASCAL que al estar todo escrito en el código de la maquina ficticia P, sólo se precisa tener un pequeño interprete distinto para cada maquina objeto dada.
Pero la hipotética maquina a pila, ya nos es tan hipotética puesto que ahora existe ya como microprocesador, con lo que el código P se ejecuta directamente.
También es como el primer caso, pero en vez de emplear un interprete, se traduce con un ensamblador para la maquina objeto dada.
El compilador puede dar directamente un módulo cargable para la maquina objeto en cuestión.
El compilador suministra un objeto responsable que se puede montar con otros objetos.

Para dar concreción vamos a definir el conjunto de instrucciones de un ordenador sencillo que tenga sólo un acumulador A y un registro índice X. Una instrucción simbólica con un ensamblador para esta maquina tendría hasta 4 partes separadas entre si por blancos:
-Una parte opcional, la etiqueta.
-El código de operación.
-El operando.
-Comentario opcional.
La forma de direccionar la memoria son las siguientes:
DIRECTA: Se toma como operando el contenido en memoria del campo de dirección.
INDIRECTA: Como antes pero, el contenido de memoria se considera a su vez como dirección del dato.
INMEDIATA: El valor de la dirección lo tomo inmediatamente como operando.
Y salvo en el caso del direccionamiento inmediato, las direcciones pueden ser: Absolutas o reales, Relativas a la instrucción actual, Indexadas con un registro.
TÉCNICAS BÁSICAS DE GENERACIÓN DE CÓDIGO
La generación de código intermedio(o generación de código objetivo directa sin código intermedio), en realidad, si el código generado se ve como un atributo de cadena, entonces este código se convierte en un atributo sintetizado.
Para ver como el código de tres direcciones, o bien, el código P se puede definir como un atributo sintetizado, considere la siguiente gramática que representa un pequeño subconjunto de expresiones en C:

exp à id = exp | aexp
aexp à aexp + factor | factor
factorà (exp)| num | id

El código P considerando el caso de la generación de código P en primer lugar, ya que la gramática con atributos es más simple debido a que no es necesario generar nombres para elementos temporales.


Por ejemplo, la expresión (x=x+3)+4 tiene ese atributo

lda      x
lod      x
ldc      3
adi
stn
ldc      4
adi
  
Gramática con atributos para código de tres direcciones de cadena sintetizada

Regla Gramatical
Reglas Semánticas
exp à id = exp2
exp1.pcode ="lda" ||id.strval++ exp2.pcode++"stn"
exp à aexp
exp. pcode =aexp.pcode
aexp1à aexp2 + factor
 aexp1. pcode = aexp2.pcode ++ factor. pcode ++ "adi"
aexp à factor
aexp. pcode =factor. pcode
factor à (exp)
factor. pcode =exp. pcode
factor à num
factor. pcode = "idc"||num.stval
factor à id
factor. pcode = "lod"||id.strval


Un parse es un procesador de cualquier lenguaje, por ejemplo, los navegadores llevan internamente procesadores de varios lenguajes, html, xhtml, xml, css, javascript, etc.
Los compiladores tienen un procesador de comandos que revisan la sintaxis antes de realizar la compilación.
Así que en general, los parsers o procesadores, son los motores de procesamiento del lenguaje (el que aplique en cada momento).




Comentarios

Entradas populares de este blog

Unidad II. Notaciones Prefija,Infija y Posfija

¿Por qué los registros de 32 bits se llaman EAX, EBX, ECX, EDX, etc.

2.2.3 Triplos.