FEX 2409 Highlights Some Of The Challenges Of Emulating x86 On RISC-V
FEX 2409 has been released for this open-source project that's known for allowing x86_64 Linux binaries -- including both games and applications -- to run rather well on AArch64. It's also been working on enabling x86_64 programs on RISC-V but there due to architectural differences it's more of a challenge than with ARM.
First up, FEX 2409 does deliver some nice performance improvements compared to last month's FEX 2408 release. The FEX-Emu project showed off some nice gains with the new FEX 2409:
The FEX 2409 announcement then went on to highlight some of the challenges in emulating x86 programs for RISC-V and the pains they cause, more so than emulating x86 on ARM:
Those wanting to read more about those challenges can do so via the FEX 2409 announcement. FEX 2409 also features FEXConfig rewritten as a Qt application for better usability and features.
First up, FEX 2409 does deliver some nice performance improvements compared to last month's FEX 2408 release. The FEX-Emu project showed off some nice gains with the new FEX 2409:
The FEX 2409 announcement then went on to highlight some of the challenges in emulating x86 programs for RISC-V and the pains they cause, more so than emulating x86 on ARM:
"Little differences between x86 and Arm can cause big performance penalties for emulators. None more so than flags.
What are those?
Flags are bits representing the processor state. For example, if an operation results in zero, the “zero flag” is set. Both x86 and Arm have flags, so for emulating x86 on Arm, we map x86 flags to Arm flags to reduce emulation overhead. That is possible because x86 and Arm have similar flags. By contrast, architectures like RISC-V lack flags, slowing down x86-on-RISC-V emulators.
Many arithmetic operations set flags. Programs can then conditionally jump (“branch”) according to the flags. On x86, the flags are thus the building blocks of if statements and loops. To check if two variables are equal, x86 code subtracts them and checks the zero flag. To check if one variable is less than another, x86 code subtracts and checks the negative flag. This pattern – subtracting, setting flags, and discarding the actual result – is so common that it has a special instruction: CMP (“CoMPare”).
If the story ended here, emulation would be easy. Unfortunately, we need to talk about the carry flag.
After an addition, the carry flag indicates if the result overflowed. Programs can then check the carry flag to detect overflows. The flag can also be input to another addition to implement 128-bit additions.
Subtractions are similar. In hardware, subtractions are additions with an operand negated. Because they are additions in hardware, subtractions set the carry flag. Precisely how is the carry flag defined for subtraction? There are two competing conventions.
The first sets the flag when there is a borrow, by mathematical analogy with addition. x86 uses this “borrow flag” convention, as it seems more natural.
The second option sets the flag when there is not a borrow. Isn’t that backwards? It turns out that adding a (two’s complement) negated operand overflows exactly when the subtraction does not borrow. This “true carry” convention matches actual hardware behaviour, while the “borrow” x86 convention requires extra gates to invert carry. Arm uses the “true carry” convention to save a few gates."
Those wanting to read more about those challenges can do so via the FEX 2409 announcement. FEX 2409 also features FEXConfig rewritten as a Qt application for better usability and features.
14 Comments