|
@@ -1100,3 +1100,145 @@ static u32 vfp_single_fdiv(int sd, int sn, s32 m, u32 fpscr)
|
|
*/
|
|
*/
|
|
if (tm & VFP_INFINITY || tn & VFP_ZERO)
|
|
if (tm & VFP_INFINITY || tn & VFP_ZERO)
|
|
goto zero;
|
|
goto zero;
|
|
|
|
+
|
|
|
|
+ if (tn & VFP_DENORMAL)
|
|
|
|
+ vfp_single_normalise_denormal(&vsn);
|
|
|
|
+ if (tm & VFP_DENORMAL)
|
|
|
|
+ vfp_single_normalise_denormal(&vsm);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Ok, we have two numbers, we can perform division.
|
|
|
|
+ */
|
|
|
|
+ vsd.exponent = vsn.exponent - vsm.exponent + 127 - 1;
|
|
|
|
+ vsm.significand <<= 1;
|
|
|
|
+ if (vsm.significand <= (2 * vsn.significand)) {
|
|
|
|
+ vsn.significand >>= 1;
|
|
|
|
+ vsd.exponent++;
|
|
|
|
+ }
|
|
|
|
+ {
|
|
|
|
+ u64 significand = (u64)vsn.significand << 32;
|
|
|
|
+ do_div(significand, vsm.significand);
|
|
|
|
+ vsd.significand = significand;
|
|
|
|
+ }
|
|
|
|
+ if ((vsd.significand & 0x3f) == 0)
|
|
|
|
+ vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32);
|
|
|
|
+
|
|
|
|
+ return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fdiv");
|
|
|
|
+
|
|
|
|
+ vsn_nan:
|
|
|
|
+ exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr);
|
|
|
|
+ pack:
|
|
|
|
+ vfp_put_float(vfp_single_pack(&vsd), sd);
|
|
|
|
+ return exceptions;
|
|
|
|
+
|
|
|
|
+ vsm_nan:
|
|
|
|
+ exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr);
|
|
|
|
+ goto pack;
|
|
|
|
+
|
|
|
|
+ zero:
|
|
|
|
+ vsd.exponent = 0;
|
|
|
|
+ vsd.significand = 0;
|
|
|
|
+ goto pack;
|
|
|
|
+
|
|
|
|
+ divzero:
|
|
|
|
+ exceptions = FPSCR_DZC;
|
|
|
|
+ infinity:
|
|
|
|
+ vsd.exponent = 255;
|
|
|
|
+ vsd.significand = 0;
|
|
|
|
+ goto pack;
|
|
|
|
+
|
|
|
|
+ invalid:
|
|
|
|
+ vfp_put_float(vfp_single_pack(&vfp_single_default_qnan), sd);
|
|
|
|
+ return FPSCR_IOC;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct op fops[16] = {
|
|
|
|
+ [FOP_TO_IDX(FOP_FMAC)] = { vfp_single_fmac, 0 },
|
|
|
|
+ [FOP_TO_IDX(FOP_FNMAC)] = { vfp_single_fnmac, 0 },
|
|
|
|
+ [FOP_TO_IDX(FOP_FMSC)] = { vfp_single_fmsc, 0 },
|
|
|
|
+ [FOP_TO_IDX(FOP_FNMSC)] = { vfp_single_fnmsc, 0 },
|
|
|
|
+ [FOP_TO_IDX(FOP_FMUL)] = { vfp_single_fmul, 0 },
|
|
|
|
+ [FOP_TO_IDX(FOP_FNMUL)] = { vfp_single_fnmul, 0 },
|
|
|
|
+ [FOP_TO_IDX(FOP_FADD)] = { vfp_single_fadd, 0 },
|
|
|
|
+ [FOP_TO_IDX(FOP_FSUB)] = { vfp_single_fsub, 0 },
|
|
|
|
+ [FOP_TO_IDX(FOP_FDIV)] = { vfp_single_fdiv, 0 },
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#define FREG_BANK(x) ((x) & 0x18)
|
|
|
|
+#define FREG_IDX(x) ((x) & 7)
|
|
|
|
+
|
|
|
|
+u32 vfp_single_cpdo(u32 inst, u32 fpscr)
|
|
|
|
+{
|
|
|
|
+ u32 op = inst & FOP_MASK;
|
|
|
|
+ u32 exceptions = 0;
|
|
|
|
+ unsigned int dest;
|
|
|
|
+ unsigned int sn = vfp_get_sn(inst);
|
|
|
|
+ unsigned int sm = vfp_get_sm(inst);
|
|
|
|
+ unsigned int vecitr, veclen, vecstride;
|
|
|
|
+ struct op *fop;
|
|
|
|
+
|
|
|
|
+ vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK);
|
|
|
|
+
|
|
|
|
+ fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)];
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * fcvtsd takes a dN register number as destination, not sN.
|
|
|
|
+ * Technically, if bit 0 of dd is set, this is an invalid
|
|
|
|
+ * instruction. However, we ignore this for efficiency.
|
|
|
|
+ * It also only operates on scalars.
|
|
|
|
+ */
|
|
|
|
+ if (fop->flags & OP_DD)
|
|
|
|
+ dest = vfp_get_dd(inst);
|
|
|
|
+ else
|
|
|
|
+ dest = vfp_get_sd(inst);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If destination bank is zero, vector length is always '1'.
|
|
|
|
+ * ARM DDI0100F C5.1.3, C5.3.2.
|
|
|
|
+ */
|
|
|
|
+ if ((fop->flags & OP_SCALAR) || FREG_BANK(dest) == 0)
|
|
|
|
+ veclen = 0;
|
|
|
|
+ else
|
|
|
|
+ veclen = fpscr & FPSCR_LENGTH_MASK;
|
|
|
|
+
|
|
|
|
+ pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride,
|
|
|
|
+ (veclen >> FPSCR_LENGTH_BIT) + 1);
|
|
|
|
+
|
|
|
|
+ if (!fop->fn)
|
|
|
|
+ goto invalid;
|
|
|
|
+
|
|
|
|
+ for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
|
|
|
|
+ s32 m = vfp_get_float(sm);
|
|
|
|
+ u32 except;
|
|
|
|
+ char type;
|
|
|
|
+
|
|
|
|
+ type = fop->flags & OP_DD ? 'd' : 's';
|
|
|
|
+ if (op == FOP_EXT)
|
|
|
|
+ pr_debug("VFP: itr%d (%c%u) = op[%u] (s%u=%08x)\n",
|
|
|
|
+ vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
|
|
|
|
+ sm, m);
|
|
|
|
+ else
|
|
|
|
+ pr_debug("VFP: itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)\n",
|
|
|
|
+ vecitr >> FPSCR_LENGTH_BIT, type, dest, sn,
|
|
|
|
+ FOP_TO_IDX(op), sm, m);
|
|
|
|
+
|
|
|
|
+ except = fop->fn(dest, sn, m, fpscr);
|
|
|
|
+ pr_debug("VFP: itr%d: exceptions=%08x\n",
|
|
|
|
+ vecitr >> FPSCR_LENGTH_BIT, except);
|
|
|
|
+
|
|
|
|
+ exceptions |= except;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * CHECK: It appears to be undefined whether we stop when
|
|
|
|
+ * we encounter an exception. We continue.
|
|
|
|
+ */
|
|
|
|
+ dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 7);
|
|
|
|
+ sn = FREG_BANK(sn) + ((FREG_IDX(sn) + vecstride) & 7);
|
|
|
|
+ if (FREG_BANK(sm) != 0)
|
|
|
|
+ sm = FREG_BANK(sm) + ((FREG_IDX(sm) + vecstride) & 7);
|
|
|
|
+ }
|
|
|
|
+ return exceptions;
|
|
|
|
+
|
|
|
|
+ invalid:
|
|
|
|
+ return (u32)-1;
|
|
|
|
+}
|