forums.ps2dev.org Forum Index forums.ps2dev.org
Homebrew PS2, PSP & PS3 Development Discussions
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

[VFPU] lvl.q/lvr.q bug : they screw up a FPU reg !

 
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development
View previous topic :: View next topic  
Author Message
hlide



Joined: 10 Sep 2006
Posts: 750

PostPosted: Mon Jul 07, 2008 12:29 am    Post subject: [VFPU] lvl.q/lvr.q bug : they screw up a FPU reg ! Reply with quote

I have two PSPs : one is my old fat PSP and the other a slim PSP.

I run a program using PSPLINK to check up a curious bug about lvl.q/lvr.q.

What is the bug ?

executing a LVL.Q or LVR.Q with a vector register scratches a FPU register this way : if VFPU register number is $N, the FPU register $fN is assigned with 0.0 (at least, this is the value I get on my fat PSP). This is a major issue for callee-saved FPU register which must be invariant before entering the callee function and after exiting the callee function, especially when this callee function doesn't use those callee-saved FPU registers.

On my slim PSP, I don't have this hardware issue so I guess Sony correct it.

Here is a piece of my program to test this bug :

Code:

...

float vec4[4] __attribute__((aligned(16))) = { 1.0, 2.0, 3.0, 4.0 };
float expected_value = -1.0;

template< int m, int c >
float vfpu_test_lvlq(float *vec)
{
    float res = expected_value;

    asm volatile
    (
        ".set    push             " "\n"
        ".set    noreorder        " "\n"
        "mov.s   $f%2, %0         " "\n"
        "lvl.q   $%2,12(%1)       " "\n" // lvl.q Cmc0.q, 12(%1)
        "mov.s   %0, $f%2         " "\n"
        ".set    pop              " "\n"
        : "+f"(res) : "r"(vec), "i"(m*4+c) : "memory"
    );

    return res;
}

template< int m, int c >
float vfpu_test_lvrq(float *vec)
{
    float res = expected_value;

    asm volatile
    (
        ".set    push             " "\n"
        ".set    noreorder        " "\n"
        "mov.s   $f%2, %0         " "\n"
        "lvr.q   $%2, 0(%1)       " "\n" // lvr.q Cmc0.q, 0(%1)
        "mov.s   %0, $f%2         " "\n"
        ".set    pop              " "\n"
        : "+f"(res) : "r"(vec), "i"(m*4+c) : "memory"
    );

    return res;
}

