Vous êtes sur la page 1sur 6

Alocac¸ao˜

de Registradores: Colorac¸ao˜ Prioridades

de Grafos Baseado em

Fernando Leite RA:80819, Gabriel Schiavon RA:80823, Lailson Tofanelli RA:80827

1 Departamento de Informatica´

- Universidade Estadual de Maringa´ (UEM)

Maringa´ – PR – Brasil

Resumo. Alocac¸ao˜ de registradores e´ uma fase importante da gerac¸ao˜ de codigo,´ essa fase e´ quem determina quais variaveis´ do codigo´ fonte ficam em registradores, se essa fase for bem constru´ıda o acesso a memoria´ e´ menor e consequentemente existe uma maior otimizac¸ao˜ do programa compilado. Esse problema pode ser resolvido a partir de um grafo e sua k-colorac¸ao.˜

1. Introduc¸ao˜

A construc¸ao˜ de compiladores abrange diversas areas´ de estudo em computac¸ao,˜ como por exemplo conceitos de linguagens de programac¸ao,˜ arquitetura de maquina,´ algoritmos e engenharia de software. Assim, um compilador converte um programa originado de uma linguagem de programac¸ao˜ para uma linguagem que possa ser entendida e executada por um computador. Uma infraestrutura de compilador e´ o LLVM (Low Level Virtual Machine), foi desenvolvido para otimizar em tempos de compilac¸ao,˜ ligac¸ao˜ e execuc¸ao˜ de programas escritos em linguagens de programac¸ao˜ variadas.

Uma das fases de um compilador e´ a alocac¸ao˜ de registradores, essa fase e´ um processo que define quais variaveis´ e valores intermediarios´ do codigo´ fonte ficam em registradores e quais ficam em memoria.´ Uma forma de solucionar esse problema e´ a partir de um grafo aplicando colorac¸ao,˜ entretanto, esse metodo´ pode apresentar algumas deficiencias.ˆ Uma maneira de se tratar e´ adotar a noc¸ao˜ de prioridades em colorac¸ao˜ de nos.´

2. Fundamentac¸ ao˜

Teorica´

Nesta sec¸ao˜ serao˜ apresentados os principais conceitos relacionados a este trabalho, sendo eles: alocac¸ao˜ de registradores, LLVM e Priority-based graph coloring.

2.1. Alocador de Registradores

Um compilador pode ser dividido em duas partes: o frontend e o backend. As tarefas realizadas pelo backend sao˜ [6]:

Selec¸ao˜ de Instruc¸ao˜ : Mapeia as instruc¸oes˜ no codigo´ intermediario´ para instruc¸oes˜ presentes na arquitetura alvo.

Escalonamento de Codigo´ : Reoordena as instruc¸oes˜ para tirar proveito de bica- racter´ısticas de tempo de execuc¸ao˜ de cada instruc¸ao.˜

Alocac¸ao˜ de Registradores: Consiste em decidir quais variaveis´ deverao˜ resi- dir em registradores presentes no processador. Se a representac¸ao˜ intermediaria´ so´ trabalha com valores em memoria,´ o alocador tem um papel de otimizador, pois o codigo´ original ja´ era funcional e as mudanc¸as apenas melhorarao˜ a perfor- mance. Caso contrario,´ o codigo´ intermediario´ usou quantos registradores fossem

em memoria´

para refletir essas decisoes.˜

A parte do compilador responsavel´ por gerar o codigo´ com um bom uso de regis- tradores e´ o Alocador de Registradores. O alocador e´ uma das partes finais da compilac¸ao,˜ sua sa´ıda e´ um codigo´ equivalente ao codigo´ da representac¸ao˜ intermediaria´ que usa ape- nas registradores presentes na maquina´ alvo. Instruc¸oes˜ com registradores sao˜ mais curtas e mais rapidas´ do que instruc¸oes˜ envolvendo memoria´ [6].

tem alto impacto no trabalho do aloca-

