Geração de Código Intermediário

Slides:



Advertisements
Apresentações semelhantes
Estruturação de Linguagens (Pascal e C++)
Advertisements

Parte 1: Organização de Computadores
Tópicos em Compiladores
gerador de código intermediário
WebDesign Redes de Computadores Aula 05
I - Noções dum compilador Partes dum compilador Ferramentas de construção de compiladores Bibliografia aconselhada: –Aho, Sethi e Ullman – Cap. 1 LFA 1999/2000.
Pearson Education Slide 1. Pearson Education Slide 2 Capítulo 2 Criado por Frederick H. Colclough, Colorado Technical University Fluxo de Controle.
Programação Concorrente
Padrão de Projeto Interpreter
Compiladores Prof. Yandre Maldonado Compiladores - Prof. Yandre - 1.
Introdução à Compilação Prof. Leandro Magno Slides adaptados a partir do material cedido pelos professores Heloise Manica Paris Teixeira, Yandre M. G.
Prof. Heloise Manica Paris Teixeira
Linguagem C Estruturas de Controle de Fluxos
Análise Léxica Supondo o trecho de programa abaixo:
Construção de Compiladores
Construção de Compiladores
Algoritmos e Estruturas de Dados II
Robson Godoi / Sandra Siebra
O Portal do Estudante de Computação
Paradigmas de programação
João Lucas de Oliveira Torres
Ponteiros em Pascal Variáveis ponteiros são aquelas que guardam o endereço de outra, possibilitando o acesso a seu conteúdo. Declaração em Pascal:  var.
Análise Semântica e Representação Intermédia
LISTAS DUPLAMENTE ENCADEADAS
Funções Declaração function nomedafunção (arg_1, arg_2,..., arg_n) corpoDaFunção end.
Aula 3©Universidade do Algarve 1 Do alto-nível ao assembly Compiladores, Aula Nº 3 João M. P. Cardoso.
Eric, Fabrício, João Vicente
Estruturas de Controle entre Instruções
Teoria e Implementação de Linguagens Computacionais – IF688
Semântica de Linguagens de Programação
Paradigmas de Linguagens de Programação Linguagem Imperativa 1
Organização do Ambiente de Execução (Runtime system) Representação de dados Avaliação de expressões Alocação de memória Rotinas Objetos e métodos.
Modelos de Paradigmas de programação
Agenda - Aula 2 Introdução (Computador Digital) Processadores
Java Bytecode Software Básico Mitsuo Takaki.
Ambiente de Execução - Rotinas
Geração de Código Intermediário
Capítulo III Modularização Procedimentos e Funções Universidade Federal de São Carlos Departamento de Computação Aluna: Luanna Lopes Lobato
Capítulo II Gramáticas e Linguagens
Analise sintática aula-07-analise-sintática.pdf.
Programação Orientada a Objetos - Java Professor: Jean Carlo Mendes.
Analise Semântica aula-10-analise-semântica.pdf.
Vetores Bidimensionais – Debug - Ponteiros – Listas Encadeadas Simples
Tradução Dirigida por Sintaxe
Conversão de Tipos Exemplo: x + i, onde x é do tipo real e i é do tipo integer. A especificação da linguagem deve indicar se a linguagem suporta este tipo.
Back-End Compilação aula-11-back-end.pdf.
A Linguagem de Máquina – Desvios e Laços
Certificação Marco Antonio. Introdução A compreensão desse capítulo é muito importante pois trata de um assunto essencial em qualquer linguagem de programação,
Revisão Compiladores – AP2
Professor Ulisses Vasconcelos
Faculdade Pernambucana - FAPE Setembro/2007
Aula Prática 5 Monitoria IP/CC (~if669). Roteiro 1. 1.Recursão 2. 2.Escopo de Variáveis 3. 3.Arrays 4. 4.Strings.
Uma Infraestrutura para a
Um Tradutor Dirigido por Sintaxe Simples
Copyright 1998, Departamento de Informática da UFPE. Todos os direitos reservados sob a legislação em vigor. Variáveis e métodos estáticos, Passagem de.
Semântica de Linguagens de Programação
Geração de Código Fase final do compilador
Arquivos. Motivação Imagine dar entrada (digitar), a cada execução do programa, em todos os dados de um sistema. por exemplo, todos os registro de livros.
Paradigmas de Linguagens de Programação Conceitos da Linguagem Imperativa 1 Augusto Sampaio e Paulo Borba Centro de Informática Universidade Federal de.
Geração de Código Difícil de generalizar, devido à variedade de características das máquinas-destino: –Conjunto de registradores (se houver) –Pilha –Instruções.
AULA 09 PROGRAMAÇÃO I. Estruturas de Dados Heterogêneas 10/08/2010 Programação I 2 Objetivo: Estudar os tipos de dados heterogêneos:  Registros;  Arrays.
 O que são arrays:  estruturas de dados homogêneas  coleção de elementos do mesmo tipo referenciada por um nome comum  um array pode ser imaginado.
