Geração de Código Intermediário
Prof. Alexandre Monteiro
Baseado em material cedido pelo Prof. Euclides Arcoverde
Recife
‹#›
Contatos

Prof. Guilherme Alexandre Monteiro Reinaldo

Apelido: Alexandre Cordel

E-mail/gtalk: [email protected]
[email protected]

Site: http://www.alexandrecordel.com.br/fbv

Celular: (81) 9801-1878
Etapas da Compilação
Análise Léxica
Análise Sintática
Analise Semântica
Geração de Código
Intermediário
Geração de Código
Final
3
Sumário da Aula

Introdução

Código de Três Endereços

Traduzindo Expressões

Traduzindo Comandos
4
Introdução
Geração de Código Intermediário

O objetivo dessa etapa é gerar um código intermediário
equivalente ao código fonte dado como entrada para o
compilador

Entrada: árvore sintática anotada

Saída: código intermediário
6
Código Intermediário


O nome “intermediário” indica que ele não é mais tão abstrato
quanto o código fonte, nem é também o código final (específico
de máquina)
Idealmente, um código intermediário deve ser simples e
independente de máquina
7
Objetivo

É simples para:
• Ser fácil de traduzir para código de máquina

É independente de máquina para:
• Deixar apenas o back-end dependente de máquina
• Permitir gerar código para várias máquinas diferentes (a
partir de uma mesma linguagem fonte)
8
Código Intermediário



Existem vários tipos de código intermediário
A escolha depende da linguagem fonte e, até mesmo, da(s)
linguagem(ns) alvo
Um desenvolvedor pode criar novos tipos de código intermediário,
conforme a necessidade
9
Código Intermediário

O livro considera a árvore sintática (já vista) como um tipo de
código intermediário
•Mas não é tão simples... :)

Veremos aqui um tipo mais interessante de código intermediário
– o código de três endereços
•Mais fácil de traduzir para códigos assembly (x86
ou MIPS, por exemplo)
10
Código de Três Endereços
Código de Três Endereços

É uma linguagem de baixo nível (simples), porém independente
de máquina

Programas representados como uma simples lista de instruções

As instruções usam, no máximo, três operandos (endereços)
12
Tipos de Endereços

Um endereço pode ser:
• Um nome – representando uma variável, função, etc.
• Uma constante – valor literal de um número, uma
string, um caractere, etc.
• Um temporário – variável auxiliar criada pelo
compilador (t1, t2, etc.)
• Um rótulo – localização de uma instrução
13
Tipos de Instruções

Instruções de atribuição da forma
x = y op z

Onde op é um operador binário aritmético,
binário ou lógico
• Exemplo:
aux = t1 + temp
14
Tipos de Instruções

Instruções de atribuição da forma
x = op y

Onde op é um operador unário, como: negação
lógica, menos, conversão de tipo
• Exemplos:
t1 = (int) temp;
t1 = - temp;
15
Tipos de Instruções

Instruções de cópia
x = y


Usadas, em alguns casos, para facilitar a geração do código
intermediário
Numa fase posterior, de otimização, essa instrução pode
ser removida
16
Tipos de Instruções

Desvio incondicional
goto L

Desvia para o rótulo L : faz com que a próxima
instrução a ser executada seja a que tem o
rótulo L
• Exemplo:
label2:
aux = t + 1;
...
goto label2
17
Tipos de Instruções

Desvios condicionais
if x goto L
ifFalse x goto L

Desviam para o rótulo L dependendo do valor booleano de x
18
Tipos de Instruções

Desvios condicionais com operadores
relacionais (<, >, ==, !=, ...)
if x op_cond y goto L

Desvia para L se o resultado da operação
relacional for verdadeiro
• Exemplo:
label2:
aux = t + 1;
if aux < temp goto label2
19
Tipos de Instruções

Chamada de procedimento
call Proc

Chama o procedimento Proc
20
Tipos de Instruções

Preparação de parâmetros
param x1
param x2
...
call Proc

Definem os valores que serão passados em uma
chamada de procedimento
• O exemplo dado representa: Proc(x1, x2, ...)
21
Tipos de Instruções

Retorno de valor
return y

Usada dentro de um procedimento para retornar o valor y
22
Tipos de Instruções

Atribuições de endereços e ponteiros
x = &y
x = *y
*x = y

Acessam, respectivamente:
•O endereço de memória da variável y
•O valor que está no endereço que y guarda
•O valor que está no endereço que x guarda
23
Traduzindo Expressões
Traduzindo Expressões

Uma expressão com várias operações...
myVar = aux + temp * 3

...é decomposta em expressões menores, com uma
operação cada
t1 = 3
t2 = temp * t1
t3 = aux + t2
myVar = t3
25
Traduzindo Expressões


A árvore sintática, na verdade, já guarda as expressões
decompostas da forma desejada
Cada sub-expressão terá o seu valor guardado por alguma
variável
•Pode ser necessário criar uma variável
temporária
•Criar atributo em cada nó da árvore
26
Traduzindo Expressões

A regra geral para traduzir um nó (que realiza uma só
operação) é:
•Gerar o código das sub-expressões
•Receber os nomes das variáveis usadas para
guardar o valor de cada uma delas
•Criar uma instrução para executar a
operação sobre essas variáveis
27
Exemplo

Figura 6.20 do livro (página 243)
28
Traduzindo Comandos
Traduzindo Instruções

Veremos como fazer a tradução de alguns
comandos mais representativos
• Atribuição
• If-Else
• While

Referências
• Figuras 6.20 (pag. 243) e 6.36 (pág. 257) do livro
30
Atribuição

Primeiro, deve-se gerar código para a expressão do lado
direito
•Retorna o nome da variável que terá o valor
da expressão

Por fim, basta fazer uma instrução de cópia entre a variável
retornada e a variável do lado esquerdo da atribuição
31
Atribuição

Exemplo:
myVar = aux + temp * 3

Código de três endereços:
t1 = 3
t2 = temp * t1
t3 = aux + t2
myVar = t3
32
If-Else

Criar dois labels (rótulos) únicos
• Fx: para o caso falso
• Rx: para a próxima instrução, após o if-else
33
If-Else

A primeira coisa a fazer é gerar o código da expressão de
teste
•Retorna o nome de uma variável

Depois, deve-se gerar um comando de desvio condicional
ifFalse-goto aplicado à variável e ao label Fx
•Só vai desviar quando for falso
34
If-Else

Depois, deve-se gerar o código do comando dentro do if
•Só vai ser atingido quando a expressão for
verdadeira

E então, gerar um desvio incondicional goto para o label Rx
•Impede que seja executado o comando do
else
35
If-Else

A seguir, deve-se gerar o label Fx
•Marca o início do código do else

Gerar o código do comando dentro do else

E, por fim, gerar o label Rx
•Marca o início do comando seguinte
36
If-Else

Exemplo:
int x = 0;
if (x < 10) {
x = 20;
} else {
x = 10;
}
int y = x;
x = 0
t1 = x < 10
ifFalse t1 goto F1
x = 20
goto R1
F1:
x = 10
R1:
y = x
37
While

Criar dois labels únicos
•Ix: para o início do while
•Rx: para o próximo comando, após o while

Naturalmente, deve-se começar gerando o label Ix na saída
38
While

Depois, gerar primeiro o código da expressão
•Receber o nome da variável

Depois, gerar um desvio condicional ifFalse-goto aplicado
à variável e ao label Rx
•Quando a expressão não for verdade, passa
para o comando após o while
39
While


Em seguida, deve-se gerar o código do comando interno do
while
Depois, gerar uma instrução de desvio incondicional goto
para o label Ix
•Para avaliar a expressão novamente

Por fim, gerar o label Rx
40
While

Exemplo:
x = 0;
while (x < 10) {
x = x + 1;
}
y = x;
x = 0
I1:
t1 = x < 10
ifFalse t1 goto R1
t2 = x + 1
x = t2
goto I1
R1:
y = x
41
Traduzindo Instruções


Essa é UMA possível maneira de gerar código para esses
comandos
É uma maneira mais direta e mais fácil de implementar,
mas gera código ineficiente
•A fase de otimização, poderia melhorá-lo

Usem como base para os demais comandos
42
Bibliografia

AHO, A., LAM, M. S., SETHI, R., ULLMAN, J. D.,
Compiladores: princípios, técnicas e ferramentas. Ed.
Addison Wesley. 2a Edição, 2008 (Capítulo 4)
43
Download

Aula 11 (08/04/2015) - Geração de Código Intermediário