elouann.dev
EN — English version

ProjetsBas-niveau & embarqué

Brainfxcker

Compilateur Brainfuck vers x86-64en cours

Période
avr. 2026 – aujourd’hui
Cadre
personnel
Équipe
solo
  • C
  • Brainfuck
  • Assembleur
  • NASM x86-64 Intel
  • Compilateur
  • Bas-niveau
  • Programmation impérative

Voir le code

Un vrai petit compilateur : il lit un programme Brainfuck, le fait passer par un lexer, un parser et une représentation intermédiaire, puis écrit de l’assembleur x86-64 que NASM et ld transforment en binaire ELF.

main.bfsourcelexertokensparserarbreIRADD, MOVE…optim.fusioncodegen.asmnasmelf64 .oldELFBrainfuckIR optimiséex86-64 (NASM)+++ADD 3add byte [rbx], 3>>MOVE 2add rbx, 2[LOOP_BEGcmp byte [rbx], 0 ; je .L0_end<MOVE -1add rbx, -1]LOOP_ENDcmp byte [rbx], 0 ; jne .L0_beg.OUTPUTmov rax, 1 … syscallles symboles répétés sont fusionnés en une seule instruction
SchémaLa chaîne de compilation, et ce que devient un petit morceau de programme à chaque étape.

Pourquoi Brainfuck

Brainfuck n’a que huit instructions, qui agissent sur un ruban de cases mémoire et un pointeur. Le langage est trivial : tout l’intérêt est dans la traduction. C’était le prétexte idéal pour comprendre l’assembleur et les étapes d’un compilateur, sans se perdre dans la grammaire d’un langage complexe.

Les étapes

Le lexer ne garde que les huit caractères utiles. Le parser construit un arbre où chaque boucle [ ] contient son propre bloc. Cet arbre est ensuite aplati en instructions intermédiaires : ADD, MOVE, SET, OUTPUT, INPUT, LOOP_BEG, LOOP_END.

Une passe d’optimisation fusionne les instructions répétées (+++ devient ADD 3, >> devient MOVE 2) et repère les boucles de remise à zéro comme [-]. Le générateur écrit enfin le code NASM : le ruban de 30 000 octets est réservé dans .bss, le pointeur vit dans rbx, et les entrées / sorties passent directement par les appels système read, write et exit.

Les détails

Les instructions sont stockées dans un « paged vector » écrit à la main : un tableau de pages de 64 éléments, qui grandit sans jamais déplacer les éléments déjà rangés. Comme je travaille sur Mac, le compilateur tourne dans un conteneur Docker linux/amd64 pour produire et lancer de vrais binaires x86-64.

Ce que j’en retiens

  • Un compilateur, c’est surtout une suite de représentations, chacune plus proche de la machine.
  • Les conventions d’appel système Linux (rax, rdi, rsi, rdx, puis syscall).
  • Ce que font réellement l’assembleur et l’éditeur de liens, et ce qu’il y a dans un ELF.