|
@@ -92,3 +92,165 @@ u32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exce
|
|
|
exponent = vd->exponent;
|
|
|
significand = vd->significand;
|
|
|
|
|
|
+ shift = 32 - fls(significand >> 32);
|
|
|
+ if (shift == 32)
|
|
|
+ shift = 64 - fls(significand);
|
|
|
+ if (shift) {
|
|
|
+ exponent -= shift;
|
|
|
+ significand <<= shift;
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef DEBUG
|
|
|
+ vd->exponent = exponent;
|
|
|
+ vd->significand = significand;
|
|
|
+ vfp_double_dump("pack: normalised", vd);
|
|
|
+#endif
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Tiny number?
|
|
|
+ */
|
|
|
+ underflow = exponent < 0;
|
|
|
+ if (underflow) {
|
|
|
+ significand = vfp_shiftright64jamming(significand, -exponent);
|
|
|
+ exponent = 0;
|
|
|
+#ifdef DEBUG
|
|
|
+ vd->exponent = exponent;
|
|
|
+ vd->significand = significand;
|
|
|
+ vfp_double_dump("pack: tiny number", vd);
|
|
|
+#endif
|
|
|
+ if (!(significand & ((1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1)))
|
|
|
+ underflow = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Select rounding increment.
|
|
|
+ */
|
|
|
+ incr = 0;
|
|
|
+ rmode = fpscr & FPSCR_RMODE_MASK;
|
|
|
+
|
|
|
+ if (rmode == FPSCR_ROUND_NEAREST) {
|
|
|
+ incr = 1ULL << VFP_DOUBLE_LOW_BITS;
|
|
|
+ if ((significand & (1ULL << (VFP_DOUBLE_LOW_BITS + 1))) == 0)
|
|
|
+ incr -= 1;
|
|
|
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
|
|
|
+ incr = 0;
|
|
|
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0))
|
|
|
+ incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1;
|
|
|
+
|
|
|
+ pr_debug("VFP: rounding increment = 0x%08llx\n", incr);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Is our rounding going to overflow?
|
|
|
+ */
|
|
|
+ if ((significand + incr) < significand) {
|
|
|
+ exponent += 1;
|
|
|
+ significand = (significand >> 1) | (significand & 1);
|
|
|
+ incr >>= 1;
|
|
|
+#ifdef DEBUG
|
|
|
+ vd->exponent = exponent;
|
|
|
+ vd->significand = significand;
|
|
|
+ vfp_double_dump("pack: overflow", vd);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If any of the low bits (which will be shifted out of the
|
|
|
+ * number) are non-zero, the result is inexact.
|
|
|
+ */
|
|
|
+ if (significand & ((1 << (VFP_DOUBLE_LOW_BITS + 1)) - 1))
|
|
|
+ exceptions |= FPSCR_IXC;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Do our rounding.
|
|
|
+ */
|
|
|
+ significand += incr;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Infinity?
|
|
|
+ */
|
|
|
+ if (exponent >= 2046) {
|
|
|
+ exceptions |= FPSCR_OFC | FPSCR_IXC;
|
|
|
+ if (incr == 0) {
|
|
|
+ vd->exponent = 2045;
|
|
|
+ vd->significand = 0x7fffffffffffffffULL;
|
|
|
+ } else {
|
|
|
+ vd->exponent = 2047; /* infinity */
|
|
|
+ vd->significand = 0;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (significand >> (VFP_DOUBLE_LOW_BITS + 1) == 0)
|
|
|
+ exponent = 0;
|
|
|
+ if (exponent || significand > 0x8000000000000000ULL)
|
|
|
+ underflow = 0;
|
|
|
+ if (underflow)
|
|
|
+ exceptions |= FPSCR_UFC;
|
|
|
+ vd->exponent = exponent;
|
|
|
+ vd->significand = significand >> 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ pack:
|
|
|
+ vfp_double_dump("pack: final", vd);
|
|
|
+ {
|
|
|
+ s64 d = vfp_double_pack(vd);
|
|
|
+ pr_debug("VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func,
|
|
|
+ dd, d, exceptions);
|
|
|
+ vfp_put_double(d, dd);
|
|
|
+ }
|
|
|
+ return exceptions;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Propagate the NaN, setting exceptions if it is signalling.
|
|
|
+ * 'n' is always a NaN. 'm' may be a number, NaN or infinity.
|
|
|
+ */
|
|
|
+static u32
|
|
|
+vfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn,
|
|
|
+ struct vfp_double *vdm, u32 fpscr)
|
|
|
+{
|
|
|
+ struct vfp_double *nan;
|
|
|
+ int tn, tm = 0;
|
|
|
+
|
|
|
+ tn = vfp_double_type(vdn);
|
|
|
+
|
|
|
+ if (vdm)
|
|
|
+ tm = vfp_double_type(vdm);
|
|
|
+
|
|
|
+ if (fpscr & FPSCR_DEFAULT_NAN)
|
|
|
+ /*
|
|
|
+ * Default NaN mode - always returns a quiet NaN
|
|
|
+ */
|
|
|
+ nan = &vfp_double_default_qnan;
|
|
|
+ else {
|
|
|
+ /*
|
|
|
+ * Contemporary mode - select the first signalling
|
|
|
+ * NAN, or if neither are signalling, the first
|
|
|
+ * quiet NAN.
|
|
|
+ */
|
|
|
+ if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN))
|
|
|
+ nan = vdn;
|
|
|
+ else
|
|
|
+ nan = vdm;
|
|
|
+ /*
|
|
|
+ * Make the NaN quiet.
|
|
|
+ */
|
|
|
+ nan->significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
|
|
|
+ }
|
|
|
+
|
|
|
+ *vdd = *nan;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If one was a signalling NAN, raise invalid operation.
|
|
|
+ */
|
|
|
+ return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Extended operations
|
|
|
+ */
|
|
|
+static u32 vfp_double_fabs(int dd, int unused, int dm, u32 fpscr)
|
|
|
+{
|
|
|
+ vfp_put_double(vfp_double_packed_abs(vfp_get_double(dm)), dd);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 vfp_double_fcpy(int dd, int unused, int dm, u32 fpscr)
|