Skip to content

mipsel-sony-psx - potential load delay slot issue #150676

@JaberwockySeamonstah

Description

@JaberwockySeamonstah

I am working on making a game for the PS1 (--target mipsel-sony-psx), and I'm experiencing an issue where an Optional is guaranteed to be Some but the check is_some will return false. Using the debugger from Duckstation (PS1 emulator) we where able to see that the Optional was not yet loaded to the register at the time of the check, which is why we assume it could be a load delay slot issue.

Sadly we have not found a small, simple code example yet that can reproduce the issue. The code that shows the behavior runs in the interrupt handler of the PS1 and relies on the serial port. The snippet below gives a rough overview.

  • CONTROLLERS_A is a global array with CONTROLLER_SLOT_COUNT = 1 many elements
    • ControllerSlot::new provides Some configuration and None controller
    • ControllerSlot::configuration will never change and is only set by the const ControllerSlot::new
    • The ControllerSlot::controller is used in the main thread
  • asm label planschi: aims at the check that will fail
  • update_controller is executed in the interrupt handler

We could observe the following behaviors:

  • Adding a nop after asm label planschi: can fix the check
  • Setting CONTROLLER_SLOT_COUNT = 2 will reliably fix the issue
  • Replacing the array of size CONTROLLER_SLOT_COUNT = 1 with a single global ControllerSlot instance will also fix the issue reliably
pub_global!(CONTROLLERS_A, [ControllerSlot; CONTROLLER_SLOT_COUNT], [const {ControllerSlot::new()}; CONTROLLER_SLOT_COUNT]);

pub struct ControllerSlot {
    controller:    Option<RawController>,
    configuration: Option<Configuration>,
}

fn update_controller() {
    let mut serial_connection = SerialConnection::activate();
        process_port(&mut serial_connection, CONTROLLERS_A.as_mut());
    serial_connection.deactivate();
}

fn process_port(serial_connection: &mut SerialConnection, port_slots: &mut [ControllerSlot; CONTROLLER_SLOT_COUNT]) {
    for slot in port_slots.iter_mut() {
        process_controller(serial_connection, &mut slot.controller, &slot.configuration, 0);
    }
}

fn process_controller(serial_connection: &mut SerialConnection, controller: &mut Option<RawController>, configuration: &Option<Configuration>, slot: u8) {
    if let Some(existing_controller) = controller {
        if let Err(_) = process_existing_controller(serial_connection, existing_controller, configuration, slot) {
            *controller = None;
        }
    }
//...

#[allow(named_asm_labels)]
fn process_existing_controller(serial_connection: &mut SerialConnection, controller: &mut RawController, configuration: &Option<Configuration>, slot: u8) -> Result<(), SerialConnectionError> {
    match controller.get_state() {
        ControllerState::New    => {
            unsafe { asm!("planschi:"); }
            if configuration.is_some() {
                if serial_connection.enter_config_mode(slot, true).is_ok() {
                    controller.set_config_mode();
                    return Ok(());
                }
            }
            
            controller.set_stable();
            Ok(())
        },
        ControllerState::InConfigMode(current_config) => {
//...

To not leave you empty handed I uploaded the assembly and the LLVM bit code files for each case. They all include the asm label planschi:.

  • The original (CONTROLLER_SLOT_COUNT = 1) faulty case can be found here.
  • The good case with CONTROLLER_SLOT_COUNT = 2 can be found here.
  • The other good case without the array can be found here.

Meta

rustc +psx --version --verbose:

rustc 1.92.0-dev
binary: rustc
commit-hash: unknown
commit-date: unknown
host: x86_64-unknown-linux-gnu
release: 1.92.0-dev
LLVM version: 21.1.3

Since I am working on making a game for the PS1 (--target mipsel-sony-psx), I had to build rust from source with the following command and config.toml:

profile   = "compiler"
change-id = 999999

[llvm]
optimize         = true
download-ci-llvm = false

[rust]
optimize = true
lld      = true

[build]
build-stage = 1
#The tripple for the bootstrap, this must be one that already exists as pre-compiled
build = "x86_64-unknown-linux-gnu"
#The tripple for the host (computer running the build)
host  = ["x86_64-unknown-linux-gnu"]
#The tripple for the output 
target = ["mipsel-sony-psx"]

command: ./x.py build rustc compiler/rustc_driver compiler/rustc_driver_impl library/core && ./x.py build library --target x86_64-unknown-linux-gnu && ./x.py build library src/tools/rust-analyzer src/tools/rust-analyzer/crates/proc-macro-srv-cli --target x86_64-unknown-linux-gnu --stage 2

This is my first time opening such an issue, any advice on how I can help with this is much appreciated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-bugCategory: This is a bug.I-miscompileIssue: Correct Rust code lowers to incorrect machine codeO-MIPSTarget: MIPS processorsP-lowLow priorityS-needs-reproStatus: This issue has no reproduction and needs a reproduction to make progress.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions