2 points par GN⁺ 2023-12-19 | 1 commentaires | Partager sur WhatsApp
  • gccrs est un projet lancé en 2014 pour implémenter un compilateur Rust au sein de GCC ; il est encore loin d’être terminé, mais progresse vers la compilation de la bibliothèque standard et une inclusion dans GCC 14
  • Le projet a choisi de viser Rust 1.49 plutôt que de suivre en permanence les dernières évolutions de Rust, mais a finalement dû implémenter des fonctionnalités comme const generics en raison de dépendances internes de la bibliothèque standard
  • La compilation de core et alloc reste bloquée par la résolution des noms de macros, les macros décoratrices, des intrinsics de compilateur LLVM absents de GCC, et l’absence de borrow checker
  • rustc_codegen_gcc est une approche plus mature, qui réutilise une partie de rustc et emploie GCC comme backend de génération de code ; en octobre 2023, il pouvait compiler Rust for Linux sans patch supplémentaire
  • gccrs a des motivations claires — plugins de sécurité GCC, analyse statique, architectures non prises en charge par LLVM, Rust for Linux — mais reste encore moins pratique que rustc_codegen_gcc

Objectifs de gccrs et état actuel

  • gccrs est un projet visant à implémenter un compilateur Rust dans le GNU Compiler Collection (GCC)
  • Il a commencé en 2014 et, d’après les rapports du projet après une précédente couverture de LWN, il a progressé
  • En 2022, l’objectif était une inclusion dans GCC 13, mais cela n’a pas été atteint ; selon le rapport mensuel de novembre 2023, l’objectif est désormais GCC 14
    • GCC 14 est présenté comme une version susceptible de sortir au milieu de 2024
  • À l’EuroRust 2023, en octobre 2023, Arthur Cohen a présenté “The road to compiling the standard library with gccrs”, expliquant le travail de compilation de la bibliothèque standard Rust et les raisons pour lesquelles elle ne compile pas encore

Pourquoi viser Rust 1.49

  • gccrs choisit de viser Rust 1.49 plutôt que de suivre en continu les versions les plus récentes de Rust
  • Rust 1.49 est sorti fin 2020 et constitue la dernière version avant la disponibilité générale de const generics dans Rust 1.50
  • Le projet voulait éviter const generics, mais cette fonctionnalité était déjà utilisée en interne dans la bibliothèque standard de Rust 1.49, ce qui l’a rendue impossible à ignorer
  • const generics a depuis été entièrement implémenté et ne constitue plus un obstacle
  • gccrs ne cherche pas à créer un sur-ensemble de Rust ni un langage séparé de type « GNU Rust »
    • L’objectif est de reproduire jusqu’aux sorties, bugs et comportements particuliers de rustc
    • Pour cela, le projet utilise à la fois la suite de tests de Rust et celle de GCC

Ce qui bloque la compilation de la bibliothèque standard

  • La bibliothèque standard Rust est composée de plusieurs crates
  • gccrs travaille à la prise en charge de la compilation des deux crates les plus importantes, core et alloc
    • core implémente les fonctionnalités de base de la bibliothèque standard, comme les types primitifs et les macros
    • alloc gère l’allocation mémoire sur le tas et plusieurs types de conteneurs
  • À l’heure actuelle, gccrs ne peut pas compiler ces crates en raison des manques suivants
    • la résolution des noms de macros ne fonctionne pas correctement
    • le support des macros décoratrices n’est pas complet
    • l’absence de borrow checker empêche une vérification correcte de la sûreté du code
    • certains intrinsics de compilateur LLVM absents de GCC doivent être implémentés
  • L’absence de borrow checker n’empêche pas la compilation en elle-même, mais ne permet pas d’effectuer correctement les vérifications de sûreté du code Rust

Macros procédurales et intégration à GCC

  • En septembre 2023, lors du GNU Tools Cauldron, Pierre-Emmanuel Patry a présenté l’avancement vers une inclusion dans GCC 14, en se concentrant sur le travail autour des macros
  • La manière d’implémenter les macros procédurales (procedural macro) nécessite des modifications du système de build de GCC
  • Les macros procédurales sont des macros fonctionnelles qui produisent des flux de jetons, et non un simple texte source comme les macros C ou C++
  • En Rust, elles sont implémentées via la crate intégrée proc_macro
  • Elles sont difficiles à implémenter, mais permettent des fonctionnalités puissantes
    • les décorateurs #[attribute]
    • les décorateurs #[derive()]
    • la création de langages spécifiques à un domaine basée sur l’évaluation au moment de la compilation
  • Au moment de cette présentation au GNU Cauldron, gccrs comptait plus de 800 commits devant encore être intégrés upstream dans GCC

