nVARI
(i)nLOADI
nVARP
(p)nMOVEP
nADDP
nCONI
(4)2
1
Figura 5.5: Floresta para int i, ∗p; f() { i = ∗p++; }
5.4
Gera¸c˜ao da Representa¸c˜ao XOR
O m´odulo XOR Generator transforma o c´odigo representado em XIR, baseada em qu´adruplas, para um c´odigo na representa¸c˜ao XOR, baseada em ASTs.
O m´odulo XOR Generator n˜ao traduz todas as informa¸c˜oes representadas em XIR, mas somente as informa¸c˜oes de opera¸c˜oes de cada procedimento. As informa¸c˜oes simb´olicas, que inclui tabelas de tipos, de vari´aveis e de parˆametros, continuam representados da mesma forma. Mais especificamente, XOR Generator percorre todos os procedimentos da XIR e gera uma floresta de ASTs para cada um dos procedimentos. As ASTs produ- zidas s˜ao ent˜ao usadas como entrada do gerador de c´odigo sintetizado por Olive, que faz sele¸c˜ao de instru¸c˜oes para cada uma das ´arvores.
O processo de tradu¸c˜ao de um programa na representa¸c˜ao XIR para um programa na representa¸c˜ao XOR ´e realizado pela fun¸c˜ao GenerateOTree. Abaixo segue o algoritmo desta fun¸c˜ao:
Algoritmo 5.1: GenerateOTree — Gera¸c˜ao de ´arvores Olive Entrada:
Lista L contendo as n-´esimas instru¸c˜oes de um procedimento. Instruction i ← consome uma instru¸c˜ao de L
int VirtualReg ← n´umero da web de i escolha opcode de i
5.4. Gera¸c˜ao da Representa¸c˜ao XOR 98
caso LOAD, MOVE, MOVEI, MOVES, NEG, COM ou ADDR Variable v ← operando destino de i
OTreeNode dst ← novo OTreeNode nVAR (v) OTreeNode nodo ← make1op (i)
Se VirtualReg 6= −1
hash→Inserir(VirtualReg, nodo) dst→VirtualReg ← VirtualReg
nodo ← novo OTreeNode nMOVE (dst ← node) retornar nodo
caso ADD, ADDI, SUB, SUBI, MUL, MULI, DIV, DIV, MOD, MODI, AND, ANDI OR, ORI, XOR, XORI, LSR, LSRI, ASR, ASRI, LSL, LSLI, ASL ou ALSI v ← operando destino de i
dst ← novo OTreeNode nVAR (v) nodo ← make2op (i)
Se VirtualReg 6= −1
hash→Inserir(VirtualReg, nodo) dst→VirtualReg ← VirtualReg
nodo ← novo OTreeNode nMOVE (dst ← node) retornar nodo
caso CVRT
v ← operando destino de i
dst ← novo OTreeNode nVAR (v) nodo ← makecvt (i)
Se VirtualReg 6= −1
hash→Inserir(VirtualReg, nodo) dst→VirtualReg ← VirtualReg
nodo ← novo OTreeNode nMOVE (dst ← node) retornar nodo
caso CALL
nodo ← makecall (i) v ← operando destino de i Se v 6= NULL
5.4. Gera¸c˜ao da Representa¸c˜ao XOR 99
dst ← novo OTreeNode nVAR (v) Se VirtualReg 6= −1
hash→Inserir(VirtualReg, nodo) dst→VirtualReg ← VirtualReg
nodo ← novo OTreeNode nMOVE (dst ← node) retornar nodo
caso STORE ou STOREI retornar makestore (i)
caso JE, JEI, JNE, JNEI, JLT, JLTI, JGT, JGTI, JLE, JLEI, JGE ou JGEI retornar makejc (i)
caso JUMP
retornar makeuc (i) caso LABEL
retornar makelabel (i) caso RET ou RETI
retornar makeret (i)
GenerateOTree recebe como entrada uma lista (L) com as n-´esimas instru¸c˜oes de um procedimento na representa¸c˜ao XIR e retorna o nodo raiz da ´arvore gerada. A lista L come¸ca a partir da instru¸c˜ao que ainda n˜ao foi traduzida em ´arvore. Para pro- duzir uma ´arvore, GenerateOTree consome um conjunto de instru¸c˜oes da lista de en- trada. Pelo algoritmo acima, a fun¸c˜ao consome apenas uma instru¸c˜ao de L, mas as de- mais instru¸c˜oes s˜ao consumidas por fun¸c˜oes auxialiares (make1op, make2op, makestore, makejc, makejuc, makelabel, makeret, makecvt e makecall) que produzem as sub- ´arvores que formam a AST. Para reaproveitar os nodos j´a inseridos na ´arvore, ´e mantida uma tabela hash4
cujos elementos s˜ao ra´ızes de sub-´arvores pr´e-constru´ıdas.
Inicialmente, o algoritmo faz i receber a primeira instru¸c˜ao de L, e VirtualReg (re- gistrador virtual) receber o n´umero do registrador virtual associado `a instru¸c˜ao. Um registrador virtual ´e dado por uma web, que ser´a melhor referenciada na se¸c˜ao 5.7. O gerador de ´arvores Olive assume que cada web dar´a origem a um registrador virtual.
O algoritmo prossegue testando o opcode da instru¸c˜ao. Se o opcode entra no primeiro,
4
5.4. Gera¸c˜ao da Representa¸c˜ao XOR 100
segundo ou terceiro casos, ser´a criado para o operando destino de i um novo nodo (dst) na ´arvore. Este nodo tem operador gen´erico nVAR. No primeiro caso, a fun¸c˜ao auxiliar make1op ´e chamada para criar uma sub-´arvore cujo nodo raiz possui um ´unico filho. No segundo caso, make2op ´e chamada para criar uma sub-´arvore cujo nodo raiz possui dois operandos filhos, isto ´e, aponta para outras duas sub-´arvores. No terceiro caso (caso CVRT), makecvt ´e chamada para criar uma sub-´arvore que faz convers˜ao entre tipos. Nos trˆes casos, se o registrador virtual ´e diferente de -1, o nodo retornado (vari´avel nodo) por make1op, make2op ou makecvt ´e inserido na tabela hash e define-se o n´umero do registrador virtual do nodo destino (dst). Por fim, ´e criada e retornada pela fun¸c˜ao, a raiz da ´arvore de express˜ao gerada. Esta ´arvore tem operador gen´erico nMOVE e filhos dst e nodo.
O caso CALL primeiro chama makecall para criar uma sub-´arvore que reproduz a chamada ao procedimento expressado na instru¸c˜ao i. Depois, o algoritmo testa se a instru¸c˜ao tem uma vari´avel destino. Ter uma vari´avel destino significa que a fun¸c˜ao chamada retorna valor. Caso contr´ario, ela ´e uma fun¸c˜ao void. Se a vari´avel destino existe, o trecho de c´odigo considerado realiza as mesmas a¸c˜oes dos casos anteriores — insere o nodo na tabela hash e define o registrador virtual do operando destino.
Os ´ultimos cinco casos s˜ao mais simples, pois a instru¸c˜ao n˜ao possui vari´avel destino e nem web associada. Logo, basta retornar a raiz da ´arvore gerada pela fun¸c˜ao auxiliar correspondente a cada caso.
As fun¸c˜oes auxiliares make1op, make2op, makestore, makejc, makejuc, makeret, makelabel, makecvt e makecall criam sub-´arvores para a instru¸c˜ao passada como en- trada. Antes de criar um novo nodo na ´arvore, essas fun¸c˜oes fazem uma consulta `a tabela hash para verificar se j´a existe um nodo correspondente. Essa consulta ´e feita utilizando o n´umero do registrador virtual associado ao operando da instru¸c˜ao. Se existe na ta- bela hash um nodo para o operando, basta simplesmente referenciar este nodo. Se n˜ao existe, um novo nodo ´e criado e inserido na tabela, para que possa ser reaproveitado pelas pr´oximas ´arvores.
A floresta de ASTs gerada para cada procedimento deve ser passada como entrada do gerador de c´odigo sintetizado por Olive, para que ele fa¸ca a sele¸c˜ao de instru¸c˜oes e armazene-as na representa¸c˜ao XAR (descrita na se¸c˜ao 5.5). O gerador de c´odigo seleciona c´odigo de uma ´unica ´arvore por vez, e n˜ao de uma floresta inteira. Ent˜ao, ´e necess´ario
5.4. Gera¸c˜ao da Representa¸c˜ao XOR 101
chamar repetidas vezes a fun¸c˜ao que faz sele¸c˜ao de instru¸c˜oes, mais precisamente, para cada ´arvore na representa¸c˜ao XOR. Para ocultar do desenvolvedor os detalhes da gera¸c˜ao de ´arvores Olive e sua integra¸c˜ao com o gerador de c´odigo (Code Generator ), foi im- plementada a fun¸c˜ao GenerateCode. Para gerar c´odigo assembly na representa¸c˜ao XAR para um determinado procedimento, o desenvolvedor precisa apenas chamar a fun¸c˜ao GenerateCode, ilustrada no algoritmo abaixo.
Algoritmo 5.2: GenerateCode — Gera¸c˜ao de c´odigo assembly Entrada:
Lista L contendo todas as instru¸c˜oes de um procedimento. Criar um array para armazenar as ra´ızes das ´arvores
Criar tabela hash para armazenar nodos referenciados m´ultiplas vezes Enquanto L n˜ao estiver vazia
OTreeNode nodo ← GenerateOTree(L) Adicionar nodo ao array
Criar uma lista aol de opera¸c˜oes assembly
Enquanto n˜ao passar por todos os elementos do array fa¸ca nodo ← pr´oximo elemento do array
top action(nodo, aol) retornar aol
GenerateCoderecebe como entrada uma lista (L) contendo as instru¸c˜oes de um proce- dimento na representa¸c˜ao XIR e retorna uma lista (aol ) contendo as instru¸c˜oes assembly geradas na representa¸c˜ao XAR.
Inicialmente, s˜ao criados um array, para armazenar as ra´ızes das ´arvores Olive geradas, e uma tabela hash, para armazenar os nodos referenciados m´ultiplas vezes. A fun¸c˜ao GenerateOTree´e quem gerencia essa tabela hash. Enquanto n˜ao forem consumidas todas as instru¸c˜oes da lista, GenerateOTree ´e chamada para gerar ´arvores Olive para um grupo de instru¸c˜oes de L. A raiz da ´arvore retornada por GenerateOTree ´e adicionada ao array. Depois de geradas todas as ´arvores para o procedimento, o array ´e percorrido, da primeira `a ´ultima raiz das ´arvores, gerando c´odigo assembly para cada uma delas. A fun¸c˜ao top action ´e a fun¸c˜ao do gerador de c´odigo chamada para fazer casamento de