CES-41 Teoria Cap 1-a

Propaganda
CES-41
COMPILADORES
Capítulo I
Introdução
Capítulo I - Introdução
1.1 – Compiladores
1.2 – Estrutura de um compilador
1.3 – Interpretadores
1.4 – Automação da construção de compiladores
1.1 – Compiladores
1.1.1 – Definição

Genericamente falando, compilador é um software que

Lê um programa escrito numa linguagem: a linguagemfonte (é o programa-fonte)

Traduz em um programa equivalente, escrito noutra
linguagem : a linguagem-objeto (é o programa-objeto)

Reporta a presença de erros no programa-fonte
Esquematicamente:
Programa
fonte
Compilador
Mensagens
de erro
Programa
objeto
Possíveis linguagens-fontes:

Mais comuns: linguagens tradicionais de programação:
Fortran, Pascal, C, Modula-2, C++, Java, etc.

Linguagens especializadas: simulação, computação gráfica,
banco de dados, experimentos
Possíveis linguagens-objetos:

Mais comuns: linguagem assembly ou linguagem de
máquina de vários computadores

Outras linguagens de programação
Exemplos de linguagens de programação como
linguagens-objetos:

Transformação de programas sequenciais escritos em C ou
Fortran, em programas paralelos escritos em HPF (High
Performance Fortran)
for (i = 1; i <= n; i++)
C[i] = A[i] + B[i]
for (i = 1; i <= n; i++)
C[i] = A[i] + C[i-1]
do parallel i = 1, n
C[i] = A[i] + B[i]
do parallel i = 1, n
C[i] = A[i] + C[i-1]


Tradução de programas realizadores de certos
experimentos:

Escritos numa linguagem especializada

Traduzidos (compilados) para uma linguagem de
programação como C

Compilados depois por um compilador similar ao de C
Programas em Lex e Yacc são traduzidos para C

Lex e Yacc são linguagens especializadas para
construir componentes de um compilador
Há ainda o “compilador” Assembler:

Linguagem-fonte: Assembly

Linguagem-objeto: linguagem de máquina
Enfoque desta disciplina:

A linguagem-fonte é uma linguagem tradicional e a
linguagem-objeto é o Assembly de uma máquina

Para a implementação, a linguagem-fonte é Lex e Yacc e a
linguagem-objeto é C
1.1.2 – O contexto de um compilador

Além do compilador, outros programas são exigidos para
criar um programa executável nalguma máquina
Programa fonte
com diretivas de
pré-processamento
Pré-processador
Programa fonte puro
Compilador
Programa objeto em Assembly
Montador
Código de máquina com endereçamento deslocado
Editor de Ligações
Bibliotecas e outros arquivos
com endereçamento deslocado
Código de máquina executável com endereçamento deslocado
Carregador
Código de máquina
executável com
endereçamento correto
a) Pré-processador – realiza várias tarefas antes da compilação:

Inclusão de arquivos ao programa-fonte – Por exemplo, na
Linguagem C:

#include <math.h>: inclui protótipos de funções
matemáticas pertencentes à biblioteca da linguagem; essas
funções já estão em linguagem de máquina

#include “sistemas.c”: inclui arquivo pertencente ao
acervo do programador; contém código fonte
Processamento de macros – para abreviar construções longas

Exemplo, em C, com as macros:
#define EHPAR(x) (((x)%2)?0:1)
#define ERRO(msg) printf (“ERRO: % s/n”, msg)
pode-se escrever comandos dos tipos:
O
O pré-processador
substitui a primeira
if (EHPAR(a+b)) --------------;
if (valor  max) ERRO(“valor muito grande”); parte do #define pela
segunda, realizando
inclusive passagem de
resultado do pré-processamento é:
argumentos
if ((((a+b)%2)?0:1)) ....... ;
if (valor > max) printf(“ERRO:%s\n”,“valor muito grande”);
Processamento de extensões de linguagens:

Algumas linguagens são acrescidas de certos artifícios para
propósitos específicos de certas aplicações

Exemplos: comandos para manipular banco de dados, para
computação gráfica, processamento paralelo, etc.