Pourquoi exploiter l’écosystème GCC pour Rust

  • L’une des principales motivations de gccrs est de pouvoir appliquer aux programmes Rust les plugins de sécurité de GCC
  • GCC dispose de nombreux plugins pour le débogage, l’analyse statique et le hardening, qui opèrent sur la représentation intermédiaire de GCC
  • gccrs veut permettre aux développeurs Rust de réutiliser les workflows existants basés sur les plugins GCC
  • Cohen a donné comme exemple le fait que les programmeurs C oublient depuis longtemps de fermer des descripteurs de fichiers, ce qui a conduit à de nombreux plugins capables de détecter ce type d’erreur
  • L’objectif est de pouvoir exploiter ces plugins GCC existants et des analyseurs statiques pour détecter des bugs dans du code Rust unsafe

Des usages existent déjà dans certains domaines

  • Selon Cohen, la communauté homebrew de la Sega Dreamcast utilise gccrs pour créer de nouveaux jeux pour la console Dreamcast
  • Cet intérêt vient du fait que le backend LLVM de rustc ne prend pas en charge l’architecture Hitachi SH-4 de la console, alors que GCC la prend en charge
  • gccrs reste inachevé, mais il est utile dans ce type de cas d’usage embarqué
  • L’analyse statique de code Rust unsafe au moyen de plugins GCC est déjà possible
  • Le travail sur gccrs a aussi mis en lumière des fonctionnalités du langage dont la spécification n’était pas suffisamment définie, comme Deref et la résolution des noms de macros, et le projet a pu contribuer à l’effort de spécification de Rust
    • Rust ne dispose pas encore de spécification officielle, mais le travail avance conformément à la RFC 3355

Fonctionnalités clés encore en développement

  • gccrs ne dispose toujours pas d’un grand nombre de fonctionnalités essentielles d’un compilateur Rust
  • Parmi les principales fonctionnalités absentes ou encore en développement figurent
    • async/await
    • des intrinsics LLVM absents de GCC
    • la macro format_args!() utilisée par des macros d’affichage comme println!()
    • le borrow checker, qui fait respecter les règles de référence de Rust
  • La solution la plus prometteuse pour le borrow checker est le projet distinct Polonius
  • Cohen a déclaré qu’il était probable que gccrs intègre Polonius dans quelques mois
  • Jakub Dupak a fait progresser ce travail ces derniers mois
  • Polonius est actuellement une bibliothèque qui implémente un borrow checker sémantiquement équivalent à celui de rustc
    • elle utilise un algorithme différent pour calculer la durée de vie des références
    • à long terme, elle vise à corriger certaines limites et corner cases du borrow checker actuel de rustc
    • une fois suffisamment mûr, Polonius pourrait aussi être adopté par rustc à l’avenir

Pourquoi format_args!() est nécessaire

  • Selon le rapport mensuel de gccrs de novembre 2023, le travail sur la macro format_args!() a commencé
  • format_args!() est une macro auxiliaire qui construit les arguments à transmettre aux macros de formatage de chaînes
  • Cette fonctionnalité est liée aux traits Display et Debug
  • Elle est nécessaire pour préparer les arguments passés à des macros comme format!() et println!()
  • Sans format_args!(), un programme Rust ne peut pas produire de sortie formatée
  • Elle est donc nécessaire avant même que gccrs puisse compiler un programme « Hello, World »
  • Une publication de blog de Mara Bos consacrée à format_args!() est également mentionnée

Différences avec rustc_codegen_gcc

  • rustc_codegen_gcc est un autre projet Rust basé sur GCC, distinct de gccrs
  • Il est plus mature que gccrs, mais son périmètre est plus limité
  • Il ne cherche pas à réimplémenter entièrement un compilateur Rust à partir de zéro
  • Il utilise la bibliothèque libgccjit pour se connecter à l’API du backend LLVM de rustc
  • Une grande partie de la compilation est effectuée par rustc, puis GCC est utilisé dans les phases ultérieures
  • Malgré le mot JIT dans le nom libgccjit, rustc_codegen_gcc vise la compilation ahead-of-time
  • Son objectif principal est de permettre la génération de code Rust sur des plateformes non prises en charge par LLVM
  • En octobre 2023, rustc_codegen_gcc pouvait compiler Rust for Linux sans patch supplémentaire
  • Au cours de l’année écoulée, il a ajouté le support du SIMD et de l’optimisation à l’édition de liens
    • ces deux fonctionnalités étaient auparavant identifiées comme causes d’échec des tests
  • Lors de sa présentation à EuroRust, Cohen a recommandé à plusieurs reprises d’utiliser rustc_codegen_gcc pour l’instant à la place de gccrs
  • rustc_codegen_gcc est déjà intégré upstream dans le dépôt du langage Rust

Rust for Linux et l’écart de version

  • Rust for Linux est une initiative visant à ajouter la prise en charge de Rust dans le noyau Linux
  • Cohen a cité le noyau Linux comme motivation centrale du projet gccrs
    • de nombreux acteurs du noyau préfèrent que celui-ci puisse être compilé uniquement avec la chaîne d’outils GNU
  • Actuellement, le projet Rust for Linux documente rustc ou rustc_codegen_gcc comme moyens de compiler le code Rust du noyau
  • Le noyau documente aussi des versions minimales prises en charge pour plusieurs outils de build
    • rustc y est traité non pas comme une version minimale, mais comme une version devant correspondre exactement
    • la version actuellement prise en charge de rustc est 1.73.0, publiée en octobre 2023
  • Il existe donc un écart important entre Rust 1.49, visé par gccrs, et Rust 1.73.0, requis par Rust for Linux
  • La prise en charge de Rust for Linux est bien un objectif explicite de gccrs, mais cet écart de version montre qu’il reste encore un long chemin à parcourir

Évaluation d’ensemble

  • Le dépôt gccrs compte plus de 3 000 commits depuis le 1er janvier 2023
  • Le projet a connu des progrès significatifs au cours de l’année écoulée
  • Mais compte tenu de l’ampleur du travail nécessaire pour implémenter un compilateur Rust complet à partir de zéro, il n’est pas encore utilisable pour presque tous les usages pratiques
  • rustc_codegen_gcc a été fusionné dans le dépôt upstream de Rust et est déjà utilisé concrètement dans Rust for Linux
  • On n’en est pas encore au stade où le langage Rust dispose de plusieurs implémentations de compilateur indépendantes réellement exploitables, mais on s’en rapproche