dor. Um alocador de registradores resolve dois problemas distintos em sequenciaˆ

Alocac¸ao˜ : Etapa na qual seleciona-se o conjunto de variaveis´ que residirao˜ nos registradores em cada ponto do programa.

Atribuic¸ao˜ : Atribui um registrador espec´ıfico em que uma variavel´ residira.´

necessarios,´

assim o alocador precisa decidir quais variaveis´

em registradores f´ısicos e gerar codigo´

ficarao˜

e quais ficarao˜

A escolha da representac¸ao˜

intermediaria´

[6]:

2.2. LLVM

de vida de

um programa e as transformac¸oes˜

que e´ transparente para os programadores. O LLVM consegue cumprir seus objetivos de duas formas:

1. Representac¸ao˜ de Codigo´ : Fornece varios´ recursos que servem como uma representac¸ao˜ comum para analise,´ transformac¸ao˜ e distribuic¸ao˜ de codigo;´ 2. Design de compilac¸ao˜ : Explora essa representac¸ao˜ para fornecer uma combinac¸ao˜ de recursos que nao˜ esta´ dispon´ıvel em qualquer abordagem de compilac¸ao˜ conhecida atualmente.

do LLVM descreve um programa

usando um conjunto de instruc¸oes˜ para uma representac¸ao˜ abstrata de uma arquitetura

RISC, mas com informac¸oes˜ em alto-n´ıvel para uma analise´ mais eficiente.

de uma maneira

O LLVM e´ um ferramenta de compilac¸ao˜

que tem por objetivo fazer a analise´

dispon´ıveis para softwares arbitrarios´

De acordo com [3], a representac¸ao˜

de codigo´

Os novos recursos que o LLVM fornece para seus usuarios´ sao:˜

Linguagem, em baixo-n´ıvel, independente de sistema que pode ser usado para implementar tipos de dados e operac¸oes˜ a partir de linguagens em alto-n´ıvel, exi- bindo seu comportamento de implementac¸ao˜ a todos os estagios´ de otimizac¸ao;˜

Instruc¸oes˜ para realizar conversoes˜ de tipos e aritmetica´ de enderec¸os de baixo n´ıvel, preservando as informac¸oes˜ de tipo;

Duas instruc¸oes˜ de manipulac¸ao˜ de tratamento de excec¸ao˜ de baixo n´ıvel para implementar a semanticaˆ de excec¸ao˜ espec´ıfica da linguagem, enquanto expor explicitamente fluxo de controle excepcional para o compilador.

Por causa da diferenc¸a de objetivos e representac¸oes,˜ o LLVM foi desenvolvido para ser um complemento das linguagens de alto n´ıvel (e.g. JVM e Small-Talk) e nao˜ uma

alternativa para esses sistemas. De acordo com [3], e´ poss´ıvel se observar tresˆ diferenc¸as.

A primeira e´ que o LLVM nao˜ tem nenhuma notac¸ao˜ de construtores em alto n´ıvel, assim

como classes, heranc¸a ou tratamento de excec¸oes,˜ mesmo quando estamos compilando arquivos a partir de uma linguagem que fornece esses recursos. Segundo, o framework e´ escrito em tao˜ baixo n´ıvel que em tempo de execuc¸ao˜ para uma linguagem em particular

pode ser implementado no proprio´ LLVM. Terceiro, LLVM nao˜ garante seguranc¸a de tipo,

ou de memoria´ ou interoperabilidade de linguagem mais do que a linguagem assembly faz para um processador f´ısico.

LLVM fornece um conjunto infinito de registros virtuais digitados que podem con- ter valores de tipos primitivos (Boolean, integer, floating point e ponteiros). O LLVM e´ uma arquitetura de load/store: os programas transferem valores entre registros e memoria´ exclusivamente por meio de operac¸oes˜ de load e store usando ponteiros de determinados tipos.

O LLVM usa a forma SSA como sua representac¸ao˜ de codigo´ primaria,´ isto e,´ cada

