0. This stage is the pre-built compiler
1. This stage is a compiler built with the compiler from stage 0
2. This stage is a compiler built from stage 1.
If the process worked, then stage 1 and stage 2's output should be identical. You can skip stage 2 if you want to skip that verification, and that sounds like what Crystal does.
Normally this wouldn't matter, but Rust also supports compiler plugins written in Rust. These link directly to the compiler that compiles them. With the stage 1 compiler's quirky ABI, the plugins it compiles might not be able to interact with it.
The stage 2 compiler, on the other hand, uses the same set of ifdefs that the stage 1 compiler has. So it was built by a compiler with the same output-ABI as itself, and so plugins have the same ABI.
So:
* snapshot: Built with snapshot ABI, produces snapshot ABI (snapshots are old stage2 compilers)
* stage0-output: built with snapshot ABI, emits stage0 ABI.
* stage1-output: built with stage0 ABI, emits not(stage0) ABI.
* stage2-output: built with not(stage0) ABI, emits not(stage0) ABI.
I'm currently working on crystal's CI infrastructure so that we can have nightly crystal builds (+ CI) for all architectures we support. Currently we release features incrementally such that the compiler can always be built with the latest release, and I'm wondering how much effect relaxing that constraint to the latest nighty would have on development speed.
Someone is working on an alternate compiler so we could try DDC, we'll see.
http://manishearth.github.io/blog/2016/12/02/reflections-on-...
(Of course, that's a proof of concept and not actually in the released binaries :P )