Muitas linguagens são, na realidade, extensões da
Linguagem C

Diretivas iniciadas pelos caracteres “##” são substituídas
pelo pré-processador por chamadas de funções, comandos do
sistema operacional, etc.
b) Montador (Assembler):

Transforma o código Assembly, produzido pelo compilador,
em código de máquina relocável

Exemplo: programa em C para o cálculo do fatorial de um
número digitado e seus correspondentes em Assembly e em
linguagem de máquina

Supõe-se uma CPU bem simples, com apenas um
registrador de propósitos gerais (AC - acumulador)
Para fazer
C=A+B
Sendo A, B e C
endereços de memória
Instrução
Significado
LD A
AC  Mem(A)
ADD B
AC  AC + Mem(B)
ST C
Mem(C)  AC
C1:
C2:
#include <stdio.h>
void main ( )
{
int n, fat, i;
scanf (“%d”, &n);
fat = 1;
i = 2;
while (i <= n) {
fat = fat * i;
i = i + 1;
}
printf (“%d”, fat);
}
Primeiramente, reserva de espaço
para as constantes 1 e 2
CONST
CONST
1
2
#include <stdio.h>
void main ( )
{
int n, fat, i;
scanf (“%d”, &n);
fat = 1;
i = 2;
while (i <= n) {
fat = fat * i;
i = i + 1;
}
printf (“%d”, fat);
}
Em seguida, reserva de espaço para
as variáveis n, i, fat
C1:
C2:
n:
fat:
i:
CONST
CONST
CONST
CONST
CONST
1
2
0
0
0
#include <stdio.h>
void main ( )
{
int n, fat, i;
scanf (“%d”, &n);
fat = 1;
i = 2;
while (i <= n) {
fat = fat * i;
i = i + 1;
}
printf (“%d”, fat);
}
Agora a tradução dos comandos
C1:
C2:
n:
fat:
i:
CONST
CONST
CONST
CONST
CONST
1
2
0
0
0
Rótulo da 1ª instrução executável:
inic
#include <stdio.h>
void main ( )
{
int n, fat, i;
scanf (“%d”, &n);
fat = 1;
i = 2;
while (i <= n) {
fat = fat * i;
i = i + 1;
}
printf (“%d”, fat);
}
Na realidade, a tradução de scanf
é algo mais complexo:
É uma chamada de subprograma
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
1
2
0
0
0
n
Rótulo da 1ª instrução executável:
inic
#include <stdio.h>
void main ( )
{
int n, fat, i;
scanf (“%d”, &n);
fat = 1;
i = 2;
while (i <= n) {
fat = fat * i;
i = i + 1;
}
printf (“%d”, fat);
}
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
1
2
0
0
0
n
C1
fat
C2
i
Rótulo da 1ª instrução executável:
inic
#include <stdio.h>
void main ( )
{
int n, fat, i;
scanf (“%d”, &n);
fat = 1;
i = 2;
while (i <= n) {
fat = fat * i;
i = i + 1;
}
printf (“%d”, fat);
}
C1:
C2:
n:
fat:
i:
inic:
loop:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
SUB
JP
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
JUMP
loop
“escrever” é o rótulo da
instrução logo após JUMP
Rótulo da 1ª instrução executável:
inic
#include <stdio.h>
void main ( )
{
int n, fat, i;
scanf (“%d”, &n);
fat = 1;
i = 2;
while (i <= n) {
fat = fat * i;
i = i + 1;
}
printf (“%d”, fat);
}
C1:
C2:
n:
fat:
i:
inic:
loop:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
Rótulo da 1ª instrução executável:
inic
#include <stdio.h>
void main ( )
{
int n, fat, i;
scanf (“%d”, &n);
fat = 1;
i = 2;
while (i <= n) {
fat = fat * i;
i = i + 1;
}
printf (“%d”, fat);
}
Na realidade, a tradução de printf
é algo mais complexo:
É uma chamada de subprograma
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
Rótulo da 1ª instrução executável:
inic
#include <stdio.h>
void main ( )
{
int n, fat, i;
scanf (“%d”, &n);
fat = 1;
i = 2;
while (i <= n) {
fat = fat * i;
i = i + 1;
}
printf (“%d”, fat);
}
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
#include <stdio.h>
void main ( )
{
int n, fat, i;
scanf (“%d”, &n);
fat = 1;
i = 2;
while (i <= n) {
fat = fat * i;
i = i + 1;
}
printf (“%d”, fat);
}
Final da compilação
Agora vem a montagem
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
0
1
2
3
4
5
6
7
8
9
10
11
12
O Assembler monta
uma tabela de
rótulos para ajudar a
preencher o
programa em
linguagem de
máquina
13
14
15
16
17
18
19
20
codop
ender
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
0
1
2
3
4
5
6
7
8
9
Mnemônico
Cod Op
LD
1
ST
2
ADD
4
SUB
5
MULT
6
JUMP
11
JP
14
READ
15
WRITE
16
STOP
17
10
11
12
13
14
15
16
17
18
19
20
codop
ender
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
C1
0
0
1
C2
1
1
2
n
2
2
0
3
0
fat
3
4
0
i
4
5
6
7
8
9
Mnemônico
Cod Op
LD
1
ST
2
ADD
4
SUB
5
MULT
6
JUMP
11
JP
14
READ
15
WRITE
16
STOP
17
10
11
12
13
14
15
16
17
18
19
20
codop
ender
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
C1
0
0
1
C2
1
1
2
n
2
2
0
3
0
fat
3
4
0
i
4
5
inic
5
6
7
8
9
Mnemônico
Cod Op
LD
1
ST
2
ADD
4
SUB
5
MULT
6
JUMP
11
JP
14
READ
15
WRITE
16
STOP
17
10
11
12
13
14
15
16
17
18
19
20
codop
15
ender
2
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
C1
0
0
1
C2
1
1
2
n
2
2
0
3
0
fat
3
4
0
i
4
5
15
2
inic
5
6
1
0
7
2
3
8
1
1
9
2
4
Mnemônico
Cod Op
LD
1
ST
2
ADD
4
SUB
5
MULT
6
JUMP
11
JP
14
READ
15
WRITE
16
STOP
17
10
11
12
13
14
15
16
17
18
19
20
codop
ender
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
C1
0
0
1
C2
1
1
2
n
2
2
0
3
0
fat
3
4
0
i
4
5
15
2
inic
5
6
1
0
loop
10
7
2
3
8
1
1
9
2
4
10
5
2
Mnemônico
Cod Op
LD
1
ST
2
ADD
4
SUB
5
MULT
6
JUMP
11
JP
14
READ
15
WRITE
16
STOP
17
11
12
13
14
15
16
17
18
19
20
codop
ender
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
C1
0
0
1
C2
1
1
2
n
2
2
0
3
0
fat
3
4
0
i
4
5
15
2
inic
5
6
1
0
loop
10
7
2
3
8
1
1
escrever
???
9
2
4
Mnemônico
Cod Op
10
5
2
LD
1
11
14
???
ST
2
ADD
4
SUB
5
MULT
6
JUMP
11
JP
14
READ
15
WRITE
16
STOP
17
12
13
14
15
16
17
18
19
20
codop
ender
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
C1
0
0
1
C2
1
1
2
n
2
2
0
3
0
fat
3
4
0
i
4
5
15
2
inic
5
6
1
0
loop
10
7
2
3
8
1
1
escrever
???
9
2
4
Mnemônico
Cod Op
10
5
2
LD
1
11
14
???
ST
2
12
1
3
ADD
4
13
6
4
SUB
5
14
2
3
MULT
6
15
1
4
JUMP
11
16
4
0
JP
14
17
2
4
READ
15
18
11
10
WRITE
16
STOP
17
19
20
codop
ender
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
C1
0
0
1
C2
1
1
2
n
2
2
0
3
0
fat
3
4
0
i
4
5
15
2
inic
5
6
1
0
loop
10
7
2
3
8
1
1
escrever
19
9
2
4
Mnemônico
Cod Op
10
5
2
LD
1
11
14
19
ST
2
12
1
3
ADD
4
13
6
4
SUB
5
14
2
3
MULT
6
15
1
4
JUMP
11
16
4
0
JP
14
17
2
4
READ
15
18
11
10
WRITE
16
19
16
3
STOP
17
20
codop
ender
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
codop
ender
C1
0
0
1
C2
1
1
2
n
2
2
0
3
0
fat
3
4
0
i
4
5
15
2
inic
5
6
1
0
loop
10
7
2
3
8
1
1
escrever
19
9
2
4
Mnemônico
Cod Op
10
5
2
LD
1
11
14
19
ST
2
12
1
3
ADD
4
13
6
4
SUB
5
14
2
3
MULT
6
15
1
4
JUMP
11
16
4
0
JP
14
17
2
4
READ
15
18
11
10
WRITE
16
19
16
3
STOP
17
20
17
0
C1:
C2:
n:
fat:
i:
inic:
CONST
CONST
CONST
CONST
CONST
READ
LD
ST
LD
ST
loop: SUB
JP
LD
MULT
ST
LD
ADD
ST
JUMP
escrever:WRITE
STOP
END
1
2
0
0
0
n
C1
fat
C2
i
n
escrever
fat
i
fat
i
C1
i
loop
fat
inic
rótulo
endereço
endereço
C1
0
0
1
C2
1
1
2
n
2
2
0
3
0
fat
3
4
0
i
4
5
15
2
inic
5
6
1
0
loop
10
7
2
3
8
1
1
escrever
19
9
2
4
10
5
2
11
14
19
12
1
3
13
6
4
14
2
3
15
1
4
16
4
0
17
2
4
18
11
10
19
16
3
20
17
0
Endereço inicial
da execução: 5
Essa informação
deve acompanhar
o programa em
linguagem de
máquina
codop
ender




