|
@@ -558,3 +558,189 @@ static u32 vfp_double_ftoui(int sd, int unused, int dm, u32 fpscr)
|
|
|
} else if (rem)
|
|
|
exceptions |= FPSCR_IXC;
|
|
|
} else {
|
|
|
+ d = 0;
|
|
|
+ if (vdm.exponent | vdm.significand) {
|
|
|
+ exceptions |= FPSCR_IXC;
|
|
|
+ if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
|
|
|
+ d = 1;
|
|
|
+ else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
|
|
|
+ d = 0;
|
|
|
+ exceptions |= FPSCR_IOC;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
|
|
|
+
|
|
|
+ vfp_put_float(d, sd);
|
|
|
+
|
|
|
+ return exceptions;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 vfp_double_ftouiz(int sd, int unused, int dm, u32 fpscr)
|
|
|
+{
|
|
|
+ return vfp_double_ftoui(sd, unused, dm, FPSCR_ROUND_TOZERO);
|
|
|
+}
|
|
|
+
|
|
|
+static u32 vfp_double_ftosi(int sd, int unused, int dm, u32 fpscr)
|
|
|
+{
|
|
|
+ struct vfp_double vdm;
|
|
|
+ u32 d, exceptions = 0;
|
|
|
+ int rmode = fpscr & FPSCR_RMODE_MASK;
|
|
|
+ int tm;
|
|
|
+
|
|
|
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
|
|
|
+ vfp_double_dump("VDM", &vdm);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Do we have denormalised number?
|
|
|
+ */
|
|
|
+ tm = vfp_double_type(&vdm);
|
|
|
+ if (tm & VFP_DENORMAL)
|
|
|
+ exceptions |= FPSCR_IDC;
|
|
|
+
|
|
|
+ if (tm & VFP_NAN) {
|
|
|
+ d = 0;
|
|
|
+ exceptions |= FPSCR_IOC;
|
|
|
+ } else if (vdm.exponent >= 1023 + 32) {
|
|
|
+ d = 0x7fffffff;
|
|
|
+ if (vdm.sign)
|
|
|
+ d = ~d;
|
|
|
+ exceptions |= FPSCR_IOC;
|
|
|
+ } else if (vdm.exponent >= 1023 - 1) {
|
|
|
+ int shift = 1023 + 63 - vdm.exponent; /* 58 */
|
|
|
+ u64 rem, incr = 0;
|
|
|
+
|
|
|
+ d = (vdm.significand << 1) >> shift;
|
|
|
+ rem = vdm.significand << (65 - shift);
|
|
|
+
|
|
|
+ if (rmode == FPSCR_ROUND_NEAREST) {
|
|
|
+ incr = 0x8000000000000000ULL;
|
|
|
+ if ((d & 1) == 0)
|
|
|
+ incr -= 1;
|
|
|
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
|
|
|
+ incr = 0;
|
|
|
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) {
|
|
|
+ incr = ~0ULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((rem + incr) < rem && d < 0xffffffff)
|
|
|
+ d += 1;
|
|
|
+ if (d > 0x7fffffff + (vdm.sign != 0)) {
|
|
|
+ d = 0x7fffffff + (vdm.sign != 0);
|
|
|
+ exceptions |= FPSCR_IOC;
|
|
|
+ } else if (rem)
|
|
|
+ exceptions |= FPSCR_IXC;
|
|
|
+
|
|
|
+ if (vdm.sign)
|
|
|
+ d = -d;
|
|
|
+ } else {
|
|
|
+ d = 0;
|
|
|
+ if (vdm.exponent | vdm.significand) {
|
|
|
+ exceptions |= FPSCR_IXC;
|
|
|
+ if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
|
|
|
+ d = 1;
|
|
|
+ else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign)
|
|
|
+ d = -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
|
|
|
+
|
|
|
+ vfp_put_float((s32)d, sd);
|
|
|
+
|
|
|
+ return exceptions;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 vfp_double_ftosiz(int dd, int unused, int dm, u32 fpscr)
|
|
|
+{
|
|
|
+ return vfp_double_ftosi(dd, unused, dm, FPSCR_ROUND_TOZERO);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static struct op fops_ext[32] = {
|
|
|
+ [FEXT_TO_IDX(FEXT_FCPY)] = { vfp_double_fcpy, 0 },
|
|
|
+ [FEXT_TO_IDX(FEXT_FABS)] = { vfp_double_fabs, 0 },
|
|
|
+ [FEXT_TO_IDX(FEXT_FNEG)] = { vfp_double_fneg, 0 },
|
|
|
+ [FEXT_TO_IDX(FEXT_FSQRT)] = { vfp_double_fsqrt, 0 },
|
|
|
+ [FEXT_TO_IDX(FEXT_FCMP)] = { vfp_double_fcmp, OP_SCALAR },
|
|
|
+ [FEXT_TO_IDX(FEXT_FCMPE)] = { vfp_double_fcmpe, OP_SCALAR },
|
|
|
+ [FEXT_TO_IDX(FEXT_FCMPZ)] = { vfp_double_fcmpz, OP_SCALAR },
|
|
|
+ [FEXT_TO_IDX(FEXT_FCMPEZ)] = { vfp_double_fcmpez, OP_SCALAR },
|
|
|
+ [FEXT_TO_IDX(FEXT_FCVT)] = { vfp_double_fcvts, OP_SCALAR|OP_SD },
|
|
|
+ [FEXT_TO_IDX(FEXT_FUITO)] = { vfp_double_fuito, OP_SCALAR|OP_SM },
|
|
|
+ [FEXT_TO_IDX(FEXT_FSITO)] = { vfp_double_fsito, OP_SCALAR|OP_SM },
|
|
|
+ [FEXT_TO_IDX(FEXT_FTOUI)] = { vfp_double_ftoui, OP_SCALAR|OP_SD },
|
|
|
+ [FEXT_TO_IDX(FEXT_FTOUIZ)] = { vfp_double_ftouiz, OP_SCALAR|OP_SD },
|
|
|
+ [FEXT_TO_IDX(FEXT_FTOSI)] = { vfp_double_ftosi, OP_SCALAR|OP_SD },
|
|
|
+ [FEXT_TO_IDX(FEXT_FTOSIZ)] = { vfp_double_ftosiz, OP_SCALAR|OP_SD },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+static u32
|
|
|
+vfp_double_fadd_nonnumber(struct vfp_double *vdd, struct vfp_double *vdn,
|
|
|
+ struct vfp_double *vdm, u32 fpscr)
|
|
|
+{
|
|
|
+ struct vfp_double *vdp;
|
|
|
+ u32 exceptions = 0;
|
|
|
+ int tn, tm;
|
|
|
+
|
|
|
+ tn = vfp_double_type(vdn);
|
|
|
+ tm = vfp_double_type(vdm);
|
|
|
+
|
|
|
+ if (tn & tm & VFP_INFINITY) {
|
|
|
+ /*
|
|
|
+ * Two infinities. Are they different signs?
|
|
|
+ */
|
|
|
+ if (vdn->sign ^ vdm->sign) {
|
|
|
+ /*
|
|
|
+ * different signs -> invalid
|
|
|
+ */
|
|
|
+ exceptions = FPSCR_IOC;
|
|
|
+ vdp = &vfp_double_default_qnan;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * same signs -> valid
|
|
|
+ */
|
|
|
+ vdp = vdn;
|
|
|
+ }
|
|
|
+ } else if (tn & VFP_INFINITY && tm & VFP_NUMBER) {
|
|
|
+ /*
|
|
|
+ * One infinity and one number -> infinity
|
|
|
+ */
|
|
|
+ vdp = vdn;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * 'n' is a NaN of some type
|
|
|
+ */
|
|
|
+ return vfp_propagate_nan(vdd, vdn, vdm, fpscr);
|
|
|
+ }
|
|
|
+ *vdd = *vdp;
|
|
|
+ return exceptions;
|
|
|
+}
|
|
|
+
|
|
|
+static u32
|
|
|
+vfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn,
|
|
|
+ struct vfp_double *vdm, u32 fpscr)
|
|
|
+{
|
|
|
+ u32 exp_diff;
|
|
|
+ u64 m_sig;
|
|
|
+
|
|
|
+ if (vdn->significand & (1ULL << 63) ||
|
|
|
+ vdm->significand & (1ULL << 63)) {
|
|
|
+ pr_info("VFP: bad FP values in %s\n", __func__);
|
|
|
+ vfp_double_dump("VDN", vdn);
|
|
|
+ vfp_double_dump("VDM", vdm);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Ensure that 'n' is the largest magnitude number. Note that
|
|
|
+ * if 'n' and 'm' have equal exponents, we do not swap them.
|
|
|
+ * This ensures that NaN propagation works correctly.
|
|
|
+ */
|
|
|
+ if (vdn->exponent < vdm->exponent) {
|
|
|
+ struct vfp_double *t = vdn;
|
|
|
+ vdn = vdm;
|
|
|
+ vdm = t;
|