pubby
4 months ago
I like this article a lot but it doesn't answer the question of "Why SSA?".
Sure, a graph representation is nice, but that isn't a unique property of SSA. You can have graph IRs that aren't SSA at all.
And sure, SSA makes some optimizations easy, but it also makes other operations more difficult. When you consider that, plus the fact that going into and out of SSA is quite involved, it doesn't seem like SSA is worth the fuss.
So why SSA?
Well, it turns out compilers have sequencing issues. If you view compilation as a series of small code transformations, your representation goes from A -> B, then B -> C, then C -> D and so on. At least, that's how it works for non-optimizing compilers.
For optimizing compilers however, passes want to loop. Whenever an optimization is found, previous passes should be run again with new inputs... if possible. The easiest way to ensure this is to make all optimizations input and output the same representation. So A -> B is no good. We want A -> A: a singular representation.
So if we want a singular representation, let's pick a good one right? One that works reasonably well for most things. That's why SSA is useful: it's a decently good singular representation we can use for every pass.
titzer
3 months ago
While iterating optimizations is nice, I think you missed the main point of SSA.
SSA makes dataflow between operations explicit; it completely eliminates the original (incidental) names from programs. Because of that, all dataflow problems (particularly forward dataflow problems) get vastly simpler.
With SSA you can throw basically all forward dataflow problems (particularly with monotonic transformations) into a single pass and they all benefit each other. Without SSA, you have every single transformation tripping over itself to deal with names from the source program and introducing transformations that might confuse other analyses.
I know we teach different compiler optimizations at different stages, but it's really important to realize that all of them need to work together and that having each as a separate pass is a good way to fail at the phase ordering problem.
And going further with the sea-of-nodes representation just makes them all more powerful; I really do recommend reading Cliff Click's thesis.
UncleEntity
3 months ago
It's funny, I've been working on a CPS (continuation passing style) graph optimizer loosly based on Cliff Click's thesis and when I show it to the robots they're like "yeah, that's academic fluffery, it'll never fly" to try inflate my ego a bit until I explain how it actually works then they're "wait, that's actually possible ...and a good idea".
His 'optimization as a constraint solving problem' thing is actually pretty powerful and it just so happens I've been fiddling with a Projective Dynamics constraint solver (which is what the VM is for, to define the constraints) whivh I can abuse to optimize CPS graphs so... take that Robots!
pubby
3 months ago
You're very correct but I suppose I was really answering why compilers centralize around SSA. It's a bold choice to choose one data structure for everything, and that requires more motivation than, "it makes certain optimizations really easy". Because again, it makes other stuff harder.
>And going further with the sea-of-nodes representation just makes them all more powerful; I really do recommend reading Cliff Click's thesis.
We might have to agree to disagree on this one. I actually found sea of nodes to be a boneheaded idea. It makes one or two optimizations a little more elegant but everything else a huge pain in the ass. At least, that was my experience.
titzer
3 months ago
A huge benefit of the sea of nodes representation, at least how I envisioned it and we implemented in the early days of TurboFan, is that branch folding, load elimination, and all of the other forward dataflow analyses that do monotonic reductions can be combined into a single pass that operates on the IR in the optimal order and can iterate to a fix point efficiently.
cryptonector
3 months ago
In particular one thing that needs to be stated is that the primary alternative to SSA is CPS (continuation passing style) transformation. The two are roughly equivalent (at least in so far as both can be used to build compilers for the same languages), but with CPS you have to do more work to avoid ending up with lots of closure allocations that are really not necessary, so SSA feels more natural. I like to think about CPS though.
ebiederm
4 months ago
In the basic block with arguments variation there is no going in and out of SSA.
Findecanor
3 months ago
Phi-functions and block arguments are just different views of the same thing. Sometimes it is more convenient to use one or the other when thinking of a problem.
If you lay out phi-functions and their parameters on a grid, you'd get a "phi-matrix" where phi-functions are rows and block arguments are the columns.
If you don't do an out-of-SSA transform before register allocation, and effectively treat block parameters like function calls then you're pushing the complexity to the register allocator.
An out-of-SSA transform before register allocation would coalesce not just registers but also variables in spill slots (thus avoiding memory-memory moves), it would reduce the complexity of parallel moves. A more advanced transform could also hoist moves out from before the hottest branch which could potentially lead to un-splitting previously split critical edges.