The branch delay slot
In every MIPS instruction set before Release 6 the instruction after a jump or a branch, its delay slot, is executed
before the jump or branch takes effect. So in jal f followed by addiu $a0, $zero, 5, the
addiu runs first and the call returns to the instruction after the delay slot. The return address that
jal, jalr, bal and the other linking instructions write is the address of the delay slot plus four:
the address of the branch plus eight. A branch target is counted from the delay slot.
The explorer marks the delay slot instruction in the disassembly (“delay slot”) so that a program is read in the order the
processor runs it. The instructions that have a delay slot are all the jumps (j, jal, jr,
jalr), all the conditional branches before Release 6, and in Release 6 beq, bne,
blez, bgtz, bltz, bgez, bal and the jumps. A jump or branch in a
delay slot has an unpredictable result.
Branch likely
From MIPS II the “likely” branches (beql, bnel, blezl, bgtzl, bltzl,
bgezl, bltzall, bgezall, bc1fl, bc1tl and the coprocessor ones) execute the delay slot only
when the branch is taken; when it is not, the delay slot instruction is cancelled. The explorer says so on the delay slot.
Release 6 removes them, and MIPS32 Release 2 deprecates them.
The MIPS I load delay slot
In MIPS I (the R2000 and R3000, so the PlayStation's R3000A) a load does not deliver its value to the very next instruction. The instruction
right after lw $v0, 0($a0) still sees the old value of $v0; the loaded value is visible from the second instruction on. The
same holds for lb, lh, lwl, lwr, lbu, lhu and for the coprocessor moves
mfc0, mfc1, mfc2, cfc1 and cfc2. Assemblers fill the slot with a nop or a useful instruction that
does not use the register. MIPS II and later have interlocks: the next instruction waits, so no code needs to avoid the slot.
When the variant is MIPS I or the PlayStation CPU, the explorer notes on each load that it has a load delay, and on the next instruction when that instruction reads the register just loaded (“load delay: reads the old value”). On a real R3000A that is how the program behaves; it is a sign of hand-written assembly or a bug, as compilers never emit it.
The two loads that write halves of the same register, lwl and lwr, may follow each other: the second one sees the first one's
partial result without a delay, which is how an unaligned word is loaded in two instructions.
Release 6: compact branches and the forbidden slot
Release 6 adds compact branches and jumps (bc, balc, beqc, bnec, jic, jialc and the
others, see Release 6) that have no delay slot: the next instruction executes only when the branch is not
taken. That instruction is called the forbidden slot: it must not be a branch or a jump, because the processor may
trap. The explorer marks it “forbidden slot”. The old delay-slot branches stay in Release 6.
Not described
Known, not yet covered
- Reference: exceptions in a delay slot (the Cause BD bit and EPC pointing at the branch), which belong to the system control page.
- Viewer: a hazard check for MIPS32 and later (the mfc0 to eret hazards need the
ehborjr.hbbarrier); the viewer only shows the MIPS I load delay.
Unknown
- Reference: the cases where a PlayStation load in a delay slot of another load to the same register behaves in an undocumented way on the real hardware.