Blog 6 min de lecture

Chris Lattner : l’homme qui révolutionne le monde du C++

Share this article
Chris Lattner : l’homme qui révolutionne le monde du C++

Presque tous les développeurs C++ connaissent probablement l'infrastructure LLVM et le compilateur Clang. Mais combien savent que Chris Lattner en est le créateur, et qu'il n'avait que 25 ans à l'époque ? Comment est-ce possible ? Je me souviens qu'à 25 ans, je passais mon temps à essayer de comprendre les bases du C++ :)

L'histoire commence par une thèse

Fin 2000, Lattner rejoint l'université de l'Illinois à Urbana-Champaign comme assistant de recherche et étudiant en master. En travaillant avec Vikram Adve, il conçoit et commence à implémenter LLVM, une infrastructure innovante pour les compilateurs optimisants, qui fera l'objet de sa thèse de master en 2002. Il soutient son doctorat en 2005, en recherchant de nouvelles techniques pour optimiser les programmes intensifs en pointeurs et en les ajoutant à LLVM.

Voici la motivation derrière la conception de LLVM, tirée du résumé de sa thèse :

Les langages de programmation modernes et les principes du génie logiciel posent des problèmes croissants aux systèmes de compilation. Les approches traditionnelles, qui utilisent un simple modèle compiler-lier-exécuter, sont incapables de fournir des performances applicatives adéquates face aux exigences des nouvelles conditions. Les approches traditionnelles de compilation interprocédurale et guidée par profil peuvent fournir les performances applicatives nécessaires, mais exigent des temps de compilation irréalistes pour construire l'application.Cette thèse présente LLVM, une conception et une implémentation d'une infrastructure de compilation qui prend en charge un système d'optimisation multi-étapes unique. Ce système est conçu pour prendre en charge des optimisations interprocédurales et guidées par profil poussées, tout en étant suffisamment efficace pour être utilisé dans des systèmes de compilation commerciaux.Le jeu d'instructions virtuel LLVM est le ciment qui unit le système. C'est une représentation de bas niveau, mais dotée d'informations de type de haut niveau. Cela offre les avantages d'une représentation de bas niveau (représentation compacte, grande variété de transformations disponibles, etc.) tout en fournissant des informations de haut niveau pour prendre en charge des optimisations interprocédurales agressives au moment de l'édition de lienset après celle-ci. En particulier, ce système est conçu pour prendre en charge l'optimisation sur le terrain, à la fois à l'exécution et pendant les temps d'inactivité autrement inutilisés de la machine.

En résumé, l'idée derrière LLVM est d'utiliser la représentation intermédiaire LLVM (IR), qui s'apparente au bytecode Java ou à l'IL de .NET.

LLVM IR est conçue pour accueillir les analyses et transformations de niveau intermédiaire que l'on trouve dans la partie optimiseur d'un compilateur. Elle a été pensée avec de nombreux objectifs précis en tête, notamment la prise en charge d'optimisations légères à l'exécution, d'optimisations inter-fonctions/interprocédurales, d'analyses de programme complet et de transformations de restructuration agressives, etc. Son aspect le plus important, cependant, est qu'elle est elle-même définie comme un langage de premier ordre doté d'une sémantique bien définie.

Considérons une fonction relativement simple qui prend trois paramètres entiers et renvoie une combinaison arithmétique de ceux-ci.

int mul_add(int x, int y, int z) {
  return x * y + z;
}

L'IR LLVM de cette fonction ressemble à ceci :

define i32 @mul_add(i32 %x, i32 %y, i32 %z) {
entry:
  %tmp = mul i32 %x, %y
  %tmp2 = add i32 %tmp, %z
  ret i32 %tmp2
}

Revenons à l'objectif de cette thèse, qui se concentre sur l'optimisation. Pour expliquer cette phase de compilation, je ne saurais faire mieux que Chris Lattner, le père de LLVM, dans ce billet:

: « Pour donner une intuition du fonctionnement des optimisations, il est utile de parcourir quelques exemples. Il existe de très nombreux types d'optimisations de compilation, si bien qu'il est difficile de fournir une recette pour résoudre un problème arbitraire. Cela dit, la plupart des optimisations suivent une structure simple en trois parties :

  • Rechercher un motif à transformer.
  • Vérifier que la transformation est sûre/correcte pour l’instance correspondante.
  • Effectuer la transformation, en mettant à jour le code.
