You are on page 1of 13

Compiladores Otimizao de Cdigo

Jos Lucas Rangel 21 nov 00

Introduo Vrias tcnicas e vrias tarefas se renem sob o nome de Otimizao. Para comear, o prprio nome um engano: pode-se demonstrar que, para qualquer critrio de qualidade razovel, impossvel construir um programa otimizador, isto , um programa que recebe como entrada um programa P e constri um programa P equivalente que o melhor possvel, segundo o critrio considerado. O que se pode construir so programas que melhoram outros programas, de maneira que o programa P , na maioria das vezes, melhor, segundo o critrio especificado do que o programa P original. A razo para essa impossibilidade a mesma que se encontra quando se estuda, por exemplo em LFA (Linguagens Formais e Autmatos), o problema da parada: um programa (procedimento, mquina de Turing, etc.) no pode obter informao suficiente sobre todas as possveis formas de execuo de outro programa (procedimento, mquina de Turing, etc.). Normalmente, muito fcil fazer uma otimizao em um programa, ou seja, uma transformao que o transforma em outro melhor. O difcil sempre obter a informao necessria que garante que a otimizao pode realmente ser aplicada, sem modificar em nenhum caso o funcionamento do programa. Por exemplo, considere um trecho de programa em que aparece um comando x=a+b; Este programa pode ser melhorado (torna-se mais rpido e menor) se este comando for retirado. Mas para que o funcionamento do programa no seja alterado, necessrio mostrar uma entre diversas propriedades, algumas das quais aparecem a seguir: 1. o comando x=a+b; nunca executado. Por exemplo, est em seguida a um if cuja condio nunca satisfeita:
if(0) x=a+b;

2. o comando intil, porque todas as vezes que executado, x recebe exatamente o mesmo valor que tinha antes . Por exemplo, podemos retirar o segundo comando na seqncia
x=a+b; x=a+b;

3. o comando intil, porque nenhum dos comandos executados posteriormente, usa o valor da varivel x. Por exemplo, se tivermos no programa uma funo
int f(int z) { int x; ... x=a+b; }

o valor de x nunca ser utilizado, simplesmente porque aps a sada da funo, a varivel x, que local funo, no mais existe.

Compiladores J.L.Rangel - 1

Este exemplo mostra que no seria prtico tentar otimizar um programa tentando sucessivamente eliminar todos os seus comandos, um de cada vez. Note que as condies para cada eliminao dependem do comando, de sua posio, e, de certa maneira, de todos os comandos restantes do programa. Para construir um otimizador de utilidade na prtica, precisamos identificar oportunidades para otimizao que sejam produtivas em situaes correntes. Por exemplo, nenhum programador escreveria intencionalmente nenhum dos trs trechos de cdigo usados como exemplo acima, de forma que no valeria a pena procurar exatamente estas situaes. Outro exemplo de otimizao que citado freqentemente a retirada de comandos de um comando de repetio (um loop). Por exemplo, se encontrarmos em um loop um comando cujo efeito independente do loop, pode valer a pena retir-lo do loop, para que ele seja executado apenas uma vez, em vez das muitas vezes que se presume que ser executado o cdigo de dentro do loop. Por exemplo, se N tem o valor 100,
for (i=0; i<N; i++) { a=j+5; f(a*i); }

poderia ser melhorado retirando-se o comando a=j+5; do for. Ficaramos com


a=j+5; for (i=0; i<N; i++) f(a*i);

