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