• Aucun résultat trouvé

Compilateurs à la volée traceurs

5.8 Outils utilisés . . . 81

5.8.1 Discussions sur l’utilisation de Nit . . . 81 5.8.2 L’interpréteur Nit . . . 81 5.8.3 Implémentation de l’héritage multiple . . . 84

5.9 Conclusion . . . 84

5.1 Introduction

Développer une machine virtuelle est un projet ambitieux. Ces systèmes représentent parfois jusqu’à plusieurs centaines de milliers de lignes de code. Dans le cadre d’un projet universitaire et d’autant plus dans la durée limitée d’une thèse, il était impératif de chercher à réutiliser le plus d’éléments possible.

76 CHAPITRE 5. ÉTAT DE L’ART DES OUTILS Ce chapitre contiendra tout d’abord un récapitulatif des outils explorés puis utilisés pour mener à bien le projet. Ensuite, l’architecture de la machine virtuelle ainsi que son fonctionnement détaillé seront décris.

La principale difficulté du projet était de réaliser une machine virtuelle suffisamment réaliste pour effectuer des expériences sur les protocoles de compilation/recompilation. L’objectif du projet n’est pas d’avoir une machine virtuelle commerciale, il s’agit de spécifier une machine virtuelle pour l’héritage multiple ainsi que son compilateur à la volée. Pour cela, il était nécessaire d’effectuer beaucoup de développement, ce qui bien sûr est très chronophage. Le principal point consistait à ne pas réécrire de zéro l’intégralité des composants d’une machine virtuelle.

5.2 Le langage Nit

Cette thèse implique d’utiliser un langage à objets en héritage multiple et en typage statique en tant que langage source de la machine virtuelle. Comme présenté précédem- ment, il n’y a pas beaucoup de langages candidats à de telles expérimentations. Dans les langages à objet à typage statique, seul C++ et Eiffel présentent des caractéristiques proches et C++ n’est pas complètement objet.

Nous avons travaillé avec le langage Nit [Privat, 2008]. Initialement nommé PRM (Programming with Refinement and Modules), ce langage a été créé par Jean Privat [Privat, 2006] au sein du LIRMM puis à l’UQAM ensuite à partir de 2008. À ce moment là, le compilateur est écrit en Ruby et le langage est compilé vers du C. Par la suite, Floréal Morandat a continué de développer PRM dans sa thèse [Morandat, 2010] en réalisant notamment le bootstrap du compilateur. Ce compilateur bootstrapé a servi à effectuer des expériences sur l’implémentation des mécanismes objets.

Le langage a été renommé Nit en 2008, il est maintenant plus mature et son déve- loppement est actif bien que Nit reste un langage académique. Il possède actuellement plusieurs compilateurs et sert de support à la recherche.

Ce langage possède plusieurs propriétés intéressantes : — Langage complètement orienté objet

— Héritage multiple de classes

— Types virtuels [Shang, 1996] ( [Bruce et al., 1998, Torgersen, 1998] présentent des améliorations)

— Système de modules et de raffinement de classes [Ducournau et al., 2007] — Types nullables [Gélinas et al., 2009]

— Gestion automatique de la mémoire via un ramasse-miettes : Boehm [Boehm and Weiser, 1988, Boehm, 1993]

5.3 Frameworks de machine virtuelle

Partant du constat que développer de zéro une machine virtuelle représente un grand effort (par exemple 250 000 lignes pour la JVM HotSpot), certaines équipes de recherches

5.3. FRAMEWORKS DE MACHINE VIRTUELLE 77 ont tentées de créer des frameworks de machine virtuelle. Ces travaux restent tout de même peu nombreux et relativement marginaux. À notre connaissance, il n’existe aucune machine virtuelle importante qui ait été développée en utilisant un framework de machine virtuelle.

Plusieurs composants caractérisent une machine virtuelle : — Gestionnaire de mémoire automatique (Garbage collector) — Compilateur à la volée

— Gestionnaire de threads

— Représentation des objets en mémoire

Une étude plus poussée des différents frameworks utilisables pour développer une machine virtuelle a été réalisée dans [Saleil, 2013].

5.3.1 Open-Runtime Platform

Open-Runtime Platform est une machine virtuelle développé par Intel [Cierniak et al., 2002,Cierniak et al., 2005] pour plusieurs langages. Le bytecode Java ainsi que le CIL sont supportés par Open-runtime Platform. Ces deux langages étant relativement similaires, il a été possible de factoriser un grand nombre de mécanismes dans la machine virtuelle. À l’origine le but était d’avoir une grande modularité dans les composants du sys- tème. Il était par exemple prévu de pouvoir changer facilement le ramasse-miettes, le compilateur à la volée ou d’autres éléments dans une machine virtuelle. Open-Runtime Platform n’est pas vraiment un framework de machine virtuelle, mais sa modularité aurait pu permettre de l’utiliser pour d’autres langages que Java et les langages .NET. La cible du projet semble être des langages en sous-typage multiple, il n’y a pas de ga- rantie qu’Open Runtime Platform puisse supporter l’héritage multiple. Aujourd’hui, le développement du projet est arrêté.

5.3.2 Truffle et Graal

Truffle et Graal [Würthinger et al., 2013, Wimmer and Würthinger, 2012] sont des sortes de frameworks permettant d’implémenter des langages sur la machine virtuelle Java standard.

Du point de vue de l’utilisateur, un interpréteur pour le langage choisi doit être écrit en Java en utilisant l’API de Truffle. C’est la seule action à effectuer pour que le langage puisse être exécuté. Cet interpréteur est ensuite exécuté sur une machine virtuelle Java. Cette machine virtuelle Java est en fait la JVM de référence (HotSpot) avec un nouveau compilateur à la volée : Graal. Ce compilateur à la volée va essentiellement faire de la réécriture des nœuds de l’arbre syntaxique pour optimiser l’exécution de l’interpréteur. Ce framework est un outil intéressant car il permet d’implémenter facilement un nouveau langage en ayant de bonnes performances. Plusieurs publications font état de l’utilisation de Truffle et Graal pour implémenter des langages, on peut par exemple citer [Bonetta, 2015] ou encore [Wimmer and Brunthaler, 2013].

78 CHAPITRE 5. ÉTAT DE L’ART DES OUTILS Néanmoins, aucun contrôle n’est possible sur l’implémentation du langage dans la machine virtuelle. Il serait donc impossible d’effectuer des expériences sur les protocoles d’optimisation en l’utilisant, cette solution est donc à écarter.

5.3.3 Le framework VMKit

VMKit [Geoffray et al., 2010] est sans doute le projet le plus abouti dans ce domaine précis. Il s’agit d’un framework en C++ développé dans le cadre de la thèse de Nicolas Geoffray [Geoffray, 2009].

VMKit est une agrégation de différents outils existants liés entre eux pour composer le framework :

— LLVM : compilateur à la volée et représentation intermédiaire du code

— MMTk : garbage collector utilisé initialement dans JikesRVM [Blackburn et al., 2004]

— Threads POSIX pour le multitâches

— Une base de code pour lier ces différents composants entre eux — Un squelette minimal de machine virtuelle

VMKit a été utilisé pour développer deux machines virtuelles. La première, J3, est une machine virtuelle Java. Une autre machine virtuelle N3 exécutait du code CLR (pla- teforme .NET). Ces deux machines virtuelles sont plutôt des preuves de faisabilité que de véritables machines virtuelles efficaces. Les techniques d’implémentations utilisées pour leur conception étaient relativement classiques et correspondent à l’état de la littérature scientifique du domaine à l’époque de leur développement.

Ce framework est indéniablement un outil intéressant, et il était initialement prévu de l’utiliser pour développer notre machine virtuelle. Mais son développement a ralenti et le projet a été officiellement arrêté fin 2013.

5.4 Machine virtuelle utilisant VMKIT

5.4.1 Présentation

L’idée initiale était de développer une machine virtuelle avec VMKIT et de définir un sous-ensemble de Nit qui serait exécutable. Ce sous-ensemble ne devait pas contenir la notion de modules et de raffinement de classes qui est profondément incompatible avec le chargement dynamique.

En effet, du point de vue de la machine virtuelle, les modules sont problématiques : le raffinement de classes permet de définir une classe à travers plusieurs modules. Charger cette classe dans la machine virtuelle implique donc en théorie de "rassembler" tous ses morceaux dans les différents modules de l’application. Il n’est pas non plus possible de charger un module bout par bout à la volée car cela impliquerait de modifier la représentation des classes dans la machine virtuelle et de possiblement modifier des instances déjà créées. Une alternative est possible : aplatir les modules de manière globale avant l’exécution pour faire disparaître cette notion à l’exécution.

5.5. MACHINE VIRTUELLE À PARTIR DE L’INTERPRÈTE NIT 79 Pour développer intégralement la machine virtuelle avec VMKit impliquerait d’avoir un compilateur de Nit vers un langage de bytecode, qu’il faudrait également définir et spécifier. Il serait également possible de traiter directement des programmes en Nit en entrée mais il faudrait aussi développer un analyseur lexical et syntaxique.

5.4.2 Faisabilité