e 100 somas a menos seriam executadas. Por outro lado, se N=0, o programa foi piorado, ou pessimizado, porque o comando a=j+5; que era executado 0 vezes, passou a ser executado 1 vez. Mas pode haver um problema maior: se a varivel a usada aps o loop, em vez de seu valor original, seu valor ser, incorretamente, o resultado dado pela atribuio. Depende muito da finalidade de um compilador o conjunto de otimizaes que ele deve oferecer. Um compilador usado em um curso introdutrio de programao no precisa de otimizao, porque os programas so executados quase sempre apenas uma vez. (Em vez de otimizao, este compilador deveria dar boas mensagens de erro, que pudessem auxiliar os principiantes na linguagem.) Por outro lado, um programa que vai ser compilado uma vez e executado muitas vezes deve ser otimizado tanto quanto possvel. Neste caso esto programas de simulao, de previso do tempo, e a maioria das aplicaes numricas. Um outro problema a quantidade de informao que se deseja manipular. Podemos examinar otimizaes locais (em trechos pequenos de programas, por exemplo trechos sem desvios, ou seja, trechos em linha reta), otimizaes em um nvel intermedirio (as otimizaes so consideradas apenas em funes, mdulos, ou classes, dependendo da linguagem) e otimizaes globais (que consideram as inter-relaes de todas as partes de um programa). A maioria dos compiladores oferece algumas otimizaes do primeiro tipo, possivelmente combinadas com a fase de gerao de cdigo, e quando muito algumas otimizaes de nvel intermedirio. A maneira de tratar a otimizao pode ser extremamente pragmtica. Em um programa grande, a execuo gasta 90% em 10% do cdigo, e 10% do tempo nos 90% do cdigo restantes. (Escolha suas percentagens, a literatura menciona valores de 90%-10% at 70%-30%.) Isto acontece porque em geral um programa constitudo de inicializaes e

Compiladores J.L.Rangel - 2

finalizaes, entre as quais se encontra um conjunto de vrios loops aninhados. O tempo maior de execuo (os 90%) gasto no loop mais interno (os 10%). Existem ferramentas que registram o perfil de execuo do programa (profilers), permitindo a identificao dos trechos mais executados, e a otimizao pode se concentrar nestes trechos, ignorando para fins de otimizao o restante do programa. Por essa razo muitos compiladores oferecem opes de compilao que podem ser ligadas e desligadas no cdigo fonte, indicando para o compilador os trechos que o programador considera importantes o suficiente para serem otimizados. Por estas razes, muito do trabalho no desenvolvimento de tcnicas de otimizao dedicado aos loops. Neste texto introdutrio, no vamos dar nenhuma definio formal de loop, mas vale a pena definir o que entendemos por bloco bsico, ou trecho de cdigo em linha reta. Para dividir um programa (ou um trecho de programa) em representao intermediria em blocos bsicos, temos o seguinte: primeiro comando inicia um bloco bsico. Qualquer comando rotulado, ou que de alguma forma seja alvo de um comando de desvio inicia um novo bloco bsico Qualquer comando de desvio, condicional ou incondicional termina um bloco bsico. ou seja, blocos bsicos so trechos de programa maximais cujas instrues so sempre executadas em ordem (em linha reta), da primeira at a ltima. Maximal aqui quer dizer que impossvel acrescentar outra instruo mantendo a mesma propriedade. Vrias tcnicas de otimizao se aplicam a blocos bsicos. A otimizao pode ser feita tipicamente em trs ocasies: (1) na representao intermediria, antes da gerao de cdigo; (2) durante o processo de gerao de cdigo, que j gerado de forma otimizada; (3) aps a gerao de cdigo diretamente no cdigo objeto. As formas de otimizao durante a gerao de cdigo esto discutidas no Captulo de Gerao de Cdigo. A maioria das otimizaes que discutiremos aqui se aplica melhor na representao intermediria. A forma mais comum de otimizao que se aplica direto ao cdigo objeto a otimizao peephole, que discutimos no captulo de Traduo Dirigida pela Sintaxe. Uma observao final. Embora a otimizao possa melhorar as caractersticas de um programa, no faz milagres, de forma que no se pode justificar um programa mal escrito, porque vai ser otimizado. Em particular, normalmente a otimizao no altera a ordem dos algoritmos, alterando apenas as constantes multiplicativas. Se existe para um problema um algoritmo O(n log n), no espere que um programa em que voc usou um algoritmo O(n2) se transforme durante a otimizao no algoritmo O(n log n). Oportunidades de otimizao. Listamos aqui uma srie de oportunidades para otimizao que so aproveitadas em compiladores. Esta lista no exaustiva, e visa apenas mostrar as possibilidades existentes. Eliminao de sub-expresses comuns. Suponha que a mesma expresso (possivelmente uma sub-expresso de outra expresso maior) ocorre mais de uma vez em um trecho de