PARADIGMAS DE LINGUAGENS EXPRESSÕES E INSTRUÇÕES DE ATRIBUIÇÃO
Programação para Web I AULA 4 ESTRUTURAS DE CONTROLE.
Compilador Software que traduz o texto (linguagem fonte) que representa um programa para código máquina(linguagem alvo) capaz de ser executado pelo.
Faculdade Pernambuca - FAPE Compiladores Abril/2007 Compiladores Abril/2007.
Computadores vs. Programas / Algoritmos Computadores vs. Programas -> Algoritmos O tratamento de problemas através do computador geralmente envolve uma.
MATA56 Tipos, Verificação de Tipos Agenda Objetivo desta aula: Introduzir os principais aspectos semânticos de variáveis Palavras-chave – nomes.
IF E ITERAÇÃO WHILE Dilvan Moreira (baseado no livro Big Java e T. Munzner)
Transcrição da apresentação:

Geração de Código Intermediário Vantagens do uso de uma notação intermediária: facilidade de retargetting: geração de código para vários processadores diferentes, reusando o máximo possível do código em todos os compiladores, mudando apenas o gerador de código final. uso de otimizador único para vários geradores de código diferentes.

Tradução dirigida por sintaxe verificador de código gerador de código intermediário gerador de código parser código intermediário

Código de Três Endereços Frequentemente usado como código intermediário, para linguagens imperativas/OO. Abstrai um assembler, onde cada instrução básica referencia 3 (ou menos) endereços (i.e. variáveis). Formato: x := y op z Exemplo: x + y * z é reescrito para t1 := y * z t2 := x + t1

Código de três endereços - exemplo t1 := -c t2 := b * t1 t3 := -c t4 := b * t3 t5 := t2 + t4 a := t5 assign a + * * b uminus b uminus c c

Tipos de código de três endereços Atribuição do tipo: x := y op z Atribuição do tipo: x := op y Cópia: x := y desvios incondicionais: goto L desvios condicionais: if x relop y goto L chamada de procedimentos: param x e call p,n e return y

Tipos de código de três endereços (cont.) atribuição indexada: x := y[i] e x[i] := y atribuição de endereços e ponteiros: x := &y, x := *y e *x := y

Tradução dirigida por sintaxe para código de três endereços S  id := E S.code := E.code || gen(id.place ‘:=‘ E.place) E  E1 + E2 E.place := newtemp; E.code := E1.code || E2.code || gen(E.place ‘:=‘ E1.place ‘*’ E2.place) E  - E1 E.place := newtemp; E.code := E1.code || gen(E.place ‘:=‘ ‘uminus’ E1.place) E  ( E1 ) E.place := E1.place; E.code := E1.code E  id E.place := id.place; E.code := ‘’

Tradução do while S  while E do S1 regra semântica: S.begin := newlabel; S.end := newlabel; S.code := gen(S.begin ‘:’) || E.code || gen(‘if’ E.place ‘=‘ ‘0’ ‘goto’ S.end) || S1.code || gen(‘goto’ S.begin) || gen(S.end ‘: ‘)

Declarações Declarações dentro de um procedimento frequentemente são tratadas como um único grupo, e são acessadas como um offset do ponto onde começam a ser armazenadas (na pilha). offset inicialmente é zero, e a cada novo símbolo declarado o valor do offset é guardado na tabela de símbolos, e o offset é incrementado. exemplo: procedimento enter(name,type,offset)

