IFT6232: Compilateur natif pour noyaux computationnels Rapport 2

IFT6232: Compilateur natif pour noyaux
computationnels
Rapport 2
Paul Khuong
13 novembre 2008
1 Introduction
L’objectif du projet est de d´eveloper un compilateur pour des codes num´eriques
g´en´er´es semi-automatiquement, avec comme architecture cible une machine mo-
derne telle que les x86-64. `
A cette fin, le langage source est tr`es r´egulier (presque
tout est une expression), et permet de sp´ecifier une forme restreinte de non-
d´eterminisme (par).
Pour le premier rapport, ont ´et´e impl´emenes :
l’infrastructure de base permettant la d´efinition des repr´esentations in-
term´ediaires (update-struct.scm)
– transformation du langage source vers une premi`ere repr´esentation in-
term´ediaire, un ASA typ´e (parse-list.scm)
propagation/v´erification de type (types.scm)
passage `a une forme interm´ediaire pour analyses, IR1 (to-ir1.scm,
ir1-annotate-deps.scm)
Lors du d´evelopement pour ce rapport, IR1 a ´et´e modifi´e pour mieux r´epondre
aux demandes, en particulier, la s´election est maintenant une expression nor-
male, et le calcul des d´ependances est plus fin.
Pour ce deuxi`eme rapport, plusieurs passes d’optimisations ont ´et´e impl´emenees,
de mˆeme que la g´en´eration de code (minimalement) :
simplifications de base de l’IR1 (to-ir1.scm)
propagation des assignations vers lectures lorsque possible
(forward-assignments.scm)
´elimination d’assignations et certaines variables inutiles
(forward-assignments.scm)
optimisation sur les expressions (calculs constants, r´ecritures alg´ebriques)
(algebraic-optimisation.scm)
transformation de s´equences par en code explicitement ordonn´e
(sequentialize-par.scm)
1
´emission de code (codegen.scm)
allocation de registres & gestion des d´ebordements de registres
2 Structure du compilateur
Le compilateur g`ere les programmes sous cinq formes : code source, ASA
typ´e, IR1, pseudo assembleur avec pseudo-registres, et pseudo assembleur avec
registres allou´es.
Le code source est transform´e en ASA (non-typ´e) via sexp->node. Cet ASA
est ensuite typ´e par annotate-node.
IR1 (typ´e) permet principalement d’effectuer les analyses plus ais´ement en
transformant l’arbre expression de l’ASA en des s´equences explicites d’instruc-
tions (impures) et d’expressions (pures). L’ASA est na¨ıvement transform´e vers
IR1 par ast-to-ir1. Certaines simplifications de base sont ensuite effectu´ees
par simplify-ir1 pour clarifier l’IR1. Cependant, il est important de noter que
l’IR1 n’est pas encore complet : des expressions ´egales peuvent ´evaluer `a des va-
leurs diff´erentes `a l’ex´ecution, puisqu’il y existe aussi des instructions impures
(assignations, ´ecriture sur des tableaux). C’est pourquoi annotate-effects an-
note chaque sous-expression de lecture une liste de d´ependances (instructions
qui affectent la valeur de la lecture). Ainsi, on garanti que deux expressions
´equivalentes ´evaluent `a la mˆeme valeur `a l’ex´ecution.
ast-to-ir1 ´emet du code tr`es simpliste, o`u chaque expression ne repr´esente
qu’une seule op´eration. Afin de pallier `a ce probl`eme et obtenir de plus grandes
expressions, forward-assignments remplace le plus possible les lectures de va-
riables par la derni`ere expression assign´ee `a cette variable. On peut alors ´eliminer
plusieurs assignations inutiles, et mˆeme certaines variables compl`etement in-
utilis´ees `a l’aide de elimination. Cela expose aussi de plus grandes expres-
sions aux optimisations alg´ebriques, de calculs constants et de canonicalisation
(optimize-all-expressions).
L’IR1 contient des sections par, o`u l’ordre d’´evaluation des instructions est
laiss´e libre au compilateur. Il est essentiel, avant de compiler pour une machine
r´eelle, de fixer l’ordre d’´evaluation. Une fois les expressions d’indexation sim-
plifi´ees et canonicalis´ees, il est souvent possible de comparer les indices dans
les acc`es aux tableaux. On peut utiliser cette information pour ordonner les
instructions afin d’obtenir des ordonnancements qui donnent lieu `a des acc`es
m´emoire en flux lin´eaire (autant que possible). reorder-all-pars transforme
les sections par en des s´equences d’instructions lin´eaires avec une heuristique
pour atteindre des acc`es en flux dans les cas communs.
On peut ensuite r´eoptimiser le r´esultat de l’ordonnancement explicite (l’ex´ecu-
tion ´etant mieux d´etermin´ee, on a acc`es `a plus d’information). Dans le code
actuel, l’IR1 n’est que simplifi´e et annot´e pour les d´ependances.
La g´en´eration de code se fait en deux ´etapes. emit-statements transforme
l’IR1 (sans par) en une liste d’instructions de niveau assembleur (`a trois re-
2
gistres), avec des registres typ´es (valeurs `a virgule flottante ou entiers/pointeurs).
Toutefois, des familles homog`enes et infinies de registres virtuels sont utilis´ees.
Par apr`es, reg-alloc alloue les registres architecturaux r´eels, g´erant les d´eborde-
ments lorsque n´ecessaire.
3 Repr´esentations interm´ediaires
Il existe principalement quatre fa¸cons de repr´esenter les programmes dans
le compilateur : code source, ASA typ´e, IR1 (pour les analyses) et pseudo as-
sembleur. Sauf pour le code source, les structures d´ecrivant ces repr´esentations
sont d´efinies dans nodes.scm.
3.1 Code source
Le code source est donn´e sous forme de s-expressions. Quelques formes
sp´eciales sont offertes :
(let1 ([var] [valeur]) [corps])
(par [expression]+)
(progn [expression]+)
(set! [var] [valeur])
(aset! [tableau] [index] [valeur])
Les variables introduites par let1 ne peuvent prendre des valeurs que d’un
seul type (int,float,(array int),(array float)). Les expressions dans une
section par sont ´evalu´ees dans un ordre arbitraire, mais de fa¸con atomiques, alors
qu’elles sont ´evalu´ees dans l’ordre donn´e pour progn.set ! permet d’assigner
une nouvelle valeur `a une variable, sauf pour les variables li´ees `a des tableaux, qui
sont elles immutables. aset ! permet d’assigner une nouvelle valeur `a l’´el´ement
index´e dans un tableau.
Quelques op´erateurs sont offerts : (aref [tableau] [index]) ´evalue `a la
valeur de l’´el´ement index´e dans le tableau. (select [condition] [alors]
[sinon]) ´evalue `a la valeur de alors si condition est vrai, et sinon sinon.
Les expressions alors et sinon doivent ˆetre compl`etement pures (pas d’´ecriture
vers variables ou tableaux). (int [float-expr]) convertit une valeur flottante
vers une valeur enti`ere, et (float [int-expr]) inversement. Les expressions
arithm´etiques sur *,+sont aussi disponibles (les types des deux arguments
doivent correspondre et ˆetre int ou float).
3.2 ASA typ´e
L’ASA typ´e a une structure extrˆemement similaire `a celle du code source,
except´e que les nodes sont annot´es avec leurs types, et la port´ee lexicale r´esolue.
3
3.3 IR1
L’IR1 est une repr´esentation dont le but est de faciliter les analyses, tant
sur les effets de bord et contrˆole que sur les expressions alg´ebriques. Il y a une
s´eparation des blocs (seq pour s´equences d’instructions et par pour les multi-
ensembles d’instructions), instructions (assignation, introduction/´elimination de
variables) et expressions. Les expressions sont typ´ees, et les lectures de valeurs
pouvant ˆetre affect´ees par les effets de bord (lecture de variables ou de tableaux)
sont annot´ees avec des listes d’instructions pouvant en affecter la valeur. Ainsi,
on garantit que les expressions equal ? (et eq ? via hash-consing) ´evalueront `a
des valeurs ´equivalentes `a l’ex´ecution.
Le flot du contrˆole est d´efini de fa¸con triviale avec les noeuds seq-section,
qui repr´esente l’ex´ecution s´equentielle d’une liste instructions, et par-section
qui repr´esente l’ex´ecution non-ordonn´ee d’une liste d’instructions.
Les instructions sont simples : introduction/´elimination de variables (create-var,
kill-var), assignation `a une variable (assign-var) et assignation `a un tableau
(assign-array).
Toutes les valeurs (expressions pures) sont repr´esent´ees par des noeuds d’ex-
pression. Y sont repr´esent´es les lectures de constantes, variables et d’´el´ements
de tableaux (read-constant,read-variable,read-array), et les op´erateurs
d´efinis pour le code source.
3.4 (Pseudo-)assembleur
Le pseudo assembleur d´efini un langage totalement lin´eaire (une s´equence
d’op´erations de niveau assembleur), et des familles de registres. Les op´erations
de l’IR1 y sont r´efl´et´ees presque directement (assignation, lecture de constantes
et tableaux, calculs et movements de valeurs). Les registres sont originalement
tir´es de familles infinies (registres `a flottants ou `a entiers/pointeurs), puis de
familles finies pour les registres architecturaux et infinies pour les positions de
d´ebordement sur la pile.
4 Passes de transformation
L’ASA (non-typ´e) est tout d’abord produit via sexp->node.
4.1 Passes sur ASA
La fonction annotate-node propage l’information de type depuis les constan-
tes et arguments, et v´erifie que ceux-ci correspondent aux op´erations effectu´ees.
Le passage `a l’IR1 est fait par ast-to-ir1, qui compile chaque noeud
d’op´eration vers l’assignation d’une expression simple `a une variable. Les blocs
4
par sont compil´es vers des section par, et toutes les s´equences d’instructions
vers des sections seq.
4.2 Passe sur IR1
La fonction simplify-ir1 effectue des simplifications minimales sur l’IR1 :
fusion de sections seq imbriqu´ees, et ´elimination de singletons par.
Afin d’assurer que les expressions similaires mais pouvant avoir des valeurs
diff´erentes ne sont pas equal ? (ni eq ?), annotate-effects e´ecrit les expres-
sions en modifiant les lectures de variables/tableaux avec la liste des op´erations
pouvant en affecter la valeur. La logique est actuellement tr`es conservative,
mais semble permettre de tester l’´equivalence assez efficacement pour effectuer
les optimisations sur IR1.
Ces informations permettent de facilement transf´erer les assignations direc-
tement aux r´ef´erences `a la variable assign´ee, ce qui subsume la propagation de
copie et de constantes (forward-assignments). Il peut sembler ind´esirable de
dupliquer toutes les expressions communes. Cependant, il semble ˆetre plus im-
portant de donner aux optimisations sur les expressions acc`es au plus d’informa-
tion possible. Puisque les expressions math´ematiquement ´equivalentes sont sou-
vent canonicalis´ees, il devrait ˆetre possible de, par la suite, identifier et ´eliminer
les sous-expressions communes.
Le transfert des assignations, en plus de donner des arbres d’expression plus
grands, rend aussi plusieurs assignations (et variables) ´evidemment inutiles.
elide-assignments ´elimine ces derni`eres.
4.2.1 ecriture des expressions
Une fois qu’on a obtenu des arbres d’expression de taille utile (au lieu d’in-
diriger `a travers des variables en tout moment), il devient utile d’effectuer
des r´e´ecritures sur les expressions elles-mˆemes (algebraic-optimisation.scm,
optimize-all-expressions).
Les calculs constants sont pli´es sur toutes les op´erations.
Sur les multiplications et additions, la commutativit´e et associativit´e sont
utilis´ees afin de canonicaliser les calculs ´equivalents `a une forme unique
(rewrite-binary-assoc-comm). De plus, la distributivit´e est utilis´ee sur les
multiplications et additions binaires, afin d’arriver `a des sommes de produits,
et les additions de sommandes dupliqu´ees transform´ees en addition de multipli-
cation par une constante
(rewrite-array-index,rewrite-add-array-index,rewrite-mul-array-index,
fold-eq-addend). Cette forme permet de facilement comparer les indices d’acc`es
dans les tableaux, et, a moyen terme, d’´emettre des adresses style x86 sim-
plement. Le hash-consing est particuli`erement utile dans cette phase, puisque
presque toutes les op´erations sont effectu´ees jusqu’`a l’atteinte d’un point fixe.
5
1 / 10 100%
La catégorie de ce document est-elle correcte?
Merci pour votre participation!

Faire une suggestion

Avez-vous trouvé des erreurs dans linterface ou les textes ? Ou savez-vous comment améliorer linterface utilisateur de StudyLib ? Nhésitez pas à envoyer vos suggestions. Cest très important pour nous !