Compiladores J.L.Rangel - 3

programa. Se as variveis que ocorrem na expresso no tem seus valores alterados entre as duas ocorrncias, possvel calcular seu valor apenas uma vez. Exemplo:
... x=a+b; ... y=a+b; ...

Se os valores de a e de b no so alterados, possvel guardar o valor da expresso a+b em uma temporria (digamos t1) e us-lo outra vez posteriormente.
... t1=a+b; x=t1; ... y=t1; ...

ou, se a varivel x ainda est disponvel com o mesmo valor da segunda vez que a expresso calculada,
... x=a+b; ... y=x; ...

dispensando o uso da temporria t1. Note que, para garantir que os valores de a, b (e, se for o caso, x) no se alteram, preciso examinar todos os comandos que podem ocorrer entre as duas avaliaes da expresso. Se estes dois comandos no esto no mesmo bloco bsico, isto significar que devemos verificar durante a execuo do programa quais outros blocos bsicos podem ter seus comandos executados, para garantir que os valores que nos interessam no so alterados. Mesmo no caso em que ambos os comandos esto no mesmo bloco bsico podemos ter problemas. Por exemplo, se uma das variveis consideradas uma referncia (uma componente) de um array, digamos, a[i], qualquer alterao de valor de uma componente a[j] do mesmo array entre as duas avaliaes da expresso faz com que no mais possamos garantir que o valor de a[i] no se alterou. Isto acontece porque no podemos garantir, a no ser em casos muito particulares, que ij. O mesmo acontece com atribuio de valores a variveis apontadas e com chamadas de funes, com efeitos colaterais desconhecidos. No caso de variveis apontadas, devemos considerar, por segurana, que todas as variveis do tipo apontado foram alteradas. Considere o exemplo

Compiladores J.L.Rangel - 4

int *p, a, b; ... x=a+b; ... *p=...; ... y=a+b; ...

Neste caso, no sabemos para qual inteiro p aponta, e portanto no podemos garantir que *p no um pseudnimo para a ou para b. No caso de uma chamada de funo, em princpio podemos fazer uma otimizao global, e examinar o cdigo da funo para determinar que efeitos colaterais ela pode ter. Isso possvel (e trabalhoso) quando o trecho que est sendo considerado para otimizao e a funo so compilados ao mesmo tempo. Mas, se acontecer que a funo em questo foi compilada separadamente, fica ainda mais difcil determinar se uma chamada da funo pode alterar os valores das variveis que nos interessam. Vamos mostrar posteriormente um algoritmo para o caso mais simples: cdigo em linha reta, sem comandos suspeitos entre as ocorrncias da expresso. Eliminao de cdigo morto. Supondo que um programa contenha cdigo morto (dead code), isto , cdigo que no pode ser alcanado durante a execuo de um programa, este programa pode ser otimizado pela remoo deste cdigo. O cdigo morto ser identificado em uma srie de situaes: ocorre aps uma instruo de encerramento de um programa ou de uma funo. Por exemplo aps um comando return;
/* o incremento no ser executado */

int f(int x) { return x++; }

ocorre aps um teste com uma condio impossvel de ser satisfeita. Por exemplo

#define DEBUG 0 ... if(DEBUG) { ... }

/* este cdigo no ser executado */

ocorre aps um comando de desvio, e no alvo de nenhum outro desvio.


/* este cdigo no ser executado */

goto x; i=3; ... x: ...

Normalmente, no se espera que um programador escreva cdigo morto. Entretanto, isso pode acontecer em vrias situaes, uma das quais um estgio intermedirio da otimizao de um programa, que sofreu algumas transformaes que causaram a existncia de cdigo no alcanvel. O exemplo de cdigo inserido para depurao que aparece acima, e que pode ser desligado ou ativado dependendo do valor na macro #define no um bom exemplo, no caso de linguagens que oferecem uma opo de compilao condicional, que evita este
Compiladores J.L.Rangel - 5

