Really? That's pretty darn cool then.
So - if I had a native X86 Linux compiled program, would this allow me (or someone) to 'static recompile' it to Linux on ARM? (Performance issues notwithstanding.)
Where is a good place to start reading up on this?
The main mechanism is the same as with dynamic recompilation. The only difference is that instead of recompiling blocks as you try to execute them you recompile blocks in advance. This means that you need to know every branch target. It also means that you can't handle any kind of dynamic code, which includes self-modifying code but also extends to anything that's not located in-place from where the binary was loaded. So anything that is decompressed, loaded, or moved around in any kind of way.
Because of indirect branches you can't directly compute all branch targets by traversing a control flow graph from the start of program execution. Heuristics can help you determine the possible targets for an indirect branch, but they won't always work. Of course, there's nothing illegal about recompiling dead code or data that isn't even code at all, it'll just bloat your result. So it pays to be liberal with what you consider may be a branch target. But most serious static recompilations will manually fill in some of the difficult branch targets, making it a process that only works for one particular program rather than a general emulator.
If you really want you can recompile the entire program and allow every possible branch target. On platforms like x86 with big byte-variable-length instructions this becomes very unfortunate because it means you'll have a lot of overlapping variations in order to handle every possible byte alignment. You'll also end up with a huge translation table. More importantly, since every instruction as an entry point you can't really do any kind of propagation optimizations between instructions, including basic ones like dead operation elimination. So this probably isn't a good idea.
The thing is, static recompilation doesn't have much potential to be faster than dynamic recompilation to begin with. The only thing you definitively save on is having to check if a block you branch to exists or not. It's often said that static recompilation can perform broader scope optimizations than dynamic recompilation, but nothing is stopping a dynarec iteratively recompiling blocks as more of their targets are resolved, and it may be possible to follow direct branches recursively as well - you can't optimize over indirect branches in both cases. In practice you won't find a lot of whole program optimization attempted for a static recompiler unless it's generating code for a high level language and leaving the rest to a compiler, in which case it probably won't end up very good anyway.
Either way you do it, for Linux to Linux you're best off performing user mode emulation instead of system mode, that'll make the biggest performance difference.