1 commentaires

 
GN⁺ 2023-12-19
Avis sur Hacker News
  • À s’en tenir aux affirmations de l’article, la motivation de gccrs paraît un peu faible
    Les plugins de sécurité GCC ou la préférence de la communauté du noyau Linux pour la chaîne d’outils GNU expliquent pourquoi on voudrait utiliser GCC comme backend, mais pas pourquoi il faudrait un frontend redondant
    J’aimerais que Rust évite de répéter l’erreur de C++, où le développement multiplateforme devient difficile à cause des différences d’options entre compilateurs, des niveaux de prise en charge du langage et des bugs propres à chaque plateforme
    Il serait donc utile d’expliquer pourquoi gccrs est une meilleure approche que rustc_codegen_gcc, alors que ce dernier semble pouvoir atteindre le même objectif avec beaucoup moins d’efforts et de risques

    • Avoir une implémentation supplémentaire de Rust peut servir d’audit pour valider la spécification de Rust et réduire les comportements indéfinis
      Quand on tombe sur un bug de compilateur avec MSVC, on peut le signaler puis passer à GCC pour continuer à travailler ; avec Rust, cette option n’existe pas aujourd’hui
    • Rust apprend déjà des erreurs de C++
      Comme cité, le fait de veiller à ne pas devenir un sur-ensemble de Rust en est un exemple
      Les problèmes de C/C++ sont nés du fait que les fournisseurs de compilateurs se faisaient concurrence en affirmant être « meilleurs » les uns que les autres, et plusieurs frontends ont généralement l’avantage de révéler beaucoup de bugs et de mauvaises implémentations
    • Je ne comprends pas pourquoi cette question revient sans cesse
      Rust n’a rien de sacré qui interdirait une réécriture du frontend, et amorcer Rust sur de nouvelles architectures reste pénible
      Sur les architectures non prises en charge par LLVM, il n’existe pas non plus de compilateur Rust fonctionnel
      codegen_rust_gcc a les mêmes problèmes de bootstrap que le compilateur Rust existant, et il faut ajouter la prise en charge des architectures à plusieurs parties de Rust, ce que les mainteneurs de Rust ont été réticents à faire
      Donc si, dans un avenir proche, on obtient un compilateur Rust utilisable permettant de reconstruire sans trop de douleur, même sur des architectures comme Alpha, des bibliothèques réécrites en Rust, ce sera très bienvenu
    • Avec ce raisonnement, on pourrait demander pourquoi la communauté LLVM n’a pas continué à utiliser DragonEgg, et a créé Clang, Clang++, libc++, etc.
      Il existait déjà GCC, G++, libstdc++, ainsi que le frontend C++ d’EDG
      GCC, Clang, MSVC et d’autres compilateurs se complètent, répondent à des objectifs et des marchés différents, et contribuent à rendre le langage robuste par rapport à une spécification plutôt qu’aux propriétés accidentelles d’une implémentation unique
      Le GNU Toolchain Project, le projet LLVM et le projet Rust ont tous connu des difficultés ; mieux vaut donc ne pas dépendre d’un point de défaillance unique, et la redondance et l’antifragilité sont nos alliées
    • Dire que Rust devrait apprendre des erreurs de C++ et de C revient-il à dire qu’il devrait apprendre des erreurs des langages parmi les plus anciens, les plus influents et les plus largement déployés de l’histoire ?
      Il est déroutant de voir quelqu’un considérer les standards de langage d’un mauvais œil et préférer dire « ce code fonctionne avec le binaire/la source rustc dont le hash SHA256 est e49d560cd008344edf745b8052ef714b07595808898c835f17f962a10012f964 » plutôt que « ce code est en C99/C++11 »
  • Rust a besoin d’un standard de langage
    https://blog.m-ou.se/rust-standard/
    https://rust-lang.github.io/rfcs/3355-rust-spec.html
    https://github.com/rust-lang/rfcs/pull/3355
    Beaucoup d’organisations et d’industries n’adopteront pas Rust tant qu’un standard n’existera pas
    C, C++, C#, et même JavaScript (ECMAScript) ont un standard de langage ; il n’y a pas de raison que Rust n’en ait pas
    C : https://www.iso.org/standard/74528.html
    C++ : https://isocpp.org/std/the-standard
    C# : https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
    JavaScript / ECMAScript : https://ecma-international.org/publications-and-standards/st...

    • Cette RFC a déjà été approuvée, et le travail a effectivement commencé
      Les progrès sont d’une lenteur décevante, mais le projet est vivant, et il pourrait accélérer l’an prochain
      https://blog.rust-lang.org/inside-rust/2023/11/15/spec-visio...
    • Le billet de blog de Mara, c’est-à-dire le premier lien, penche plutôt vers l’idée que Rust n’a fondamentalement pas besoin de standard, puisqu’il dispose déjà de moyens pour ajouter des fonctionnalités et maintenir la compatibilité
    • Grâce à la spécification Ferrocene, Rust peut être utilisé même dans ces industries
    • Go dispose d’une spécification vraiment bien faite et de plusieurs implémentations
      https://go.dev/ref/spec
    • En réponse à l’idée que beaucoup d’organisations et d’industries ne l’adopteront pas sans standard : Rust se porte très bien sans elles
      Je ne vois pas pourquoi il faudrait changer une méthode qui fonctionne bien
  • Je suis surpris qu’il y ait autant de réactions négatives à GCC-RS
    À mon avis, un langage qui n’a pas plusieurs implémentations est un langage assez pauvre

    • Autrefois, c’était l’opinion communément admise, surtout à cause de C/C++, mais aujourd’hui c’est beaucoup plus controversé
      Le consensus de la communauté Rust va plutôt dans le sens que l’approche actuelle — un compilateur qui fait office de standard par définition, beaucoup de documentation, une spécification minimale pour les secteurs critiques en matière de sûreté, et des spécifications pour certains sous-domaines de modules — apporte la plupart des avantages de plusieurs implémentations tout en évitant leurs inconvénients
    • À long terme, le problème est la stabilité syntaxique et la prévention de l’inflation fonctionnelle due aux extensions/attributs
      C en souffre réellement, et C++ est déjà presque irrécupérable à cause de sa complexité absurde et monstrueuse
      Sans cela, il sera difficile de voir émerger des implémentations alternatives réalistes
    • Personnellement, je comprends l’intérêt de plusieurs implémentations, mais le problème est de construire ça sur GCC
      La toolchain GNU est un foutoir, et je ne vois vraiment pas comment on développe dessus
      Ce n’est pas une question idéologique : littéralement, je ne comprends pas comment mettre en place l’environnement de développement de GCC lui-même
      J’ai eu la malchance de le bootstrapper quelques fois, et c’est la chose qui s’est comportée le plus mal parmi tous les logiciels que j’ai vus
  • J’aime bien le passage où la communauté homebrew Sega Dreamcast pourrait créer de nouveaux jeux avec gccrs, et où l’on pourrait faire de l’analyse statique de code Rust unsafe avec des plugins GCC
    L’architecture Hitachi SH-4 de la Dreamcast n’est pas prise en charge par le backend LLVM de rustc, mais GCC la prend en charge ; même dans son état inachevé, gccrs peut donc être utile pour ce genre d’usage embarqué

    • C’est un peu trompeur
      Pour cela, il n’y a pas besoin d’un frontend GCC, seulement du backend GCC
  • On pourrait maintenant voir du support Rust aussi sur des architectures comme Alpha, SuperH, VAX, que LLVM ne prend pas en charge mais que GCC prend en charge

    • C’est aussi le cas de mips64, dont rustc a récemment abandonné le support après avoir échoué à obtenir des financements/ressources de Loongson
      https://github.com/rust-lang/compiler-team/issues/648
      Le plus gros problème de la mentalité LLVM est d’utiliser le support des architectures comme un moyen d’obtenir du soutien des fabricants de matériel, c’est-à-dire des postes de développeurs rémunérés
      GNU peut avoir des critères d’intégration péniblement élevés, mais une fois qu’un support est accepté, il est maintenu sur le long terme
      C’est un peu la différence entre acheter et louer : ajouter un support dans GCC demande beaucoup plus de temps de développement, mais une fois qu’il y est, il reste
    • Il devrait aussi y avoir des options de configuration supplémentaires pour des architectures déjà prises en charge
      Par exemple, j’ai récemment découvert que, sur RISC-V, GCC prend en charge la cible RV32E, contrairement à LLVM
    • J’ai hâte de voir du code machine PDP-11 généré depuis Rust
      La dernière fois que j’ai vérifié, la compilation C autonome fonctionnait encore dans GCC
  • D’après mon expérience, le fait que gccrs essaie de reproduire la sortie de rustc, y compris ses bugs et ses particularités, plutôt que de créer un langage spécial appelé « GNU Rust », est une grave erreur
    Rust n’a pas de spécification ; il existe de la documentation de référence, mais elle n’est explicitement pas normative
    Un langage qui n’est pas documenté autrement que par une implémentation de référence unique a une faiblesse à long terme
    L’objectif de garantir que le code existant fonctionne avec les deux implémentations est raisonnable, mais promettre une compatibilité avec les bugs fossilise de mauvaises décisions et des bugs
    Microsoft consacre beaucoup de personnel à corriger des bugs de sécurité et de fiabilité tout en continuant à faire fonctionner d’anciens programmes ; Rust n’a pas besoin de porter ce fardeau si tôt dans sa vie
    Si l’on veut faire évoluer le langage, il faut accepter l’assurance qualité et le contrôle qualité
    La qualité ne peut pas être injectée après coup par des tests ; il faut la construire par l’architecture et la conception, et par des processus comme les revues de conception et de code, de façon à ce que le système fonctionne correctement et, s’il échoue, échoue dans la bonne direction
    Des standards forts comme Common Lisp, C++ ou FORTRAN ont adopté cette conviction ; des langages plus faibles, proches d’un standard de fait, comme Python, peuvent aussi devenir populaires, mais leur difficulté à évoluer se voit dans la longue transition de Python 2 à 3 et dans le faible nombre d’implémentations

    • Si un gros bug est trouvé, il suffirait sans doute de le signaler en upstream et de modifier les deux implémentations
  • J’arrive tard dans la deuxième moitié de ce fil, mais ce n’est pas forcément une bonne chose
    Les distributions n’arrivent déjà pas à suivre les langages qui sortent une nouvelle version tous les quelques mois, ce qui rend difficile l’utilisation de rustc ou de Go via les paquets de distribution
    Aujourd’hui, étonnamment, il existe des systèmes où GCC n’est plus nécessaire et a été supprimé, et où l’on ne maintient que les versions upstream de Go et Rust pour mettre à jour les logiciels existants
    Quand j’ai mis à jour Go il y a quelques mois à cause d’une CVE, c’était cauchemardesque de voir que des applications basées sur Go avaient stocké quatre environnements Go différents chacune de leur côté

  • Linux peut déjà être compilé avec Clang si on le souhaite, et utiliser ainsi une toolchain entièrement basée sur LLVM
    Cet effort dupliqué pour développer et maintenir cela au nom de la « pureté » GNU ne semble pas en valoir la peine

    • Ce n’est pas une question de pureté, mais de choix
      La communauté ClangBuiltLinux soutenait que Linux ne devait pas dépendre d’un compilateur unique ; avec l’arrivée de Rust, beaucoup de ces mêmes personnes ont soudain commencé à considérer qu’un compilateur unique pouvait très bien convenir
    • Il semble y avoir un malentendu ici
      Il ne s’agit pas d’enfermer le noyau dans du GNU uniquement, mais simplement de permettre de choisir une toolchain GNU pure