problema. No caso, a presena de cdigo morto seria indicada exatamente pelos comandos de compilao condicional. Renomeao de variveis temporrias. J vimos em vrias ocasies que as variveis temporrias introduzidas durante a gerao de cdigo intermedirio podem no ser estritamente necessrias. Normalmente esta eliminao feita dando outros nomes para as variveis (temporrias ou no) que vao guardar os valores temporrios. Por exemplo, se tivermos no cdigo fonte x=a+b;, o cdigo intermedirio ser
t1=a+b; x=t1; e a varivel t1 pode ser eliminada.

Na eliminao de sub-expresses comuns, podemos ficar com vrias variveis desnecessrias, associadas s vrias cpias da sub-expresso. Por exemplo, se tivermos no cdigo fonte
x=a+b; x=t1; y=(a+b)*c; z=d+(a+b);

termos no cdigo intermedirio


t1=a+b; x=t1; t2=a+b; t3=t2*c; y=t3; t4=a+b; t5=d+t4; z=t5;

e, eliminando as duas ltimas cpias de a+b,


t1=a+b; x=t1; t2=t1; t3=t2*c; y=t3; t4=t1; t5=d+t4; z=t5;

de maneira que (neste exemplo) todas as variveis temporrias podem ser eliminadas:
x=a+b; y=x*c; z=d+x;

Como as variveis temporrias so (como o nome diz) temporrias, pode acontecer que uma varivel temporria possa ser re-usada aps uma ocorrncia anterior. Isto feito mudando os nomes de algumas ocorrncias dessas variveis. Por exemplo, se tivermos
x=(a+b)*(c+d); y=(e*f)+(g*h);

o cdigo intermedirio seria

Compiladores J.L.Rangel - 6

t1=a+b; t2=c+d; t3=t1*t2; x=t3; t4=e*f; t5=g*h; t6=t4+t5; y=t5;

e poderamos renomear t4 como t1, t5 como t2 e eliminar t3 e t6:


t1=a+b; t2=c+d; x=t1*t2; t1=e*f; t2=g*h; y=t1+t2;

Uma soluo menos bvia seria


x=a+b; t2=c+d; x=x*t2; y=e*f; t2=g*h; y=y+t2;

Esta soluo usa x como varivel temporria, porque o valor de x inicial irrelevante. O mesmo acontece com y. Transformaes algbricas. Podemos aplicar algumas transformaes baseadas em propriedades algbricas, como comutatividade, associatividade, identidade, etc. Por exemplo, como a soma comutativa, podemos transformar x=a+b*c; em x=b*c+a; o que corresponde a trocar cdigo como
Load b Mult c Store t1 Load a Add t1 Store x

por
Load b Mult c Add a Store x

que dispensa a temporria t1, e as instrues que a manipulam. Transformaes semelhantes podem ser baseadas na associatividade, permitindo trocar x=(a+b)+(c+d); por x=(((a+b)+c)+d); o que corresponde a trocar

Compiladores J.L.Rangel - 7

Load a Add b Store t1 Load c Add d Store t2 Load t1 Add t2 Store x

por
Load a Add b Add c Add d Store x

dispensando o uso das temporrias t1 e t2. Entretanto, estas transformaes s devem ser utilizadas com autorizao explcita do usurio, uma vez que algumas destas transformaes podem criar problemas para a convergncia ou a estabilidade em relao a erros de arredondamento dos programas. Normalmente, quando a ordem de precedncia indicada explicitamente com o uso de parnteses, a ordem de avaliao dada no deve ser alterada. Este seria o caso de x=(a+b)+(c+d);, em que os parnteses so desnecessrios. Dobramento de constantes. Expresses ou sub-expresses compostas de valores constantes podem ser avaliadas em tempo de compilao (dobradas), evitando sua avaliao repetida em tempo de execuo. Por exemplo, se tivermos
#define N 100 ... while (i<N-1) { ... }