Declarações P  {offset := 0} D D  D ; D D  id : T { enter(id.name, T.type, offset); offset := offset + T.width } T  integer { T.type := integer; T.width := 4; } T  real { T.type := real; T.width := 8; } T  array [ num ] of T1 { T.type := array(num.val, T1.type); T.width := num.val * T1.width }

Declarações –tratando escopo uma possibilidade é manter tabelas de símbolos separadas para cada procedimento, com referências ao contexto mais externo (outra tabela de símbolos).

Declarações –tratando escopo procedimentos utilizados: mktable(previous) – cria uma nova tabela de símbolos, con referência à anterior enter(table, name, type, offset) – insere um novo símbolo na tabela addwidth(table, width) – incrementa contador do tamanho da tabela enterproc(table,name,newtable) – insere um novo símbolo (procedimento) na tabela e pilhas offset e tblptr

Declarações –tratando escopo P  M D { addwidth(top(tblptr),top(offset)); pop(tblptr); pop(offset); } M   {t := mktable(nil); push(t,tblptr); push(0,offset);} D  D1 ; D2 D  proc id ; N D1 ; S { t := top(tblptr); addwidth(t, top(offset)); pop(tblptr); pop(offset); enterproc(top(tblptr),id.name,t)} D  id : T { enter(top(tblptr),id.name,T.type,top(offset)); top(offset) := top(offset) + T.width } N   { t := mktable(top(tblptr)); push(t,tblptr); push (0,offset) }

Declarações –tratando registros nomes de campos de registros também podem ser guardados usando tabelas de símbolos específicas para cada (tipo) registro. T  record L D end { T.type := record(top(tblptr)); T.width := top(offset); pop(tblptr); pop(offset); } L   { t := mktable(nil); push(t,tblptr); push (0,offset) }

Revendo Atribuições S  id := E { p := lookup(id.name); if p != nil then emit (p ‘:=‘ E.place) else error } E  E1 + E2 { E.place := newtemp; emit (E.place ‘:=‘ E1.place ‘*’ E2.place) } E  - E1 { E.place := newtemp; emit(E.place ‘:=‘ ‘uminus’ E1.place) } E  ( E1 ) { E.place := E1.place } E  id { p := lookup(id.name); if p != nil then E.place := p else error }

Reuso de nomes controlando o tempo de vida dos nomes usados dentro de cada statement ou expressão, é possível fazer o reuso de nomes de variáveis temporárias. Exemplo: usar contador incrementado para cada novo temporário, e decrementado para cada uso. evaluate E1 to t1 evaluate E2 to t2 t := t1 + t2

Reuso de nomes - exemplo x := ((a * b) + (c * d)) – (e * f) $0 := a * b $1 := c * d $0 := $0 + $1 $1 := e * f $0 := $0 - $1 x := $0

Acesso a Arrays controlado de acordo com o tamanho e o tipo do array: base + (i – low) * w ou melhor ainda: i * w + (base – low * w) existem fórmulas genéricas para arrays multidimensionais: 2D: A[i1 , i2] base + ((i1 – low1) * n2+ i2 – low2) * w row-major vs. column-major

Expressões Booleanas Podem ser avaliadas de duas formas: codificar true e false como números, e avaliar as expressões da mesma forma que expressões matemáticas. usar o fluxo de controle, i.e. representar um valor por uma posição atingida no programa. Usada em if-then-else, while-do etc. Permite “curto-circuito” de expressões: se E1 é avaliado como true, na expressão E1 or E2, não é necessário avaliar E2.

Representação numérica - exemplo a or b and not c t1 := not c t2 := b and t1 t3 := a or t2

Representação numérica - exemplo a < b é traduzido para if a < b then 1 else 0 100: if a < b goto 103 101: t := 0 102: goto 104 103: t := 1 104:

Esquema de Tradução E  E1 or E2 { E.place := newtemp; emit (E.place ‘:=‘ E1.place ‘or’ E2.place) } E  id1 relop id2 { E.place := newtemp; emit(‘if’ id1.place relop.op id2.place ‘goto’ nextstat + 3); emit(E.place ‘:=‘ ‘0’); emit(‘goto’ nextstat + 2); emit(E.place ‘:=‘ ‘1’)} E  ( E1 ) { E.place := E1.place } E  true { E.place := newtemp; emit(E.place ‘:=‘ ‘0’) }

Representação numérica - exemplo a < b or c < d and e < f 100: if a < b goto 103 101: t1 := 0 102: goto 104 103: t1 := 1 104: if c < d goto 107 105: t2 := 0 106: goto 108 107: t2 := 1 108: if e < f goto 111 109: t3 := 0 110: goto 112 111: t3 := 1 112: t4 := t2 and t3 113: t5 := t1 or t4

Fluxo de controle Associamos a cada expressão booleana desvios para labels caso a expressão seja avaliada para true ou false. E.true e E.false

Tradução do if-then S  if E then S1 regra semântica: E.true := newlabel; E.false := S.next; S1.next := S.next; S.code := E.code || gen(E.true ‘:’) || S1.code

Tradução do if-then-else S  if E then S1 else S2 regra semântica: E.true := newlabel; E.false := newlabel; S1.next := S.next; S2.next := S.next; S.code := E.code || gen(E.true ‘:’) || S1.code gen(‘goto’ S.next); gen(E.false ‘:’) || S2.code

Tradução do while S  while E do S1 regra semântica: S.begin := newlabel; E.true := newlabel; E.false := S.next; S1.next := S.begin; S.code := gen(S.begin ‘:’) || E.code || gen(E.true ‘:’) || S1.code || gen(‘goto’ S.begin)

Fluxo de controle a < b traduzido para: if a < b goto E.true goto E.false

Esquema de Tradução E  E1 or E2 { E1.true := E.true; E1.false := newlabel; E2.true := E.true; E2.false := E.false; E.code := E1.code || gen(E1.false ‘:’) || E2.code) } E  E1 and E2 {E1.true := newlabel; E1.false := E.false; E2.true := E.true; E2.false := E.false; E.code := E1.code || gen(E1.true ‘:’) || E2.code) }

Esquema de Tradução E  not E1 { E1.true := E.false; E1.false := E.true; E2.true := E.true; E2.false := E.false; E.code := E1.code} E  id1 relop id2 {E.code := gen(‘if’ id1.place relop.op id2.place ‘goto’ E.true || gen(‘goto’ E.false) } E  true {E.code := gen(‘goto’ E.true } E  false {E.code := gen(‘goto’ E.false }

Exemplo 1 a < b or c < d and e < f if a < b goto Ltrue goto L1 L1: if c < d goto L2 goto Lfalse L2: if e < f goto Ltrue goto LFalse é possível otimizar o código acima.

Exemplo 2 while a < b do if c < d then x := y + z else x := y – z L1: if a < b goto L2 goto Lnext L2: if c < d goto L3 goto L4 L3: t1 := y + z x := t1 goto L1 L4: t2 := y – z x := t2 goto L1

switch/case switch expression begin case value: statement case value: statement … case value: statement default: statement end

implementação de switch/case sequência de goto’s condicionais, um para cada caso. loop percorrendo uma tabela de valores e labels para o código do statement correspodente. hash table com valores e labels criar um array de labels, com o label para o statement j na posição da tabela índice j.

tradução de switch/case switch E begin case V1: S1 case V2: S2 … case Vn-1: Sn-1 default: Sn end

tradução de switch/case código para avaliar E em t goto test L1: código para S1 goto next L2: código para S2 goto next … Ln-1: código para Sn-1 goto next Ln: código para Sn goto next test: if t = V1 goto L1 if t = V2 goto L2 … if t = Vn-1 goto Ln-1 goto Ln next:

outra tradução de switch/case código para avaliar E em t if t  V1 goto L1 código para S1 goto next L1 : if t  V2 goto L2 código para S2 goto next L2: … Ln-2: if t  Vn-1 goto Ln-1 código para Sn-1 goto next Ln-1: código para Sn next:

Backpatching uso de um passo extra para resolver as referências a labels definidos posteriormente.

Esquema de Tradução E  E1 or M E2 { backpatch(E1.falselist,M.quad); E.truelist := merge(E1.truelist,E2.truelist); E.falselist := E2.falselist } M   {M.quad := nextquad } E  E1 and M E2 { backpatch(E1.truelist,M.quad); E.truelist := E2.truelist; E.falselist := merge(E1.falselist,E2.falselist); }

Esquema de Tradução E  true { E.truelist := makelist(nextquad); emit(‘goto _’) } E  false { E.falselist := makelist(nextquad); emit(‘goto _’) }