Compilation

publicité
Compilation
Génération de code
Génération de code
●
●
La partie la plus difficile de la compilation
À partir du code intermédiaire, il faut générer de
l'assembleur :
–
choisir les instructions
–
choisir où stocker les variables (registre ou mémoire)
Choix d'instructions
●
●
On choisit les instructions assembleurs utilisées, mais en
laissant les noms de variables comme paramètres.
Version simple :
–
pour chaque instruction ou nœud d'expression, on choisit
l'instruction ou les instructions les plus rapides qui réalisent
le calcul demandé (c'est là qu'on choisit entre décalage de bits
et division par deux)
–
si ce choix dépend des valeurs, on peut essayer de voir si on a
un indice sur les valeurs prises par les variables (indice de
tableau, cas d'un switch, type énuméré)
Choix d'instructions
●
En pratique, une instruction d'assembleur effectue
plusieurs nœuds à la fois :
–
●
●
leal 3(%eax,%ebx,4),%ecx : %ecx = %eax+4*%ebx+3
On cherche un recouvrement de poids minimum des
arbres des expresions et des instructions du langage
intermédiaire
Comme ce problème est NP-complet, on doit choisir une
heuristique, par exemple gloutonne.
Attribution des registres
●
●
●
Cette étape choisit quelles variables on va stocker dans
un registre. Le reste sera stocké en mémoire
Pour cela, on détermine la zone de vie des variables
(rien à voir avec le scope), c'est-à-dire la portion de code
pendant laquelle la variable doit être mémorisée
La zone commence à l'affectation de la variable, et
termine à sa dernière utilisation
Graphe de flot d'instructions
●
Le graphe de flot d'instructions est un graphe orienté
dont les nœuds sont les instructions, et qui relie les
instructions qui peuvent se suivre pendant l'exécution
du programme :
–
une flèche entre deux instructions qui se suivent quand la
première n'est pas un saut inconditionnel
–
une entre entre un instruction de saut (conditionnel ou non)
et sa destination
Calcul de la zone de vie
●
●
●
●
Pour calculer la zone de vie d'une variable x, on part de
toutes les instructions qui lisent x, et propage la zone de
vie en suivant les flêches en sens inverse
On arrête la propagation aux instructions où x est
affectée
Quand la zone de vie n'est pas connexe, on considère
que chaque composante connexe est une variable
différente
Quand une variable est copiée dans une autre, et que les
zones de vie sont disjointes, on peut considérer que c'est
la même.
Graphe d'incompatibilité
●
●
●
●
Deux variables sont incompatibles quand leurs zones de
vie se recouvrent
On dessine un graphe d'incompatibilité, dont les nœuds
sont les variables, et qui relie les variables incompatibles
On effectue un coloriage du graphe, et à chaque couleur
correspond un registre (encore un problème
NP‑complet)
S'il n'y a pas assez de registres, il faut choisir de stocker
les variables les moins utilisées (attention aux boucles)
en mémoire.
Exemple
function f(X,Y)
if X != 0 goto L1
N=5
goto L2
L1: N=Y
L2: L=1
I=N+1
L3: T=2*N
if T >= I goto L4
L=L*I
I=I+1
goto L3
L4: U="Ici\n"
U="Ici2\n"
call printf(U)
call printf(U)
I=2
T=2*N
L5: T=N-I
U=L
if T==0 goto L6
call display(W,T,U)
L=L/I
I=I+1
goto L5
L6: T=2*N
U=40*L
W=alloc_byte_array T U
call fill(0,1,N,W)
Téléchargement