-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LLVM can't target the Playstation CPU (MIPS-I) #1
Comments
If it's mostly the same, why not copy whatever MIPS-II does and change that behavior? Would be much easier than writing it all from scratch. |
I'm not at all familiar with LLVM so I didn't pursue this further. I assume it can't be too complicated but I really have no idea. |
There is a patch available at impiaaa/llvm-project@f2ecffe but according to the developer it could use additional testing. |
That's great news although I must say that I wouldn't even know where to start to generate a rustc using a custom LLVM so I'm probably going to wait until it (hopefully) ends up in a stable release. A few weeks ago I saw that there was a GCC backend for rustc in the works: https://github.com/sapir/gcc-rust That might be an other way to get MIPSI support in Rust. |
I wanted to play around with the Zig programming language (see https://github.com/XaviDCR92/psx-zig), whose compiler is also based on LLVM, so merged the patch into the release/10.x branch of https://github.com/XaviDCR92/llvm-project . It patch seems to work correctly, so I encourage you to resume your work on psx-sdk-rs. Well-known issues with Zig's packed structs are preventing further development, but I think should be now fine with Rust. |
I recently got rustc working with LLVM 11 patched to support load delay slots if there's still interest in this. My fork has instructions in the README along with @impiaaa's LLVM patch (with a tiny fix) and a patch to build I think it would've been convenient to use the JSON target, but it seems building the target into rustc is required since libcore won't compile unless the Atomic* API is disabled (by setting Also I threw in a stripped-down copy of @simias's Makefile for the linker and merged libbios into a submodule of libpsx to make the SDK more user-friendly. |
@ayrtonm , the delay slot patch seems to work well for me too (I also had to apply the tiny fix to avoid a compile-time error). However, I am having a curious issue that's making my Zig/C application crash by jumping into an invalid address. The root cause of this issue is After some investigation, it looks like bad generated code from the compiler side (I can't tell so far whether it's caused by either the LLVM backend or the Zig frontend). Consider the following lines of C code from PSXSDK: for(a = 0; a < l; a+=2)
GPU_DATA_PORT = image[a]|(image[a+1]<<16); Among other low-level instructions, the following branch instruction is generated:
And this is what address
Therefore, The compiler patch has added 18
Would it be possible that the LLVM backend is miscalculating destination addresses in branch instructions such as |
I haven't touched this code in forever but I'm glad to see that there's progress! @XaviDCR92 Have you tried to see the difference the various optimization levels make? Regardless of the miscalculated branch addresses it would be odd to end up with a bunch of useless NOPs in optimized code. What I'm getting at here is that a bunch of leftover NOPs with optimizations enabled seems like it would be a good sign that something messes up in the compiler. |
@simias Zig provides a OTOH, these apparently useless Surprisingly though, I have found a possible reason why the code above was inadvertently jumping to a
More worryingly, I have also found other places where
Compared to the implementation by
In the meantime, I'm improving my fork of PCSX-r so |
After playing around with rust on the playstation for a while, I haven't been able to reproduce @XaviDCR92's issue with out of place I haven't used zig extensively, but I think the issue may be runtime checks or UB on shift overflow. For example, I also tried compiling some C with zig (0.6.0) myself and found that
It seems to me that zig uses the smallest type possible to represent integer literals, but I couldn't find any specific documentation on that. Granted that was all on x86 not MIPS, but I think the issue might be in that general area. |
Not sure how relevant it is to you (does PSX have an FPU?), but floating point comparisons also have a delay slot rust-lang/rust#91442 (comment) |
nah the PSX doesn't have a floating point unit. It supports fixed point but that's all custom coprocessor opcodes from inline assembly so llvm's not an issue there. |
LLVM so far has only supported the MIPS-II and above architectures. MIPS-II is pretty close to MIPS-I, the major difference being that "load" instructions always take one extra instruction slot to propogate to registers. This patch adds support for MIPS-I by adding hazard handling for load delay slots, alongside MIPSR6 forbidden slots and FPU slots, inserting a NOP instruction between a load and any instruction immediately following that reads the load's destination register. I also included a simple regression test. Since no existing tests target MIPS-I, those all still pass. Issue ref: simias/psx-sdk-rs#1 I also tested by building a simple demo app with Clang and running it in an emulator. Patch by: @impiaaa Differential Revision: https://reviews.llvm.org/D122427
Seems initial support for load delay slots is now part of the latest LLVM release if I understand that commit correctly. |
It's not in upstream rust yet, but I was going to cherry-pick that commit onto the rustc branch and open a PR to add the psx target. |
Also, filling load delay slots in code generation is only one part of full MIPS-I support. Remaining tasks include:
|
Yeah those would be definitely be good to upstream for MIPS-I, but the psx target's still fairly usable without them. I think the only really critical issue is the |
LLVM so far has only supported the MIPS-II and above architectures. MIPS-II is pretty close to MIPS-I, the major difference being that "load" instructions always take one extra instruction slot to propogate to registers. This patch adds support for MIPS-I by adding hazard handling for load delay slots, alongside MIPSR6 forbidden slots and FPU slots, inserting a NOP instruction between a load and any instruction immediately following that reads the load's destination register. I also included a simple regression test. Since no existing tests target MIPS-I, those all still pass. Issue ref: simias/psx-sdk-rs#1 I also tested by building a simple demo app with Clang and running it in an emulator. Patch by: @impiaaa Differential Revision: https://reviews.llvm.org/D122427
This target's now usable with just a target JSON on nightly, so building the rust compiler isn't necessary anymore. I updated the crate's instructions in case anyone wants to try it out. |
LLVM so far has only supported the MIPS-II and above architectures. MIPS-II is pretty close to MIPS-I, the major difference being that "load" instructions always take one extra instruction slot to propogate to registers. This patch adds support for MIPS-I by adding hazard handling for load delay slots, alongside MIPSR6 forbidden slots and FPU slots, inserting a NOP instruction between a load and any instruction immediately following that reads the load's destination register. I also included a simple regression test. Since no existing tests target MIPS-I, those all still pass. Issue ref: simias/psx-sdk-rs#1 I also tested by building a simple demo app with Clang and running it in an emulator. Patch by: @impiaaa Differential Revision: https://reviews.llvm.org/D122427
I opened a PR adding a built-in target to rustc so hopefully the target JSON will soon be unnecessary. |
LLVM so far has only supported the MIPS-II and above architectures. MIPS-II is pretty close to MIPS-I, the major difference being that "load" instructions always take one extra instruction slot to propogate to registers. This patch adds support for MIPS-I by adding hazard handling for load delay slots, alongside MIPSR6 forbidden slots and FPU slots, inserting a NOP instruction between a load and any instruction immediately following that reads the load's destination register. I also included a simple regression test. Since no existing tests target MIPS-I, those all still pass. Issue ref: simias/psx-sdk-rs#1 I also tested by building a simple demo app with Clang and running it in an emulator. Patch by: @impiaaa Differential Revision: https://reviews.llvm.org/D122427
That's amazing work! Now I have to find a way to get my test setup working again... |
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 anop
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:Unfortunately this code doesn't work on the Playstation's R2000 CPU: the
lw
doesn't have the time to finish before thejr
is called (thejr
is in the load delay slot), so the jump takes whatever value was inra
before thelw
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"
intarget.json
I get this error: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.
The text was updated successfully, but these errors were encountered: