Mr.Confuzed said:
I agree that this particular problem will be much more difficult with a static recompiler. However, some of your other posts seem to suggest that there is something wrong with static recompilation. I'm not sure where that's coming from, but I am curious: Have you tried writing one? Is there some sort of problem with the method? I don't see one.
I've written dynamic recompilers. People bring up static recompilers a lot, but if they're so viable then why do so few
real static recompilers exist, much less ones that are completely automatic and run a significant amount of real software? Sometimes one will come along that calls itself a static recompiler but it's actually just a deeply recursive dynamic one. It still generates code at runtime - as much as it can, but it'll stop when it hits a dead end and has to be capable of picking up again later.
Every program has indirect branches, and potential indirect branch targets could be basically anything. There are heuristics to try to guess these, and it helps when you're recompiling an executable that has some sort of structure to it, but most emulators are emulating hardware, not operating systems, and only have a binary image of some sort to work with. Ultimately heuristics will certainly fail in some circumstances, especially considering that even with structured executables you'll find code doing things like putting jump tables in the code segment, or for instance on something like ARM, incrementing the PC based on a computed value. Static recompilers I've seen literature on go to extremes such as making everything in the data segment a branch target and recompiling that. This creates huge bloat and is still not foolproof because branch targets don't have to be stored verbatim. The only way to really get all branch targets, if you're certain that they all exist in your binary, is to recompile a block starting at every possible starting location in the binary. This gets pretty out of hand with an architecture that has variable width instructions like x86. You'll also end up with a huge indirect branch table that you have to hash through at runtime, unlike the code which could get swapped out.
Of course, this still only works if your code is resident at load time. Anything dynamically loading, generating, or modifying code will fail. So if your program, say, uses overlays or loads DLLs in a way that's not determined by the executable structure at compile time, then it'll fail. Like for instance, plugins in a media player or emulator.
Meanwhile, people assume that static recompilers will have benefits because you can spend more time performing optimizations, but they should understand that recompilers aren't the same as high level language compilers and a lot of optimizations don't apply in the same fashion.
WizardStan said:
Well, a dynamic recompiler only knows what is code because it's about to try and run it; if it wants to work ahead of itself, it's hard because it hasn't got there yet.
A static recompiler can afford to try everything, even spend the time to work backwards to see whether a given block is ever referenced.
A static recompiler can't try anything, since it has no idea if it's right or not. A dynamic recompiler can work ahead as much as is actually advantageous for recompilation, and it can recompile again later based on new information if that's somehow desirable. Besides, this has little to do with what you claimed, that a static recompilation has benefits in determining what's code vs what's data and in dynamic recompilation you can't be sure. The complete opposite is true.
Tor said:
It becomes much simpler if the source CPU in question uses a Harvard architecture.. i.e. separate data and code. E.g. MC68k.
You're thinking of Harvard address spaces, not Harvard internal buses (ie, icache/dcache, they both go to the same bus at some level up). Few architectures that are that interesting to recompile actually employ split address spaces. 68k certainly doesn't. Even architectures that do employ this in some capacity usually have a way to load data from the code space and code from the data space.