in arch/mips/math-emu/cp1emu.c [1682:2806]
static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
mips_instruction ir)
{
int rfmt; /* resulting format */
unsigned int rcsr = 0; /* resulting csr */
unsigned int oldrm;
unsigned int cbit;
unsigned int cond;
union {
union ieee754dp d;
union ieee754sp s;
int w;
s64 l;
} rv; /* resulting value */
u64 bits;
MIPS_FPU_EMU_INC_STATS(cp1ops);
switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) {
case s_fmt: { /* 0 */
union {
union ieee754sp(*b) (union ieee754sp, union ieee754sp);
union ieee754sp(*u) (union ieee754sp);
} handler;
union ieee754sp fd, fs, ft;
switch (MIPSInst_FUNC(ir)) {
/* binary ops */
case fadd_op:
MIPS_FPU_EMU_INC_STATS(add_s);
handler.b = ieee754sp_add;
goto scopbop;
case fsub_op:
MIPS_FPU_EMU_INC_STATS(sub_s);
handler.b = ieee754sp_sub;
goto scopbop;
case fmul_op:
MIPS_FPU_EMU_INC_STATS(mul_s);
handler.b = ieee754sp_mul;
goto scopbop;
case fdiv_op:
MIPS_FPU_EMU_INC_STATS(div_s);
handler.b = ieee754sp_div;
goto scopbop;
/* unary ops */
case fsqrt_op:
if (!cpu_has_mips_2_3_4_5_r)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(sqrt_s);
handler.u = ieee754sp_sqrt;
goto scopuop;
/*
* Note that on some MIPS IV implementations such as the
* R5000 and R8000 the FSQRT and FRECIP instructions do not
* achieve full IEEE-754 accuracy - however this emulator does.
*/
case frsqrt_op:
if (!cpu_has_mips_4_5_64_r2_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(rsqrt_s);
handler.u = fpemu_sp_rsqrt;
goto scopuop;
case frecip_op:
if (!cpu_has_mips_4_5_64_r2_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(recip_s);
handler.u = fpemu_sp_recip;
goto scopuop;
case fmovc_op:
if (!cpu_has_mips_4_5_r)
return SIGILL;
cond = fpucondbit[MIPSInst_FT(ir) >> 2];
if (((ctx->fcr31 & cond) != 0) !=
((MIPSInst_FT(ir) & 1) != 0))
return 0;
SPFROMREG(rv.s, MIPSInst_FS(ir));
break;
case fmovz_op:
if (!cpu_has_mips_4_5_r)
return SIGILL;
if (xcp->regs[MIPSInst_FT(ir)] != 0)
return 0;
SPFROMREG(rv.s, MIPSInst_FS(ir));
break;
case fmovn_op:
if (!cpu_has_mips_4_5_r)
return SIGILL;
if (xcp->regs[MIPSInst_FT(ir)] == 0)
return 0;
SPFROMREG(rv.s, MIPSInst_FS(ir));
break;
case fseleqz_op:
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(seleqz_s);
SPFROMREG(rv.s, MIPSInst_FT(ir));
if (rv.w & 0x1)
rv.w = 0;
else
SPFROMREG(rv.s, MIPSInst_FS(ir));
break;
case fselnez_op:
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(selnez_s);
SPFROMREG(rv.s, MIPSInst_FT(ir));
if (rv.w & 0x1)
SPFROMREG(rv.s, MIPSInst_FS(ir));
else
rv.w = 0;
break;
case fmaddf_op: {
union ieee754sp ft, fs, fd;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(maddf_s);
SPFROMREG(ft, MIPSInst_FT(ir));
SPFROMREG(fs, MIPSInst_FS(ir));
SPFROMREG(fd, MIPSInst_FD(ir));
rv.s = ieee754sp_maddf(fd, fs, ft);
goto copcsr;
}
case fmsubf_op: {
union ieee754sp ft, fs, fd;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(msubf_s);
SPFROMREG(ft, MIPSInst_FT(ir));
SPFROMREG(fs, MIPSInst_FS(ir));
SPFROMREG(fd, MIPSInst_FD(ir));
rv.s = ieee754sp_msubf(fd, fs, ft);
goto copcsr;
}
case frint_op: {
union ieee754sp fs;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(rint_s);
SPFROMREG(fs, MIPSInst_FS(ir));
rv.s = ieee754sp_rint(fs);
goto copcsr;
}
case fclass_op: {
union ieee754sp fs;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(class_s);
SPFROMREG(fs, MIPSInst_FS(ir));
rv.w = ieee754sp_2008class(fs);
rfmt = w_fmt;
goto copcsr;
}
case fmin_op: {
union ieee754sp fs, ft;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(min_s);
SPFROMREG(ft, MIPSInst_FT(ir));
SPFROMREG(fs, MIPSInst_FS(ir));
rv.s = ieee754sp_fmin(fs, ft);
goto copcsr;
}
case fmina_op: {
union ieee754sp fs, ft;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(mina_s);
SPFROMREG(ft, MIPSInst_FT(ir));
SPFROMREG(fs, MIPSInst_FS(ir));
rv.s = ieee754sp_fmina(fs, ft);
goto copcsr;
}
case fmax_op: {
union ieee754sp fs, ft;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(max_s);
SPFROMREG(ft, MIPSInst_FT(ir));
SPFROMREG(fs, MIPSInst_FS(ir));
rv.s = ieee754sp_fmax(fs, ft);
goto copcsr;
}
case fmaxa_op: {
union ieee754sp fs, ft;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(maxa_s);
SPFROMREG(ft, MIPSInst_FT(ir));
SPFROMREG(fs, MIPSInst_FS(ir));
rv.s = ieee754sp_fmaxa(fs, ft);
goto copcsr;
}
case fabs_op:
MIPS_FPU_EMU_INC_STATS(abs_s);
handler.u = ieee754sp_abs;
goto scopuop;
case fneg_op:
MIPS_FPU_EMU_INC_STATS(neg_s);
handler.u = ieee754sp_neg;
goto scopuop;
case fmov_op:
/* an easy one */
MIPS_FPU_EMU_INC_STATS(mov_s);
SPFROMREG(rv.s, MIPSInst_FS(ir));
goto copcsr;
/* binary op on handler */
scopbop:
SPFROMREG(fs, MIPSInst_FS(ir));
SPFROMREG(ft, MIPSInst_FT(ir));
rv.s = (*handler.b) (fs, ft);
goto copcsr;
scopuop:
SPFROMREG(fs, MIPSInst_FS(ir));
rv.s = (*handler.u) (fs);
goto copcsr;
copcsr:
if (ieee754_cxtest(IEEE754_INEXACT)) {
MIPS_FPU_EMU_INC_STATS(ieee754_inexact);
rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S;
}
if (ieee754_cxtest(IEEE754_UNDERFLOW)) {
MIPS_FPU_EMU_INC_STATS(ieee754_underflow);
rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S;
}
if (ieee754_cxtest(IEEE754_OVERFLOW)) {
MIPS_FPU_EMU_INC_STATS(ieee754_overflow);
rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S;
}
if (ieee754_cxtest(IEEE754_ZERO_DIVIDE)) {
MIPS_FPU_EMU_INC_STATS(ieee754_zerodiv);
rcsr |= FPU_CSR_DIV_X | FPU_CSR_DIV_S;
}
if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) {
MIPS_FPU_EMU_INC_STATS(ieee754_invalidop);
rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S;
}
break;
/* unary conv ops */
case fcvts_op:
return SIGILL; /* not defined */
case fcvtd_op:
MIPS_FPU_EMU_INC_STATS(cvt_d_s);
SPFROMREG(fs, MIPSInst_FS(ir));
rv.d = ieee754dp_fsp(fs);
rfmt = d_fmt;
goto copcsr;
case fcvtw_op:
MIPS_FPU_EMU_INC_STATS(cvt_w_s);
SPFROMREG(fs, MIPSInst_FS(ir));
rv.w = ieee754sp_tint(fs);
rfmt = w_fmt;
goto copcsr;
case fround_op:
case ftrunc_op:
case fceil_op:
case ffloor_op:
if (!cpu_has_mips_2_3_4_5_r)
return SIGILL;
if (MIPSInst_FUNC(ir) == fceil_op)
MIPS_FPU_EMU_INC_STATS(ceil_w_s);
if (MIPSInst_FUNC(ir) == ffloor_op)
MIPS_FPU_EMU_INC_STATS(floor_w_s);
if (MIPSInst_FUNC(ir) == fround_op)
MIPS_FPU_EMU_INC_STATS(round_w_s);
if (MIPSInst_FUNC(ir) == ftrunc_op)
MIPS_FPU_EMU_INC_STATS(trunc_w_s);
oldrm = ieee754_csr.rm;
SPFROMREG(fs, MIPSInst_FS(ir));
ieee754_csr.rm = MIPSInst_FUNC(ir);
rv.w = ieee754sp_tint(fs);
ieee754_csr.rm = oldrm;
rfmt = w_fmt;
goto copcsr;
case fsel_op:
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(sel_s);
SPFROMREG(fd, MIPSInst_FD(ir));
if (fd.bits & 0x1)
SPFROMREG(rv.s, MIPSInst_FT(ir));
else
SPFROMREG(rv.s, MIPSInst_FS(ir));
break;
case fcvtl_op:
if (!cpu_has_mips_3_4_5_64_r2_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(cvt_l_s);
SPFROMREG(fs, MIPSInst_FS(ir));
rv.l = ieee754sp_tlong(fs);
rfmt = l_fmt;
goto copcsr;
case froundl_op:
case ftruncl_op:
case fceill_op:
case ffloorl_op:
if (!cpu_has_mips_3_4_5_64_r2_r6)
return SIGILL;
if (MIPSInst_FUNC(ir) == fceill_op)
MIPS_FPU_EMU_INC_STATS(ceil_l_s);
if (MIPSInst_FUNC(ir) == ffloorl_op)
MIPS_FPU_EMU_INC_STATS(floor_l_s);
if (MIPSInst_FUNC(ir) == froundl_op)
MIPS_FPU_EMU_INC_STATS(round_l_s);
if (MIPSInst_FUNC(ir) == ftruncl_op)
MIPS_FPU_EMU_INC_STATS(trunc_l_s);
oldrm = ieee754_csr.rm;
SPFROMREG(fs, MIPSInst_FS(ir));
ieee754_csr.rm = MIPSInst_FUNC(ir);
rv.l = ieee754sp_tlong(fs);
ieee754_csr.rm = oldrm;
rfmt = l_fmt;
goto copcsr;
default:
if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) {
unsigned int cmpop;
union ieee754sp fs, ft;
cmpop = MIPSInst_FUNC(ir) - fcmp_op;
SPFROMREG(fs, MIPSInst_FS(ir));
SPFROMREG(ft, MIPSInst_FT(ir));
rv.w = ieee754sp_cmp(fs, ft,
cmptab[cmpop & 0x7], cmpop & 0x8);
rfmt = -1;
if ((cmpop & 0x8) && ieee754_cxtest
(IEEE754_INVALID_OPERATION))
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
else
goto copcsr;
} else
return SIGILL;
break;
}
break;
}
case d_fmt: {
union ieee754dp fd, fs, ft;
union {
union ieee754dp(*b) (union ieee754dp, union ieee754dp);
union ieee754dp(*u) (union ieee754dp);
} handler;
switch (MIPSInst_FUNC(ir)) {
/* binary ops */
case fadd_op:
MIPS_FPU_EMU_INC_STATS(add_d);
handler.b = ieee754dp_add;
goto dcopbop;
case fsub_op:
MIPS_FPU_EMU_INC_STATS(sub_d);
handler.b = ieee754dp_sub;
goto dcopbop;
case fmul_op:
MIPS_FPU_EMU_INC_STATS(mul_d);
handler.b = ieee754dp_mul;
goto dcopbop;
case fdiv_op:
MIPS_FPU_EMU_INC_STATS(div_d);
handler.b = ieee754dp_div;
goto dcopbop;
/* unary ops */
case fsqrt_op:
if (!cpu_has_mips_2_3_4_5_r)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(sqrt_d);
handler.u = ieee754dp_sqrt;
goto dcopuop;
/*
* Note that on some MIPS IV implementations such as the
* R5000 and R8000 the FSQRT and FRECIP instructions do not
* achieve full IEEE-754 accuracy - however this emulator does.
*/
case frsqrt_op:
if (!cpu_has_mips_4_5_64_r2_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(rsqrt_d);
handler.u = fpemu_dp_rsqrt;
goto dcopuop;
case frecip_op:
if (!cpu_has_mips_4_5_64_r2_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(recip_d);
handler.u = fpemu_dp_recip;
goto dcopuop;
case fmovc_op:
if (!cpu_has_mips_4_5_r)
return SIGILL;
cond = fpucondbit[MIPSInst_FT(ir) >> 2];
if (((ctx->fcr31 & cond) != 0) !=
((MIPSInst_FT(ir) & 1) != 0))
return 0;
DPFROMREG(rv.d, MIPSInst_FS(ir));
break;
case fmovz_op:
if (!cpu_has_mips_4_5_r)
return SIGILL;
if (xcp->regs[MIPSInst_FT(ir)] != 0)
return 0;
DPFROMREG(rv.d, MIPSInst_FS(ir));
break;
case fmovn_op:
if (!cpu_has_mips_4_5_r)
return SIGILL;
if (xcp->regs[MIPSInst_FT(ir)] == 0)
return 0;
DPFROMREG(rv.d, MIPSInst_FS(ir));
break;
case fseleqz_op:
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(seleqz_d);
DPFROMREG(rv.d, MIPSInst_FT(ir));
if (rv.l & 0x1)
rv.l = 0;
else
DPFROMREG(rv.d, MIPSInst_FS(ir));
break;
case fselnez_op:
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(selnez_d);
DPFROMREG(rv.d, MIPSInst_FT(ir));
if (rv.l & 0x1)
DPFROMREG(rv.d, MIPSInst_FS(ir));
else
rv.l = 0;
break;
case fmaddf_op: {
union ieee754dp ft, fs, fd;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(maddf_d);
DPFROMREG(ft, MIPSInst_FT(ir));
DPFROMREG(fs, MIPSInst_FS(ir));
DPFROMREG(fd, MIPSInst_FD(ir));
rv.d = ieee754dp_maddf(fd, fs, ft);
goto copcsr;
}
case fmsubf_op: {
union ieee754dp ft, fs, fd;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(msubf_d);
DPFROMREG(ft, MIPSInst_FT(ir));
DPFROMREG(fs, MIPSInst_FS(ir));
DPFROMREG(fd, MIPSInst_FD(ir));
rv.d = ieee754dp_msubf(fd, fs, ft);
goto copcsr;
}
case frint_op: {
union ieee754dp fs;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(rint_d);
DPFROMREG(fs, MIPSInst_FS(ir));
rv.d = ieee754dp_rint(fs);
goto copcsr;
}
case fclass_op: {
union ieee754dp fs;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(class_d);
DPFROMREG(fs, MIPSInst_FS(ir));
rv.l = ieee754dp_2008class(fs);
rfmt = l_fmt;
goto copcsr;
}
case fmin_op: {
union ieee754dp fs, ft;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(min_d);
DPFROMREG(ft, MIPSInst_FT(ir));
DPFROMREG(fs, MIPSInst_FS(ir));
rv.d = ieee754dp_fmin(fs, ft);
goto copcsr;
}
case fmina_op: {
union ieee754dp fs, ft;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(mina_d);
DPFROMREG(ft, MIPSInst_FT(ir));
DPFROMREG(fs, MIPSInst_FS(ir));
rv.d = ieee754dp_fmina(fs, ft);
goto copcsr;
}
case fmax_op: {
union ieee754dp fs, ft;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(max_d);
DPFROMREG(ft, MIPSInst_FT(ir));
DPFROMREG(fs, MIPSInst_FS(ir));
rv.d = ieee754dp_fmax(fs, ft);
goto copcsr;
}
case fmaxa_op: {
union ieee754dp fs, ft;
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(maxa_d);
DPFROMREG(ft, MIPSInst_FT(ir));
DPFROMREG(fs, MIPSInst_FS(ir));
rv.d = ieee754dp_fmaxa(fs, ft);
goto copcsr;
}
case fabs_op:
MIPS_FPU_EMU_INC_STATS(abs_d);
handler.u = ieee754dp_abs;
goto dcopuop;
case fneg_op:
MIPS_FPU_EMU_INC_STATS(neg_d);
handler.u = ieee754dp_neg;
goto dcopuop;
case fmov_op:
/* an easy one */
MIPS_FPU_EMU_INC_STATS(mov_d);
DPFROMREG(rv.d, MIPSInst_FS(ir));
goto copcsr;
/* binary op on handler */
dcopbop:
DPFROMREG(fs, MIPSInst_FS(ir));
DPFROMREG(ft, MIPSInst_FT(ir));
rv.d = (*handler.b) (fs, ft);
goto copcsr;
dcopuop:
DPFROMREG(fs, MIPSInst_FS(ir));
rv.d = (*handler.u) (fs);
goto copcsr;
/*
* unary conv ops
*/
case fcvts_op:
MIPS_FPU_EMU_INC_STATS(cvt_s_d);
DPFROMREG(fs, MIPSInst_FS(ir));
rv.s = ieee754sp_fdp(fs);
rfmt = s_fmt;
goto copcsr;
case fcvtd_op:
return SIGILL; /* not defined */
case fcvtw_op:
MIPS_FPU_EMU_INC_STATS(cvt_w_d);
DPFROMREG(fs, MIPSInst_FS(ir));
rv.w = ieee754dp_tint(fs); /* wrong */
rfmt = w_fmt;
goto copcsr;
case fround_op:
case ftrunc_op:
case fceil_op:
case ffloor_op:
if (!cpu_has_mips_2_3_4_5_r)
return SIGILL;
if (MIPSInst_FUNC(ir) == fceil_op)
MIPS_FPU_EMU_INC_STATS(ceil_w_d);
if (MIPSInst_FUNC(ir) == ffloor_op)
MIPS_FPU_EMU_INC_STATS(floor_w_d);
if (MIPSInst_FUNC(ir) == fround_op)
MIPS_FPU_EMU_INC_STATS(round_w_d);
if (MIPSInst_FUNC(ir) == ftrunc_op)
MIPS_FPU_EMU_INC_STATS(trunc_w_d);
oldrm = ieee754_csr.rm;
DPFROMREG(fs, MIPSInst_FS(ir));
ieee754_csr.rm = MIPSInst_FUNC(ir);
rv.w = ieee754dp_tint(fs);
ieee754_csr.rm = oldrm;
rfmt = w_fmt;
goto copcsr;
case fsel_op:
if (!cpu_has_mips_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(sel_d);
DPFROMREG(fd, MIPSInst_FD(ir));
if (fd.bits & 0x1)
DPFROMREG(rv.d, MIPSInst_FT(ir));
else
DPFROMREG(rv.d, MIPSInst_FS(ir));
break;
case fcvtl_op:
if (!cpu_has_mips_3_4_5_64_r2_r6)
return SIGILL;
MIPS_FPU_EMU_INC_STATS(cvt_l_d);
DPFROMREG(fs, MIPSInst_FS(ir));
rv.l = ieee754dp_tlong(fs);
rfmt = l_fmt;
goto copcsr;
case froundl_op:
case ftruncl_op:
case fceill_op:
case ffloorl_op:
if (!cpu_has_mips_3_4_5_64_r2_r6)
return SIGILL;
if (MIPSInst_FUNC(ir) == fceill_op)
MIPS_FPU_EMU_INC_STATS(ceil_l_d);
if (MIPSInst_FUNC(ir) == ffloorl_op)
MIPS_FPU_EMU_INC_STATS(floor_l_d);
if (MIPSInst_FUNC(ir) == froundl_op)
MIPS_FPU_EMU_INC_STATS(round_l_d);
if (MIPSInst_FUNC(ir) == ftruncl_op)
MIPS_FPU_EMU_INC_STATS(trunc_l_d);
oldrm = ieee754_csr.rm;
DPFROMREG(fs, MIPSInst_FS(ir));
ieee754_csr.rm = MIPSInst_FUNC(ir);
rv.l = ieee754dp_tlong(fs);
ieee754_csr.rm = oldrm;
rfmt = l_fmt;
goto copcsr;
default:
if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) {
unsigned int cmpop;
union ieee754dp fs, ft;
cmpop = MIPSInst_FUNC(ir) - fcmp_op;
DPFROMREG(fs, MIPSInst_FS(ir));
DPFROMREG(ft, MIPSInst_FT(ir));
rv.w = ieee754dp_cmp(fs, ft,
cmptab[cmpop & 0x7], cmpop & 0x8);
rfmt = -1;
if ((cmpop & 0x8)
&&
ieee754_cxtest
(IEEE754_INVALID_OPERATION))
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
else
goto copcsr;
}
else {
return SIGILL;
}
break;
}
break;
}
case w_fmt: {
union ieee754dp fs;
switch (MIPSInst_FUNC(ir)) {
case fcvts_op:
/* convert word to single precision real */
MIPS_FPU_EMU_INC_STATS(cvt_s_w);
SPFROMREG(fs, MIPSInst_FS(ir));
rv.s = ieee754sp_fint(fs.bits);
rfmt = s_fmt;
goto copcsr;
case fcvtd_op:
/* convert word to double precision real */
MIPS_FPU_EMU_INC_STATS(cvt_d_w);
SPFROMREG(fs, MIPSInst_FS(ir));
rv.d = ieee754dp_fint(fs.bits);
rfmt = d_fmt;
goto copcsr;
default: {
/* Emulating the new CMP.condn.fmt R6 instruction */
#define CMPOP_MASK 0x7
#define SIGN_BIT (0x1 << 3)
#define PREDICATE_BIT (0x1 << 4)
int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK;
int sig = MIPSInst_FUNC(ir) & SIGN_BIT;
union ieee754sp fs, ft;
/* This is an R6 only instruction */
if (!cpu_has_mips_r6 ||
(MIPSInst_FUNC(ir) & 0x20))
return SIGILL;
if (!sig) {
if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) {
switch (cmpop) {
case 0:
MIPS_FPU_EMU_INC_STATS(cmp_af_s);
break;
case 1:
MIPS_FPU_EMU_INC_STATS(cmp_un_s);
break;
case 2:
MIPS_FPU_EMU_INC_STATS(cmp_eq_s);
break;
case 3:
MIPS_FPU_EMU_INC_STATS(cmp_ueq_s);
break;
case 4:
MIPS_FPU_EMU_INC_STATS(cmp_lt_s);
break;
case 5:
MIPS_FPU_EMU_INC_STATS(cmp_ult_s);
break;
case 6:
MIPS_FPU_EMU_INC_STATS(cmp_le_s);
break;
case 7:
MIPS_FPU_EMU_INC_STATS(cmp_ule_s);
break;
}
} else {
switch (cmpop) {
case 1:
MIPS_FPU_EMU_INC_STATS(cmp_or_s);
break;
case 2:
MIPS_FPU_EMU_INC_STATS(cmp_une_s);
break;
case 3:
MIPS_FPU_EMU_INC_STATS(cmp_ne_s);
break;
}
}
} else {
if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) {
switch (cmpop) {
case 0:
MIPS_FPU_EMU_INC_STATS(cmp_saf_s);
break;
case 1:
MIPS_FPU_EMU_INC_STATS(cmp_sun_s);
break;
case 2:
MIPS_FPU_EMU_INC_STATS(cmp_seq_s);
break;
case 3:
MIPS_FPU_EMU_INC_STATS(cmp_sueq_s);
break;
case 4:
MIPS_FPU_EMU_INC_STATS(cmp_slt_s);
break;
case 5:
MIPS_FPU_EMU_INC_STATS(cmp_sult_s);
break;
case 6:
MIPS_FPU_EMU_INC_STATS(cmp_sle_s);
break;
case 7:
MIPS_FPU_EMU_INC_STATS(cmp_sule_s);
break;
}
} else {
switch (cmpop) {
case 1:
MIPS_FPU_EMU_INC_STATS(cmp_sor_s);
break;
case 2:
MIPS_FPU_EMU_INC_STATS(cmp_sune_s);
break;
case 3:
MIPS_FPU_EMU_INC_STATS(cmp_sne_s);
break;
}
}
}
/* fmt is w_fmt for single precision so fix it */
rfmt = s_fmt;
/* default to false */
rv.w = 0;
/* CMP.condn.S */
SPFROMREG(fs, MIPSInst_FS(ir));
SPFROMREG(ft, MIPSInst_FT(ir));
/* positive predicates */
if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) {
if (ieee754sp_cmp(fs, ft, cmptab[cmpop],
sig))
rv.w = -1; /* true, all 1s */
if ((sig) &&
ieee754_cxtest(IEEE754_INVALID_OPERATION))
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
else
goto copcsr;
} else {
/* negative predicates */
switch (cmpop) {
case 1:
case 2:
case 3:
if (ieee754sp_cmp(fs, ft,
negative_cmptab[cmpop],
sig))
rv.w = -1; /* true, all 1s */
if (sig &&
ieee754_cxtest(IEEE754_INVALID_OPERATION))
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
else
goto copcsr;
break;
default:
/* Reserved R6 ops */
return SIGILL;
}
}
break;
}
}
break;
}
case l_fmt:
if (!cpu_has_mips_3_4_5_64_r2_r6)
return SIGILL;
DIFROMREG(bits, MIPSInst_FS(ir));
switch (MIPSInst_FUNC(ir)) {
case fcvts_op:
/* convert long to single precision real */
MIPS_FPU_EMU_INC_STATS(cvt_s_l);
rv.s = ieee754sp_flong(bits);
rfmt = s_fmt;
goto copcsr;
case fcvtd_op:
/* convert long to double precision real */
MIPS_FPU_EMU_INC_STATS(cvt_d_l);
rv.d = ieee754dp_flong(bits);
rfmt = d_fmt;
goto copcsr;
default: {
/* Emulating the new CMP.condn.fmt R6 instruction */
int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK;
int sig = MIPSInst_FUNC(ir) & SIGN_BIT;
union ieee754dp fs, ft;
if (!cpu_has_mips_r6 ||
(MIPSInst_FUNC(ir) & 0x20))
return SIGILL;
if (!sig) {
if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) {
switch (cmpop) {
case 0:
MIPS_FPU_EMU_INC_STATS(cmp_af_d);
break;
case 1:
MIPS_FPU_EMU_INC_STATS(cmp_un_d);
break;
case 2:
MIPS_FPU_EMU_INC_STATS(cmp_eq_d);
break;
case 3:
MIPS_FPU_EMU_INC_STATS(cmp_ueq_d);
break;
case 4:
MIPS_FPU_EMU_INC_STATS(cmp_lt_d);
break;
case 5:
MIPS_FPU_EMU_INC_STATS(cmp_ult_d);
break;
case 6:
MIPS_FPU_EMU_INC_STATS(cmp_le_d);
break;
case 7:
MIPS_FPU_EMU_INC_STATS(cmp_ule_d);
break;
}
} else {
switch (cmpop) {
case 1:
MIPS_FPU_EMU_INC_STATS(cmp_or_d);
break;
case 2:
MIPS_FPU_EMU_INC_STATS(cmp_une_d);
break;
case 3:
MIPS_FPU_EMU_INC_STATS(cmp_ne_d);
break;
}
}
} else {
if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) {
switch (cmpop) {
case 0:
MIPS_FPU_EMU_INC_STATS(cmp_saf_d);
break;
case 1:
MIPS_FPU_EMU_INC_STATS(cmp_sun_d);
break;
case 2:
MIPS_FPU_EMU_INC_STATS(cmp_seq_d);
break;
case 3:
MIPS_FPU_EMU_INC_STATS(cmp_sueq_d);
break;
case 4:
MIPS_FPU_EMU_INC_STATS(cmp_slt_d);
break;
case 5:
MIPS_FPU_EMU_INC_STATS(cmp_sult_d);
break;
case 6:
MIPS_FPU_EMU_INC_STATS(cmp_sle_d);
break;
case 7:
MIPS_FPU_EMU_INC_STATS(cmp_sule_d);
break;
}
} else {
switch (cmpop) {
case 1:
MIPS_FPU_EMU_INC_STATS(cmp_sor_d);
break;
case 2:
MIPS_FPU_EMU_INC_STATS(cmp_sune_d);
break;
case 3:
MIPS_FPU_EMU_INC_STATS(cmp_sne_d);
break;
}
}
}
/* fmt is l_fmt for double precision so fix it */
rfmt = d_fmt;
/* default to false */
rv.l = 0;
/* CMP.condn.D */
DPFROMREG(fs, MIPSInst_FS(ir));
DPFROMREG(ft, MIPSInst_FT(ir));
/* positive predicates */
if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) {
if (ieee754dp_cmp(fs, ft,
cmptab[cmpop], sig))
rv.l = -1LL; /* true, all 1s */
if (sig &&
ieee754_cxtest(IEEE754_INVALID_OPERATION))
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
else
goto copcsr;
} else {
/* negative predicates */
switch (cmpop) {
case 1:
case 2:
case 3:
if (ieee754dp_cmp(fs, ft,
negative_cmptab[cmpop],
sig))
rv.l = -1LL; /* true, all 1s */
if (sig &&
ieee754_cxtest(IEEE754_INVALID_OPERATION))
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S;
else
goto copcsr;
break;
default:
/* Reserved R6 ops */
return SIGILL;
}
}
break;
}
}
break;
default:
return SIGILL;
}
/*
* Update the fpu CSR register for this operation.
* If an exception is required, generate a tidy SIGFPE exception,
* without updating the result register.
* Note: cause exception bits do not accumulate, they are rewritten
* for each op; only the flag/sticky bits accumulate.
*/
ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr;
if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
/*printk ("SIGFPE: FPU csr = %08x\n",ctx->fcr31); */
return SIGFPE;
}
/*
* Now we can safely write the result back to the register file.
*/
switch (rfmt) {
case -1:
if (cpu_has_mips_4_5_r)
cbit = fpucondbit[MIPSInst_FD(ir) >> 2];
else
cbit = FPU_CSR_COND;
if (rv.w)
ctx->fcr31 |= cbit;
else
ctx->fcr31 &= ~cbit;
break;
case d_fmt:
DPTOREG(rv.d, MIPSInst_FD(ir));
break;
case s_fmt:
SPTOREG(rv.s, MIPSInst_FD(ir));
break;
case w_fmt:
SITOREG(rv.w, MIPSInst_FD(ir));
break;
case l_fmt:
if (!cpu_has_mips_3_4_5_64_r2_r6)
return SIGILL;
DITOREG(rv.l, MIPSInst_FD(ir));
break;
default:
return SIGILL;
}
return 0;
}