Sortie de LLVM et Clang 5.0

Approximativement selon le calendrier prévu, voici donc la version 5.0 de LLVM, y compris le compilateur C et C++ Clang. En peu de mots, cette version apporte la compatibilité avec pas mal de matériel récent ; côté C++, on compte C++17 au complet et les coroutines (qui apparaîtront probablement dans une prochaine version de C++), en utilisant les fonctionnalités arrivées avec LLVM 4.0.

Cette version apporte énormément de nouveautés du côté matériel, pour les architectures ARM, AVR, MIPS, PowerPC 3.0, x86, ainsi que les GPU AMD Radeon Vega. Plus en détail, pour l’architecture ARM, la sélection d’instructions et la fusion d’instructions ont été fortement améliorées, ce qui devrait avoir un impact non négligeable sur la performance. Les instructions ARMv8.1, 8.2 et 8.3 ont été ajoutées au compilateur (comme les Cortex M23 et M33). Les données de l’ordonnanceur Cavium ThunderX2 ont été ajoutées, ce qui devrait améliorer fortement la performance pour ce processeur prévu pour les centres informatiques et superordinateurs.

Bien évidemment, la famille x86 n’est pas oubliée. Dans les processeurs ajoutés, on compte AMD Ryzen (les données pour l’ordonnanceur seront encore améliorées avec LLVM 6.0) et Intel Goldmont (la dernière génération de Pentium, Celeron et Atom). Pas mal de modifications ont eu lieu du côté AVX-512, les instructions vectorielles des Xeon haut de gamme. Les données concernant une série de processeurs plus anciens ont été mises à jour (Intel Silvermont — Atom, Celeron et Pentium de 2013 — et Sandy Bridge — Core 2e génération —, AMD Jaguar — 2013).

Plus spécifiquement, pour Clang, les coroutines sont une des fonctionnalités les plus attendues pour la version 5.0. N’étant pas encore dans la norme C++, il faut les activer séparément (-fcoroutines-ts -stdlib=libc++). Un coroutine est une fonction particulière qui peut suspendre et reprendre son exécution tout en gardant son état. Une utilisation assez simple est d’éviter de générer explicitement une liste d’éléments à travers laquelle le programme itère — notamment dans le cas où cette liste est infinie. Ainsi, on pourrait écrire ceci pour afficher tous les nombres en partant de zéro jusqu’à l’infini par pas de cinq (0, 5, 10, etc.) :

generator generatorForNumbers(int begin, int inc= 1){
  for (int i= begin;; i += inc){
    co_yield i;
  }
}

int main(){
  for (auto n: getForNumbers(0, 5)) 
    std::cout << n << " ";
}

Ces coroutines auront ainsi une certaine utilité notamment dans les applications par événements, comme un serveur Web (en attendant des requêtes) ou une interface graphique (idem pour des interactions avec l’utilisateur). Elles peuvent aussi avoir des applications pour le multitâche coopératif, où chaque tâche à effectuer décide du moment où elle rend l’exécution aux autres tâches (alors qu’un appel de fonction est préemptif : les autres fonctions ne peuvent s’exécuter qu’à la fin de l’actuelle). Ainsi, par exemple, un serveur Web pourrait s’écrire comme ceci :

Acceptor acceptor{80};
while (true){
  Socket socket = co_await acceptor.accept(); // Exécuté dès qu'une nouvelle connexion arrive. 
  auto request = co_await socket.read(); // Dès qu'un paquet est disponible. 
  auto response = handleRequest(request); 
  socket.write(responste); 
}

Voir aussi : les notes de version de LLVM 5.0 et de Clang 5.0.

Advertisements

Des nouvelles de Flang, le compilateur Fortran basé sur LLVM

Après un an et demi de travaux, le compilateur Fortran basé sur LLVM promis par NVIDIA arrive enfin sur GitHub, bien que sans annonce officielle. Le projet a commencé avec un étudiant (financé par le programme Google Summer of Code) en 2013 sous le nom de Flang (analogue de Clang, le compilateur LLVM pour le C et ses dérivés), puis a été repris par NVIDIA et la sécurité nucléaire du département américain en 2015. L’objectif était de fournir un compilateur libre de très haute qualité, notamment basé sur celui de PGI.