registro virtual e´ escrito em exatamente uma instruc¸ao,˜

e cada uso de um registro e´ domi-

nado por sua definic¸ao.˜

store atraves´ de

um ponteiro, tornando dif´ıcil a construc¸ao˜

e razoavelmente compacta para tais localizac¸oes˜

porque muitas localizac¸oes˜

na forma SSA

As localizac¸oes˜

de memoria´

no LLVM nao˜

estao˜

poss´ıveis podem ser modificadas num unico´

de uma representac¸ao˜

[3].

de codigo´

SSA expl´ıcita

O LLVM tambem´ faz com que o Control Flow Graph (CFG) de cada func¸ao˜ seja

expl´ıcito na representac¸ao.˜ De acordo com [3], uma func¸ao˜ e´ um conjunto de blocos basicos,´ e cada bloco basico´ e´ uma sequenciaˆ de instruc¸oes˜ LLVM, terminando em exa-

tamente uma instruc¸ao˜ de terminac¸ao.˜ Cada terminador especifica explicitamente seus sucessivos blocos basicos.´

2.3. Priority-based graph coloring

A colorac¸ao˜ de grafos e´ uma tarefa onde para cada no´ de um grafo e´ atribu´ıdo uma cor,

entretanto, nos´ vizinhos nao˜ podem possuir a mesma cor, essa tarefa busca encontrar o

menor numero´ poss´ıvel de cores para colorir o grafo. Para a alocac¸ao˜ de registradores,

o grafo, chamado de interferenciaˆ ou conflito e´ constru´ıdo a partir do codigo´ fonte do

programa. Cada no´ do grafo representa uma variavel´ do programa que e´ candidato a residir em um registrador qualquer.Um grupo de variaveis´ pertencentes a um mesmo bloco basico´ de codigo´ correspondentes a um no´ de interferencia,ˆ essas variaveis´ nao˜ devem residir no mesmo registrador [2].

Na colorac¸ao˜ do grafo de interferencia,ˆ o numero´ de cores (K) utilizadas para a

colorac¸ao,˜ corresponde ao numero´ de registradores dispon´ıveis para a alocac¸ao,˜ esse valor

e´ chamado de numero´ cromatico.´ Assim, um alocador de registradores precisa encontrar

uma atribuic¸ao˜ das variaveis´ do programa para registradores de modo a minimizar o tempo de execuc¸ao,˜ ou seja, encontrar o menor valor cromatico´ para atribuir a esse grafo [2].

A colorac¸ao˜ do grafo para a alocac¸ao˜ de registradores e´ resolvida por meio de prioridades. O algoritmo de alocac¸ao˜ de registradores com base em prioridades, deter- mina as prioridades para os candidatos residentes no registrador utilizando estimativas derivadas do programa e os parametrosˆ da maquina´ e, em seguida, atribui os candidatos aos registros de acordo com a ordem de prioridade. Ele produz resultados similares com

o metodo´ de colorac¸ao˜ pura se o grafo e´ color´ıvel. Quando o algoritmo e´ bloqueado no

processo de colorac¸ao,˜ ou seja, se nao˜ puder atribuir uma cor ao proximo´ no´ de prioridade

mais alta, ele divide o bloco que nao˜ pode ser colorido e continua [2].

No processo de colorac¸ao,˜ e´ distinguido os blocos entre limitados e nao-limitados.˜ Os nao-limitados˜ tem um numero´ de vizinhos menor do que o numero´ de registradores dispon´ıveis. O algoritmo de colorac¸ao˜ consiste nos seguintes passos [2]:

1: separar os blocos nao-limitados;˜

2: repetir os passos A, B e C ate´ que todas as variaveis´ recebam um registrador ou ate´ que nao˜ haja nenhum registrador que possa mais ser atribu´ıdo:

