|
@@ -744,3 +744,169 @@ vfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn,
|
|
|
struct vfp_double *t = vdn;
|
|
|
vdn = vdm;
|
|
|
vdm = t;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Is 'n' an infinity or a NaN? Note that 'm' may be a number,
|
|
|
+ * infinity or a NaN here.
|
|
|
+ */
|
|
|
+ if (vdn->exponent == 2047)
|
|
|
+ return vfp_double_fadd_nonnumber(vdd, vdn, vdm, fpscr);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We have two proper numbers, where 'vdn' is the larger magnitude.
|
|
|
+ *
|
|
|
+ * Copy 'n' to 'd' before doing the arithmetic.
|
|
|
+ */
|
|
|
+ *vdd = *vdn;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Align 'm' with the result.
|
|
|
+ */
|
|
|
+ exp_diff = vdn->exponent - vdm->exponent;
|
|
|
+ m_sig = vfp_shiftright64jamming(vdm->significand, exp_diff);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the signs are different, we are really subtracting.
|
|
|
+ */
|
|
|
+ if (vdn->sign ^ vdm->sign) {
|
|
|
+ m_sig = vdn->significand - m_sig;
|
|
|
+ if ((s64)m_sig < 0) {
|
|
|
+ vdd->sign = vfp_sign_negate(vdd->sign);
|
|
|
+ m_sig = -m_sig;
|
|
|
+ } else if (m_sig == 0) {
|
|
|
+ vdd->sign = (fpscr & FPSCR_RMODE_MASK) ==
|
|
|
+ FPSCR_ROUND_MINUSINF ? 0x8000 : 0;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ m_sig += vdn->significand;
|
|
|
+ }
|
|
|
+ vdd->significand = m_sig;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static u32
|
|
|
+vfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn,
|
|
|
+ struct vfp_double *vdm, u32 fpscr)
|
|
|
+{
|
|
|
+ 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;
|
|
|
+ pr_debug("VFP: swapping M <-> N\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ vdd->sign = vdn->sign ^ vdm->sign;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If 'n' is an infinity or NaN, handle it. 'm' may be anything.
|
|
|
+ */
|
|
|
+ if (vdn->exponent == 2047) {
|
|
|
+ if (vdn->significand || (vdm->exponent == 2047 && vdm->significand))
|
|
|
+ return vfp_propagate_nan(vdd, vdn, vdm, fpscr);
|
|
|
+ if ((vdm->exponent | vdm->significand) == 0) {
|
|
|
+ *vdd = vfp_double_default_qnan;
|
|
|
+ return FPSCR_IOC;
|
|
|
+ }
|
|
|
+ vdd->exponent = vdn->exponent;
|
|
|
+ vdd->significand = 0;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If 'm' is zero, the result is always zero. In this case,
|
|
|
+ * 'n' may be zero or a number, but it doesn't matter which.
|
|
|
+ */
|
|
|
+ if ((vdm->exponent | vdm->significand) == 0) {
|
|
|
+ vdd->exponent = 0;
|
|
|
+ vdd->significand = 0;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We add 2 to the destination exponent for the same reason
|
|
|
+ * as the addition case - though this time we have +1 from
|
|
|
+ * each input operand.
|
|
|
+ */
|
|
|
+ vdd->exponent = vdn->exponent + vdm->exponent - 1023 + 2;
|
|
|
+ vdd->significand = vfp_hi64multiply64(vdn->significand, vdm->significand);
|
|
|
+
|
|
|
+ vfp_double_dump("VDD", vdd);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#define NEG_MULTIPLY (1 << 0)
|
|
|
+#define NEG_SUBTRACT (1 << 1)
|
|
|
+
|
|
|
+static u32
|
|
|
+vfp_double_multiply_accumulate(int dd, int dn, int dm, u32 fpscr, u32 negate, char *func)
|
|
|
+{
|
|
|
+ struct vfp_double vdd, vdp, vdn, vdm;
|
|
|
+ u32 exceptions;
|
|
|
+
|
|
|
+ vfp_double_unpack(&vdn, vfp_get_double(dn));
|
|
|
+ if (vdn.exponent == 0 && vdn.significand)
|
|
|
+ vfp_double_normalise_denormal(&vdn);
|
|
|
+
|
|
|
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
|
|
|
+ if (vdm.exponent == 0 && vdm.significand)
|
|
|
+ vfp_double_normalise_denormal(&vdm);
|
|
|
+
|
|
|
+ exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr);
|
|
|
+ if (negate & NEG_MULTIPLY)
|
|
|
+ vdp.sign = vfp_sign_negate(vdp.sign);
|
|
|
+
|
|
|
+ vfp_double_unpack(&vdn, vfp_get_double(dd));
|
|
|
+ if (negate & NEG_SUBTRACT)
|
|
|
+ vdn.sign = vfp_sign_negate(vdn.sign);
|
|
|
+
|
|
|
+ exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr);
|
|
|
+
|
|
|
+ return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, func);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Standard operations
|
|
|
+ */
|
|
|
+
|
|
|
+/*
|
|
|
+ * sd = sd + (sn * sm)
|
|
|
+ */
|
|
|
+static u32 vfp_double_fmac(int dd, int dn, int dm, u32 fpscr)
|
|
|
+{
|
|
|
+ return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, 0, "fmac");
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * sd = sd - (sn * sm)
|
|
|
+ */
|
|
|
+static u32 vfp_double_fnmac(int dd, int dn, int dm, u32 fpscr)
|
|
|
+{
|
|
|
+ return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac");
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * sd = -sd + (sn * sm)
|
|
|
+ */
|
|
|
+static u32 vfp_double_fmsc(int dd, int dn, int dm, u32 fpscr)
|
|
|
+{
|
|
|
+ return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc");
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * sd = -sd - (sn * sm)
|
|
|
+ */
|
|
|
+static u32 vfp_double_fnmsc(int dd, int dn, int dm, u32 fpscr)
|
|
|
+{
|
|
|
+ return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
|
|
|
+}
|
|
|
+
|