• Nenhum resultado encontrado

O gerador de código do LLVM é responsável pela alocação de registos tal como foi visto no capítulo 2. No entanto é necessário fornecer ao LLVM informações detalhadas sobre o conjunto de registos da arquitetura alvo para que a alocação de registos seja feita corretamente. Grande parte da informação é descrita recorrendo a um ficheiro de descrição que utiliza a ferramenta TableGen referida no capítulo 2.

4.4.1 Ficheiro de descrição do conjunto de registos

O gerador de código LLVM fornece a classe Register (Figura 2.7) a partir da qual podem ser implementadas as classes de registos do M2up. A partir desta classe foram implementadas duas classes para os registos do M2up no ficheiro de descrição de registos como pode ser observada na Figura 4.21.

Figura 4.21- Classes de Registos definidas a partir da classe Register fornecida pela Framework

A classe M2upSPRReg é utilizada para podermos definir o registo PSW e a classe M2upReg é utilizada para definir os outros registos. O parâmetro field bits Num desta classe permite identificar os registos desta classe numericamente. Cada registo físico deve então ser definido como uma instância das classes acima.

Figura 4.22-Definição dos registos físicos do LLVM no ficheiro de descrição de registos

Como pode ser observado na Figura 4.22, o nome e número do registo são passados como argumentos.

Para terminar o ficheiro de descrição dos registos é necessário definir um conjunto de register classes. Cada register class é definida por um tipo, um alinhamento e o conjunto de registos que lhe pertencem. Estas classes de registos são utilizadas no processo de seleção de instruções pois todas as instruções que utilizem registos como operandos devem definir estes operandos como sendo de uma register class específica.

Figura 4.23-Definição da RegisterClass Reg_bank que contêm os Registos R0 até R7

Os registos do M2up são portanto agrupados numa classe denominada por Reg_Bank, indicando que são todos eles registos de 16 bits de inteiros. O registo PSW

não foi colocado em nenhuma register classe porque apesar do seu valor poder ser alterado por uma instrução, ele nunca é um operando de uma instrução.

4.4.2 Informações dos registos não estáticas

A partir do ficheiro de descrição dos registos o Tablegen gera automaticamente um conjunto de informações sobre os registos da máquina alvo. No entanto algumas características dos registos têm de ser determinadas em runtime. Estas informações foram codificadas em c++ no M2upRegisterInfo.cpp. Algumas das informações necessárias são:

Registos reservados: O M2upRegisterInfo contém um método que marca todos os registos reservados num vetor de bits. O Registo R0 retorna sempre zero e por isso é um registo reservado. O Registo R7 guarda o valor de retorno após um salto. O M2up não define nenhum registo como

stack pointer por isso o autor optou por definir o R6 como stack pointer. Registos Callee-Saved: Normalmente o ABI (Application Binary

Interface) define um conjunto de registos que devem ser guardados na entrada e retorno de uma função.

O registo R7 que guarda o endereço de retorno de uma função deve em alguns casos ser guardado no início de uma função e restaurado na saída da função e por isso está definido como registo Callee-Saved. Todos os outros registos estão definidos como Caller-Saved.

Frame Register: O frame register é um endereço base para todos os acessos á stack. Na maior parte dos casos, o tamanho da frame é fixo e por isso os endereços da stack são calculados a partir do stack pointer (R6). No caso de o tamanho da frame ser variável (por exemplo se for utilizada uma função de alocação) é necessário utilizar o frame pointer.

No ficheiro M2upRegisterInfo.cpp foram também implementados alguns métodos que emitem fragmentos de código. Estes métodos são chamados numa fase final da geração de código onde os processos de seleção e escalonamento de instruções assim como a alocação de registos já terminou. Os métodos implementados são então:

eliminateFrameIndex( ): Antes da chamada desta função, o gerador de código endereça a stack através de um frame índex abstrato e de um imediato. Esta função é chamada sempre que encontra uma instrução que aceda á stack para substituir o endereço por um registo e por um Offset real. Este registo tanto pode ser o stack pointer como o frame pointer conforme a função tenha um stack frame fixo ou variável. Quando uma função está a aceder a um dado que está fora do seu espaço de stack o offset abstrato vem com o valor negativo e por isso é necessário adicionar a este offset o tamanho da stack da função para obtermos o offset real. As instruções de STORE e LOAD com registos e imediatos incrementam automaticamente os registos com os valores dos imediatos. Estas instruções não podem por isso ser utilizadas pois o valor do stack pointer ou frame pointer iria alterar sempre que um elemento da stack fosse acedido, tornando assim impossível o cálculo dos offsets corretos. O valor do Offset é então passado para um Registo através da Instrução LDI e depois é emitida uma instrução de STORE/LOAD que só tem registos como operandos.

eliminateCallFramePseudoInstr( ): Sempre que uma instrução de CALL é emitida, as pseudo-instruções ADJCALLSTACKDOWN e ADJCALLSTACKUP são emitidas respetivamente antes e depois do CALL. Se a função que estiver a ser chamada tiver uma stack frame fixa estas pseudo-instruções são removidas porque o espaço para todos os argumentos já foi alocado no prólogo da função. Caso a stack frame contenha objetos de dimensão variável estas funções são substituídas por adições/subtrações ao stack pointer.

Após a alocação de registos o código para o bloco entry da função recursiva do código LLVM IR da figura Figura 4.4 é o seguinte:

Documentos relacionados