Mini-compilateur
David Monniaux
8 d´ecembre 2005
Ce projet vise `a faire compiler des expressions arithm´etiques vers du bytecode
Java.
URL de suivi: http://www.dix.polytechnique.fr/profs/informatique/David.Monniaux/mini-compilo.
1 Motivations
Consid´erons un logiciel tel que Maple ou Mathematica. Ces logiciels permet-
tent entre autres de tracer des courbes, calculer des inegrales, etc., toutes sortes
d’activit´es n´ecessitant l’´evaluation ep´et´ee d’une fonction math´ematique sp´ecifi´ee
par l’utilisateur sur un grand nombre de valeurs.
La fa¸con la plus simple de erer un tel probl`eme est d’analyser syntaxiquement
le texte tap´e par l’utilisateur (par exemple : sin(x)+x/3) et de le stocker sous la
forme d’un arbre, ici :
+
/
3
sin
xx
Ensuite, on peut, en fonction de la donn´ee de x, ´evaluer ecursivement la fonction
sur l’arbre. Nous noterons ceci ´evaluer(v, f) o`u fest une table qui associe `a chaque
nom de variable (comme x) une valeur, et fest la formule.
Le probl`eme de cette approche est que si l’on doit ´evaluer la fonction un grand
nombre nde fois pour la eme valeur de f, on va faire nfois tout un parcours
d’arbre, de choix de fonctions `a appeler, etc. C’est globalement assez lent.
Mathematica (et d’autres logiciels) r´esolvent ce probl`eme en compilant les ex-
pressions arithm´etiques `a ´evaluer ep´etitivement : on obtient en fonction de fune
forme compil´ee calculant x7→ ´evaluer(v, f ). La compilation prend un temps ne
ependant pas de n, mais uniquement de fet de sa complexit´e. Ensuite, l’´evaluation
ep´et´ee nfois est plus rapide.
Il est plutˆot malais´e de compiler vers du code machine pour la machine mat´erielle
utilis´ee ; qui plus est, cette approche n’est pas portable entre diff´erents types de
processeurs, ce qui oblige `a fournir un en´erateur par type de processeur (Pentium,
Athlon64 en mode 64 bits, PowerPC...). Nous ´ecarterons cette solution. Une autre
solution serait d’´ecrire un code source C, C++, Java ou autre, de lancer un compila-
teur pour le langage consid´er´e, et de r´ecup´erer le code objet produit pour le lancer.
Cela est plutˆot lourd et demande que l’utilisateur de notre application dispose d’une
chaˆıne de compilation, ce qui n’est typiquement le cas que des programmeurs ou
administrateurs syst`eme.
10
Nous proposons donc l’approche suivante : on compilera vers la Java virtual
machine, qui a une architecture simple et compr´ehensible, en s’aidant d’une petite
biblioth`eque fournie pour egler les etails techniques. Ensuite, on chargera le code
fourni dans l’application !
2 Compilation vers la JVM
Le format d’entr´ee de la JVM (les fichiers .class) est compl`etement sp´ecifi´e dans
The Java Virtual Machine Specification [1], disponible ´egalement en ligne. Cepen-
dant, cette documentation est complexe et ecrit une quantit´e de choses dont nous
n’aurons pas besoin ; par ailleurs, de nombreux aspects fastidieux de la g´en´eration
de code Java peuvent ˆetre automatis´es par ailleurs. Nous sugg´erons de produire un
fichier de classe ne contenant qu’une fonction de signature public static void
evaluate(double x).
Les m´ethodes Java sont ´ecrites `a l’aide d’un ensemble assez limit´e d’op´erations.
Chaque fonction peut manipuler :
une zone de variables locales ; les premi`eres variables locales sont occup´ees
par les param`etres de la m´ethode ;
une pile d’arguments.
Ces deux zones contiennent des mots de 32 bits (pouvant contenir un entier, un car-
act`ere, un flottant simple pr´ecision) ; les donn´ees de 64 bits (entiers longs, flottants
double pr´ecision) sont stocees dans 2 mots cons´ecutifs (faire donc attention `a la
num´erotation induite).
Les op´erations prennent en argument les donn´ees au sommet de la pile, et
poussent leur r´esultat sur la pile. Il s’agit d’un fonctionnement classique, utilis´e
dans les calculatrices fonctionnant en notation polonaise invers´ee (HP48, par exem-
ple). Rappelons `a toutes fins utiles qu’un affichage en polonaise invers´ee s’obtient
facilement par parcours postfixe de l’arbre d’expression.
Consid´erons la classe suivante :
class Foo {
public static double evaluate(double x) {
return (Math.sin(x) + (x / 3.0));
}
}
Examinons son code objet avec javap -c Foo :
public static double evaluate(double);
Code:
0: dload_0
1: invokestatic #2; //Method java/lang/Math.sin:(D)D
4: dload_0
5: ldc2_w #3; //double 3.0d
8: ddiv
9: dadd
10: dreturn
}
Regardons ce code ligne par ligne. On commence par pousser dans la pile la
variable locale de type double situ´ee `a l’indice 0 (donc les mots d’indices 0 et 1),
donc x. On lance ensuite la fonction statique java.lang.Math.sin (double x)
(le type des arguments est pr´ecis´e par (D)), retournant un double (le dernier D).
11
Cette fonction consomme l’argument pouss´e sur la pile, calcule le sinus, et pousse le
esultat sur la pile. On pousse ensuite la variable locale d’indice 0, puis la constante
flottante 3.0, et on demande une division en double, qui va consommer les deux
op´erandes et pousser le quotient. `
A ce point, la pile contient sin(x), puis x/3, du
bas vers le sommet. On additionne les deux op´erandes, qui sont remplac´es par leur
somme, que l’on retourne `a l’appelant.
Ils est assez fastidieux de r´ediger du bytecode Java directement `a l’aide d’un
programme ; il vaut mieux passer par une biblioth`eque ad hoc. Nous sugg´erons
l’utilisation de la biblioth`eque ASM. Le code de g´en´eration de la fonction ci-dessus
est :1
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
"evaluate", "(D)D", null, null);
mv.visitCode();
mv.visitVarInsn(DLOAD, 0);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Math", "sin", "(D)D");
mv.visitVarInsn(DLOAD, 0);
mv.visitLdcInsn(new Double("3.0"));
mv.visitInsn(DDIV);
mv.visitInsn(DADD);
mv.visitInsn(DRETURN);
mv.visitMaxs(6, 2);
mv.visitEnd();
6 est la hauteur maximale de la pile demand´ee, en mots, 2 le nombre de mots
de variables locales (ne pas oublier que chaque double prend deux mots).
3 Travail demand´e
`
A partir d’une grammaire d’expressions arithm´etiques (+, -, *, /, ´eventuellement
fonctions comme sinus et cosinus), avec constantes et variables, r´ediger :
un analyseur syntaxique
une fonction d’´evaluation des expressions par descente r´ecursive
une fonction de compilation
Des fichiers seront fournis afin de echarger totalement le travail d’interface avec
ASM et la JVM.
`
A titre indicatif, l’impl´ementation de l’enseignant responsable du projet prend
environ 1000 lignes de Java, y compris les ethodes de diagnostic, affichage, et
calcul de eriv´ees formelles.
4 Suggestion d’extension
Se servir de cette compilation dynamique pour impl´ementer une m´ethode rapide de
esolution par la m´ethode de Newton (xn+1 =xnf(xn)/f0(xn)), avec l’utilisateur
qui donne au clavier une fonction et un point x0.
References
[1] Tim Lindholm and Frank Yellin. The Java Virtual Ma-
chine Specification. Addison-Wesley, 2nd edition, 1999.
http://java.sun.com/docs/books/vmspec/.
1http://asm.objectweb.org/
12
1 / 3 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 !