/* * linux/arch/arm/vfp/vfpdouble.c * * This code is derived in part from John R. Housers softfloat library, which * carries the following notice: * * =========================================================================== * This C source file is part of the SoftFloat IEC/IEEE Floating-point * Arithmetic Package, Release 2. * * Written by John R. Hauser. This work was made possible in part by the * International Computer Science Institute, located at Suite 600, 1947 Center * Street, Berkeley, California 94704. Funding was partially provided by the * National Science Foundation under grant MIP-9311980. The original version * of this code was written as part of a project to build a fixed-point vector * processor in collaboration with the University of California at Berkeley, * overseen by Profs. Nelson Morgan and John Wawrzynek. More information * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ * arithmetic/softfloat.html'. * * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT * TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. * * Derivative works are acceptable, even for commercial purposes, so long as * (1) they include prominent notice that the work is derivative, and (2) they * include prominent notice akin to these three paragraphs for those parts of * this code that are retained. * ===========================================================================
*/ #include <linux/kernel.h> #include <linux/bitops.h>
/* * 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;
/* * 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;
}
/* * Equal := ZC * Less than := N * Greater than := C * Unordered := CV
*/ static u32 vfp_compare(int dd, int signal_on_qnan, int dm, u32 fpscr)
{
s64 d, m;
u32 ret = 0;
m = vfp_get_double(dm); if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) {
ret |= FPSCR_C | FPSCR_V; if (signal_on_qnan || !(vfp_double_packed_mantissa(m) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1)))) /* * Signalling NaN, or signalling on quiet NaN
*/
ret |= FPSCR_IOC;
}
d = vfp_get_double(dd); if (vfp_double_packed_exponent(d) == 2047 && vfp_double_packed_mantissa(d)) {
ret |= FPSCR_C | FPSCR_V; if (signal_on_qnan || !(vfp_double_packed_mantissa(d) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1)))) /* * Signalling NaN, or signalling on quiet NaN
*/
ret |= FPSCR_IOC;
}
if (ret == 0) { if (d == m || vfp_double_packed_abs(d | m) == 0) { /* * equal
*/
ret |= FPSCR_Z | FPSCR_C;
} elseif (vfp_double_packed_sign(d ^ m)) { /* * different signs
*/ if (vfp_double_packed_sign(d)) /* * d is negative, so d < m
*/
ret |= FPSCR_N; else /* * d is positive, so d > m
*/
ret |= FPSCR_C;
} elseif ((vfp_double_packed_sign(d) != 0) ^ (d < m)) { /* * d < m
*/
ret |= FPSCR_N;
} elseif ((vfp_double_packed_sign(d) != 0) ^ (d > m)) { /* * d > m
*/
ret |= FPSCR_C;
}
}
return ret;
}
static u32 vfp_double_fcmp(int dd, int unused, int dm, u32 fpscr)
{ return vfp_compare(dd, 0, dm, fpscr);
}
static u32 vfp_double_fcmpe(int dd, int unused, int dm, u32 fpscr)
{ return vfp_compare(dd, 1, dm, fpscr);
}
static u32 vfp_double_fcmpz(int dd, int unused, int dm, u32 fpscr)
{ return vfp_compare(dd, 0, VFP_REG_ZERO, fpscr);
}
static u32 vfp_double_fcmpez(int dd, int unused, int dm, u32 fpscr)
{ return vfp_compare(dd, 1, VFP_REG_ZERO, fpscr);
}
static u32 vfp_double_fcvts(int sd, int unused, int dm, u32 fpscr)
{ struct vfp_double vdm; struct vfp_single vsd; int tm;
u32 exceptions = 0;
vfp_double_unpack(&vdm, vfp_get_double(dm));
tm = vfp_double_type(&vdm);
/* * If we have a signalling NaN, signal invalid operation.
*/ if (tm == VFP_SNAN)
exceptions = FPSCR_IOC;
if (tm & VFP_DENORMAL)
vfp_double_normalise_denormal(&vdm);
/* * If we have an infinity or a NaN, the exponent must be 255
*/ if (tm & (VFP_INFINITY|VFP_NAN)) {
vsd.exponent = 255; if (tm == VFP_QNAN)
vsd.significand |= VFP_SINGLE_SIGNIFICAND_QNAN; goto pack_nan;
} elseif (tm & VFP_ZERO)
vsd.exponent = 0; else
vsd.exponent = vdm.exponent - (1023 - 127);
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;
}
/* * 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;
} elseif (m_sig == 0) {
vdd->sign = (fpscr & FPSCR_RMODE_MASK) ==
FPSCR_ROUND_MINUSINF ? 0x8000 : 0;
}
} else {
m_sig += vdn->significand;
}
vdd->significand = m_sig;
/* * 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);
/* * Is n a NAN?
*/ if (tn & VFP_NAN) goto vdn_nan;
/* * Is m a NAN?
*/ if (tm & VFP_NAN) goto vdm_nan;
/* * If n and m are infinity, the result is invalid * If n and m are zero, the result is invalid
*/ if (tm & tn & (VFP_INFINITY|VFP_ZERO)) goto invalid;
/* * If n is infinity, the result is infinity
*/ if (tn & VFP_INFINITY) goto infinity;
/* * If m is zero, raise div0 exceptions
*/ if (tm & VFP_ZERO) goto divzero;
/* * If m is infinity, or n is zero, the result is zero
*/ if (tm & VFP_INFINITY || tn & VFP_ZERO) goto zero;
if (tn & VFP_DENORMAL)
vfp_double_normalise_denormal(&vdn); if (tm & VFP_DENORMAL)
vfp_double_normalise_denormal(&vdm);
/* * 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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.