3 ms·
If your experience of ARM emulation of x86 is qemu, especially the full-system variant, you're getting something much slower than emulation needs to be: - Anyt
by comex 10y ago
If your experience of ARM emulation of x86 is qemu, especially the full-system variant, you're getting something much slower than emulation needs to be:
- Anything floating point or SIMD is generally done with a big mass of C helper functions rather than fully inline, and it's always unrolled rather than translated into native SIMD. You can blame qemu's choice to use an architecture neutral IR, but even with that design it could be a lot smarter than it is. In any case, a JIT designed for a specific guest/native architecture combo should be able to produce much more efficient instruction sequences.
- In system mode, qemu has to emulate page tables, which it does by translating every load/store to call a stub that maps the given virtual address to a "physical" one before loading. This is quite slow. User-mode qemu (where qemu is a host-arch Linux process pretending to be a single target-arch Linux process) is faster because it doesn't need to perform any translation itself (the host page tables provided by the native kernel do the job). Here too qemu could be improved - it's possible to use host page tables for full system emulation, though in some cases at the expense of hardware accuracy - but AFAIK Microsoft's emulator is user-mode, so this too isn't an issue.
- Those are only the big issues; I think there are a lot of small cases where there's room to do better than qemu, either in general or by optimizing for a specific architecture pair.
Also, I'm not sure how much this matters, but translating x86->ARM64 has some small builtin advantages over other pairs due to the nature of the ISAs. x86 has few general-purpose registers, while ARM64 has 32, so you can map each x86 register to an ARM register throughout the emulation; going the other way around you need to be constantly shuffling registers to and from memory. And a small one: ret on x86 requires the use of the stack pointer and memory, while ARM64 ret just takes an arbitrary register argument. (In both cases, the visible semantics are the same as a regular indirect jump, but the instructions are optimized to quickly return to the corresponding call instruction using a shadow stack in hardware.) All indirect jumps, including returns, have some emulation overhead because you need to translate the host code address to a native one; it's a bit easier when the host instructions are more flexible.
- TheCoreh 10y agoDoesn't ARM have very different memory consistency guarantees than x86 across cores, requiring memory barriers? I figured that would be one of the trickiest parts of emulating x86 on ARM