– A: computar a func¸ao˜ de prioridade P(lr) para cada bloco limitado lr que ainda nao˜ foi calculado. Se P(lr) for negativo, o que ocorre quando todos os registros dispon´ıveis tiverem sido usados em toda a sua regiao,˜ marque o bloco como um nao˜ candidato e nao˜ atribua registrador a ele;

– B: encontre o bloco com a func¸ao˜ de prioridade mais alta e atribua um registrador a ele;

se

– C: verifique se cada bloco precisa ser dividido.

todos seus registradores tiverem sidos atribu´ıdos a seus vizinhos no grafo de interferencia.ˆ

3: atribuir um registrador a´ uma variavel,´ desde que o registrador nao˜ pertenc¸a ao ”conjunto proibido”(conjunto com registradores ja´ utilizados em uma vizinhanc¸a).

A func¸ao˜ de prioridade P(lr) e´ uma medida emp´ırica dos benef´ıcios de atribuir

A seguir a

´

E necessario´

divisao˜

um registrador a´ um bloco ou variavel.´

´

E proporcional ao tempo de execuc¸ao.˜

equac¸ao˜ [2]:

Si = LODSAV E u + ST RSAV E d MOV COST n

1 Para calcular S(lr) e´ necessario´ avaliar os valores individuais. Para cada unidade i e´ utilizado u para o numero´ de usos e d indica o numero´ de definic¸oes.˜ n denota o numero´ de movimentos de registro necessarios´ para determinado bloco. LODSAVE e´ a quantidade de tempo de execuc¸ao˜ economizado com uma variavel´ no registrador em vez de estar na memoria.´ STRSAVE e´ a quantidade de tempo de execuc¸ao˜ para uma variavel´ em um regis- trador. MOVCOST e´ o custo do movimento memoria-registrador´ e registrador-memoria´

[2].

S(lr) e´ calculada a partir de todos os blocos [2]:

S(lr) = Si Wi

ilr

Um fator que tambem´ e´ necessario´ considerar e´ o numero´ de blocos N. Um bloco que contem´ maior codigo´ utiliza mais recursos, ou seja, mais registradores serao˜ utiliza- dos. O valor final deve ser normalizado por N de modo que os blocos menores tenham maior prioridade. Assim, a equac¸ao˜ final para a prioridade e´ [2]:

P (lr) = S(lr)

N

A

func¸ao˜

de prioridade estima o total de registradores normalizados pelo tamanho

da regiao˜

ocupada [2].

De forma geral, o algoritmo atribui um registrador para uma variavel´ a cada iterac¸ao.˜ O lac¸o termina quando todas as variaveis´ recebem um registrador ou quando todos os registradores ja´ tiverem sido utilizados nos blocos mais prioritarios.´

3.

Execuc¸ao˜

Nesta sec¸ao˜ serao˜ apresentados os passos seguidos e os resultados obtidos com a realizac¸ao˜ deste trabalho.

3.1.

O

Passos para a Execuc¸ao˜

a

LLVM

foi

baixado

partir

do

SVN

e

instalado

os comandos

cmake

-G

"Unix Makefiles"

/llvm

e

make,

com o que constroi´

o LLVM

e CLANG para o modo de depurac¸ao.˜

Para a execuc¸ao˜ do algoritmo no LLVM foi necessario´ o uso de PASS. PASS e´ uma tecnica´ estruturante para o codigo´ do compilador, ele executa as transformac¸oes˜ e otimizac¸oes˜ que compoem˜ o compilador, e constroi´ os resultados de analise´ que sao˜ utili- zados por essas transformac¸oes˜ [5]. Os passos seguidos sao˜ [4]:

1 - Criac¸ao˜ da pasta do PASS dentro da pasta do LLVM em /lib/Transforms;

2 - Adic¸ao˜ do subdiretorio´ add_subdirectory(PASS), que e´ a pasta do PASS, dentro do arquivo CMakeLists.txt;

3 - Inserc¸ao˜ do algoritmo .cpp dentro da pasta;

4 - Criac¸ao˜ de um arquivo CMakeLists.txt dentro da pasta do PASS;

