Description
I've been struggling for the past few days with extremely erratic behaviour from my playstation code. Simple code would work correctly but when things get more complicated (in particular function calls and BIOS calls) the program would appear to behave semi-randomly (bus errors, freezes, functions get executed several times in a row when they shouldn't etc...)
After some intense hair pulling I figured out the cause of the problem: the MIPS-I architecture has load delay slots. It means that when the CPU loads a value from memory (through a lw
instruction for instance) the next instruction will run before the value is put in the target register. That means that you have to wait one cycle (using a nop
or some independant instruction) before you can use the value.
The problem is that LLVM doesn't support MIPS-I at the moment and MIPS-II doesn't have this constraint. So for instance when the compiler generates the function epilogue that pops the value of ra
(the return address) from the stack and jumps to it we end up with something like:
lw ra,20(sp)
jr ra
Unfortunately this code doesn't work on the Playstation's R2000 CPU: the lw
doesn't have the time to finish before the jr
is called (the jr
is in the load delay slot), so the jump takes whatever value was in ra
before the lw
instruction. And that explains the very erratic behaviour.
Unfortunately as far as I can tell LLVM doesn't support MIPS-I at all, if I attempt to put "cpu": "mips1"
in target.json
I get this error:
LLVM ERROR: Code generation for MIPS-I is not implemented
Unfortunately there's no workaround. The solution would be to add MIPS-I support to LLVM but that's of course a pretty ambitious project and my plate is full at the moment.
As long as this issue is not fixed there's no point working on this SDK, nothing but the most trivial code will run as intended on the console.