SectorC, un compilateur C conçu en 512 octets
(xorvoid.com)- 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
- Exemple :
- 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
- Les littéraux entiers, mots-clés et identifiants sont tous traités à partir du résultat de
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
axcomme 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/lodswet le maintien de déplacements de saut sur 8 bits
- Utilisation de techniques comme le fall-through (suppression de sauts), le tail-call, la fusion d’appels, l’usage de
- 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/whileimbriqué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
- Blocs
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
asmpermet 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 Crt/lib.c: routines de bibliothèque fondées sur l’assembleur inlinert/_start.c: point d’entrée du programme_start()
Programmes d’exemple
examples/hello.c: affiche du texte directement dans la mémoire0xB8000examples/sinwave.c: affiche une animation sinusoïdale en mode VGA 0x13examples/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
Commentaires sur Hacker News
Les compilateurs d’optimisation des années 2000 auraient peut-être même optimisé ces tokens en simple no-op 😉
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
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 »
Du coup, j’ai l’impression de ne plus être spécial
On empile les abstractions au point qu’il faut 200 Mo de
node_modulesjuste 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
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
if,whileetforen simples routinesgotoAprè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
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
La discussion de l’époque avait eu lieu ici
SectorC: A C Compiler in 512 bytes - lien - mai 2023 (80 commentaires)
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
Heureusement, je n’ai sans doute jamais utilisé assez de symboles pour provoquer des collisions en 32 bits
Au passage, il reste 21 octets d’espace libre entre
0x01e0et0x01fd. Il y a peut-être encore moyen d’y glisser quelque chose ;)atoi()se comporte comme une mauvaise fonction de hachage sur du texte brut » est intéressantMais si je me souviens bien,
atoi()est défini comme renvoyant 0 pour une entrée invalidePour 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
On dirait qu’il est temps d’y ajouter un compilateur C
Lien vers mon projet d’OS