AI will write your next compiler!
Discuss on Hacker News, Twitter or Reddit. Also, consider hiring me!
It was a dark and stormy night
Well, a gloomy Kyiv morning.
I opened my laptop and discovered that my Claude Code account had been disabled. Not only that — I’d been refunded the unused portion of my $200/month Max plan and downgraded to the Free tier. I had 10–15 active Claude Code Web sessions working across multiple open-source projects, and everything ground to a halt.
Sensing impending catastrophe, I navigated to the billing page and paid $200 again… only to watch it refunded a few minutes later.
How it started
A couple of weeks earlier, I suddenly found myself with $1000 in Claude Web credits and just one week to burn them. I decided to use the free money for the greater good: push AI as far as it could and try to improve existing open-source projects.
So I started hacking.
I tried adding REPL history navigation and editing to ocamldebug, the OCaml bytecode debugger. It went surprisingly well — except I missed that line-based breakpoints exist already.
Then I tried adding native binary debugging to OCaml. That also worked. Yes, my implementation is fully working. Yes, it’s tested!
Yes, dumping a huge PR on unsuspecting OCaml maintainers was a stunt and I regret it now. I also apologize for burdening the OCaml maintainers with it!
I should have started a discussion or opened a tiny PR against your own repo first to validate the idea, like I did with Zig.
Except that experiment didn’t go well either.
I spent two weeks building a non-LLVM ARM64 backend for Zig… only to discover such a backend already existed. Then I found a small compiler bug, wrote a minimal patch, included detailed examples—and got slapped down because Zig has a strict no-LLM contributions policy.
The moral: go play in your own playground!
Growing compilers
Some insist that AI “steals” rather than creates. For example, someone claimed my DWARF debugging support was copied from OxCaml. I disagree — and I think the premise doesn’t matter. Humans copy each other’s code constantly. Writers read books to write new books. Training on existing code is no different.
AI can write a compiler using the knowledge it’s trained on.
I’m putting money where my mouth is: I’m working on two such projects now:
- A Common Lisp compiler targeting embedded and bare-metal environments.
- A source-to-source compiler from Ghidra’s Sleigh to Rust plugins for Binary Ninja.
So what does it look like when a model writes a Lisp compiler?
Pretty damn sentient.
It figured out that it’s missing labels a feature of the language and that it needs to implement them.
⏺ I can confirm that labels is not currently supported in the bootstrap compiler.
The compiler itself uses labels heavily (because it runs on SBCL), but when
compiling Habu source code, labels is not handled. I need to implement labels
support.
Let me first understand what forms are already supported to see what patterns I
can follow. The closest analogs are LET/LET* and lambda/funcall. Let me look
at how defun is handled to understand the function environment:
It knows how to search through existing code.
⏺ Search(pattern: "defun", path: "bootstrap/compiler.lisp", output_mode: "content")
⎿ Found 100 lines (ctrl+o to expand)
⏺ Let me look at the compile-forms function to understand how defun is handled at
the top level:
⏺ Search(pattern: "nc-compile-forms", path: "bootstrap/compiler.lisp", output_mode:
"content")
⎿ Found 100 lines (ctrl+o to expand)
And it knows how to put two and two together, e.g. that labels are local recursive functions.
⏺ Now I understand the structure. Let me look at how lambda captures free
variables and how funcall works, since labels is essentially local recursive
functions:
⏺ Read(bootstrap/compiler.lisp)
⎿ Read 100 lines
And here’s the kicker: it knows how to simplify labels transform them into something it has already implemented!
⏺ I see how lambda and funcall work. For labels, I can transform it into nested
lambdas with mutual recursion. The key insight is:
(labels ((fn1 (args...) body1)
(fn2 (args...) body2))
main-body)
Can be transformed into letrec-style bindings using closures. The standard
approach is to use a Y-combinator-like technique or to use mutation (setq) to
tie the knot.
Let me implement labels by transforming it into a let/setq pattern that
establishes the recursive bindings:
(labels ((fact (n) (if (= n 0) 1 (* n (fact (- n 1))))))
(fact 5))
becomes:
(let ((fact nil))
(setq fact (lambda (n) (if (= n 0) 1 (* n (funcall fact (- n 1))))))
(funcall fact 5))
Where this is going
My prediction: AI will write your next compiler.
Existing projects will struggle to accept AI contributions — not because AI is incapable, but because our development process isn’t ready for it. Reviewing large AI-generated code changes is uncomfortable, foreign, and time-consuming.
But a brand-new compiler? A greenfield project?
AI will thrive there.
Working with AI today is more art than science. You have to guide it, correct it, and force it to confront its own design decisions.
- AI: My tests are slow because I’m rebuilding the whole stdlib` all the time.
- Me: Why are you rebuilding it all the time?
- AI: Because I have no caching of MIR, etc. code and no incremental compilation. And because I’m rebuilding
compiler_rtfor every test. - Me: Well, then focus on caching of build artifacts and incremental completion to speed up your damn tests!
A glimpse of what’s coming
Eventually, we’ll build the infrastructure to let AI write code without human micromanagement. You review assembly emitted by your existing compiler — you run it, test it, benchmark it, and trust the toolchain.
AI-written code will be no different.
If it:
- meets your requirements
- passes your test suite
- introduces no regressions
- performs within expected bounds
…why would you care how it made the sausage?
P.S. If you enjoyed this, you may also like:
👉 Build Your Dreams
The Zen of Coding Newsletter
Join the newsletter to receive the latest updates in your inbox.