My situation is a little bit different since I only use one thread (the main application thread).
Now, from what I've read about PPC exception, the FPU is generally disabled during the IRQ handler then restored when returning from exception, by saving MSR initial bits value in SRR1 then clearing the FP bit (bit 18) in MSR (the return from interrupt instruction restoring MSR bits from SRR1) before executing interrupt routine.
In libogc IRQ handler however, it seems the FP bit is not cleared when entering the handler and instead cleared on exception return:
in irq_hanlder.S, irq_exceptionhandler after c_irqdispatcher
Code: Select all
EXCEPTION_EPILOG
mfmsr r4
rlwinm r4,r4,0,19,17 // bit 18 is cleared
rlwinm r4,r4,0,31,29
mtmsr r4 // this clears FP bit in MSR (why here ?)
isync
lwz r0,GPR0_OFFSET(sp)
lwz toc,GPR2_OFFSET(sp)
lwz r4,SRR0_OFFSET(sp)
mtsrr0 r4
lwz r4,SRR1_OFFSET(sp) // retrieve stacked SRR1
rlwinm r4,r4,0,19,17 // bit 18 is cleared (should not be done to keep the saved value of FP ?)
mtsrr1 r4 // this clears FP bit in SRR1
lwz r4,GPR4_OFFSET(sp)
lwz r3,GPR3_OFFSET(sp)
addi sp,sp,EXCEPTION_FRAME_END
rfi // return from interrupt, MSR bits 16-31 are copied with SRR1 bits 16-31
maybe I am wrongly reading the assembly code but it seems to me it leaves FPU disabled when returning from interrupt ?
If I'm understanding well, the FPU is re-enabled when FPU exception occurs, which I imagine happen with the first FPU instruction executed with FPU disabled. Maybe this very first instruction is not executed correctly ?
EDIT: I just removed the line that clears SRR1 bit 18 in irq_exceptionhandler, recompiled libogc and it fixed my issue as well (without the need of IRQ_Disable/IRQ_Restore)
I'm not sure if it has any unknown side effect in libogc to have FPU enabled on exception exit though (for multi-threading implementation maybe ?)
EDIT2: Ive been studying libogc some more and it seems that FPU is disabled by default in thread contexts (see __lwp_thread_loadenv, called by __lwp_thread_start in lwp_threads.c). While it doesn't matter when using only one thread (the FPU is initially enabled), it would however disable FPU when switching threads and restoring thread context . According to the PowerPC 7xx user manual, when the FPU is disabled and a FPU instruction is dispatched, FPU exception occurs (which, in the case of libogc implementation, would re-enable FPU) but the instruction that caused the exception
is not executed, which can obviousy lead to wrong results in floating point operations.
I believe this is what happen: actually, if external interrupt (irq_exceptionhandler), decrementer exception (dec_exceptionhandler) or thread switching (_cpu_context_switch, _cpu_context_switch_ex, _cpu_context_restore) occurs in the middle of such operation, in all cases, FPU is forced and left disabled and the first floating point instruction that is dispatched when the preempted thread returns is not executed properly.
On that note, in the case of thread switching, it seems to me there is actually no point in restoring the MSR since this is called in an exception callback (__thread_dispatch in decrementer exception) and it will be overriden with SRR1 content when returning from the exception (rfi instruction). This SRR1 register is reseted at the beginning of exception handler, with current MSR bits (which is recommended exception implementation afaik) thus it holds dispatched thread MSR. For an unknown reason, FP bit is forced to 1 in SRR1 after that (exceptionhandler_start) then generally cleared (except for FPU and default exception handler) just before calling rfi instruction, leaving FPU disabled when returning from exception. I might be wrong but it seems to me there is something weird in libogc regarding exception implementation and MSR/SRR1 registers handling.