no h necessidade de calcular repetidamente o valor de N-1. Este valor pode ser prcalculado, e substitudo por 99. Em alguns casos, problemas de portabilidade podem exigir que a expresso constante seja avaliada na mquina em que a execuo vai se realizar, porque a mquina em que a compilao se realiza no a mquina alvo. Neste caso em vez de avaliar a expresso em tempo de compilao, ela avaliada uma vez, logo no incio da execuo. Reduo de fora. H vrios exemplos em que operaes mais caras podem ser substitudas por operaes mais baratas. Por exemplo, para calcular o comprimento da concatenao de duas cadeias, podemos somar os comprimentos das duas. Em vez de
strlen(strcat(s1, s2))

usamos
strlen(s1) + strlen(s2)

Outro caso em que a reduo de fora possvel no clculo do quadrado de um nmero. Se usarmos pow(x, 2), em C++, o cdigo gerado dever calcular e2.0 *ln x, utilizando sries para calcular (aproximadamente) o logaritmo natural e a exponencial. Em vez disso, podemos usar x*x, com apenas uma multiplicao.
Compiladores J.L.Rangel - 8

( provvel que as duas otimizaes acima precisem ser feitas mo.) Otimizaes de loop. H vrias otimizaes que se aplicam a loops, a mais comum das quais a transferncia de computaes invariantes do loop para fora dele. (Suponha que o comando a ser movido para antes do loop x=e;.) Para que isto possa ser feito, necessrio verificar: A expresso e composta apenas de constantes, ou de variveis cujos valores no so alterados dentro do loop. Nenhum uso de x, dentro ou fora do loop deve ter acesso a um valor de x diferente do valor a que tinha acesso antes do original. Em particular, o valor de x aps a sada do loop deve ser o mesmo do programa original. Mesmo assim, como vimos anteriormente, possvel piorar alguns programas, quando o loop executado zero vezes: o comando dentro do loop no seria executado, mas fora do loop ser sempre executado uma vez. Um exemplo de aplicao desta otimizao seria (vamos supor N>0, para simplificar)
for (i=0, i<N, i++) { k=2*N; f(k*i); }

que se transformaria em
k=2*N; for (i=0, i<N, i++) f(k*i);

Eliminao de variveis de induo. Outra possibilidade de otimizao a eliminao de variveis de induo. Variveis de induo so variveis que assumem valores em progresso aritmtica, a cada vez que o cdigo do loop executado. Por exemplo, em Pascal o salto da varivel do for pode ser 1, e uma varivel que deve saltar, digamos de 2 em 2 deve ser uma varivel distinta, cujos valores so controlados pelo programador. Podemos ter
for i=1 to N do begin j=2*i; p(j); end; As variveis i e j so variveis de induo. No caso, a varivel j a varivel importante, e a varivel i pode ser eliminada.

Podemos fazer
j=0; z: if (j>2*N) goto x; p(j); j=j+2; goto z; x: ...

Note que este exemplo mostra tambm uma reduo de fora: em vez das N multiplicaes 2*i, temos N adies j+2 para calcular os valores sucessivos de j. A expresso constante 2*N pode ser pr-calculada (dobrada).

Compiladores J.L.Rangel - 9