L'optimiseur lit l'IR LLVM en entrée, la « mâche » un peu, puis émet de l'IR LLVM qui, on l'espère, s'exécutera plus rapidement. Dans LLVM (comme dans beaucoup d'autres compilateurs), l'optimiseur est organisé comme un pipeline de passes d'optimisation distinctes, chacune étant exécutée sur l'entrée avec la possibilité d'agir. Parmi les exemples courants de passes figurent l'inline (qui substitue le corps d'une fonction aux sites d'appel), la réassociation d'expressions, le déplacement de code invariant de boucle, etc. Selon le niveau d'optimisation, différentes passes sont exécutées : par exemple, à -O0 (aucune optimisation), le compilateur Clang n'exécute aucune passe ; à -O3, il en exécute une série de 67 dans son optimiseur (à compter de LLVM 2.8).

LLVM et Clang comme infrastructure pour construire des outils

Un concept de conception majeur de Clang est son utilisation d'une architecture basée sur des bibliothèques. Dans cette conception, les différentes parties du front-end peuvent être proprement divisées en bibliothèques séparées, qui peuvent ensuite être combinées selon les besoins et les usages. De plus, l'approche par bibliothèques encourage de bonnes interfaces et facilite l'implication des nouveaux développeurs (car ils n'ont besoin de comprendre que de petites parties de l'ensemble).

llvm1

La conception de LLVM/Clang en fait une infrastructure puissante pour construire des outils, grâce à son architecture basée sur des bibliothèques, qui facilite la réutilisation des composants et l'intégration de nouvelles fonctionnalités dans d'autres projets.

De nombreux outils reposent sur LLVM et Clang, notamment clang-tidy, clang-query, CppDepend et d'autres.

Clang est toujours à jour avec les nouvelles normes

Clang suit constamment le rythme des nouvelles normes ; de plus, il offre toujours une prise en charge expérimentale des fonctionnalités à venir. La version actuelle propose une prise en charge expérimentale de certaines fonctionnalités proposées pour la norme C++ suivant C++17, provisoirement nommée C++2a. Notez que la prise en charge de ces fonctionnalités peut changer ou être retirée sans préavis, au gré de l'évolution du projet de norme C++2a.

Vous pouvez utiliser Clang en mode C++2a avec l'option -std=c++2a.

Fonctionnalité du langage Proposition C++2a Disponible dans Clang ?
Initialiseurs de membres par défaut pour les champs de bits P0683R1 Clang 6
const&-pointeurs qualifiés vers des membres P0704R1 Clang 6
Autoriser la capture lambda [=, this] P0409R2 Clang 6
__VA_OPT__ pour l'élision de virgule du préprocesseur P0306R4 Clang 6
Initialiseurs désignés P0329R4 Partiel (extension)
Constructeurs de liste d'initialisation dans la déduction d'arguments de templates de classes P0702R1 Clang 6
Vérification d'accès sur les spécialisations P0692R1 Partiel

Clang est désormais adopté par de nombreuses grandes entreprises, dont Google, Microsoft et bien sûr Apple, qui l'utilise depuis le début.

L'infrastructure LLVM peut être utilisée pour implémenter des compilateurs pour de nombreux langages de programmation et des interpréteurs. Par exemple, PostgreSQL utilise désormais le JIT LLVM pour optimiser l'exécution des requêtes, comme spécifié ici:

: Actuellement, PostgreSQL exécute les requêtes SQL à l'aide de l'interpréteur, ce qui est assez lent. Cependant, un gain de vitesse significatif peut être obtenu en compilant la requête « à la volée » avec le JIT LLVM : de cette façon, il est possible pour une requête SQL donnée de générer du code plus efficace, spécialisé à l'aide des informations connues à l'exécution, puis de l'optimiser en plus avec LLVM. Cette approche est particulièrement importante pour les requêtes complexes, où les performances sont limitées par le CPU.

Merci à tous les développeurs de LLVM/Clang, et un merci spécial à Chris Lattner, qui a initié la révolution et nous a offert une infrastructure puissante pour développer des outils utiles.

Enfin, un message aux jeunes développeurs : soyez le prochain Chris Lattner et lancez une révolution dans le monde de la programmation :)

Share this article