|
@@ -1058,3 +1058,147 @@ static u32 vfp_double_fdiv(int dd, int dn, int dm, u32 fpscr)
|
|
|
if (tm & VFP_DENORMAL)
|
|
|
vfp_double_normalise_denormal(&vdm);
|
|
|
|
|
|
+ /*
|
|
|
+ * Ok, we have two numbers, we can perform division.
|
|
|
+ */
|
|
|
+ vdd.exponent = vdn.exponent - vdm.exponent + 1023 - 1;
|
|
|
+ vdm.significand <<= 1;
|
|
|
+ if (vdm.significand <= (2 * vdn.significand)) {
|
|
|
+ vdn.significand >>= 1;
|
|
|
+ vdd.exponent++;
|
|
|
+ }
|
|
|
+ vdd.significand = vfp_estimate_div128to64(vdn.significand, 0, vdm.significand);
|
|
|
+ if ((vdd.significand & 0x1ff) <= 2) {
|
|
|
+ u64 termh, terml, remh, reml;
|
|
|
+ mul64to128(&termh, &terml, vdm.significand, vdd.significand);
|
|
|
+ sub128(&remh, &reml, vdn.significand, 0, termh, terml);
|
|
|
+ while ((s64)remh < 0) {
|
|
|
+ vdd.significand -= 1;
|
|
|
+ add128(&remh, &reml, remh, reml, 0, vdm.significand);
|
|
|
+ }
|
|
|
+ vdd.significand |= (reml != 0);
|
|
|
+ }
|
|
|
+ return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fdiv");
|
|
|
+
|
|
|
+ vdn_nan:
|
|
|
+ exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr);
|
|
|
+ pack:
|
|
|
+ vfp_put_double(vfp_double_pack(&vdd), dd);
|
|
|
+ return exceptions;
|
|
|
+
|
|
|
+ vdm_nan:
|
|
|
+ exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr);
|
|
|
+ goto pack;
|
|
|
+
|
|
|
+ zero:
|
|
|
+ vdd.exponent = 0;
|
|
|
+ vdd.significand = 0;
|
|
|
+ goto pack;
|
|
|
+
|
|
|
+ divzero:
|
|
|
+ exceptions = FPSCR_DZC;
|
|
|
+ infinity:
|
|
|
+ vdd.exponent = 2047;
|
|
|
+ vdd.significand = 0;
|
|
|
+ goto pack;
|
|
|
+
|
|
|
+ invalid:
|
|
|
+ vfp_put_double(vfp_double_pack(&vfp_double_default_qnan), dd);
|
|
|
+ return FPSCR_IOC;
|
|
|
+}
|
|
|
+
|
|
|
+static struct op fops[16] = {
|
|
|
+ [FOP_TO_IDX(FOP_FMAC)] = { vfp_double_fmac, 0 },
|
|
|
+ [FOP_TO_IDX(FOP_FNMAC)] = { vfp_double_fnmac, 0 },
|
|
|
+ [FOP_TO_IDX(FOP_FMSC)] = { vfp_double_fmsc, 0 },
|
|
|
+ [FOP_TO_IDX(FOP_FNMSC)] = { vfp_double_fnmsc, 0 },
|
|
|
+ [FOP_TO_IDX(FOP_FMUL)] = { vfp_double_fmul, 0 },
|
|
|
+ [FOP_TO_IDX(FOP_FNMUL)] = { vfp_double_fnmul, 0 },
|
|
|
+ [FOP_TO_IDX(FOP_FADD)] = { vfp_double_fadd, 0 },
|
|
|
+ [FOP_TO_IDX(FOP_FSUB)] = { vfp_double_fsub, 0 },
|
|
|
+ [FOP_TO_IDX(FOP_FDIV)] = { vfp_double_fdiv, 0 },
|
|
|
+};
|
|
|
+
|
|
|
+#define FREG_BANK(x) ((x) & 0x0c)
|
|
|
+#define FREG_IDX(x) ((x) & 3)
|
|
|
+
|
|
|
+u32 vfp_double_cpdo(u32 inst, u32 fpscr)
|
|
|
+{
|
|
|
+ u32 op = inst & FOP_MASK;
|
|
|
+ u32 exceptions = 0;
|
|
|
+ unsigned int dest;
|
|
|
+ unsigned int dn = vfp_get_dn(inst);
|
|
|
+ unsigned int dm;
|
|
|
+ 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)];
|
|
|
+
|
|
|
+ /*
|
|
|
+ * fcvtds takes an sN register number as destination, not dN.
|
|
|
+ * It also always operates on scalars.
|
|
|
+ */
|
|
|
+ if (fop->flags & OP_SD)
|
|
|
+ dest = vfp_get_sd(inst);
|
|
|
+ else
|
|
|
+ dest = vfp_get_dd(inst);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * f[us]ito takes a sN operand, not a dN operand.
|
|
|
+ */
|
|
|
+ if (fop->flags & OP_SM)
|
|
|
+ dm = vfp_get_sm(inst);
|
|
|
+ else
|
|
|
+ dm = vfp_get_dm(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) {
|
|
|
+ u32 except;
|
|
|
+ char type;
|
|
|
+
|
|
|
+ type = fop->flags & OP_SD ? 's' : 'd';
|
|
|
+ if (op == FOP_EXT)
|
|
|
+ pr_debug("VFP: itr%d (%c%u) = op[%u] (d%u)\n",
|
|
|
+ vecitr >> FPSCR_LENGTH_BIT,
|
|
|
+ type, dest, dn, dm);
|
|
|
+ else
|
|
|
+ pr_debug("VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)\n",
|
|
|
+ vecitr >> FPSCR_LENGTH_BIT,
|
|
|
+ type, dest, dn, FOP_TO_IDX(op), dm);
|
|
|
+
|
|
|
+ except = fop->fn(dest, dn, dm, 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) & 3);
|
|
|
+ dn = FREG_BANK(dn) + ((FREG_IDX(dn) + vecstride) & 3);
|
|
|
+ if (FREG_BANK(dm) != 0)
|
|
|
+ dm = FREG_BANK(dm) + ((FREG_IDX(dm) + vecstride) & 3);
|
|
|
+ }
|
|
|
+ return exceptions;
|
|
|
+
|
|
|
+ invalid:
|
|
|
+ return ~0;
|
|
|
+}
|