La sortie de la nouvelle version majeure du compilateur GCC du projet GNU va être annoncée. Écrit à l’origine par Richard Stallman, le logiciel GCC (GNU Compiler Collection) est le compilateur de référence du monde du logiciel libre. Il accepte des codes sources écrits en C, C++, Objective-C, Fortran, Java, Go et Ada et fonctionne sur une multitude d’architectures.
La suite de la dépêche vous propose en avance de phase une revue de certaines parties des améliorations et nouvelles fonctionnalités. Alors que GCC devenait un peu plus lent à chaque publication d’une nouvelle version, cette mouture marque un tournant en étant plus rapide que les deux versions précédentes, et plus rapide que d’autres compilateurs dans la plupart des situations, tout en générant souvent des binaires plus petits.
Sommaire
Introduction
De nouvelles optimisations, un raffinement de celles existantes, ainsi que de nouvelles fonctionnalités pour les langages et architectures prises en charge, sont à l’ordre du jour. L’amélioration de l’usage par les développeurs continue par l’optimisation des alertes et des outils à sa disposition.
GCC suit donc désormais un système de numérotation de versions majeures en .1 puis .x. Cette 6.1 est la première version stable, tout comme 5.1 le fut.
Nouvelles fonctionnalités à la compilation
En particulier, l’optimisation à l’édition des liens a été améliorée (tant pour les performances du code généré que les performances du compilateur), et on voit pour la première fois arriver la possibilité de décharger le processeur de certains calculs sur la puce graphique avec les puces d’AMD et la bibliothèque OpenMP !
Nouvelles optimisations
-
L’analyse d’aliasing de types gère maintenant mieux les accès à des pointeurs différents. Cela résulte en une amélioration des informations de type de l’ordre de 20 à 30 % pour certains programmes C++ de très haut niveau (donc avec des types complexes). Cette meilleure prédiction de type lors du déréférencement de pointeurs permet d’activer plusieurs optimisations.
- Ces optimisations cassent bien sûr le code si les déréférencements se faisaient avec des constructions de typage ambigu (comme une union en C ou en utilisant
reinterpret_cast
en C++). Ce genre de programme pourrait maintenant avoir besoin de l’option-fno-strict-aliasing
pour être compilé correctement. Les typages ambigus invalides sur des variables globales sont maintenant rapportées par l’alerte (warning) spécifique-Wodr-type-mismatch
.
- Ces optimisations cassent bien sûr le code si les déréférencements se faisaient avec des constructions de typage ambigu (comme une union en C ou en utilisant
Cette même analyse prend maintenant en charge les attributs GNU
weakref
etalias
, ce qui permet d’utiliser une variable et son alias dans une même unité de compilation, ce qui arrive souvent avec les optimisations à l’édition des liens.La propagation de valeurs fait maintenant l’hypothèse que le pointeur
this
des classes C++ est non nul. Cela permet de se passer de nombreuses vérifications de non‐nullité de pointeurs, tout en cassant des bases de code qui se reposaient sur ce comportement indéfini du langage (comme Qt 5, Chromium et Kdevelop !). Il est possible d’utiliser-fno-delete-null-pointer-checks
pour maintenir la compatibilité du code, et d’identifier les portions qui posent problème avec des tests dynamiques utilisant l’option-fsanitize=undefined
.-
Nouvelles fonctionnalités d’optimisation inter‐procédurales. Rappelons que le compilateur décide de l’inlining des fonctions : c’est‐à‐dire que le code va être dupliqué à chaque endroit où elle est appelée, gagnant ainsi un appel de fonction (et les sauvegarde et création de contexte de pile afférents), mais perdant en taille de code (et donc mettant plus de pression sur le cache et les décodeurs). Il décide aussi de cloner les fonctions : par exemple, si une fonction à deux arguments
f(a, b)
est appelée soit parf(1, b)
soit parf(2, b)
, elle peut être clonée en deux fonctions différentesf1(b)
etf2(b)
. Il faut bien sûr que le compilateur soit sûr de la valeur dea
et cette information s’obtient par la propagation des constantes ou une analyse statique.- La passe d’inlining ou de clonage des fonctions repose sur des heuristiques de tailles et de durée d’exécution du code. Ces heuristiques sont maintenant plus précises grâce à une analyse des sauts, réalisée avant la construction du profil du programme.
- Le clonage des fonctions élimine maintenant des paramètres des fonctions de manière plus agressive.
Améliorations des performances du code généré par les optimisations au moment de l’édition des liens (link‐time optimization, LTO).
Les attributs
warning
eterror
sont maintenant préservés à l’édition des liens, on peut donc compiler des programmes avec à la fois l’optimisation à l’édition des liens et l’amélioration de la robustesse des sources face aux attaques de type buffer / stack overflow permise par l’option-D_FORTIFY_SOURCE=2
.La fusion de types définie par le standard Fortran 2008 a été corrigée, permettant l’interopérabilité entre des programmes C et Fortran. Plus de détails sur ce point sont disponibles dans les notes de version sur le site de GCC.
Comme précisé plus haut, plus d’informations sur les types sont passées à l’édition des liens ce qui permet une meilleure précision sur les types lors de la LTO en cas d’aliasing.
La taille des fichiers d’objets LTO a été réduite : par exemple, sur Firefox 46.0, on gagne 11 %.
La parallélisation de la phase d’optimisation à l’édition des liens (qu’on active par l’option
-flto=n
) a été significativement améliorée : les données étudiées en partitionnant le programme (pour découper en blocs optimisables indépendamment et donc en parallèle). Par exemple, toujours sur Firefox 46.0, ces données ont été réduites de 66 % !-
Le greffon de l’éditeur de liens (
gold
oubfd
) a été étendu de sorte à passer des informations sur le type de binaire généré par le back‐end de GCC. Cela permet de configurer le générateur de code pour prendre en charge une édition de liens (avec optimisation, bien sûr) incrémentale ! Il suffit de passer l’option-r
àgcc
et d’utiliser un éditeur de liens à greffons. Pour rappel, la prise en charge des greffons des éditeurs de liens à greffons a été activée dans la version précédente de GCC pour réduire la quantité d’information nécessaire à la phase de LTO qui était stockée dans les bibliothèques statiques (fichiers.a
qui sont en réalité une collection de fichiers.o
). Il n’y a cependant pas de magie, mais un nouveau compromis est possible :- soit on édite les liens avec
ld -r
, qui réalise la LTO lors de l’édition finale des liens et réalise avec les informations transmises individuellement sur chaque objets une optimisation globale (donc lente à l’édition des liens) sur le programme ; - soit on édite les liens avec
gcc -r
, qui produit l’objet binaire avec LTO sur les informations dont il dispose, mais qui ne reviendra pas sur les optimisations déjà réalisées quand il arrivera à l’édition finale des liens. Cette dernière édition des liens sera ainsi plus rapide mais manquera peut‐être des opportunités qu’une LTO globale aurait vues.
- soit on édite les liens avec
Honza Hubička, développeur LibreOffice, propose un article de comparaison de compilations entre différentes versions de GCC, mais aussi avec LLVM, en utilisant les LTO sur GCC 6.
Nouvelles informations sur les erreurs et alertes à la compilation
Peut‐être suite à la pression mise par clang (du projet LLVM) sur les aspects de facilitation du travail du programmeur, il y a déjà plusieurs années de cela, les développeurs de GCC améliorent depuis plusieurs versions les messages d’erreur en vue d’améliorer la productivité des utilisateurs. Voir en particulier cette publication détaillée de Mark Wielaard.
Quelques exemples :
- après l’apparition de messages d’erreur plus explicites et en couleurs dans les versions précédentes, la version 6.1 de GCC indique maintenant l’ensemble des caractères qui posent problème plutôt qu’un seul ;
- les messages d’erreur sont agrémentés de recommandations sur la manière de résoudre le problème (par exemple une faute de frappe sur un nom de variable ou remplacer
.
par->
sur un pointeur) ; - certaines fautes de frappe d’arguments sur la ligne de commande gcc sont maintenant détectées et une suggestion est faite à l’utilisateur (par exemple, tenter d’éditer des liens avec la bibliothèque static-fortran au lieu de static-gfortran) ;
- le développeur est maintenant prévenu lorsqu’il effectue certaines comparaisons tautologiques ou encore lorsque des chaînes de
if … else … if
contiennent plusieurs fois la même condition ; - de nombreuses alertes supplémentaires sont levées. Parmi elles, les indentations trompeuses sont maintenant détectées et le compilateur lance une alerte avec le flag
-Wmisleading-indentation
. Le déni plausible d’Apple sur la faillegoto fail;
deviendra impossible à tenir à l’avenir. Concernant cette nouvelle alerte, voir la publication sur le blog Red Hat.
De manière générale il est fortement recommandé de compiler tout code avec -Wall -Wextra
et de traiter toutes les alertes remontées par le compilateur ! Selon le principe « pas de fenêtre brisée » (no broken window), manquer de soin par petites touches sur un projet incite à prendre de moins en moins soin du code. Il devient vite peu fiable et très coûteux à maintenir et à faire évoluer.
Tests dynamiques de code
Dans la lignée des outils qui instrumentent le code, souvent portés depuis clang, et qui permettent de détecter des problèmes à l’exécution dans la famille des fsanitize=
:
- Une nouvelle option a été ajoutée parmi celles qui permettent de détecter certains problèmes lors de tests dynamiques au développement : on peut maintenant vérifier les bornes des tableaux C de type
array
de manière plus stricte qu’auparavant, via l’option-fsanitize=bounds-strict
. Cela active la vérification déjà existante-fsanitize=bounds
et instrumente le code pour les tableaux de longueur variable.
Nouvelles bibliothèques et fonctionnalités
Implémentation de la spécification d’OpenMP en version 4.5 pour les compilateurs C et C++.
Les compilateurs C/C++ permettent d’utiliser des attributs au sein des énumérations (par exemple, marquer via un attribut déprécié l’une des valeurs d’une énumération).
Le compilateur C++ suppose que le code est en C++ 2014 par défaut (contre C++ 98 auparavant). L’activation du C++ 14 strict se fait avec
-std=c++14
. Sinon on bénéficie des extensions GNU au langage, correspondant à-std=gnu++14
.Le compilateur et la bibliothèque standard C++ (libstdc++) proposent les concepts et quelques extensions du futur standard C++ 2017 de manière expérimentale. En particulier, les rapports techniques (fonctionnalités expérimentales considérées pour inclusion éventuelle dans les futures évolutions du standard) File Systems ou Library Fundamentals v2.
Améliorations de la bibliothèque libgccjit qui permet de compiler du code à la volée.
Prise en charge de la nouvelle bibliothèque standard C Musl sous Linux (architectures AArch64 / ARM / MIPS / PowerPC / i386 / x32 / x86_64). Rappelons que cette nouvelle bibliothèque se veut à la fois performante et très légère, ce qui permet de la compiler en statique dans les exécutables sans qu’ils grossissent démesurément.
Nouveautés sur les architectures gérées
Outre les habituelles dépréciations et/ou suppression d’architectures, pour lesquelles personnes ne s’est manifesté pour les maintenir, on note à côté les améliorations et nouveautés suivantes :
Améliorations pour les architectures ARM : on notera la prise en charge de l’option
-march=native
sous AArch64 (architectures ARM 64 bits) pour que GCC détecte tout seul le processeur sur lequel il est exécuté, afin d’optimiser le code spécifiquement pour lui.Prise en charge du langage intermédiaire HSA (pour les systèmes AMD, généralement avec un processeur central et un processeur graphique Radeon intégré) : en utilisant une extension pour la bibliothèque OpenMP du projet GNU (libgomp), on peut transformer des constructions OpenMP simples en langage HSAIL, pour les exécuter sur les puces graphiques d’AMD dont le pilote prend ce langage en charge.
Prise en charge des instructions vectorielles AVX512 (donc, comme leur nom l’indique, sur 512 bits) pour les encore rares processeurs Intel Xeon de la génération Skylake.
Prise en charge des nouvelles instructions
monitorx
andmwaitx
d’AMD. Elles sont similaires aux instructionsmonitor
etmwait
déjà prises en charge dans le (vieux) jeu d’instructions complémentaires SSE3, en ajoutant de nouvelles fonctionnalités (un compte à rebours) et un nouvel encodage. Ces instructions surveillent une zone mémoire et réveillent le processeur lors d’un accès ou, maintenant, quand le compte à rebours est expiré.Prise en charge des futurs processeurs AMD fondés sur l’architecture Zen. Cette nouvelle architecture amd64 (x86-64) ne sera plus basée sur le type Bulldozer (avec deux cœurs d’exécution entiers avec chacun leur cache de données partageant un cache d’instructions, les unités de calcul sur les flottants et parfois les étages de décodage d’instructions) mais sera toute nouvelle (et partagera vraisemblablement des idées de conception avec les processeurs ARM 64 bits conçus par AMD). Espérons que cela permette à AMD de se relancer dans la course à des processeurs x86 64 bits performants !
Prise en charge initiale des processeurs POWER9 d’IBM. On en sait peu sur ces processeurs à l’heure actuelle, si ce n’est qu’ils reposeront sur la spécification OpenPOWER ISA 3.0, avec de nouvelles instructions vectorielles VSX-3 et un bus de transfert de données entre processeurs central et graphique baptisé NVLink et conçu par NVIDIA. Comme d’habitude avec les processeurs POWER d'IBM, on peut s’attendre à des monstres de puissance de calcul.
Prise en charge du processeur z13 d’IBM, ainsi que des améliorations pour systèmes IBM S/390.
En résumé, GCC 6 avec les LTO compile mieux, plus vite et sort des binaires plus petits que toutes les autres versions antérieures. Il dépasse également le compilateur clang du projet LLVM. Pour ce dernier, seule une ancienne version 3.5 compile plus vite, mais en produisant des binaires jusqu’à 20 % plus gros. Toutes les autres versions de LLVM sont dépassées par cette nouvelle version majeure de GCC. Quant à la dernière version de LLVM et son usage des LTO, GCC 6 lui donne une leçon, puisqu’il est jusqu’à 40 % plus rapide. LLVM travaille déjà dans sa version en développement à essayer de rattraper ce retard.
Aller plus loin
- Liste des changements de GCC 6.1 (585 clics)
- Le récapitulatif sur le blog Red Hat (240 clics)
- La précédente dépêche pour la version 5.1 (157 clics)
# Petite correction
Posté par whity . Évalué à 5.
Je suppose que ce sont des attributs sur les énumérateurs, non ? (après vérification, ça semble bien être ça). Les attributs sur les énumérations étaient déjà supportés en 5 (et peut-être même en 4.9).
Donc c’est plutôt : « Les compilateurs C/C++ permettent d'utiliser des attributs au sein des énumérations (par exemple, marquer via un attribut déprécié l’une des valeurs d’une énumération). ».
Sinon, ce sont de bonnes nouvelles tout ça. Clang garde quand même pour lui l’extensibilité et tous les outils (d’analyse statique ou de refactorisation, notamment) qui se greffent dessus, ou gcc évolue aussi de ce côté-là ?
Mes commentaires sont en wtfpl. Une licence sur les commentaires, sérieux ? o_0
[^] # Re: Petite correction
Posté par bubar🦥 (Mastodon) . Évalué à 3.
Corrigé, merci.
[^] # Re: Petite correction
Posté par Clément V . Évalué à 3.
Autre erreur : le lien "La précédente dépêche pour la version 5.1" pointe vers https://linuxfr.org/moderation/news/… au lieu de https://linuxfr.org/news/… et n'est donc pas accessible pour les simples mortels.
[^] # Re: Petite correction
Posté par Benoît Sibaud (site web personnel) . Évalué à 4.
Corrigé, merci.
# Commentaire supprimé
Posté par Anonyme . Évalué à 10.
Ce commentaire a été supprimé par l’équipe de modération.
[^] # Re: LLVM vs GCC
Posté par liberforce (site web personnel) . Évalué à 6.
Je n'utilise pas LLVM, mais un des liens donné dans la dépêche où le testeur fait des benchmarks de build de libreoffice montre que LLVM utilise beaucoup moins de RAM (6 Go vs 10 à 11 Go pour GCC). Juste pour remettre un peu de pondération.
[^] # Re: LLVM vs GCC
Posté par khivapia . Évalué à 2.
Le développeur de LibreOffice explique qu'il lance 64 (!)jobs de compilation en parallèle avec make, ce qui explique cette consommation de mémoire moyennement élevée.
[^] # Re: LLVM vs GCC
Posté par dinomasque . Évalué à 4.
GCC date peut être de 1987, mais le compilateur des origines a été entièrement remplacé par EGCS qui date de la fin des années 90.
LLVM aurait remplacé le "coeur" EGCS si la FSF n'avait pas eu une politique de saborder la modularité de GCC pour empêcher qu'on puisse y ajouter un frontend non libre.
Résultat, LLVM poussé par Apple s'est envolé et le "vieux" GCC tente de résister.
BeOS le faisait il y a 20 ans !
[^] # Re: LLVM vs GCC
Posté par barmic . Évalué à 10.
Il fait un peu plus que résister :)
Il y a de la place pour différents compilateurs, il n'y a pas de mal à avoir du choix.
Personnellement les dernières fois où j'ai fais du C++, je compilais avec LLVM lors des phases de développements et avec GCC pour les releases. Outre le fait que LLVM avait à l'époque de bien meilleurs messages d'erreur que gcc, ça permet de varier le compilateur et donc d'éviter de reposer sur une fonctionnalité (trop) spécifique.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: LLVM vs GCC
Posté par Michaël (site web personnel) . Évalué à 6.
C'est une approche intéressante, par curiosité t'est-il arrivé d'avoir des bugs de production avec le binaire GCC que tu ne pouvais pas reproduire avec le binaire LLVM?
[^] # Re: LLVM vs GCC
Posté par barmic . Évalué à 5.
Non, mais j'étais loin de pousser le langage dans ses retranchements et au pire je pouvais toujours pour un cas donné travailler avec gcc.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: LLVM vs GCC
Posté par Albert_ . Évalué à 4.
LLVM est tres bien mais gcc supporte plus de langage et donc ce dernier est pas vraiment pres a mourir.
[^] # Re: LLVM vs GCC
Posté par barmic . Évalué à 5.
Il supporte aussi plus d'architectures et le code généré est (réputé) meilleur, mais je vois pas en quoi l'un turerait l'autre.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
# Bien joué !
Posté par notimeu . Évalué à 3.
C'est cool que GCC avance bien. Même si c'est vrai que rentrer dans le code source et de rajouter un langage ou un architecture c'est très compliqué, c'est bien de voir qu'ils avancent à grands pas. En plus le support de la lib Musl est un très bon point pour ceux qui travaillent dans des environnements embarqués.
# libgccjit incompréhensible
Posté par Anthony Jaguenaud . Évalué à 8. Dernière modification le 26 avril 2016 à 12:45.
En lisant libgccjit, je me suis dis : « Super on va pouvoir intégrer du code directement en live. » Faille de sécurité garanti, mais plutôt rigolo à mettre en œuvre.
Donc j’ai chercher libgccjit tutorial ce qui m’amène à ce hello world. Le code est le suivant :
Heu, je m’attendais plus à un truc contenant également l’interpréteur de source, un truc dans ce genre :
On donne une chaine de caractère de code C, et on récupère du code assemblé pouvant être exécuté directement.
J’ai surement raté un truc.
[^] # Re: libgccjit incompréhensible
Posté par jcelerier . Évalué à 3.
De ce que j'ai cru comprendre, GCCJIT permet d'accéder au back-end de GCC et permet donc d'implémenter un interpréteur pour ton propre langage, pas de réutiliser les front-end existant (C, C++, ADA, Go)… C'est plus proche de LLVM que de Clang si tu veux.
[^] # Re: libgccjit incompréhensible
Posté par Anthony Jaguenaud . Évalué à 2.
Ok, je comprends mieux.
J’aurais bien aimé quand même pouvoir interpréter du code C brut.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.