Le fait de spécifier un langage de bytecode est une tâche complexe : il faut en effet spécifier le langage et ensuite maintenir le compilateur de Nit vers le bytecode. Les nouveautés du langage devraient alors être supportées pour maintenir la compatibilité et pouvoir continuer à exécuter des benchmarks. En parallèle, il faudrait développer la machine virtuelle : au total deux systèmes sont donc à développer et maintenir.

Du point de vue des performances, il s’agirait du meilleur système possible car nous posséderions un compilateur à la volée très performant (LLVM) ainsi qu’une gestion mémoire poussée.

Par contre, cette approche oblige à réécrire tout le méta-modèle de Nit en C++, ce qui est long et redondant car il est déjà présent dans les compilateurs Nit. On peut également imaginer que la complexité de C++ et l’assimilation des différents composants de VMKit seraient des freins au développement.

Compte tenu des délais restreints et de la seconde partie du projet qui consiste à étudier des protocoles de compilation/recompilation, cette option pour développer la machine virtuelle semble complexe. Cependant, les spécifications seraient remplies avec VMKit. Historiquement, il était prévue d’utiliser cette méthode pour réaliser la machine virtuelle, mais l’arrêt du support de VMKit met définitivement un terme à cette option.

5.5 Machine virtuelle à partir de l’interprète Nit

5.5.1 Présentation

L’abandon du projet VMKit a demandé de chercher des alternatives. Le langage Nit dispose de plusieurs systèmes d’exécution dont un interpréteur. Une solution alternative est de se baser sur l’interpréteur Nit pour le transformer progressivement en machine virtuelle.

L’interpréteur de Nit est basé sur l’arbre syntaxique du programme (AST) qui est décoré par le modèle du programme. L’exécution est réalisée en parcourant arbre syn- taxique, pour produire le résultat attendu, il n’y a pas de notion de compilation.

5.5.2 Faisabilité

Cet interpréteur n’effectue pas de chargement dynamique des classes, les structures nécessaires à l’évaluation sont donc créées globalement au début de l’exécution. Il est par contre déjà paresseux lors de l’exécution. Il faudrait donc commencer par ajouter le chargement dynamique.

80 CHAPITRE 5. ÉTAT DE L’ART DES OUTILS Avec cette solution, il est est possible de commencer à travailler sur différents pro- tocoles de compilation/recompilation après un relativement peu de temps passé à dé- velopper. La compilation du code à la volée est quelque chose de fondamental pour le travail sur les protocoles de compilation/recompilation. Cette compilation pourrait être réalisée de deux manières :

— Simulée : les implémentations des mécanismes objets seront décidées avant de les utiliser, sans produire de code machine

— Réelle : du code serait généré à la volée

Le développement d’un véritable compilateur à la volée est une tâche importante, au début du projet, il n’existait pas de possibilités d’utiliser LLVM en Nit pour réaliser ceci. Il faudrait donc développer un compilateur à la volée en utilisant d’autres moyens. Cette solutions a également l’avantage de n’avoir pas besoin de spécifier un langage de bytecode ni de développer un compilateur de Nit vers ce bytecode. L’interpréteur est déjà capable d’exécuter des programmes Nit, du temps serait ainsi économisé.

De plus, le modèle du programme est également présent dans l’interpréteur et pour- rait donc servir lors de l’étude des protocoles sans avoir besoin de le réécrire.

5.6 Compilateurs à la volée traceurs

Les compilateurs à la volée traceurs (tracing-JIT en anglais) sont une nouvelle ap- proche permettant d’obtenir de relative bonnes performances dans un interpréteur. Cette approche a été par exemple été testée dans un interpréteur python [Bolz et al., 2009].

L’idée est qu’un programme passera beaucoup de temps dans les boucles, plusieurs itérations de ces boucles seront relativement similaires du point de vue du code exécuté. Un compilateur traceur va donc enregistrer les instructions exécutées dans une boucle, puis compiler ces instructions en code machine à la volée.

Aux prochaines itérations de cette même boucle, ce code compilé sera exécuté au lieu d’interpréter le code. Pour que l’approche soit viable, il faut bien sûr introduire des gardes dans le code compilé pour garantir une exécution correcte. Si jamais le code compilé diffère du code qui devrait être exécuté, l’exécution continue en mode interprétation seulement.

Cette technique est un fonctionnement mixte entre un interpréteur et un compilateur. De bonnes performances sont promises pour une complexité moindre que de développer un compilateur à la volée uniquement. Mais il faut tout de même posséder un compila- teur à la volée capable de compiler vers du code machine des instructions élémentaires du code. Cette technique pourrait être utilisée en tant qu’approche mixte entre un in- terpréteur et un compilateur à la volée pour implémenter la machine virtuelle.