2 points par GN⁺ 2026-02-08 | 1 commentaires | Partager sur WhatsApp
  • SectorC, écrit en assembleur x86-16, est un compilateur C ultracompact qui tient dans le secteur d’amorçage (512 octets) d’une machine x86, et il prend en charge un sous-ensemble du langage C suffisant pour écrire de vrais programmes exécutables
  • Il inclut notamment les variables globales, fonctions, instructions if/while, opérateurs, déréférencement de pointeurs, commentaires et assembleur inline, ce qui permet d’exécuter des programmes complets avec une structure minimale
  • Pour simplifier le tokenizer, il introduit un langage Barely C fondé sur une tokenisation par espaces et un hachage via atoi(), tout en conservant la syntaxe C existante pour obtenir une réduction de taille extrême
  • Au cours de l’optimisation, diverses techniques de compression au niveau assembleur ont été appliquées, comme la suppression de sauts, la fusion d’appels, l’usage d’offsets sur 8 bits et de stosw/lodsw, ce qui a permis de passer de 468 octets à 303 octets
  • Au final, le projet parvient à implémenter dans 512 octets un compilateur C complet incluant tokenizer, parseur, générateur de code et runtime, illustrant un cas extrême de minimisation logicielle

Présentation de SectorC

  • SectorC est un compilateur C écrit en assembleur x86-16 qui tient entièrement dans un secteur d’amorçage de 512 octets
    • Le dépôt GitHub est xorvoid/sectorc
    • Le langage pris en charge est un sous-ensemble de C suffisant pour écrire de vrais programmes
  • Les fonctionnalités prises en charge comprennent les variables globales, fonctions, structures de contrôle (if/while), divers opérateurs, déréférencement de pointeurs, assembleur inline et commentaires
  • Un programme d’exemple montre du code qui dessine une animation sinusoïdale en mode VGA

