Before we start on rewriting the compiler in Zig I'd like us to agree on our approach to optimization of runtime. I feel we currently have substantial differences between individuals as to where compiler runspeed falls on the priority ladder.
One important note; the consensus here seemed to be that we want to write small compiler passes. With the current compiler I believe we've taken the approach that we plan things for maximum execution speed because it's too hard to change it later. But with small passes this does not seem to apply, they should be much easier to re-write for execution speed if that pass is a serious bottleneck.
I would personally always prioritize correctness, maintainability~simplicity~debugability and error message quality over run speed. I think we should not allow any optimization that complicates the code in a pass that is nowhere close to being the bottleneck pass. Any optimization that does meet this standard should be proven to be beneficial through benchmarks before it is merged.
What do you think?
I think you just, for each pass, try to make the fastest possible pass implementation you can that is simple to understand, easy to debug, and a joy to maintain.
So for instance, Arena allocation makes sense and is easy to reason about and provides great performance. In some cases SoAs can have the same qualities (even if they can take some adjustment to those not familiar with the pattern). But we just avoid performance optimization patterns that sacrifice those other two.
some thoughts on passes:
some thoughts on parallelization:
(these are coming in a sporadic order because we just started potty training today and I'm writing them in between cleaning up accidents :joy:)
some thoughts on memory allocation and data structures:
One of the really nice things about working with mlir over the years is that I think it gets a clear priority correct. First figure out your IR (that is your fundamental interface). Then right passes that operate over IRs. If constraints change enough consider adding a new IR and changing to it. If not, it is fine to add more details to the same IR or save side band info.
Any stage can be a theoretically cutting point for writing the ir to disk and caching. On top of that mlir can auto parallelize to some extent. Though that mostly depends on passes running patterns on individual IR nodes instead of larger groups (which isn't always practical).
I think we fundamentally need to focus on our IRs, our interfaces between different stacks of the compiler and. Not writing ourselves into a box that we can't easily get out of
We have some ideas about where the North Star is for the compiler. Our work should be align with the idea of reaching the North Star. We want to right the cleanest thing possible that allows for easily taking steps towards optimal.
yeah, and then later if we want to merge some of them together to improve performance, we can do that incrementally and after we've already gotten things correct
So it isn't strictly about simple. It is about correct now, but also easy to improve upon
Totally agreed. You weren't there for the meeting, but that's what I wrote in Rust for the post-typechecking part of the compiler: a set of IRs for each of the stages. By next Saturday, my hope is to have the IRs for all of the stages of the compiler (not just the build part like before) translated to Zig so we can collaborate on it and agree what this all should look like.
If anyone wants to work on that on their own in parallel, go for it, it's valuable to have different people try to come up with these IRs separately so we can see what the good ideas we agree on are.
So in my mind the two most important pieces outside of correctness are making the IR and passes have a CPU/memory friendly core along with making splits in places that will help enabe improvements in the future (parallelism, caching, etc).
that's almost exactly what I advocated for in the meeting :smiley:
focus on the boundaries
and dependencies
Yeah, I guess I just add on top CPU/memory friendly core design.
Then all the correctness, debugability simplicity,, etc without too much concern for the rest of the performance.
But yeah making good bones first.
Richard Feldman said:
- There may be some things that are quick and don't really affect debugging, such as storing some info in a few bits of identifiers.
Yeah, I think this can even help debugging because you can gather more information from just the value without having to look it up in a side table. For Purity Inference, I reserved one bit of Symbol
s as a !
-prefixed flag and it greatly simplified the implementation overall.
Last updated: Jul 06 2025 at 12:14 UTC