MIPS: math-emu: Add support for the CMP.condn.fmt R6 instruction
authorMarkos Chandras <markos.chandras@imgtec.com>
Thu, 13 Aug 2015 07:56:28 +0000 (09:56 +0200)
committerRalf Baechle <ralf@linux-mips.org>
Thu, 3 Sep 2015 10:08:12 +0000 (12:08 +0200)
Add support for emulating the new CMP.condn.fmt R6 instructions and
return SIGILL for the old C.cond.fmt if R2 emulation is not enabled
since it's not supported by R6.

The functionality of the new CMP.condn.fmt is the following one:

If the comparison specified by the condn field of the instruction
is true for the operand values, the result is true; otherwise, the
result is false. If no exception is taken, the result is written into
FPR fd; true is all 1s and false is all 0s repeated the operand width
of fmt. All other bits beyond the operand width fmt are UNPREDICTABLE.

Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/10953/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/math-emu/cp1emu.c

index 8a5b0eb4ddef441448bab23a1603afdf466fd39f..ef41fc895e75ae7dad4fc33356f2cc23443b9db7 100644 (file)
@@ -1394,6 +1394,14 @@ static const unsigned char cmptab[8] = {
        IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN,        /* cmp_ule (sig) cmp_ngt */
 };
 
+static const unsigned char negative_cmptab[8] = {
+       0, /* Reserved */
+       IEEE754_CLT | IEEE754_CGT | IEEE754_CEQ,
+       IEEE754_CLT | IEEE754_CGT | IEEE754_CUN,
+       IEEE754_CLT | IEEE754_CGT,
+       /* Reserved */
+};
+
 
 /*
  * Additional MIPS4 instructions
@@ -1838,7 +1846,7 @@ copcsr:
                        goto copcsr;
 
                default:
-                       if (MIPSInst_FUNC(ir) >= fcmp_op) {
+                       if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) {
                                unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op;
                                union ieee754sp fs, ft;
 
@@ -2015,7 +2023,7 @@ dcopuop:
                        goto copcsr;
 
                default:
-                       if (MIPSInst_FUNC(ir) >= fcmp_op) {
+                       if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) {
                                unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op;
                                union ieee754dp fs, ft;
 
@@ -2057,10 +2065,65 @@ dcopuop:
                        rv.d = ieee754dp_fint(fs.bits);
                        rfmt = d_fmt;
                        goto copcsr;
-               default:
-                       return SIGILL;
+               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;
+
+                       /* 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 */
+                                       pr_err("Reserved MIPS R6 CMP.condn.S operation\n");
+                                       return SIGILL;
+                               }
+                       }
+                       break;
+                       }
                }
-               break;
        }
 
        case l_fmt:
@@ -2081,11 +2144,60 @@ dcopuop:
                        rv.d = ieee754dp_flong(bits);
                        rfmt = d_fmt;
                        goto copcsr;
-               default:
-                       return SIGILL;
-               }
-               break;
+               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;
 
+                       /* 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 */
+                                       pr_err("Reserved MIPS R6 CMP.condn.D operation\n");
+                                       return SIGILL;
+                               }
+                       }
+                       break;
+                       }
+               }
        default:
                return SIGILL;
        }