Eliminao de sub-expresses comuns: o algoritmo do dag. Vamos mostrar um algoritmo para determinar a possibilidade de eliminao de subexpresses comuns em um trecho de cdigo em linha reta, como um bloco bsico. Durante a execuo do algoritmo, vamos construir um grafo acclico dirigido, cujos ns representam os valores distintos assumidos pelas diversas variveis do programa, durante a execuo do trecho considerado. Um grafo acclico dirigido (dag) um grafo dirigido em que no h caminhos fechados. Note que em um grafo dirigido um caminho fechado s existe quando todas as arestas consideradas tem o mesmo sentido. Ou seja, podem existir caminhos fechados se a orientao das arestas for ignorada, como acontece no grafo no dirigido correspondente. Assim, embora tenha semelhanas com uma rvore, um dag pode ter mais de um caminho de um n i para um n j, e pode ter vrias componentes desconexas. Cada n do dag corresponde ao valor de alguma varivel do programa. Em princpio, ns distintos correspondem a valores que podem ser distintos durante a execuo. Isto quer dizer que os ns que inicialmente correspondem a duas variveis distintas x e y so ns distintos. Num ponto do programa aps a execuo de alguns comandos, digamos x=a+b; e y=a+b;, os valores de x e de y sero sempre iguais, e, assim, apenas um n corresponder a x e y. Dizemos que x e y rotulam o mesmo n. Por coincidncia, os valores de variveis que rotulam dois ns distintos podem ser iguais, mas isso no garantido, e essa igualdade no ser levada em considerao. Entretanto, quando duas variveis rotulam o mesmo n, garantido que tm o mesmo valor. A cada momento, cada varivel rotula apenas um n, mas pode rotular mais de um n, medida que os comandos que mudam o valor da varivel so considerados. Podemos distinguir dois tipos de variveis: variveis definidas pelo programador, que tem valores definidos no incio do trecho considerado, e cujos valores ao fim do trecho considerado sero presumivelmente usados na continuao do programa, e variveis temporrias, que so usadas apenas durante a execuo do trecho em questo e que portanto tem valores irrelevantes no incio e no fim do trecho considerado. Estas variveis temporrias so normalmente criadas durante a gerao de cdigo intermedirio, e podem perder sua utilidade durante o processo de otimizao aqui considerado, caso em que no sero necessrias no cdigo final otimizado. Inicialmente, cada varivel definida pelo programador rotula um n separado, que associado ao valor inicial da varivel. Os ns iniciais correspondentes a cada varivel esto representados na figura em negrito. Cada vez que um comando x=y op z; for considerado, procuraremos um n n com o operador op, que tenha com filhos ns rotulados por y e z. Se este n existir, passar a ser o n rotulado com x, alm de outros rtulos que j existam para n. Se no existir, um n ser criado com essas caractersticas, e um nico rtulo, x. Este processo vale para todos os operadores binrios op da linguagem intermediria. Operadores unrios, ternrios, etc. so tratados de forma semelhante.

Compiladores J.L.Rangel - 10

Exemplo: considere o trecho de cdigo fonte


x=c*(a+b); y=d*e; a=a+b; y=(a+b)+y; z=x+y; x=z;

O trecho de cdigo de 3 endereos correspondente


t1=a+b; t2=c*t1; x=t2; t3=d*e; y=t3; t4=a+b a=t4; t5=a+b; t6=t5+y; y=t6; t7=x+y; z=t7; x=z;

A Figura abaixo mostra o dag correspondente aos comandos dados. Inicialmente, temos apenas os ns com os valores iniciais.

Os passos para construo do dag so apresentados a seguir. 0. Inicialmente so construdos os 8 ns iniciais, rotulados pelas variveis respectivas. 1. Para o comando t1=a+b; criamos um n com operador +, tendo como filhos os ns iniciais correspondentes a a e b. Este n recebe o rtulo t1. 2. Para o comando t2=c*t1; criamos um n com operador *, tendo como filhos o n inicial de c, e o n criado em (1). Este n recebe o rtulo t2. 3. Para o comando x=t2; acrescentamos apenas ao n criado em (2) o rtulo x. Este rtulo , portanto, retirado do n inicial de x, onde foi cancelado: x.

Compiladores J.L.Rangel - 11

