1 points par GN⁺ 3 시간 전 | 1 commentaires | Partager sur WhatsApp
  • crustc est une démo qui convertit l’intégralité de rustc 1.98.0-nightly (c712ea946 2026-06-16) en 46 millions de lignes de code C, et sa compilation avec GCC et make produit un compilateur Rust fonctionnel
  • L’outil sous-jacent, cilly, est un backend de compilateur Rust qui compile Rust vers C, et ce dépôt constitue sa vitrine la plus marquante en montrant un compilateur se compilant lui-même
  • cilly interroge le compilateur C cible et la plateforme sur la disposition des types, leur taille, leur alignement, l’encodage des caractères, les formats d’entiers, etc., au moyen de programmes witness, afin de générer du code C accepté par un compilateur C donné
  • L’objectif principal est de rendre Rust utilisable sur du matériel ancien ou atypique qui n’a pas de support LLVM/GCC mais dispose d’un compilateur C, avec en plus une transparence réseau permettant de communiquer avec un compilateur C distant via TCP
  • Le C généré actuellement cible ARM64 Linux, l’ISA de la station de travail de l’auteur, et l’ensemble de la toolchain cilly n’est pas encore prêt pour un usage public ; des bugs liés à l’optimisation sont encore en cours d’investigation

Démo de conversion de rustc en C

  • crustc est un dépôt qui convertit rustc 1.98.0-nightly (c712ea946 2026-06-16) en 46 millions de lignes de code C
  • Ce code C peut être compilé avec GCC et make, et le résultat est un compilateur Rust fonctionnel
  • L’exemple d’exécution consiste à indiquer le chemin de la bibliothèque LLVM puis à lancer ./rustc/rustc --version, qui affiche la même version rustc 1.98.0-nightly
  • Le compilateur Rust généré peut compiler du code et construire core, alloc et std
  • En plus du code C, le dépôt inclut quelques wrappers LLVM en C++
    • Rust utilise du C++ pour exposer certaines fonctionnalités de LLVM
    • Ces wrappers dépendent de la version de LLVM et sont pénibles à construire séparément ; ils sont donc fournis précompilés

Rôle de cilly

  • crustc est une démo / un teaser du nouvel ensemble d’outils Rust-vers-C qu’est cilly
  • La toolchain complète cilly vise à compiler le code Rust de l’utilisateur vers du C adapté à n’importe quelle cible
  • Ce dépôt a été conçu pour montrer cilly en train de compiler le compilateur lui-même
  • cilly est à la fois une bibliothèque Rust et un backend de compilateur Rust, autrement dit un plugin qui compile Rust vers C
  • L’auteur explique travailler depuis trois ans sur la compilation de Rust vers C et indique que cilly est sa 14e tentative, après des essais publics comme rustc_codegen_clr et plusieurs essais privés

Comment le code est généré pour s’adapter au compilateur C

  • La principale caractéristique de cilly est qu’il s’adapte au compilateur C
  • Il peut générer des programmes witness qui vérifient ce qu’un compilateur et une plateforme donnés prennent en charge
    • Un exemple est _Thread_local int KEYWORD_TLS_SUPPORTED;, qui ne compile que si le compilateur C prend en charge _Thread_local
  • cilly cherche à produire du code C qu’un compilateur C précis est capable d’accepter
  • Il interroge notamment la disposition des types, leur taille, leur alignement, l’encodage des caractères et les formats d’entiers
    • Pour l’encodage des caractères, il vérifie s’il s’agit d’ASCII
    • Pour les formats d’entiers, il vérifie s’il s’agit de two's complement
  • Il utilise des solutions de repli quand c’est possible
  • Il essaie d’éviter les hypothèses hors ANSI C et prévoit aussi des contournements pour des comportements liés aux standards C modernes, comme strict aliasing
  • Dans de rares cas, des hypothèses raisonnables peuvent être nécessaires, comme un aller-retour (void*)(uintptr_t)(ptr)
    • Ces hypothèses sont documentées et, quand c’est possible, accompagnées d’assertions comme CHAR_BIT = 8

Code C spécifique à la cible et contraintes ABI

  • Le code C produit par cilly est spécifique au compilateur
    • Le C généré par cilly pour Arm64 ne peut pas être exécuté tel quel sur riscv32
    • En revanche, il est possible de générer séparément du C cilly pour riscv32
  • Dans ce dépôt, le C généré pour rustc cible ARM64 Linux en raison de l’ISA de la station de travail de l’auteur
  • Le code produit par cilly est globalement compatible ABI avec celui compilé par un rustc classique
  • Sur certaines plateformes, rustc choisit des ABI impossibles à exprimer en C, ce qui empêche une compatibilité complète
  • Sur Arm64, il existe une contrainte liée au pointeur de retour de structure sret
    • Sur la plupart des plateformes, sret est transmis dans le même registre que le premier argument, ce qui permet de placer le pointeur de sortie en premier argument
    • Sur Arm64, le pointeur sret est transmis dans un autre registre
    • L’auteur explique qu’il faudrait qu’un compilateur C natif choisisse return-by-sret pour les petites structures, ce qu’il ne fait pas pour celles de moins de 16 octets

Support des cibles anciennes ou atypiques

  • L’un des objectifs majeurs du projet est de permettre l’usage de Rust sur du matériel ancien ou atypique qui n’a pas de support LLVM/GCC mais dispose de C
  • Quand un projet passe de Rust à C, ou qu’une alternative Rust à un projet C est créée, l’absence de support de telles cibles peut être présentée comme un point faible de Rust
  • cilly encapsule rustc et le compilateur C et convertit à la volée le code Rust en C
  • Du point de vue de l’utilisateur, cela ressemble surtout à une manière de définir quel compilateur C utiliser pour une cible donnée
  • Un exemple de configuration utilise le triple sdcc_z180-unknown-none avec /usr/bin/sdcc et les arguments -mz180, --std-c89, -c

Transparence réseau et compilateur C distant

  • cilly offre une transparence réseau et peut communiquer avec un compilateur C via TCP
  • Si nécessaire, cette approche pourrait être étendue à des moyens de communication plus atypiques, comme l’UART
  • Cette méthode vise à résoudre le paradoxe du bootstrap sur les plateformes qui n’ont pas de compilateur C croisé
  • Il est possible de compiler et lancer un petit serveur C sur l’OS cible, puis d’exécuter rustc sur une plateforme classique comme Linux pendant que cilly communique via le réseau
  • L’auteur indique avoir réussi à compiler un petit programme Rust pour une VM x86 Plan 9 tout en exécutant rustc sur ARM64 Linux
    • La sortie de l’environnement Plan 9 est gnot osversion 2000 cputype 386
    • L’exécution de /tmp/hello_plan9 affiche Hello, world!
    • La sortie de nm montre le symbole rust_begin_unwind

Fonction de génération de makefile

  • cilly peut, en option, insérer des marqueurs dans les fichiers objets et stocker l’IR dans un répertoire de cache
  • Il peut ensuite relire ces marqueurs pour répartir fonctions et globales selon leur emplacement de définition
  • À partir de ces informations, il génère un répertoire contenant un makefile, de sorte qu’on puisse construire du Rust avec seulement un compilateur C et make

Conditions de build et d’exécution

  • Le système utilisé pour construire la démo est un ARM64 Linux basé sur Ubuntu
    • La chaîne du noyau est Linux spark-2773 6.17.0-1021-nvidia ... aarch64
  • Les informations sur le compilateur C utilisé sont GCC 13.3.0 et Ubuntu LLD 18.1.3
  • Pour la compilation, il faut la bonne bibliothèque LLVM ; le plus simple est d’installer cette nightly avec rustup install nightly-2026-06-16
  • La commande de build consiste à définir LLVM_LIB_DIR vers le chemin de libLLVM.so.22.1-rust-1.98.0-nightly puis à lancer make -j20
  • CFLAGS fonctionne, mais certains drapeaux peuvent ralentir la compilation
  • L’optimisation n’est pas recommandée
    • La démo est encore brute et l’optimisation peut provoquer des problèmes
    • À cette échelle, l’optimisation prend aussi beaucoup de temps
  • Sans optimisation, le build se fait en quelques minutes sur la machine de l’auteur
    • Les mesures sont 937.98s user, 123.77s system, 1352% cpu, 1:18.48 total
  • Avec l’optimisation activée, l’essentiel du code passe rapidement, mais certains gros fichiers Rust peuvent bloquer longtemps