Avec le développement de LLVM, ce projet apporte de plus en plus de promesses à l’écosystème Fortran. Par exemple, LLVM permet de compiler du code pour les GPU NVIDIA : grâce à Flang, il sera très facile d’offrir un compilateur libre pour ce faire (sans devoir utiliser celui de PGI). Également, OpenMP permet de décharger l’exécution du code sur un accélérateur : GCC ne le permet pas encore pour des cartes graphiques, uniquement les coprocesseurs Intel Xeon Phi ;  LLVM, quant à lui, gère les cartes NVIDIA et les coprocesseurs Intel Xeon Phi.

Pour le moment, Flang fonctionne avec LLVM 3.9 (presque la dernière version). Les sources de la version 1.0 sont en ligne depuis une semaine (avec des commits d’employés de NVIDIA et de PGI), mais aucune annonce officielle n’a encore été faite (la documentation fait référence à un site qui n’existe pas encore, http://flang.nvidia.com/). La norme Fortran 2008 est implémentée, mais aucune information n’est donnée sur le niveau de compatibilité (le compilateur de PGI n’implémente pas toute la norme). La grande différence par rapport au compilateur de PGI devrait être la qualité des messages d’erreur, du niveau de Clang.

Sortie de GCC 7.1

La nouvelle version du compilateur libre GCC vient de sortir. Selon la nouvelle convention de numérotation, son numéro de version est 7.1, pour la première version stable de la branche GCC 7. Elle marque le trentième anniversaire du compilateur GCC limité au langage C, qui a depuis lors évolué comme une suite de compilateurs pour une flopée de langages : Ada, C, C++, Fortran, Go.

C++17

Parmi les nouveautés concernant C++, on peut noter une gestion expérimentale de l’entièreté de la norme C++17. Il faut encore l’activer manuellement lors de la compilation (avec l’option -std=c++1z), au moins jusqu’à la publication de la version finale de la norme. Les progrès par rapport à GCC 6 sont notables, comme montré dans le tableau ci-dessous. Néanmoins, la bibliothèque standard n’est pas encore à ce niveau d’implémentation.

[[fallthrough]] attribute P0188R1 7 __has_cpp_attribute(fallthrough)
Extension to aggregate initialization P0017R1 7 __cpp_aggregate_bases >= 201603
Wording for constexpr lambda P0170R1 7 __cpp_constexpr >= 201603
Lambda capture of *this by Value P0018R3 7 __cpp_capture_star_this >= 201603
Construction Rules for enum class variables P0138R2 7
Dynamic memory allocation for over-aligned data P0035R4 7 __cpp_aligned_new >= 201606
Guaranteed copy elision P0135R1 7
Refining Expression Evaluation Order for Idiomatic C++ P0145R3 7
constexpr if P0292R2 7 __cpp_if_constexpr >= 201606
Selection statements with initializer P0305R1 7
Template argument deduction for class templates P0091R3 7 __cpp_deduction_guides >= 201606
Declaring non-type template parameters with auto P0127R2 7 __cpp_template_auto >= 201606
Using attribute namespaces without repetition P0028R4 7
Structured bindings P0217R3 7 __cpp_structured_bindings >= 201606
Remove Deprecated Use of the register Keyword P0001R1 7
Remove Deprecated operator++(bool) P0002R1 7
Make exception specifications be part of the type system P0012R1 7 __cpp_noexcept_function_type >= 201510
Rewording inheriting constructors (core issue 1941 et al) P0136R1 7 __cpp_inheriting_constructors >= 201511
Inline variables P0386R2 7 __cpp_inline_variables >= 201606
DR 150, Matching of template template arguments P0522R0 7 __cpp_template_template_args >= 201611
Removing dynamic exception specifications P0003R5 7
Pack expansions in using-declarations P0195R2 7 __cpp_variadic_using >= 201611
A byte type definition P0298R0 7

Pour la bibliothèque standard :

constexpr std::hardware_{constructive,destructive}_interference_size P0154R1 No __cpp_lib_hardware_interference_size >= 201603
Core Issue 1776: Replacement of class objects containing reference members P0137R1 7 __cpp_lib_launder >= 201606
Variant: a type-safe union for C++17 P0088R3 7 __has_include(<variant>)
Library Fundamentals V1 TS Components: optional P0220R1 7 __has_include(<optional>)
Library Fundamentals V1 TS Components: any P0220R1 7 __has_include(<any>)
Library Fundamentals V1 TS Components: string_view P0220R1 7 __has_include(<string_view>)
Library Fundamentals V1 TS Components: memory_resource P0220R1 No __has_include(<memory_resource>)
Library Fundamentals V1 TS Components: apply P0220R1 7 __cpp_lib_apply >= 201603
Library Fundamentals V1 TS Components: shared_ptr<T[]> P0220R1 7 __cpp_lib_shared_ptr_arrays >= 201603
Library Fundamentals V1 TS Components: Searchers P0220R1 7 __cpp_lib_boyer_moore_searcher >= 201603
Library Fundamentals V1 TS Components: Sampling P0220R1 7 __cpp_lib_sample >= 201603
Constant View: A proposal for a std::as_const helper function template P0007R1 7 __cpp_lib_as_const >= 201510
make_from_tuple: apply for construction P0209R2 7 __cpp_lib_make_from_tuple >= 201606
Removing auto_ptr, random_shuffle(), And Old <functional> Stuff N4190 No (kept for backwards compatibility)
Deprecating Vestigial Library Parts in C++17 P0174R2 No
Making std::owner_less more flexible P0074R0 7 __cpp_lib_transparent_operators >= 201510
std::addressof should be constexpr LWG2296 7 __cpp_lib_addressof_constexpr >= 201603
Re-enabling shared_from_this P0033R1 7 __cpp_lib_enable_shared_from_this >= 201603
Adopt not_fn from Library Fundamentals 2 for C++17 P0005R4 7 __cpp_lib_not_fn >= 201603
Fixes for not_fn P0358R1 7
Fixing a design mistake in the searchers interface in Library Fundamentals P0253R1 7
Extending memory management tools P0040R3 7
shared_ptr::weak_type P0163R0 7 __cpp_lib_shared_ptr_weak_type >= 201606
Adopt Type Traits Variable Templates from Library Fundamentals TS for C++17 P0006R0 7 __cpp_lib_type_trait_variable_templates >= 201510
Adding [nothrow-]swappable traits P0185R1 7 (__is_swappable available since 6.1) __cpp_lib_is_swappable >= 201603
is_callable, the missing INVOKE related trait P0077R2 7 __cpp_lib_is_callable >= 201603
has_unique_object_representations P0258R2 7 __cpp_lib_has_unique_object_representations >= 201606
Polishing <chrono> P0092R1 7 __cpp_lib_chrono >= 201510
Adding more constexpr to <chrono> P0505R0 7 ???
Constexpr for std::char_traits P0426R1 7 (partial) ???
Integrating std::string_view and std::string P0254R2 7
Give ‘std::string’ a non-const ‘.data()’ member function P0272R1 7
Contiguous Iterators N4284 N/A
Emplace return type P0084R2 7 ???
Splicing Maps and Sets P0083R3 7 __cpp_lib_node_extract >= 201606
A Proposal to Add Constexpr Modifiers to reverse_iterator, move_iterator, array and Range Access P0031R0 7 __cpp_lib_array_constexpr >= 201603
The Parallelism TS Should be Standardized P0024R2 No __has_include(<execution>) , __cpp_lib_parallel_algorithm >= 201603
An algorithm to “clamp” a value between a pair of boundary values P0025R0 7 __cpp_lib_clamp >= 201603
Adopt Selected Library Fundamentals V2 Components for C++17 P0295R0 7 __cpp_lib_gcd >= 201606 , __cpp_lib_lcm >= 201606
Proposal to Introduce a 3-Argument Overload to std::hypot P0030R1 7 __cpp_lib_hypot >= 201603
Mathematical Special Functions for C++17 P0226R1 7 (6.1) __cpp_lib_math_special_functions >= 201603 (for GCC 6 or pre-C++17 define __STDCPP_WANT_MATH_SPEC_FUNCS__ to a non-zero value and test for __STDCPP_MATH_SPEC_FUNCS__ >= 201003L)
Adopt the File System TS for C++17 P0218R1 No __has_include(<filesystem>) , __cpp_lib_filesystem >= 201603
Relative Paths for Filesystem P0219R1 No
Adapting string_view by filesystem paths P0392R0 No
constexpr atomic<T>::is_always_lock_free P0152R1 7 __cpp_lib_atomic_is_always_lock_free >= 201603
Variadic lock_guard P0156R2 7 __cpp_lib_scoped_lock >= 201703
byte type definition P0298R3 7 ???
Elementary string conversions P0067R5 No ???

Diagnostics C et C++

Les diagnostics pour le code C et C++ ont été encore largement améliorés : GCC 6 apportait bon nombre d’améliorations, cette nouvelle itération continue le travail. Principalement, le compilateur propose des corrections pour les fautes de frappe, que ce soit des membres de structures, des fonctions, des macros, des énumérations ou des types.

spellcheck-fields.cc:52:13: error: 'struct s' has no member named 'colour'; did you mean 'color'?
   return ptr->colour;
               ^~~~~~
               color

De même, le préprocesseur propose des corrections pour les directives mal écrites :

test.c:5:2: error: invalid preprocessing directive #endfi; did you mean #endif?
 #endfi
  ^~~~~
  endif

Les chaînes de formattage (utilisées par printf et compagnie) ont aussi droit à des propositions de corrections :

test.c:51:29: warning: format '%s' expects argument of type 'char *', but argument 3 has type 'int' [-Wformat=]
   printf ("foo: %d  bar: %s baz: %d", 100, i + j, 102);
                          ~^                ~~~~~
                          %d

En C++, plus spécifiquement, certains points-virgules manquants sont signalés :

test.cc:4:11: error: expected ';' after class definition
 class a {} 
           ^
           ;

Au niveau de la sécurité, bon nombre de nouveaux messages permettent de détecter les risques de dépassement de capacité et d’autres formes d’accès invalides à la mémoire. Par exemple, des appels à malloc() avec une taille de mémoire négative donne un avertissement :

void* f (int n)
{
  return malloc (n > 0 ? 0 : n);
}

warning: argument 1 range [2147483648, 4294967295] exceeds maximum object size 2147483647 [-Walloc-size-larger-than=]

De même, certaines failles potentielles lors de l’utilisation de fonctions de la famille printf sont détectées :

void* f (int mday)
{
  char *buf = malloc (3);
  sprintf (buf, "%02i", mday % 100);
  return buf;
}

warning: 'sprintf may write a terminating nul past the end of the destination [-Wformat-overflow=]
note: 'sprintf' output between 3 and 4 bytes into a destination of size 3

Optimisation du code

GCC 7 apporte bon nombre de nouvelles passes d’optimisation du code. Par exemple, certains appels à sprintf peuvent être remplacés par des constantes, la taille de leur sortie peut être estimée (avec des bornes supérieures et inférieures). Les opérations d’écriture en mémoire sont rassemblées, afin d’effectuer moins d’instructions, chacune copiant plus de données.

Les passes interprocédurales peuvent analyser les bits des variables forcément à zéro (ce qui inclut l’alignement des pointeurs, déjà disponible avec GCC 6) et donner l’information aux autres passes. Pour les variables entières, les bornes sur les valeurs sont transmises aux autres algorithmes d’optimisation.

Certaines boucles ont la particularité d’avoir une condition toujours vraie pour une partie des itérations puis fausse pour les suivantes. Dans ce cas, GCC 7 peut séparer la boucle en deux parties, de telle sorte que la condition ne doit plus être vérifiée.

Calcul de haute performance

OpenMP est une série d’annotations pour le code C, C++ et Fortran qui indique au compilateur comment le paralléliser. GCC 6 implémentait la version 4.5 de la norme pour C et C++, GCC 7 l’apporte à Fortran, avec l’exception de la correspondance des éléments de structure.

GCC peut maintenant produire des exécutables au format BRIG 1.0. Il est utilisé dans le cadre de la plateforme AMD HSA. L’objectif est de déporter facilement du code OpenMP sur n’importe quel type d’accélérateur, comme un GPU, ce qui est défini depuis OpenMP 4.

NVIDIA n’est pas en reste, GCC peut maintenant produire des binaires au format NVPTX, utilisé sur les cartes graphiques de la marque. Cet export est utilisé pour décharger l’exécution de code OpenMP.

Autres langages

GCJ, le compilateur Java, n’était plus vraiment maintenu depuis des années, il est maintenant supprimé. Le compilateur Go est mis à jour pour la version 1.8 du langage.

Sources : GCC 7 Release Series, C++ Standards Support in GCC, libstdc++ Implementation Status.

Sortie de LLVM et Clang 4.0

Comme tous les six mois, voici une nouvelle version de LLVM, l’infrastructure de compilateur, notamment utilisée par Clang. Elle est numérotée 4.0, suite aux changement effectués dans les conventions du projet : auparavant, les versions successives étaient obtenues en ajoutant 0.1 (par exemple, la 3.9 a succédé à la 3.8) ; désormais, elles le seront par incrément de 1.0 (dans six mois sortira LLVM 5.0). En effet, le projet LLVM cherche à sortir une version tous les six mois, peu importe les fonctionnalités qui y ont trouvé leur place (et, pour les utilisateurs de l’API interne de LLVM et Clang, passer d’une version à l’autre pose presque toujours des problèmes). Certes, les versions 2.0 et 3.0 coïncidaient avec des changements majeurs qui cassaient la rétrocompatibilité de manière marquée, mais ce n’était pas tellement voulu (ces versions suivaient les 1.9 et 2.9, respectivement).

En quelques mots, LLVM 4.0 peut utiliser les informations de profilage avec ThinLTO (optimisation lors de l’édition des liens). Son élimination du code mort agressive supprime plus de code. Les coroutines sont arrivée dans le langage intermédiaire (LLVM IR) mais restent expérimentales (plus d’informations données lors du Developpers’ Meeting). LLVM 4.0 peut produire des binaires pour la plateforme Atmel AVR (après un développement sur GitHub, le code est intégré dans LLVM). L’éditeur de liens LLD a vu sa performance largement améliorée. Deux nouvelles conventions de passage d’arguments et de valeur de retour des fonctions ont été implémentées ; elles ont pour objectif de limiter le nombre d’éléments qui sortent des registres.

Côté C++, de nouvelles fonctionnalités du prochain standard (probablement numéroté C++17, mais dénommé C++1z pour le moment) sont intégrées à Clang. L’attribut diagnose_if peut être utilisé pour afficher des messages à l’utilisateur sous condition. De nouvelles optimisations par dévirtualisation des appels de méthode virtuelle ont été implémentées et peuvent être activées séparément. La gestion d’OpenCL a vu bon nombre de défauts corrigés.

Quelques expériences de Dart avec LLVM

Dart est l’un des nombreux langages de programmation développés par Google. Il avait pour objectif premier de remplacer JavaScript dans les navigateurs (certaines versions préliminaires de Chrome avaient une machine virtuelle Dart), puis ses objectifs ont évolué pour être plus consensuels : un remplaçant potentiel de Java, mais avec toujours en tête l’idée de générer du code JavaScript très efficace. Le langage a donc un système de type dynamique et optionnel (les types des variables ne sont connus qu’à l’exécution), mais en limitant ses capacités dynamiques à la portion congrue (pas de fonction eval, pas d’ajout de propriétés à un objet après sa création).

Des ingénieurs de Google ont tenté une expérience : remplacer la machine virtuelle derrière l’implémentation standard de Dart par LLVM et une compilation avant l’exécution, voir si cela permettrait d’améliorer la performance à l’exécution, notamment — LLVM est l’infrastructure de compilation derrière Clang, notamment. La tâche n’est pas aisée a priori : la machine virtuelle de Dart effectue déjà un gros travail de compilation du code à la volée avec des optimisations. LLVM n’est pas non plus prévu pour des langages avec ramasse-miettes, qui perturbent ses routines d’optimisation du code.

De manière générale, une idée reçue est que l’implémentation d’un langage a droit à soit un ramasse-miette précis et efficace, soit un haut niveau d’optimisation du code. En effet, le ramasse-miette parcourt régulièrement les pointeurs sur de la mémoire allouée pour vérifier qu’elle est toujours utile ; après une passe d’optimisation, le code a tellement changé d’apparence qu’il est impossible de s’y retrouver. Une technique pour contourner le problème est de stocker les pointeurs dans une zone particulière, mais cela met en échec bon nombre d’optimisations possibles du code.

Il y a peu, LLVM a intégré une nouvelle fonctionnalité expérimentale, les points d’état, qui sert justement à laisser le compilateur effectuer ses optimisations sans perturber le fonctionnement d’un ramasse-miettes. Le principe est de marquer explicitement les modifications du code au ramasse-miettes, de telle sorte que, en cas de déplacement d’un objet en mémoire, tous les pointeurs concernés peuvent être mis à jour (plus d’informations dans la documentation de LLVM).

L’expérience s’est basée sur le code de Dartino, qui utilisait déjà LLVM pour optimiser du code Dart. Cependant, Dartino n’utilisait pas de ramasse-miettes, cette implémentation se limitait à lamentablement planter en cas de manque de mémoire. L’opération principale a été d’ajouter un ramasse-miettes à l’exécution et de le faire travailler de pair avec les optimisations. (Voir les détails d’implémentation.) Les résultats sont prometteurs : la performance est similaire à Flutter, qui a le même objectif (compiler du code Dart), tout en utilisant une infrastructure plus générique. Très peu de travail spécifique à Dart a été requis côté LLVM — certaines modifications pourraient augmenter la performance drastiquement, cependant. Des améliorations du ramasse-miettes pourraient aussi améliorer fortement la situation, les algorithmes actuellement utilisés pour cette expérience n’étant pas au même niveau que Flutter.

Globalement, l’objectif de cette “petite” expérience est atteint : oui, il est possible de mélanger LLVM, des optimisations très pointues du code, avec un ramasse-miettes, avec une performance tout à fait respectable.

Source : Dart-on-LLVM.

Les systèmes de JIT peuvent améliorer la performance des systèmes de gestion de bases de données

Les systèmes de gestion de bases de données traitent des volumes de données de plus en plus gros, avec des requêtes de plus en plus complexes, notamment pour la prise de décision. Les problèmes de performance du modèle relationnel ne viennent plus seulement de l’accès aux données sur les disques durs ou SSD, mais également du processeur, qui doit effectuer les opérations complexes.

Chaque système de gestion optimise les requêtes SQL avant de les exécuter… et ces techniques ont permis de garder un très bon niveau de performance jusqu’à présent. C’est d’ailleurs la raison pour laquelle SQL a pu s’imposer par rapport à des langages d’interrogation de plus bas niveau : ces requêtes peuvent s’exécuter plus vite que du code écrit spécifiquement pour chaque requête (à moins que chaque développeur passe énormément de temps à optimiser ce code). La requête est analysée, puis l’optimiseur détermine un plan d’exécution de la requête : comment itérer à travers les éléments d’une table, les filtrer, les agréger, mais surtout l’ordre de ces opérations.

Cependant, quand les accès au disque ne sont plus prépondérants dans les temps de réaction, repenser cette partie devient important. La situation actuelle revient à utiliser un interpréteur pour un langage de programmation avec des optimisations simples (comme Python). Les dernières expériences de la communauté LLVM (plus connue pour le compilateur C et C++ Clang) montrent que, en changeant de paradigme, on peut diminuer les temps d’exécution des requêtes les plus complexes d’un facteur cinq à sept (sur des tests synthétiques).

Pour ce faire, les développeurs ont utilisé des techniques similaires à celles utilisées pour les implémentations rapides de Python ou pour les machines virtuelles Java et .Net : la compilation juste à temps. Il s’agit donc de générer du code qui effectue la requête, puis de l’optimiser avec les mêmes techniques que les compilateurs C et C++, notamment : elles sont éprouvées et donnent d’excellents résultats sur n’importe quel type de programme.

L’extension développée pour PostgreSQL exploite une bonne partie du code d’exécution déjà développé. Ainsi, les fonctions de base d’accès à la base de données sont réutilisés (comme pour itérer dans une table), puis appelées depuis le code de la requête : le tout est passé au moteur de compilation juste à temps (JIT). Il inclut automatiquement le code des primitives de PostgreSQL dans celui de la requête, ce qui lui permet d’optimiser fortement le fonctionnement du code complet.

Ces premiers développements sont assez partiels, toutes les opérations ne sont pas accélérées de la sorte, mais les résultats actuels sont prometteurs. Le fonctionnement global est améliorable, puisque l’implémentation est loin d’être peaufinée. Notamment, la compilation peut s’effectuer de manière parallèle. Après ces étapes de nettoyage, cette extension pourra être distribuée au plus grand nombre sous une licence libre.

Source : Speeding up query execution in PostgreSQL using LLVM JIT compiler.

Sortie de LLVM 3.9

La suite de compilateurs LLVM a vu une nouvelle version biannuelle paraître à la mi-août, avec quelques nouveautés intéressantes dans le domaine du calcul de haute performance (OpenCL, OpenMP), notamment, mais aussi pour diminuer les temps de compilation avec les optimisations lors de l’édition des liens (LTO).

Nouveautés pour LLVM

LLVM fournit un nouveau mode pour effectuer une passe LTO sur le code compilé. ThinLTO diminue drastiquement les besoins en mémoire et s’exécute nettement plus vite qu’une passe LTO habituelle, mais sans atteindre les mêmes gains en performance pour les exécutables générés. La manière de penser est radicalement différente : ThinLTO est prévu pour s’exécuter massivement en parallèle, en n’utilisant que des résumés des différents modules à optimiser pour les passes globales.

Au niveau des plateformes, LLVM peut générer des binaires pour les nouveaux processeurs Intel (génération Skylake, ainsi que les coprocesseurs Xeon Phi) en utilisant les instructions AVX-512. Côté ARM, les processeurs Qualcomm Kryo et Broadcom Vulcan sont gérés (les cœurs Cortex M8 et l’architecture ARMv8.2-A le sont de manière expérimentale).

Pour effectuer ses optimisations, la suite de compilation utilise une analyse de la mémoire utilisée par le code, principalement des interactions entre les opérations. Celle-ci est, jusqu’à présent, effectuée par une passe MemoryDependenceAnalysis, dont les inconvénients sont la lenteur et le manque de précision. Elle devrait être remplacée par MemorySSA, dont la complexité algorithmique est linéaire en la taille du code (quadratique pour MemoryDependenceAnalysis). Cette passe travaille sur une représentation virtuelle des opérations en mémoire sous forme SSA.

Nouveautés pour Clang

Le compilateur phare autour du projet LLVM est, sans nul doute, Clang, un compilateur pour les langages de la famille du C (principalement, le C++, mais aussi Objective-C, OpenCL ou encore CUDA).

La version 3.9 implémente maintenant complètement OpenCL 2.0, avec des fonctionnalités comme la conversion entre les espaces d’adressage de l’hôte et du périphérique et de meilleurs messages de diagnostic pour les noyaux OpenCL.

Côté OpenMP, la version 4.5 de la norme est maintenant complètement implémentée, à l’exception des parties sur le déchargement du code sur un accélérateur (l’infrastructure est toujours en chantier) — cela signifie que OpenMP est géré jusqu’à la version 3.1 complètement, puis seulement en partie pour les versions 4.0 et 4.5. Les nouveautés incluent des données membres dans des clauses statiques de fonctions non statiques et l’utilisation de telles variables comme itérateurs de boucle. Le code généré est aussi bien plus stable et rapide.

Comme pour chaque version, les analyses de code ont été améliorées. Pour cette fois, une nouvelle passe vérifie l’utilisation de l’API MPI. Sous Windows, les tests pour les fuites de mémoire, les doubles libérations de mémoire et les utilisations après libération sont activées par défaut.

Sources : notes de version de LLVM et de Clang.