4. Para o comando t3=d*e; criamos um n com operador *, tendo como filhos os ns iniciais correspondentes a d e e. Este n recebe o rtulo t3. 5. Para o comando y=t3; acrescentamos apenas ao n criado em (4) o rtulo y. Este rtulo , portanto, retirado do n inicial de y, onde foi cancelado: y. 6. Para o comando t4=a+b; j existe o n com operador +, tendo como filhos os ns iniciais correspondentes a a e b, criado em (1). Este n recebe, adicionalmente, o rtulo t4. 7. Para o comando a=t4;, acrescentamos apenas ao n criado em (6) o rtulo a. Este rtulo , portanto, retirado do n inicial de a, onde foi cancelado: a. 8. Para o comando t5=a+b;, o n j existente criado em (1) no serve, porque agora outro o n rotulado por a. Portanto, um n novo criado, tendo como filhos o n criado em (6) e o n inicial de b. Este n tem rtulo t5. 9. Para o comando t6=t5+y;, um novo n criado, com operador +, tendo como filhos o n criado em (8) e o n criado em (4). Este n rotulado com t6. 10. Para o comando y=t6;, basta mudar o rtulo y do n criado em (4) para o n criado em (9). 11. Para o comando t7=x+y;, um novo n com operador +, tendo como filhos os ns criados em (2) e (9). Este n recebe rtulo t7. 12. Para os comandos z=t7; e x=z;, basta acrescentar dois rtulos z e x ao n criado em (11), cancelando os rtulos x e z anteriores. O ltimo passo retirar dos ns os rtulos desnecessrios: se um n est rotulado por alguma varivel declarada pelo programador, todas as variveis temporrias so removidas; em caso contrrio apenas uma varivel temporria ser deixada. Nesta fase, eliminamos os rtulos t1, t4, t6, t7. Os rtulos restantes so z e x (no mesmo n), t2, t3, t5, a e y. Para gerar o cdigo otimizado, podemos usar uma o ordem qualquer dos ns desde que um valor s seja usado aps a instruo que gerou o mesmo valor. Por exemplo,
a=a+b; t5=a+b; t3=d*e; y=t5+t3; t2=c*a; x=t2*y; z=x;

No caso de mais de uma varivel rotulando um n (z e x, no nosso caso) apenas para uma delas o comando gerado, sendo o valor copiado para as demais. Para verificar que o cdigo est correto vamos indicar para o cdigo original e para o cdigo otimizado os valores obtidos em cada comando, para que os valores finais de cada varivel definida pelo programador sejam conferidos. Como na figura, os valores iniciais esto indicados em negrito.

Compiladores J.L.Rangel - 12

Cdigo original
t1=a+b; t2=c*t1; x=t2; t3=d*e; y=t3; t4=a+b a=t4; t5=a+b; t6=t5+y; y=t6; t7=x+y; z=t7; x=z; t1=a+b t2=c*(a+b) x=c*(a+b) t3=d*e y=d*e t4=a+b a=a+b t5=(a+b)+b t6=((a+b)+b)+(d*e) y=((a+b)+b)+(d*e) t7=(c*(a+b))+(((a+b)+b)+(d*e)) z=(c*(a+b))+(((a+b)+b)+(d*e)) x=(c*(a+b))+(((a+b)+b)+(d*e)) a=a+b t5=(a+b)+b t3=d*e y=((a+b)+b)+(d*e) t2=c*(a+b) x=(c*(a+b))*(((a+b)+b)+(d*e)) x=(c*(a+b))*(((a+b)+b)+(d*e))

Cdigo otimizado
a=a+b; t5=a+b; t3=d*e; y=t5+t3; t2=c*a; x=t2*y; z=x;

Apenas os valores finais das variveis definidas pelo programador precisam ser conferidos, uma vez que as variveis temporrias no sero usadas posteriormente. Diz-se que esto mortas no final do bloco. Isso pode acontecer tambm com algumas das variveis definidas pelo programador, mas consideramos neste exemplo, que todas as variveis definidas pelo programador esto vivas ao final do trecho considerado. Dependendo do nmero de ns esperado para o dag, a estrutura de dados usada na implementao pode ser mais ou menos complicada. Se o nmero esperado grande, pode ser usada uma tabela hash para acelerar a busca dos ns. O passo bsico de construo do dag consiste em procurar um n dado pelo operador op, e pelos rtulos dos filhos. A tabela hash usa uma funo que cria os ns numa posio dada pela funo de hash aplicada combinao do cdigo numrico do operador com os endereos dos filhos.

Compiladores J.L.Rangel - 13

You might also like