O programa em linguagem C é o
programa-fonte
O programa gerado pelo Assembler
é o programa-objeto
O programa-objeto foi montado a
partir do endereço zero da RAM
Esse programa é guardado num
arquivo (extensão obj)
Endereço inicial
da execução: 5
endereço
codop
ender
0
1
1
2
2
0
3
0
4
0
5
15
2
6
1
0
7
2
3
8
1
1
9
2
4
10
5
2
11
14
19
12
1
3
13
6
4
14
2
3
15
1
4
16
4
0
17
2
4
18
11
10
19
16
3
20
17
0



O local para execução é estabelecido
pelo sistema operacional do
computador
Esse local depende da
disponibilidade da RAM
E se o local não for o endereço
zero (por exemplo, endereço 3000)?
Endereço inicial
da execução: 5
endereço
codop
ender
0
1
1
2
2
0
3
0
4
0
5
15
2
6
1
0
7
2
3
8
1
1
9
2
4
10
5
2
11
14
19
12
1
3
13
6
4
14
2
3
15
1
4
16
4
0
17
2
4
18
11
10
19
16
3
20
17
0




Os locais para C1, C2, n, fat e i não
são mais 0, 1, 2, 3 e 4, mas sim 3000,
3001, 3002, 3003 e 3004
Os rótulos inic, loop e escrever
mudarão para os endereços 3005,
3010 e 3019
Então todos os endereços das
instruções estarão com um erro
(deslocamento de 3000 posições)
Isso tem de ser corrigido antes da
execução
Endereço inicial
da execução: 3005
endereço
codop
ender
3000
1
3001
2
3002
0
3003
0
3004
0
3005
15
2
3006
1
0
3007
2
3
3008
1
1
3009
2
4
3010
5
2
3011
14
19
3012
1
3
3013
6
4
3014
2
3
3015
1
4
3016
4
0
3017
2
4
3018
11
10
3019
16
3
3020
17
0
c) Editor de ligações

Antes de corrigir os endereços do programa-objeto, é
necessário juntar a ele todos os subprogramas auxiliares
pertencentes à biblioteca da linguagem

Exemplos: funções para entrada e saída (scanf, printf, etc.),
funções matemáticas (sqr, pow, sqrt, log, sin, cos, etc.)

Esse trabalho de juntar o programa-objeto com tais
subprogramas é feito por um software denominado editor de
ligações (linkage-editor)

O produto do editor de ligações é um arquivo denominado
programa-executável (extensão exe)
d) Carregador

A região de memória onde um programa será alocado para
execução só será conhecida quando ele for chamado para
isso

Então o endereçamento do arquivo executável precisa ser
corrigido, quando sua execução for solicitada

Esse trabalho é feito pelo carregador (loader), que produz a
versão final do programa, pronto para rodar
1.2 – Estrutura de um Compilador
1.2.1 – Componentes de um compilador

O trabalho de compilação é dividido em 2 fases:
Fase de análise e fase de síntese

Além disso, existem atividades que fazem parte das duas
fases
O processo
não precisa
ser
sequencial
while (i < n) i = i + j;
Analisador
léxico
while
Analisador
sintático
Analisador
semântico
Gerador de
código
intermediário
Otimizador de
código
intermediário
Gerador de
código objeto
Programa-fonte
(caracteres)
i
(
=
i
int
---
n
int
---
j
int
---
Tabela de
símbolos
R1: T1 = i < n
JF =
T1iR2
R1: T1
< n
T2
+ j
JF =
T1iR2
T2+ j
i = i
JUMP R1
R2: - - - - -
i
i
<
+
n
j
)
Sequência de
átomos
;
Árvore
sintática
while
<
i
=
n
i
Código
+
objeto
load i
i nj
R1: sub
JZ R2
Código
JP R2
load i
intermediário
add j
st i
Exemplo
J R1
R2: - - - - -
1.2.2 – A fase de análise

São realizados três tipos de análise:



Análise linear ou léxica
Análise hierárquica ou sintática
Análise semântica
a) Análise léxica

Os caracteres do texto são agrupados em átomos (tokens)

A validade dos átomos é verificada

Os átomos recebem uma classificação

Exemplo: frase da Língua Portuguesa:
ajbxswn o homem alto apanhou a laranja madura na
laranjeira tdhf

Exemplo: um comando while em Pascal:
while num  50 do num := num * 2
b) Análise sintática

Os átomos são agrupados em frases, em estrutura de árvore
(árvore sintática)

A validade da posição dos átomos é verificada

Exemplo: frase:
o homem alto apanhou a laranja madura na laranjeira

Exemplo: comando while de Pascal:
while num  50 do num := num * 2
c) Análise semântica

Verifica se a árvore sintática tem sentido

Coleta informações de tipos para a fase de síntese

Exemplo: frase sem sentido:
a laranja apanhou o homem
Erro: o verbo apanhar não admite sujeito do tipo vegetal
Exemplo: comando while de Pascal:
while n+3 do n*5 (com integer n)
Erro:
A expressão de
um comando
while deve ser
do tipo lógico
Erro:
O operador de
uma atribuição
deve ser “:=”
1.2.3 – A fase de síntese

Depois da análise, a fase de síntese constrói o programa
objeto

São realizadas três tarefas:

Geração do código intermediário

Otimização do código intermediário

Geração do código objeto
a) Geração do código intermediário:

A estrutura do programa-fonte costuma ser bem diferente
da estrutura do programa-objeto

A transformação da primeira estrutura para a segunda deve
ser aliviada passando por uma fase intermediária

Fazendo analogia com metamorfose na vida animal:
Ovo  Lagarta  Borboleta
Ovo  Girino  Sapo

Exemplo:
while n  50 do n := n * 2
Código de três endereços:
Quádruplas:
2 endereços p/operandos
1 endereço p/resultado
1º elemento: operador
2º e 3º elementos: operandos
4º elemento: resultado

O código intermediário deve ser fácil de:

Ser produzido

Ser transformado em código objeto (Assembly da
máquina)
b) Otimização do código intermediário:

Elimina operações desnecessárias e repetidas

Visa simplificar o código intermediário

Visa tornar o programa executável mais rápido

Visa economia de memória

Exemplo: sejam os comandos
x := a + b + c;
y := a + b + c + d;


Outros casos para otimização:

Detecção de atribuições e operações cujos valores não são
usados no programa

Detecção de código morto, ou seja, de código que não é
executado, qualquer que seja a entrada de dados
Otimização faz análise de fluxo de dados e de controle
c) Geração do código objeto:

Transformação do código intermediário otimizado no código
objeto (normalmente o Assembly da máquina alvo)

O código objeto pode também receber otimizações

Exemplo: seja o comando
while n < 50 do n := n * 2
1.2.4 – Atividades em ambas as fases


Manipulação da tabela de símbolos
Tratamento de erros
a) Tabela de símbolos

Guarda informações sobre todos os identificadores usados
em um programa
Informações sobre os identificadores:

Tipo do identificador:
Variável
 Constante
 Definição de tipo
 Nome de subprograma
 Rótulo


Escopo: em que trecho ele é válido

Se for nome de variável:
Tipo
Se for variável indexada
Se é ou não indexada
Número de dimensões
Se é ou não estrutura
Número de elementos
em cada dimensão
Se é ou não ponteiro
Endereço de memória
Espaço a ser alocado
Se for estrutura:
Nome e tipo de cada
campo
Se for ponteiro:
Tipo dos locais
apontados por ela

Exemplos em C:
int a;
a
var
nome tipo
int
não
tipovar eharray
float X[7][5][4];
X
var
nome tipo
real
sim
3
tipovar eharray ndim
0
7
1
5
dims
2
4

Se for nome de subprograma:

Tipo de subprograma (função ou procedimento)

Número de parâmetros

Lista de parâmetros por valor

Lista de parâmetros por referência

Tipo a ser retornado

Lista de variáveis locais

Exemplo em C:
int fff (int m, int n) {float a, b; - - - - - }
n
nome
var
tipo
m
nome
b
tipovar
nome
var
nome
fff
int
func
tipo
tipo
int
tipovar
int
tipofun
var
real
tipo
tipovar
a
var
real
tipo
tipovar
nome
2
nparam
List
param
List Var
Loc
A tabela de símbolos é manipulada pelos vários componentes
das fases de análise e síntese:

Análise léxica detecta e pode armazenar o identificador

Análise sintática pode armazenar seu tipo e outras
informações

Análise semântica faz consultas à tabela

Geração de código introduz e usa informações sobre espaço
alocado
b) Detecção e tratamento de erros

Cada componente do compilador pode encontrar erros

Encontrado um erro, um componente deve tratá-lo de
forma a permitir que outros erros sejam detectados

Um compilador que para, quando encontra o primeiro erro,
não pode ajudar muito

Existem erros léxicos, sintáticos e semânticos


Exemplos:

Erro léxico: Conjunto de caracteres que não corresponde
a nenhum átomo

Erro sintático: Sequência de átomos que viola construções
sintáticas

Erro semântico: Construções sintáticas corretas porém
sem sentido
Cada componente da estrutura do compilador será estudado
detalhadamente em capítulo específico
1.2.5 – Outra decomposição de um compilador

Compilador é uma interface entre:
linguagem-fonte e máquina-alvo

Frente ou front-end : parte do compilador dependente da
linguagem-fonte

Retaguarda ou back-end: parte do compilador dependente
da máquina-alvo
Frente (front-end) compreende:

Análises léxica, sintática e semântica

Criação e consultas à tabela de símbolos

Geração do código intermediário

Muita otimização do código intermediário

Tratamento de erros léxicos, sintáticos e semânticos
Retaguarda (back-end) compreende:

Alguma otimização do código intermediário

Geração de código objeto

Otimização do código objeto

Operações na tabela de símbolos

Tratamento de alguns erros

Essa decomposição facilita a criação de compiladores de:

Mesma linguagem-fonte para várias máquinas-alvos

Várias linguagens-fontes para mesma máquina-alvo
L
M1
M2
L1
Mi
L2
Lj
M
Uma única linguagem para código intermediário
No primeiro caso: sucesso absoluto

Pode-se projetar um código intermediário que se aproxime
bem da linguagem de máquina de uma variedade de
arquiteturas
L
M1
M2
L1
Mi
L2
Lj
M
No segundo caso: sucesso limitado

Diferenças sutis entre algumas linguagens dificultam a
obtenção de um código intermediário comum entre elas
L
M1
M2
L1
Mi
L2
Lj
M
Download