static int fpu_emu()

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;
}