frno7 / linux

Linux 2.2, 2.6, 3.x, 4.x and 5.x kernels for the PlayStation 2.
Other
86 stars 5 forks source link

Workaround for the R5900 short loop hardware bug #8

Open frno7 opened 5 years ago

frno7 commented 5 years ago

The R5900 short loop hardware bug under certain conditions causes loops to execute only once or twice. The Gnu Assembler (GAS) has the following note about it:

On the R5900 short loops need to be fixed by inserting a NOP in the branch delay slot.

The short loop bug under certain conditions causes loops to execute only once or twice. We must ensure that the assembler never generates loops that satisfy all of the following conditions:

  • a loop consists of less than or equal to six instructions (including the branch delay slot);
  • a loop contains only one conditional branch instruction at the end of the loop;
  • a loop does not contain any other branch or jump instructions;
  • a branch delay slot of the loop is not NOP (EE 2.9 or later).

We need to do this because of a hardware bug in the R5900 chip.

GAS handles this short loop bug in the majority of cases. However, GAS is unable to correct machine code in the following two cases:

  1. Assembly code making use of the noreorder directive, as used by the kernel on several occasions: Commit a3559f2df0ba6f227ed88adb1adb60e34dcbc254 is a rough workaround sketch, with a lot of unnecessary NOPs, that can be improved, perhaps using some kind of R5900 hazard macro? GAS could be improved by emitting warnings on the relevant occasions, because finding the places needing corrections is difficult, whereas manual adjustments are often easy once the problematic places are known.
  2. Machine code not generated by GAS: The kernel generates machine code by itself, for example to handle exceptions. Assuming such code has loops, it needs to be verified with regard to the conditions for the short loop bug, for example by inspecting kernel memory at run-time.

Note that this R5900 short loop hardware bug also affects user space code, which is why generic MIPS code cannot be used uncorrected with the R5900 and the PlayStation 2. The GAS and GCC option -mfix-r5900 can be used in such cases.

Ravenslofty commented 5 years ago

I can't recall the exact source, but I'm pretty sure I saw that this got fixed in 2.0 silicon. We'll have to look at the decap.

mirh commented 5 years ago

https://assemblergames.com/threads/the-ee-short-loop-bug-how-many-versions-of-it-were-there.62101/

frno7 commented 4 years ago

I have a simple tool in commit 59a11ab94a4020408c7dfbf92bd55778e17b43f1 that can detect problematic short loops in ELF files. I’ve used it to find most if not actually all of the remaining cases in the Linux kernel, most notably with inline assembly and the noreorder directive. Many noreorder directives have recently been revised and often removed in the Linux kernel, mainly due to the introduction of the microMIPSr6 ISA.

Recent GCC seems to emit a particular machine code sequence that is formally undefined for the R5900 but it seems to work in practice. It turns out GCC emits assembly code like

loop:   addiu   $5,$5,1
        addiu   $4,$4,1
        lb      $2,-1($5)
        .set    noreorder
        .set    nomacro
        bne     $2,$3,loop
        sb      $2,-1($4)
        .set    macro
        .set    reorder

that is undefined for the R5900 (this short loop has five instructions), for simple C code such as

        while ((*s++ = *p++) != '\n')
                ;

I believe that GCC is only case that remains to be investigated in this issue. A GCC patch proposal was posted as MIPS: Fix GCC noreorder for undefined R5900 short loops to the GCC mailing list, but it must be revised to proceed.