5 - Adicionar a instruc¸ao˜ add_llvm_loadable_module(LLVMRegAllocColoring RegAllocColoring.cpp PLUGIN_TOOL opt) no arquivo criado;

6 - Criar uma pasta build e executar cmake -G "Unix Makefiles" para a criac¸ao˜ os makes do novo PASS;

7 - Executar make para criar o .so;

8 - Gerac¸ao˜ do bitcode do .cpp:

/

clang -emit-llvm RegAllocColoring.cpp -c -o RegAllocColoring.bc;

9 - E para executar o PASS:

opt -load ./PASS.so -PASS <RegAllocColoring.bc> /dev/null;

3.2. Resultados

Para encontrar os resultados referentes ao trabalho, seria feito algumas execuc¸oes˜ de codigos´ fonte implementados em linguagem C para testes, essas execuc¸oes˜ seriam reali- zadas utilizando o alocador de registradores padrao˜ do LLVM e o alocador utilizando pri- oridades em colorac¸ao˜ de grafos descrito nesse relatorio.´ Entretanto, houveram algumas dificuldades no momento da execuc¸ao˜ do PASS. No passo 5 apresentado na sec¸ao˜ anterior,

o LLVM utilizado (4.0) nao˜ reconhecia a func¸ao:˜ add_llvm_loadable_module.

Apos´ algumas tentativas essa parte foi conclu´ıda, mas no passo 6 ocorreram alguns erros no codigo´ fonte que nao˜ puderam ser solucionados.

Desse modo, apenas foi obtido o resultado das execuc¸oes˜ do alocador padrao˜ do LLVM. Entretanto, em [2] os resultados confirmam que, usando a colorac¸ao˜ baseada

em prioridade, a alocac¸ao˜ global de registradores pode ser realizada de forma pratica´

e eficiente.

4. Conclusao˜

Compiladores modernos devem melhorar o desempenho do programa, uma forma bas- tante eficaz e´ o bom uso da memoria.´ Assim, a partir da boa implementac¸ao˜ do alocador

de registradores e´ poss´ıvel economizar um grande tempo de execuc¸ao˜ com buscas em memoria.´ O algoritmo apresentado nesse relatorio´ e descrito em [2], mostrou um esquema de colorac¸ao˜ de grafos baseado em prioridades utilizado para solucionar a alocac¸ao˜ de re- gistradores. E de acordo com [2], o algoritmo pode ser visto como bastante flex´ıvel, pois a func¸ao˜ de prioridade utiliza aspectos inerentes a maquina´ que ele estiver executando.

Referenciasˆ

[1] AHO, Alfred V. SETHI, Ravi. ULLMAN, Jeffrey D. Compiladores : princ´ıpios, tecnicas´ e ferramentas. Rio de Janeiro, 1995.

[2] CHOW, Fred C. HENNESSY, John L. The Priority-Based Colorin Approach to Register Allocation. ACM Transactions on Programming Languages and Systems, Vol. 12, No. 4, 1990, Pages 501-536.

[3] Lattner, Chris. Adve, Vikram. LLVM: A Compilation Framework for Lifelong Program Analysis and Transformation. University of Illinois, Proceedings of the International Symposium on Code Generation and Optimization, 2004.

[4] LLVM.

Developing

LLVM

passes

out

of

source.

Dispon´ıvel

em:

http://llvm.org/docs/CMake.html Acesso em: 20 jan. 2017.

 

[5] LLVM.

Writing

an

LLVM

Pass.

Dispon´ıvel

em:

http://llvm.org/docs/WritingAnLLVMPass.htm Acesso em: 20 jan. 2017.

[6] SILVEIRA, Felipe dos Santos. Alocac¸ao˜

de Registradores Utilizando Inteligenciaˆ

Arti-

ficial. Trabalho de Conclusao˜

do Curso de Cienciaˆ

Estadual de Santa Catarina. Florianopolis,´

2013.

da Computac¸ao.˜

Universidade