4
$\begingroup$

Rosetta 2 is a program that enables the new ARM-based Macs to run programs for old x86-based Macs by translating x86 machine code to ARM machine code. But how does it do that? What seems especially problematic is translating x87 (the legacy FPU) instructions to ARM machine code. x87 instructions are stack-based, and there is no stack-based FPU on ARM processors.

I am thinking about extending my Arithmetic Expression Compiler to be able to output not just x86 assembly code, but also ARM assembly code, so that knowledge will come useful to me.

$\endgroup$
7
  • 1
    $\begingroup$ are you sure they handle x87 instructions? most FPU math in x84-64 land is done using the simd execution units $\endgroup$ Commented Apr 9 at 11:22
  • 1
    $\begingroup$ the larger challenge by far for rosetta is implementing 80-bit floats, which are not supported in hardware on aarch64; they are likely implemented in software. and the fact that precision is runtime-configurable. fast emulation of x87 is unlikely to be a priority for rosetta, since it is somewhat slow even on intel/amd cpus (where it is natively supported in hardware), and is no longer commonly used by software $\endgroup$
    – Moonchild
    Commented Apr 9 at 11:23
  • $\begingroup$ what you are looking for is a register allocation algorithm. there are some straightforward algorithms that can be used for simple tree-based recursive expression languages and give acceptable results. alternately, if you don't care about performance much (likely if you are targeting x87), then you could just generate aarch64 code that uses a stack manually $\endgroup$
    – Moonchild
    Commented Apr 9 at 11:25
  • $\begingroup$ There is a trivial way to translate stack machine code to register machine code, which is to use the register machine's stack as the stack. Then each operation is preceded by popping the operands to the appropriate registers, and followed by pushing the result to the stack. The trivial way can be optimised by remembering what's in each register, and cancelling "push, pop" instructions to the same register, or replacing them with "mov" if the value you want is still in a different register. $\endgroup$
    – kaya3
    Commented Apr 9 at 13:09
  • 2
    $\begingroup$ I took a look at some translated x87 code, and indeed it's mostly just a bunch of bl instructions that presumably call functions in the runtime to emulate the x87 instructions. I found it tedious to trace into it (for some reason the program dies in lldb if you try to single-step or continue after a breakpoint) but I strongly suspect the virtual x87 registers are just kept in memory, so that the x87 stack pointer is just a pointer and can be moved with regular arithmetic. $\endgroup$ Commented Apr 28 at 6:20

1 Answer 1

2
$\begingroup$

Stack based languages can be converted into register based languages with register allocation. Register allocation can be solved with graph coloring, which is fairly advanced. However, if performance is not critical, then it can be done with a simpler algorithm. I think based on comments that others have left that in the case of Rosetta 2, it merely simulates a stack with registers. However, I think that what you are actually wondering is about how this can be done in the more general case. In a stack based language, the program is almost a linear substructure, excluding loads and stores. Thus a temporary should be dead once it is used, unless it is loaded or stored. When a substructure is linear and the number of pushs and pops of each instruction is known ahead of time, then one can simply keep a list (or set and stack) of live numbered temporaries and create a new temporary for each result and use the most recent temporaries to pass into instructions. Unfortunately, real world programs contain control flow, which means the number of pushs and pops may not be known at compile time. To handle control flow, one might want to consider simulating a stack.

I don't know much about stack based assembly instructions; however, I have tried working with converting an intermediate representation both to 3 address code and back to 4 address code, when working on a hobby compiler, where performance is not critical. I hope everything I have said is accurate and clear.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .