Departamento de Estatística e Informática Universidade Federal de Sergipe Compiladores Tradução para Código Intermediário Giovanny Lucero [email protected] 1 CI: Portabilidade e Modularidade Linguagem Arquitetura Alvo Java Sparc MIPS 4 diferentes compiladores completos Pentium Alpha Compilação 2 CI: Portabilidade e Modularidade Linguagem Arquitetura Alvo Java Sparc ML MIPS Pascal Pentium 20 diferentes compiladores completos C Alpha C++ Compilação 3 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 4 CI: Portabilidade e Modularidade Java ML Sparc MIPS Pascal Pentium C C++ Alpha 5 CI: Portabilidade e Modularidade Java ML Sparc vanguardas Java MIPS ML Pascal Pascal Pentium C C++ retaguardas Sparc MIPS Código Intermediário Pentium C Alpha C++ Representação abstrata do código de máquina Alpha 6 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 7 Código Intermediário • Exemplos: – TREE (Appel & Palsberg) – Código de 3 endereços (Aho et al) – E muitos outros... 8 A linguagem intermediária Tree 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) 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) constantes em CJUMP BINOP PLUS, EQ, NE,MINUS, LT, GT,MUL, LE, GE, DIV,ULT, ULE, UGT, UGE AND, OR, LSHIFT, RSHIFT, ARSHIFT, XOR 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 } ... stm; public Tree.Stm } public lps.semantica.vinculaveis.Type ty; } 10 Tradução de variáveis simples MEM Ou então: BINOP TEMP t PLUS TEMP fp CONST k MEM(BINOP(PLUS, TEMP fp, CONST k)) • k é o offset da variável dentro do frame • Calculado na tradução da declaração da variável 11 • Simplificaremos as árvores, assim: MEM + CONST k TEMP fp MEM(+( TEMP fp, CONST k)) 12 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-1 são os offsets do vínculo estático nas funções aninhadas e Kn é o offset de x no seu frame • As informações de aninhamento deverão estar registradas no ambiente (tabela de símbolos) 13 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 14 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 15 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 e MEM(+(MEM(e), *(i, CONST(W))) i CONST w 16 Seleção de componentes • Seleção de campo: a.f a + offset de f em a – A informação do offset deve estar no ambiente – A árvore para a.f é: MEM (+(TEMP fp, +(CONST ka, CONST ka.f))) 17 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 runtime system (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))) 18 Criação de registros e arrays 19 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. 20 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 CJUMP(LT, CONST(5), x, t, f ) • 5 < x && x < 10 SEQ ( CJUMP(LT, CONST(5), x, c, f ), SEQ ( LABEL c, CJUMP(LT, x, CONST(10), t, f ) ) ) 21 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. 22 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))) 23 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. 24 While • Layout: While (cond) { body } 25 While • Layout: While (cond) { body } test: if not (condition) goto done body goto test done: 26 While • Layout: test: if not (condition) goto done body goto test done: LABEL(test); CJUMP(BINOP, e1, e2, done, body) LABEL(body); Loop body statements JUMP (NAME test) LABEL(done); • Se um break é encontrado no interior do laço, a tradução é simplesmente um jump(done). 27 FOR • O for pode ser expresso usando o que foi definido pelo while: For (i = lo; i <= hi; i++;){ //body } i = lo; Limit = hi; While (i <= limit){ //body i++ } 28 FOR • Problema – E se limit igual ao maior inteiro positivo possível? i = lo; limit = hi; While (i <= limit){ //body i++ } 29 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++ } 30 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. 31 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. 32 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 33 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. 34 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. 35 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. 36 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. 37