Contexte de conception et approche

  • Un tokenizer C classique étant bien trop volumineux pour tenir dans 512 octets, il a fallu simplifier la structure même du langage
  • Big Insight #1 : à la manière de Forth, le projet adopte une structure de tokens séparés par des espaces et conçoit une variante appelée « Barely C »
    • Exemple : int(main)(){while(!done){ est traité comme un unique « méga-token »
    • Cela reste reconnu comme du code C valide par les compilateurs C existants
  • Big Insight #2 : la fonction atoi() est utilisée comme fonction de hachage pour convertir les tokens en nombres
    • Les littéraux entiers, mots-clés et identifiants sont tous traités à partir du résultat de atoi()
    • Les identifiants sont accessibles comme index dans un tableau de 64K

Implémentation de Barely C

  • La première implémentation faisait 468 octets et reposait sur un parseur récursif descendant utilisant des tokens basés sur atoi
    • Sans table des symboles, elle accédait directement au segment 64K via la valeur de hachage
    • La génération de code suivait un style OTCC en utilisant le registre ax comme emplacement de résultat
  • Ensuite, une expérimentation avec du byte-threaded code a tenté une structure à la Forth, mais elle s’est révélée au contraire inefficace dans la limite des 512 octets et a donc été abandonnée

Techniques de minimisation du code

  • Le projet est revenu à une structure linéaire et a réduit la taille de 468 octets à 303 octets
    • Utilisation de techniques comme le fall-through (suppression de sauts), le tail-call, la fusion d’appels, l’usage de stosw/lodsw et le maintien de déplacements de saut sur 8 bits
  • Cela a libéré 207 octets d’espace pour ajouter des fonctionnalités

Extension vers des fonctionnalités C complètes

  • Avec 200 octets supplémentaires, le projet a atteint une prise en charge complète de la syntaxe C visée
    • Blocs if/while imbriqués, divers opérateurs binaires (+, -, *, &, |, ^, <<, >>, ==, !=, <, >, <=, >=)
    • Prise en charge des définitions de fonctions et appels récursifs, de l’assembleur inline (asm), ainsi que des commentaires sur une ligne et multilignes
    • Une table des opérateurs (binary_oper_tbl) permet de définir chaque opérateur en 4 octets, soit 14 opérateurs pour 56 octets

Structure grammaticale

  • La grammaire complète se compose notamment de program, var_decl, func_decl, statement, expr, etc.
  • Les commentaires // et /* */ sont tous deux pris en charge
  • Le texte décrivant la grammaire occupe à lui seul 704 octets, soit plus que l’implémentation elle-même

Assembleur inline et runtime

  • L’instruction asm permet d’insérer directement du code machine x86-16
    • C’est une capacité indispensable pour les entrées/sorties
  • Le runtime (rt/) se compose de deux fichiers écrits en C
    • rt/lib.c : routines de bibliothèque fondées sur l’assembleur inline
    • rt/_start.c : point d’entrée du programme _start()

Programmes d’exemple

  • examples/hello.c : affiche du texte directement dans la mémoire 0xB8000
  • examples/sinwave.c : affiche une animation sinusoïdale en mode VGA 0x13
  • examples/twinkle.c : joue « Twinkle Twinkle Little Star » sur le haut-parleur PC (avec avertissement sonore)

Conclusion

  • SectorC est un compilateur C minuscule qui concrétise un objectif apparemment impossible, et montre un cas extrême de minimisation logicielle et de conception créative de langage
  • La fin de l’article se conclut sur des choix humoristiques autour de « ce que l’on a appris », soulignant avec ironie l’intérêt de s’attaquer à l’impossible et la valeur de la simplification logicielle

1 commentaires

 
GN⁺ 2026-02-08
Commentaires sur Hacker News
  • Si une implémentation comme celle-ci avait existé dans les années 1980, le standard C aurait probablement fini par inclure une règle disant qu’une collision de hachage entre des tokens distincts produisant la même valeur sur 16 bits entraîne un comportement indéfini (UB)
    Les compilateurs d’optimisation des années 2000 auraient peut-être même optimisé ces tokens en simple no-op 😉
    • Réponse en forme de blague : « Tu n’avais qu’à activer l’option -wTokenHashCollision ! L’UB est de ta faute, à cause de ton ignorance. La spécification est parfaitement claire ! »
    • C’est tellement réaliste que ça m’a fait éclater de rire. LMAO
  • Ça ressemble au compilateur C pour boot sector X86-16 que j’ai fait récemment
    Créer des jeux en boot sector procure vraiment une expérience magique, pleine de nostalgie. Ça rappelle l’époque où programmer était vraiment amusant
    C’est dommage qu’à l’ère actuelle de l’IA, ce genre de projets soit autant sous-estimé
    Lien vers mon projet
    • Hum, ce dont parle l’article n’est pas du C complet, mais plutôt un compilateur d’un langage proche du C qui tient dans 512 octets
      Ton projet semble avoir une option permettant de générer du code pour boot sector.
      Les deux sont intéressants, mais leur point commun se limite à peu près à « boot sector », « C » et « compiler »
    • Je comprends le sentiment derrière « l’époque où programmer était vraiment amusant », mais aujourd’hui beaucoup plus de gens peuvent faire la même chose
      Du coup, j’ai l’impression de ne plus être spécial
  • Ce genre de projet rappelle à quel point le développement moderne s’est éloigné du matériel
    On empile les abstractions au point qu’il faut 200 Mo de node_modules juste pour faire tourner un « Hello World »
    Et pendant ce temps, quelqu’un met un compilateur C dans 512 octets
    Ça ne veut pas dire que tout le monde devrait écrire du code de boot sector, mais lire ce genre de projet est vraiment une expérience qui remet à sa place. La valeur pédagogique est aussi énorme
  • Il se pourrait bien que je sois justement l’auteur de ce projet. C’était vraiment un plaisir à réaliser !
    • Vraiment superbe. Moi aussi je suis en train de faire un compilateur C minimaliste, mais au lieu de viser un boot sector, je cible un système 8 bits
      Ce projet montre bien à quel point la structure fondamentale de C est simple.
      C’est intéressant de voir que C a évolué à partir du langage B, lui-même dérivé d’une version réduite de Fortran
    • Je me demande de combien on pourrait encore réduire la taille en transformant if, while et for en simples routines goto
      Après tout, en assembleur, il n’y a finalement que jmp.
      Et il faudrait dire « chose your own adventure », pas « choose your own adventure » :)
      J’adore le minimalisme
    • Ce compilateur, ou le concept de « barely-C », pourrait aussi servir dans une chaîne de bootstrap
      En partant d’un tout petit binaire spécifique à une plateforme, puis en construisant progressivement des outils et compilateurs de plus en plus complexes
      On peut par exemple regarder les projets mishmashvm et tcc_bootstrap_alt
  • Magnifique projet. Cela dit, il vaudrait mieux ajouter 2023 dans le titre rapidement
    La discussion de l’époque avait eu lieu ici
    • Merci ! Avec la version à expansion de macros, on pourrait le présenter ainsi :
      SectorC: A C Compiler in 512 bytes - lien - mai 2023 (80 commentaires)
  • C’est intéressant de comparer ça avec le compilateur C de 100 000 lignes, fait avec Claude en deux semaines pour 20 000 dollars, qui est passé sur HN hier
    • Comparaison amusante, mais celui-là peut compiler le noyau Linux et générer du code pour plusieurs architectures
      Alors que ce projet-ci ne peut traiter qu’une partie de C
      Autrement dit, ce n’est pas vraiment un compilateur C complet, mais plutôt un compilateur pour un sous-ensemble de C.
      Le code que ce compilateur peut compiler passera aussi avec un vrai compilateur C, mais pas l’inverse
  • La manière d’utiliser le hachage des tokens pour créer une pseudo table des symboles est vraiment élégante
    • J’avais déjà fait quelque chose de similaire en écrivant un parseur pour un outil de test, où les identifiants étaient gérés uniquement via leur valeur de hachage
      Heureusement, je n’ai sans doute jamais utilisé assez de symboles pour provoquer des collisions en 32 bits
    • Je pense pareil. L’astuce du hachage des tokens est vraiment brillante
      Au passage, il reste 21 octets d’espace libre entre 0x01e0 et 0x01fd. Il y a peut-être encore moyen d’y glisser quelque chose ;)
  • Le passage disant que « atoi() se comporte comme une mauvaise fonction de hachage sur du texte brut » est intéressant
    Mais si je me souviens bien, atoi() est défini comme renvoyant 0 pour une entrée invalide
  • Il y a pas mal d’intérêt pour les compilateurs destinés aux boot sectors
    Pour l’exécuter sous Linux, il suffit de remplacer l’appel à qemu de coreaudio par alsa
    J’ai ouvert une pull request sur GitHub pour ça. Si l’auteur apprécie mon style de script shell verbeux, elle sera peut-être fusionnée
  • Très bel article ! Ça m’a rappelé l’OS en boot sector que j’avais créé autrefois
    On dirait qu’il est temps d’y ajouter un compilateur C
    Lien vers mon projet d’OS