int main(int argc, char *argv[])
{

    printf("\n$f0 = %f, %f", vfpu_test_lvlq<0, 0>(vec4), vfpu_test_lvrq<0, 0>(vec4));
    printf("\n$f1 = %f, %f", vfpu_test_lvlq<0, 1>(vec4), vfpu_test_lvrq<0, 1>(vec4));
    printf("\n$f2 = %f, %f", vfpu_test_lvlq<0, 2>(vec4), vfpu_test_lvrq<0, 2>(vec4));
    printf("\n$f3 = %f, %f", vfpu_test_lvlq<0, 3>(vec4), vfpu_test_lvrq<0, 3>(vec4));
    printf("\n$f4 = %f, %f", vfpu_test_lvlq<1, 0>(vec4), vfpu_test_lvrq<1, 0>(vec4));
    printf("\n$f5 = %f, %f", vfpu_test_lvlq<1, 1>(vec4), vfpu_test_lvrq<1, 1>(vec4));
    printf("\n$f6 = %f, %f", vfpu_test_lvlq<1, 2>(vec4), vfpu_test_lvrq<1, 2>(vec4));
    printf("\n$f7 = %f, %f", vfpu_test_lvlq<1, 3>(vec4), vfpu_test_lvrq<1, 3>(vec4));
    printf("\n$f8 = %f, %f", vfpu_test_lvlq<2, 0>(vec4), vfpu_test_lvrq<2, 0>(vec4));
    printf("\n$f9 = %f, %f", vfpu_test_lvlq<2, 1>(vec4), vfpu_test_lvrq<2, 1>(vec4));
    printf("\n$f10= %f, %f", vfpu_test_lvlq<2, 2>(vec4), vfpu_test_lvrq<2, 2>(vec4));
    printf("\n$f11= %f, %f", vfpu_test_lvlq<2, 3>(vec4), vfpu_test_lvrq<2, 3>(vec4));
    printf("\n$f12= %f, %f", vfpu_test_lvlq<3, 0>(vec4), vfpu_test_lvrq<3, 0>(vec4));
    printf("\n$f13= %f, %f", vfpu_test_lvlq<3, 1>(vec4), vfpu_test_lvrq<3, 1>(vec4));
    printf("\n$f14= %f, %f", vfpu_test_lvlq<3, 2>(vec4), vfpu_test_lvrq<3, 2>(vec4));
    printf("\n$f15= %f, %f", vfpu_test_lvlq<3, 3>(vec4), vfpu_test_lvrq<3, 3>(vec4));
    printf("\n$f16= %f, %f", vfpu_test_lvlq<4, 0>(vec4), vfpu_test_lvrq<4, 0>(vec4));
    printf("\n$f17= %f, %f", vfpu_test_lvlq<4, 1>(vec4), vfpu_test_lvrq<4, 1>(vec4));
    printf("\n$f18= %f, %f", vfpu_test_lvlq<4, 2>(vec4), vfpu_test_lvrq<4, 2>(vec4));
    printf("\n$f19= %f, %f", vfpu_test_lvlq<4, 3>(vec4), vfpu_test_lvrq<4, 3>(vec4));
    printf("\n$f20= %f, %f", vfpu_test_lvlq<5, 0>(vec4), vfpu_test_lvrq<5, 0>(vec4));
    printf("\n$f21= %f, %f", vfpu_test_lvlq<5, 1>(vec4), vfpu_test_lvrq<5, 1>(vec4));
    printf("\n$f22= %f, %f", vfpu_test_lvlq<5, 2>(vec4), vfpu_test_lvrq<5, 2>(vec4));
    printf("\n$f23= %f, %f", vfpu_test_lvlq<5, 3>(vec4), vfpu_test_lvrq<5, 3>(vec4));
    printf("\n$f24= %f, %f", vfpu_test_lvlq<6, 0>(vec4), vfpu_test_lvrq<6, 0>(vec4));
    printf("\n$f25= %f, %f", vfpu_test_lvlq<6, 1>(vec4), vfpu_test_lvrq<6, 1>(vec4));
    printf("\n$f26= %f, %f", vfpu_test_lvlq<6, 2>(vec4), vfpu_test_lvrq<6, 2>(vec4));
    printf("\n$f27= %f, %f", vfpu_test_lvlq<6, 3>(vec4), vfpu_test_lvrq<6, 3>(vec4));
    printf("\n$f28= %f, %f", vfpu_test_lvlq<7, 0>(vec4), vfpu_test_lvrq<7, 0>(vec4));
    printf("\n$f29= %f, %f", vfpu_test_lvlq<7, 1>(vec4), vfpu_test_lvrq<7, 1>(vec4));
    printf("\n$f30= %f, %f", vfpu_test_lvlq<7, 2>(vec4), vfpu_test_lvrq<7, 2>(vec4));
    printf("\n$f31= %f, %f", vfpu_test_lvlq<7, 3>(vec4), vfpu_test_lvrq<7, 3>(vec4));
    printf("\n");

    sceKernelExitGame();

    return 0;
}

...


normally, we should have -1.0 for all the FPU registers (as it is the case for slim PSP), but for fat PSP I get 0.0 (no idea where this value comes from).
Back to top
View user's profile Send private message
adrahil



Joined: 16 Mar 2006
Posts: 277

PostPosted: Mon Jul 07, 2008 1:14 am    Post subject: Reply with quote

Strange problem indeed...
Back to top
View user's profile Send private message
crazyc



Joined: 17 Jun 2005
Posts: 410

PostPosted: Mon Jul 07, 2008 2:00 am    Post subject: Reply with quote

It might have something to do with the way those opcodes are encoded.

Code:
# float vfpu_test_lvlq<0, 0>(float *)
               lwc1    $f0, -0x7FF0($gp)
               mov.s   $f0, $f0
               ldc1    $f0, 0xC($a0)
               mov.s   $f0, $f0
               jr      $ra
               nop
# End of function vfpu_test_lvlq<0,0>(float *)

# float vfpu_test_lvrq<0, 0>(float *)
               lwc1    $f0, -0x7FF0($gp)
               mov.s   $f0, $f0
               ldc1    $f0, 2($a0)
               mov.s   $f0, $f0
               jr      $ra
               nop
# End of function vfpu_test_lvrq<0,0>(float *)

# float vfpu_test_lvq<0, 0>(float *)
               lwc1    $f0, -0x7FF0($gp)
               mov.s   $f0, $f0
               ldc2    $0, 0($a0)
               mov.s   $f0, $f0
               jr      $ra
               nop
# End of function vfpu_test_lvq<0,0>(float *)


ldc1 may be executed along with lvl.q.
Back to top
View user's profile Send private message
hlide



Joined: 10 Sep 2006
Posts: 750

PostPosted: Mon Jul 07, 2008 2:33 am    Post subject: Reply with quote

crazyc wrote:
ldc1 may be executed along with lvl.q.


Not only lvl.q but lvr.q as they share the same opcode (bit 1 in offset tells us if it is lvl or lvr and bit 0 in offset tells us if it is a column or row register). So, yes. That's a perfect explanation ! SONY simply forget to "disable" the FPU register write back circuitry for ldc1. A very stupid error indeed.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.ps2dev.org Forum Index -> PSP Development All times are GMT + 10 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group