370 likes | 482 Views
Departamento de Estatística e Informática Universidade Federal de Sergipe Compiladores. Tradução para Código Intermediário. Giovanny Lucero giovanny@ufs.br. CI: Portabilidade e Modularidade. Linguagem. Arquitetura Alvo. Java. Sparc. 4 diferentes compiladores completos. MIPS. Pentium.
E N D
Departamento de Estatística e Informática Universidade Federal de Sergipe Compiladores Tradução para Código Intermediário Giovanny Lucero giovanny@ufs.br
CI: Portabilidade e Modularidade Linguagem Arquitetura Alvo Java Sparc 4 diferentes compiladores completos MIPS Pentium Alpha Compilação
CI: Portabilidade e Modularidade Linguagem Arquitetura Alvo Java Sparc ML MIPS 20 diferentes compiladores completos Pascal Pentium C Alpha C++ Compilação
Problema • A cada nova arquitetura, n novos compiladores (um para cada linguagem). • O projeto dos n novos compiladores não faz reuso do que foi desenvolvido anteriormente. • Solução: • Geração de Código Intermediário
CI: Portabilidade e Modularidade Java Sparc ML MIPS Pascal Pentium C Alpha C++
CI: Portabilidade e Modularidade vanguardas retaguardas Java Sparc Java Sparc ML ML MIPS MIPS Pascal Pascal Código Intermediário Pentium Pentium C C Alpha C++ Alpha C++ Representação abstrata do código de máquina
Código Intermediário • Independente da máquina • Independente da linguagem fonte • Conveniente para que a análise semântica o produza • Conveniente para traduzir para código de máquina • Semântica clara e simples de c/construtor
Código Intermediário • Exemplos: • TREE (Appel & Palsberg) • Código de 3 endereços (Aho et al) • E muitos outros...
A linguagem intermediária Tree abstract class Stm MOVE(Exp dst, Exp src) EXP(Exp exp) JUMP(Exp exp, LabelList targets) CJUMP(int relop, Exp left, Exp right, Label iftrue, Label iffalse) SEQ(Stm left, Stm right) LABEL(Label label) abstract class Exp CONST(int value) NAME(Label label) TEMP(Temp temp) BINOP(int binop, Exp left, Exp right) MEM(Exp exp) CALL(Exp func, ExpList args) ESEQ(Stm stm, Exp exp) constantes em BINOP PLUS, MINUS, MUL, DIV, AND, OR, LSHIFT, RSHIFT, ARSHIFT, XOR constantes em CJUMP EQ, NE, LT, GT, LE, GE, ULT, ULE, UGT, UGE 4
Tradução • Pode ser feita depois ou em paralelo com a checagem de tipos. public class ExpTranslator extends ExpVisitorR { ... public ExpTy translate(Expression e) { return (StmTy) e.accept(this); } protected Object visitAndExpR(AndExp e) { // código para checagem e tradução } ... } class StmTy public Tree.Stm stm; public lps.semantica.vinculaveis.Type ty; }
Tradução de variáveis simples Ou então: MEM • k é o offset da variável dentro do frame • Calculado na tradução da declaração da variável BINOP TEMP t TEMP fp PLUS CONST k MEM(BINOP(PLUS, TEMP fp, CONST k))
Simplificaremos as árvores, assim: MEM + TEMP fp CONST k MEM(+( TEMP fp, CONST k))
Variáveis não locais • Quando x é declarada num escopo externo, vínculos estáticos devem ser usados MEM(+(CONS Kn, MEM(+(CONS Kn-1, ...MEM(+(CONS K1, TEMP fp))...) Onde K1,...,Kn-1são os offsets do vínculo estático nas funções aninhadas e Kné o offset de xno seu frame • As informações de aninhamento deverão estar registradas no ambiente (tabela de símbolos)
Valores estruturados • Valores não atômicos (não escalares) • Tratamento depende da linguagem. Por exemplo: • Pascal • Variável estruturada armazena um valor estruturado • Variáveis estruturadas são criadas na Pilha • Atribuição e comparação por cópia • Java • Não há variáveis estruturadas, só valores estruturados. • Variáveis de tipo estruturado armazenam, na pilha, uma referência a um valor estruturado • Valores estruturados são criados no heap • Atribuição e comparação por referência
Variáveis array • Pascal • Criação na pilha • Limites fazem parte da variável (para checagem dinâmica) • O gerenciamento de heap é abstraído por uma biblioteca de procedimentos externos
Seleção de componentes • Array em Pascal: a[i] (i-lb)*s+a, onde a é o endereço base, s é o tamanho de cada elemento, lb é o limite inferior dos índices de a • Se a for global a-s*lb pode ser calculado na compilação • Adicionalmente checagem de limites deve ser feita MEM + MEM(+(MEM(e), *(i, CONST(W))) * MEM e CONST i w
Seleção de componentes • Seleção de campo: a.f a + offset def em a • A informação do offset deve estar no ambiente • A árvore para a.f é: MEM (+(TEMP fp, +(CONST ka, CONST ka.f)))
Criação de registros e arrays • Em geral: • Campos inicializados com null ou 0 • Obs. Outra linguagem pode requerer a criação recursiva dos componentes • Criação no heap • Chamando uma função do runtimesystem (função externa) • Esta função retorna um ponteiro para área onde o registro/array é criado. CALL(NAME(malloc), EXPLIST(A1,...(new EXPLIST(AN,NULL)))
Aritmética • Em geral, um operador aritmético binário da sintaxe abstrata tem um correspondente em Tree. • Tree não tem operadores unários. • Negação unária de inteiros implementada como subtração de zero. • Etc.
Condicionais • Expressões booleanas são representadas usando CJUMP • Combinar com if_then_else, while ... • Combinar em expressões com operadores lógicos de circuito curto • 5 < xé traduzido para • 5 < x && x < 10 • CJUMP(LT, CONST(5), x, t, f ) SEQ ( CJUMP(LT, CONST(5), x, c, f ), SEQ ( LABEL c, CJUMP(LT, x, CONST(10), t, f ) ) )
If then else • Considere: • s1(t, f) = CJUMP(BINOP, left, right, t, f) • If e1 then e2 else e3. • e1 é sempre uma expressão booleana • No entanto e2 e e3 podem ser: • Um statement que não retorna valor algum • Uma expressão booleana • Uma expressão numérica • É preciso analisar cada um dos casos.
If then else • Caso e2 e e3 statements: • SEQ ( S1(t, f ), SEQ ( LABEL t, SEQ(e2, SEQ(LABEL f, e3)))) • Caso e2 e e3 expressões booleanas: SEQ(s1(z,f), SEQ(LABEL z, SEQ(s2(t,f), SEQ(LABEL f, s3(r,f)))
If then else • Caso e2 e e3 expressões: ESEQ(s1(t,f), SEQ(LABEL t, SEQ( MOVE(TEMP r, e2), SEQ(LABEL F, MOVE(TEMP r, e3)))), Temp r) • Observação: • e2 e e3 podem ser diferentes. Neste casos emprega-se as abordagens apresentadas anteriormente de forma híbrida.
While • Layout: • While (cond) { • body • }
While • Layout: • test: • if not (condition) goto done • body • goto test • done: • While (cond) { • body • }
While • Layout: • Se um break é encontrado no interior do laço, a tradução é simplesmente um jump(done). • LABEL(test); • CJUMP(BINOP, e1, • e2, done, body) • LABEL(body); • Loop body statements • JUMP (NAME test) • LABEL(done); • test: • if not (condition) goto done • body • goto test • done:
FOR • O for pode ser expresso usando o que foi definido pelo while: • i = lo; • Limit = hi; • While (i <= limit){ • //body • i++ • } For (i = lo; i <= hi; i++;){ //body }
FOR • Problema • E se limit igual ao maior inteiro positivo possível? • i = lo; • limit = hi; • While (i <= limit){ • //body • i++ • }
FOR • Solução • Colocar um teste adicional no fim do looping, antes do incremento. • i = lo; • limit = hi; • While (i <= limit){ • //body • if (i == maxInt) • break; • i++ • }
Chamada a subrotina • f(a1,a2,...,an) call(NAME(lf),[e1,...,en]) • Onde lf é o label para a função f. • [e1,...,en] representa um Explist.
Chamada a subrotina • Para linguagens que suportam funções aninhadas, temos: • f(a1,a2,...,an) call(NAME(lf),[sL,e1,...,en]) • Onde SL é o vínculo estático.
Declarações • Declaração de tipos • Em geral, não geram código • Declaração de variáveis • Define o local de memória • Definição de função • Prólogo • Corpo • Epílogo
Definição de função • Uma função é traduzida para um segmento de linguagem assembly com um prólogo, corpo e epilogo. • O prologo contém: • Pseudo-instruções • O label relativo ao nome da função • Uma função para ajustar o stack pointer (alocar um novo frame) • Instruções para salvar argumentos escapados para o frame e mover argumentos não escapados para os registradores. • Armazenar instruções para salvar qualquer callee save registers, incluindo o registro que armazena o endereço de retorno.
Definição de função • Em seguida vem o corpo. • Depois do corpo vem o epilogo, que contém: • Uma instrução para mover o valor de retorno para o registro reservado para esta proposta. • Instruções de carregamento para restaurar os callee save registers. • Uma instrução para reiniciar o stack pointer (desalocar frames). • Uma instrução de retorno (jump to return address). • Pseudo instruções para anunciar o fim de uma função.
Strings • Um literal String é implementado como um endereço constante de um segmento de memória inicializado para os próprios caracteres. • NAME(lab) • Toda string é colocada em uma lista global (lista de fragmentos). • Todas operações com string são executadas por funções providas pelo próprio sistema.
Classes e Objetos • Criação de objetos similar a criação de variáveis registro. • Chamada a métodos similar a chamadas de funções. • Primeiro deve determinar qual classe declara o método. • This pointer é passado como argumento do método. • Acesso a variáveis • Similar ao acesso a campo de registro.