Nanopass Framework: Clean Compiler Creation Language

nanopass.org

127 points by NordStreamYacht 5 days ago


darrickw - 11 minutes ago

I built a nanopass-like compiler using Clojure (plus a pattern-matching library[1] that I created) to compile a subset of Python all the way down to bytecode for Untether AI's inference accelerator, a RISC ISA extended with an at-memory compute array. The python subset allowed full control of the array via type-inference instead of intrinsic ops.

I learned how to build a compiler using the nanopass approach and I still think it was a good way to learn, but I would not build another real compiler that way.

Building a lot of passes was great for initial development of the compilation pipeline, but was terrible for maintenance and relatively limited for creating optimizations. Bugfixes in an early pass would frequently require changes across several subsequent ones, especially if any change in representation were required.

About 8 months in, I adopted the a Sea of Nodes[2] approach for the optimizer, type inference, and scheduling but kept my nanopass ingestion, allocation and codegen passes. At the 12 month mark, the v1 compiler was functional but had limitations.

For the next leg of the project I decided to switch fully to Sea of Nodes. The final compiler was much better; orders of magnitude faster, more flexible, much easier to debug and test, more features, and about the same amount of code.

Sea of Nodes takes the core idea of isolating passes to its logical conclusion by narrowing the scope of each change down to a single transformation on a single graph node. Operations are worklist based and can happen in any order, eliminating the pass ordering problem. The IR is a graph that follows simple and consistent semantics from parsing through all phases down to code generation. Going from 20+ IR dialects/sub-dialects to a single graph representation was a huge plus.

[1] https://github.com/pangloss/pattern [2] https://github.com/SeaOfNodes/Simple

verdagon - 15 hours ago

I'm often skeptical of the desire to create a lot of passes. In the early Vale compiler, and in the Mojo compiler, we were paying a lot of interest on tech debt because features were put in the wrong pass. We often incurred more complexity trying to make a concept work across passes than we would have had in fewer, larger passes. I imagine this also has analogies to microservices in some way. Maybe other compiler people can weigh in here on the correct number/kind of passes.

Panzerschrek - an hour ago

As a compiler developer I see no reason to use a lot of passes. My language compiler is designed in such a way, that it does its frontend work (except tokenization and syntax analysis) in one big pass, which does all the stuff necessary. Splitting this work in multiple passes is impossible due to language specifics - interleaving many compilation stages is necessary for many language features to function properly.

Also I doubt that a common framework can be used by many languages at all. Usually a mature language compiler is self-hosted, which makes near to impossible to incorporate in it some thirdparty library/framework written in some other language.

s20n - 14 hours ago

I agree with the notion that having multiple passes makes compilers easier to understand and maintain but finding the right number of passes is the real challenge here.

The optimal number of passes/IRs depends heavily on what language is being compiled. Some languages naturally warrant this kind of an architecture that would involve a lot of passes.

Compiling Scheme for instance would naturally entail several passes. It could look something like the following:

Lexer -> Parser -> Macro Expander -> Alpha Renaming -> Core AST (Lowering) -> CPS Transform -> Beta / Eta Reduction -> Closure Conversion -> Codegen

rezaprima - 5 hours ago

the getting started button only links to github of the owner. I found this link [1] gives better info about this.

[1] https://docs.racket-lang.org/nanopass/index.html

Mathnerd314 - 16 hours ago

website is not up to date, https://www.youtube.com/watch?v=lqVN1fGNpZw is not on there

vmsp - 14 hours ago

Wouldn't this kind of architecture yield a slower compiler, regardless of output quality? Conceptually, trying to implement the least-amount of passes with each doing as much work as possible would make more sense to me.

presz - 13 hours ago

We really need to get more designers interested in Scheme, because that logo is awful

ape4 - 12 hours ago

Can it only make compilers for Lisp-like languages

- 17 hours ago
[deleted]