3

I need to run software compiled for ARM on my x86 machine. Usually, to do that, you use an emulator like QEMU.

I'd like to know why we can't just process the binary for one ISA and compile it to a binary for another? Would doing so have the advantage that even if your codegen is sub-optimal, we could perhaps use existing optimizers to make it better?

I'm pretty sure there's something I'm missing on why this isn't possible / or a good idea, otherwise it'd perhaps have been done. So what am I missing?

3
  • 3
    Of course it's possible. Machine language is a programming language, like any other. Any Turing-complete language can be translated into any other Turing-complete language, given enough time and effort. Commented Oct 19, 2020 at 19:52
  • 2
    For instruction sets, the process is called Binary Translation. Have a look. Commented Oct 19, 2020 at 19:53
  • 1
    Do it while executing and we call it emulation. Commented Oct 19, 2020 at 20:54

1 Answer 1

5

Machine code is a language like any other language. "Translating" from one language to another language is called "compilation", and we have compilers for all sorts of source and target languages, machine languages are no exception.

JPC has a JIT compiler that compiles x86 machine code to JVM byte code (not quite the kind of "machine code" you were probably expecting, but machine code nonetheless).

You mentioned QEMU, which has a JIT compiler that compiles from the emulated machine language to the host machine language on the fly.

HP's Project Dynamo really brought Dynamic Binary Translation in particular, and JIT compilation in general into the mainstream. The Lisp and Smalltalk communities had always had amazing JIT compilers (the Animorphic Smalltalk VM from the '90s still survives to this day in both Oracle JDK and Google V8), but the concept was not widely accepted outside of those communities.

When HP and Intel designed the IA-64 architecture and the Itanium CPU as a common replacement for both x86 on servers and HP PA-RISC on HP mainframes, HP needed a backwards-compatibility story for their customers. (Mainframes often run software for which no source code is available for many decades, so you have to able to upgrade the hardware.)

HP started Project Dynamo, which was a dynamic binary translator from HP PA-RISC to IA-64. They wanted to run performance tests to measure the slowdown of the translation overhead. But there were no IA-64 chips yet. So, they decided to implement a PA-RISC backend. They could have "cheated" of course, and implemented it as a no-op, but they of course were interested in accurate performance measurements, and so they implemented their PA-RISC backend as a proper backend: the PA-RISC frontend analyzed the binary code at runtime and turned it into an abstract intermediate language, the optimizer went to work, and then the backend generated PA-RISC code again.

This was actually an advantage, because it allowed completely fair and accurate measurements of the slowdown, by running the PA-RISC binaries once natively and once in the PA-RISC-to-PA-RISC "emulator". To their complete shock, the slowdown they measured was negative. The "emulation" actually made the code faster.

Since then, we see JIT compilers pop up everywhere in emulators. I believe both the iPhone simulator in Xcode and the Android simulator in Android Studio use a JIT. PearPC (don't know if it still exists, an Apple emulator) used JIT. Both Apple's original Rosetta (for running PowerPC code on Intel Macs) and the new one (for running Intel code on Apple Silicon Macs) are dynamic translators. Before the two Rosettas, there was a fast dynamic translator from Motorola 68k to PowerPC; Apple didn't even ship a fully native OS with the first PowerMacs, parts of the actual core operating system were still m68k and running in the translator.

When DEC introduced the Alpha, they developed the FX!32 translator from x86 to Alpha. When Intel acquired DEC, they used this technology to develop the IA-32 Emulation Layer for IA-64 that shipped with Windows.

There are static translators, too, but they often struggle generating good code, because machine code is very unstructured. It is much easier to observe its behavior at runtime, than to analyze it statically. They have been used quite successfully in the retrogaming community.

2
  • The iPhone simulator in Xcode actually simulates an iPhone with an x86 processor. The compiler compiles to x86 code if the target is a simulator, and the simulator running on your Mac runs the compiled code as is. The simulator also comes with iOS compiled for x86.
    – gnasher729
    Commented Oct 19, 2020 at 20:41
  • Rosetta2 is actually a mix: Applications are translated completely as they are installed. For the rare situation that an app includes a built-in compiler, like a JavaScript compiler inside a browser translating to x86, that code will be translated at runtime.
    – gnasher729
    Commented Oct 19, 2020 at 20:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.