And if yoy worry about performance, your compiler can fuse passes together in many cases.
And if yoy worry about performance, your compiler can fuse passes together in many cases.
My own experience with far fewer stages is that while it becomes easy to understand what each stage does and how, it becomes hard to keep track of how each stage interact, as each intermediate output in effect becomes a language dialect of sorts.
I'm still not decided on whether it's a net win or loss.
Yes, but as Wirth showed already in the 70's, you don't even need an IR in order to do this, much less separate passes.
For a highly optimizing compiler like yours the complexity might have been unavoidable anyway, though (a lot of the simplicity of Wirth's compilers comes from a long held insistence that no optimization could be added to the compiler unless it sped up the compilation of the compiler itself - in other words, it needed to be simple enough and cheap enough to apply to the compiler source code to pay for itself... needless to say this implicitly means that most Wirth-compilers omit a lot of optimizations that are usually included elsewhere, though some of his students did implement some impressive "unofficial" optimizing variations of his compilers that were also blazing fast)
Check it out - a list of all the phases in the source! https://github.com/lampepfl/dotty/blob/master/src/dotty/tool...
It really helps that they are different, you always know which stage you're in.
Earlier you wrote that "And if you worry about performance, your compiler can fuse passes together in many cases."
What kind of fusion do you use in your approach?
In the last paragraph of Keep's thesis, he mentions pass fusion, citing Wadler's 1988 work on deforestation. Keep does not, however, give a working implementation.
I know of no-one doing nanopass fusion, but I'd love to be corrected.
It is using an IR which allows to easily compare the visitor shapes (i.e., source AST, target AST, traversing order, omitted nodes), and if they do match and the second pass is not using results of any of the collectors from the first pass (too long to explain what collectors are, treat them as a kind of a constrained side effect), corresponding visitor nodes are chained together.
Deforestation is supposed to work on much lower level, and I'm not sure it's possible to deduce the original visitor shapes and traversing order from an already lowered code.
A previous version of this framework is available on github, username 'combinatorylogic' (it does not provide fusion or any other optimisations, because it's dynamically typed; A glimpse of the IR of the next version can be seen in the recform AST library, see 'ast2-...'). The current version will be published soon.
https://github.com/combinatorylogic/clike
Because you have a functional and also imperative language (pfront?) that your compiler is written in, you avoid memory churn by ensuring "visitor nodes are chained together", is that right?
So pass fusion for compiling clike does away with the destructive matching and rebuilding of untransformed subexpressions by linking pointers back to the original.
As compilers go, I don't think gcc has been a good example of maintainable software for a very long time, if ever.