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: Select all
...
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;
}
...