Tests et problèmes connus

  • Le test de build consiste à définir LD_LIBRARY_PATH avec la bibliothèque LLVM de la nightly et ./rustc_driver, puis à lancer ./rustc/rustc --version
  • Pour construire des programmes ordinaires, il faut compiler std
    • Sans std, on obtient l’erreur error[E0463]: can't find crate for std
    • Pour construire la bibliothèque standard, il faut consulter BUILDING_STD.md
  • Un bug connu fait que crustc peut planter lorsqu’il est exécuté depuis le répertoire où il a été construit, c’est-à-dire la racine du dépôt, à cause d’un problème étrange de normalisation de chemin
  • Depuis un autre emplacement, il fonctionne normalement

État de publication de cilly

  • cilly n’est pas encore prêt pour un usage public
  • L’auteur dit prévoir de le publier dès que possible
  • Il cite comme raisons du retard son travail, un mémoire universitaire et une blessure à la main
  • L’une des raisons pour lesquelles la toolchain complète cilly n’a pas encore été publiée est qu’il traque encore des bugs liés à l’optimisation

1 commentaires

 
GN⁺ 3 시간 전
Avis sur Lobste.rs
  • Il est intéressant que cela génère des programmes témoins pour vérifier ce que prennent en charge un compilateur et une plateforme donnés.
    Le fait que les chaînes de build C configure traditionnelles fonctionnent globalement de cette manière paraît assez étrange, mais il est compréhensible que ce compilateur, ou transpileur, suive ce schéma.
    « 14e tentative : cilly », quelle persévérance impressionnante ; j’envie une telle ténacité.
  • À titre de comparaison, il existe une version du compilateur Zig entier convertie en C, ce qui fait partie de la procédure normale de build depuis les sources.
    Elle fait 4,6 millions de lignes, soit presque exactement un ordre de grandeur de moins que ce projet. Le code C généré varie selon la cible, mais pas selon le compilateur.
  • Je ne suis pas programmeur système, mais je me demande si ce type de projet pourrait influencer le choix entre Rust et Zig.
    Parmi les avantages de Zig figurent la prise en charge de nombreuses cibles de compilation croisée, une toolchain auto-hébergée et une dépendance optionnelle à LLVM ; la promesse de compiler Rust en C pour des plateformes rares ressemble aussi à un signal envoyé en direction de Zig.
    • Pas simplement en utilisant crustc. Le compilateur converti ne prend en charge que les mêmes cibles de compilation que le compilateur d’origine.
      En revanche, il est possible de convertir d’autres projets Rust en C pour les exécuter et les compiler sur des cibles qui ne prennent en charge que C.
      Cela dit, il ne faut pas en surestimer l’impact. C’est plutôt une solution un peu lourde à un problème très de niche. rustc prend déjà en charge un large éventail de cibles, et cela devrait encore s’étendre via le backend gcc.
      Les outils de compilation croisée de Zig sont chouettes, mais ils relèvent surtout d’un gain de confort, en particulier pour la partie C qui est plus pénible que Zig ou Rust ; l’idée qu’ils prendraient en charge davantage de cibles est relativement moins importante.
      Au final, la prise en charge des cibles par Zig et Rust est déjà assez similaire pour ne pas être un facteur décisif, et il existe des différences bien plus importantes entre les deux langages.
  • C’est cool, mais au final, n’est-on pas limité par les capacités de LLVM ?
    • Je ne vois pas pourquoi ce serait le cas. crustc n’est qu’un exemple de ce que peut faire cilly, une toolchain qui transforme Rust en C.
      L’ensemble de la toolchain cilly compile le code Rust de l’utilisateur en C pour une cible arbitraire. Ce dépôt montre le compilateur en train de se compiler lui-même, parce que c’est probablement la démonstration la plus spectaculaire.
  • mrustc (https://github.com/thepowersgang/mrustc) vaut aussi le coup d’œil. C’est un compilateur Rust écrit en C++ qui transpile vers C avant de passer le résultat à GCC, ce qui lui permet aussi de convertir rustc en C.