162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/arch/arm/vfp/vfpdouble.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This code is derived in part from John R. Housers softfloat library, which 562306a36Sopenharmony_ci * carries the following notice: 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * =========================================================================== 862306a36Sopenharmony_ci * This C source file is part of the SoftFloat IEC/IEEE Floating-point 962306a36Sopenharmony_ci * Arithmetic Package, Release 2. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Written by John R. Hauser. This work was made possible in part by the 1262306a36Sopenharmony_ci * International Computer Science Institute, located at Suite 600, 1947 Center 1362306a36Sopenharmony_ci * Street, Berkeley, California 94704. Funding was partially provided by the 1462306a36Sopenharmony_ci * National Science Foundation under grant MIP-9311980. The original version 1562306a36Sopenharmony_ci * of this code was written as part of a project to build a fixed-point vector 1662306a36Sopenharmony_ci * processor in collaboration with the University of California at Berkeley, 1762306a36Sopenharmony_ci * overseen by Profs. Nelson Morgan and John Wawrzynek. More information 1862306a36Sopenharmony_ci * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ 1962306a36Sopenharmony_ci * arithmetic/softfloat.html'. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort 2262306a36Sopenharmony_ci * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT 2362306a36Sopenharmony_ci * TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO 2462306a36Sopenharmony_ci * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY 2562306a36Sopenharmony_ci * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Derivative works are acceptable, even for commercial purposes, so long as 2862306a36Sopenharmony_ci * (1) they include prominent notice that the work is derivative, and (2) they 2962306a36Sopenharmony_ci * include prominent notice akin to these three paragraphs for those parts of 3062306a36Sopenharmony_ci * this code that are retained. 3162306a36Sopenharmony_ci * =========================================================================== 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci#include <linux/kernel.h> 3462306a36Sopenharmony_ci#include <linux/bitops.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <asm/div64.h> 3762306a36Sopenharmony_ci#include <asm/vfp.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "vfpinstr.h" 4062306a36Sopenharmony_ci#include "vfp.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct vfp_double vfp_double_default_qnan = { 4362306a36Sopenharmony_ci .exponent = 2047, 4462306a36Sopenharmony_ci .sign = 0, 4562306a36Sopenharmony_ci .significand = VFP_DOUBLE_SIGNIFICAND_QNAN, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void vfp_double_dump(const char *str, struct vfp_double *d) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci pr_debug("VFP: %s: sign=%d exponent=%d significand=%016llx\n", 5162306a36Sopenharmony_ci str, d->sign != 0, d->exponent, d->significand); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void vfp_double_normalise_denormal(struct vfp_double *vd) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int bits = 31 - fls(vd->significand >> 32); 5762306a36Sopenharmony_ci if (bits == 31) 5862306a36Sopenharmony_ci bits = 63 - fls(vd->significand); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci vfp_double_dump("normalise_denormal: in", vd); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (bits) { 6362306a36Sopenharmony_ci vd->exponent -= bits - 1; 6462306a36Sopenharmony_ci vd->significand <<= bits; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci vfp_double_dump("normalise_denormal: out", vd); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciu32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci u64 significand, incr; 7362306a36Sopenharmony_ci int exponent, shift, underflow; 7462306a36Sopenharmony_ci u32 rmode; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci vfp_double_dump("pack: in", vd); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* 7962306a36Sopenharmony_ci * Infinities and NaNs are a special case. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci if (vd->exponent == 2047 && (vd->significand == 0 || exceptions)) 8262306a36Sopenharmony_ci goto pack; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * Special-case zero. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci if (vd->significand == 0) { 8862306a36Sopenharmony_ci vd->exponent = 0; 8962306a36Sopenharmony_ci goto pack; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci exponent = vd->exponent; 9362306a36Sopenharmony_ci significand = vd->significand; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci shift = 32 - fls(significand >> 32); 9662306a36Sopenharmony_ci if (shift == 32) 9762306a36Sopenharmony_ci shift = 64 - fls(significand); 9862306a36Sopenharmony_ci if (shift) { 9962306a36Sopenharmony_ci exponent -= shift; 10062306a36Sopenharmony_ci significand <<= shift; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#ifdef DEBUG 10462306a36Sopenharmony_ci vd->exponent = exponent; 10562306a36Sopenharmony_ci vd->significand = significand; 10662306a36Sopenharmony_ci vfp_double_dump("pack: normalised", vd); 10762306a36Sopenharmony_ci#endif 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * Tiny number? 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci underflow = exponent < 0; 11362306a36Sopenharmony_ci if (underflow) { 11462306a36Sopenharmony_ci significand = vfp_shiftright64jamming(significand, -exponent); 11562306a36Sopenharmony_ci exponent = 0; 11662306a36Sopenharmony_ci#ifdef DEBUG 11762306a36Sopenharmony_ci vd->exponent = exponent; 11862306a36Sopenharmony_ci vd->significand = significand; 11962306a36Sopenharmony_ci vfp_double_dump("pack: tiny number", vd); 12062306a36Sopenharmony_ci#endif 12162306a36Sopenharmony_ci if (!(significand & ((1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1))) 12262306a36Sopenharmony_ci underflow = 0; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* 12662306a36Sopenharmony_ci * Select rounding increment. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci incr = 0; 12962306a36Sopenharmony_ci rmode = fpscr & FPSCR_RMODE_MASK; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (rmode == FPSCR_ROUND_NEAREST) { 13262306a36Sopenharmony_ci incr = 1ULL << VFP_DOUBLE_LOW_BITS; 13362306a36Sopenharmony_ci if ((significand & (1ULL << (VFP_DOUBLE_LOW_BITS + 1))) == 0) 13462306a36Sopenharmony_ci incr -= 1; 13562306a36Sopenharmony_ci } else if (rmode == FPSCR_ROUND_TOZERO) { 13662306a36Sopenharmony_ci incr = 0; 13762306a36Sopenharmony_ci } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0)) 13862306a36Sopenharmony_ci incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci pr_debug("VFP: rounding increment = 0x%08llx\n", incr); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci * Is our rounding going to overflow? 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci if ((significand + incr) < significand) { 14662306a36Sopenharmony_ci exponent += 1; 14762306a36Sopenharmony_ci significand = (significand >> 1) | (significand & 1); 14862306a36Sopenharmony_ci incr >>= 1; 14962306a36Sopenharmony_ci#ifdef DEBUG 15062306a36Sopenharmony_ci vd->exponent = exponent; 15162306a36Sopenharmony_ci vd->significand = significand; 15262306a36Sopenharmony_ci vfp_double_dump("pack: overflow", vd); 15362306a36Sopenharmony_ci#endif 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* 15762306a36Sopenharmony_ci * If any of the low bits (which will be shifted out of the 15862306a36Sopenharmony_ci * number) are non-zero, the result is inexact. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci if (significand & ((1 << (VFP_DOUBLE_LOW_BITS + 1)) - 1)) 16162306a36Sopenharmony_ci exceptions |= FPSCR_IXC; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * Do our rounding. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci significand += incr; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * Infinity? 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci if (exponent >= 2046) { 17262306a36Sopenharmony_ci exceptions |= FPSCR_OFC | FPSCR_IXC; 17362306a36Sopenharmony_ci if (incr == 0) { 17462306a36Sopenharmony_ci vd->exponent = 2045; 17562306a36Sopenharmony_ci vd->significand = 0x7fffffffffffffffULL; 17662306a36Sopenharmony_ci } else { 17762306a36Sopenharmony_ci vd->exponent = 2047; /* infinity */ 17862306a36Sopenharmony_ci vd->significand = 0; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci } else { 18162306a36Sopenharmony_ci if (significand >> (VFP_DOUBLE_LOW_BITS + 1) == 0) 18262306a36Sopenharmony_ci exponent = 0; 18362306a36Sopenharmony_ci if (exponent || significand > 0x8000000000000000ULL) 18462306a36Sopenharmony_ci underflow = 0; 18562306a36Sopenharmony_ci if (underflow) 18662306a36Sopenharmony_ci exceptions |= FPSCR_UFC; 18762306a36Sopenharmony_ci vd->exponent = exponent; 18862306a36Sopenharmony_ci vd->significand = significand >> 1; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci pack: 19262306a36Sopenharmony_ci vfp_double_dump("pack: final", vd); 19362306a36Sopenharmony_ci { 19462306a36Sopenharmony_ci s64 d = vfp_double_pack(vd); 19562306a36Sopenharmony_ci pr_debug("VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func, 19662306a36Sopenharmony_ci dd, d, exceptions); 19762306a36Sopenharmony_ci vfp_put_double(d, dd); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci return exceptions; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * Propagate the NaN, setting exceptions if it is signalling. 20462306a36Sopenharmony_ci * 'n' is always a NaN. 'm' may be a number, NaN or infinity. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic u32 20762306a36Sopenharmony_civfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn, 20862306a36Sopenharmony_ci struct vfp_double *vdm, u32 fpscr) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct vfp_double *nan; 21162306a36Sopenharmony_ci int tn, tm = 0; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci tn = vfp_double_type(vdn); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (vdm) 21662306a36Sopenharmony_ci tm = vfp_double_type(vdm); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (fpscr & FPSCR_DEFAULT_NAN) 21962306a36Sopenharmony_ci /* 22062306a36Sopenharmony_ci * Default NaN mode - always returns a quiet NaN 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci nan = &vfp_double_default_qnan; 22362306a36Sopenharmony_ci else { 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Contemporary mode - select the first signalling 22662306a36Sopenharmony_ci * NAN, or if neither are signalling, the first 22762306a36Sopenharmony_ci * quiet NAN. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN)) 23062306a36Sopenharmony_ci nan = vdn; 23162306a36Sopenharmony_ci else 23262306a36Sopenharmony_ci nan = vdm; 23362306a36Sopenharmony_ci /* 23462306a36Sopenharmony_ci * Make the NaN quiet. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci nan->significand |= VFP_DOUBLE_SIGNIFICAND_QNAN; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci *vdd = *nan; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* 24262306a36Sopenharmony_ci * If one was a signalling NAN, raise invalid operation. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * Extended operations 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cistatic u32 vfp_double_fabs(int dd, int unused, int dm, u32 fpscr) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci vfp_put_double(vfp_double_packed_abs(vfp_get_double(dm)), dd); 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic u32 vfp_double_fcpy(int dd, int unused, int dm, u32 fpscr) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci vfp_put_double(vfp_get_double(dm), dd); 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic u32 vfp_double_fneg(int dd, int unused, int dm, u32 fpscr) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci vfp_put_double(vfp_double_packed_negate(vfp_get_double(dm)), dd); 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic u32 vfp_double_fsqrt(int dd, int unused, int dm, u32 fpscr) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct vfp_double vdm, vdd; 27162306a36Sopenharmony_ci int ret, tm; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 27462306a36Sopenharmony_ci tm = vfp_double_type(&vdm); 27562306a36Sopenharmony_ci if (tm & (VFP_NAN|VFP_INFINITY)) { 27662306a36Sopenharmony_ci struct vfp_double *vdp = &vdd; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (tm & VFP_NAN) 27962306a36Sopenharmony_ci ret = vfp_propagate_nan(vdp, &vdm, NULL, fpscr); 28062306a36Sopenharmony_ci else if (vdm.sign == 0) { 28162306a36Sopenharmony_ci sqrt_copy: 28262306a36Sopenharmony_ci vdp = &vdm; 28362306a36Sopenharmony_ci ret = 0; 28462306a36Sopenharmony_ci } else { 28562306a36Sopenharmony_ci sqrt_invalid: 28662306a36Sopenharmony_ci vdp = &vfp_double_default_qnan; 28762306a36Sopenharmony_ci ret = FPSCR_IOC; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci vfp_put_double(vfp_double_pack(vdp), dd); 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* 29462306a36Sopenharmony_ci * sqrt(+/- 0) == +/- 0 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_ci if (tm & VFP_ZERO) 29762306a36Sopenharmony_ci goto sqrt_copy; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * Normalise a denormalised number 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci if (tm & VFP_DENORMAL) 30362306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* 30662306a36Sopenharmony_ci * sqrt(<0) = invalid 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci if (vdm.sign) 30962306a36Sopenharmony_ci goto sqrt_invalid; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci vfp_double_dump("sqrt", &vdm); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* 31462306a36Sopenharmony_ci * Estimate the square root. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci vdd.sign = 0; 31762306a36Sopenharmony_ci vdd.exponent = ((vdm.exponent - 1023) >> 1) + 1023; 31862306a36Sopenharmony_ci vdd.significand = (u64)vfp_estimate_sqrt_significand(vdm.exponent, vdm.significand >> 32) << 31; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci vfp_double_dump("sqrt estimate1", &vdd); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci vdm.significand >>= 1 + (vdm.exponent & 1); 32362306a36Sopenharmony_ci vdd.significand += 2 + vfp_estimate_div128to64(vdm.significand, 0, vdd.significand); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci vfp_double_dump("sqrt estimate2", &vdd); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* 32862306a36Sopenharmony_ci * And now adjust. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci if ((vdd.significand & VFP_DOUBLE_LOW_BITS_MASK) <= 5) { 33162306a36Sopenharmony_ci if (vdd.significand < 2) { 33262306a36Sopenharmony_ci vdd.significand = ~0ULL; 33362306a36Sopenharmony_ci } else { 33462306a36Sopenharmony_ci u64 termh, terml, remh, reml; 33562306a36Sopenharmony_ci vdm.significand <<= 2; 33662306a36Sopenharmony_ci mul64to128(&termh, &terml, vdd.significand, vdd.significand); 33762306a36Sopenharmony_ci sub128(&remh, &reml, vdm.significand, 0, termh, terml); 33862306a36Sopenharmony_ci while ((s64)remh < 0) { 33962306a36Sopenharmony_ci vdd.significand -= 1; 34062306a36Sopenharmony_ci shift64left(&termh, &terml, vdd.significand); 34162306a36Sopenharmony_ci terml |= 1; 34262306a36Sopenharmony_ci add128(&remh, &reml, remh, reml, termh, terml); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci vdd.significand |= (remh | reml) != 0; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci vdd.significand = vfp_shiftright64jamming(vdd.significand, 1); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fsqrt"); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* 35362306a36Sopenharmony_ci * Equal := ZC 35462306a36Sopenharmony_ci * Less than := N 35562306a36Sopenharmony_ci * Greater than := C 35662306a36Sopenharmony_ci * Unordered := CV 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_cistatic u32 vfp_compare(int dd, int signal_on_qnan, int dm, u32 fpscr) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci s64 d, m; 36162306a36Sopenharmony_ci u32 ret = 0; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci m = vfp_get_double(dm); 36462306a36Sopenharmony_ci if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) { 36562306a36Sopenharmony_ci ret |= FPSCR_C | FPSCR_V; 36662306a36Sopenharmony_ci if (signal_on_qnan || !(vfp_double_packed_mantissa(m) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1)))) 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * Signalling NaN, or signalling on quiet NaN 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ci ret |= FPSCR_IOC; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci d = vfp_get_double(dd); 37462306a36Sopenharmony_ci if (vfp_double_packed_exponent(d) == 2047 && vfp_double_packed_mantissa(d)) { 37562306a36Sopenharmony_ci ret |= FPSCR_C | FPSCR_V; 37662306a36Sopenharmony_ci if (signal_on_qnan || !(vfp_double_packed_mantissa(d) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1)))) 37762306a36Sopenharmony_ci /* 37862306a36Sopenharmony_ci * Signalling NaN, or signalling on quiet NaN 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci ret |= FPSCR_IOC; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (ret == 0) { 38462306a36Sopenharmony_ci if (d == m || vfp_double_packed_abs(d | m) == 0) { 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * equal 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci ret |= FPSCR_Z | FPSCR_C; 38962306a36Sopenharmony_ci } else if (vfp_double_packed_sign(d ^ m)) { 39062306a36Sopenharmony_ci /* 39162306a36Sopenharmony_ci * different signs 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_ci if (vfp_double_packed_sign(d)) 39462306a36Sopenharmony_ci /* 39562306a36Sopenharmony_ci * d is negative, so d < m 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci ret |= FPSCR_N; 39862306a36Sopenharmony_ci else 39962306a36Sopenharmony_ci /* 40062306a36Sopenharmony_ci * d is positive, so d > m 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci ret |= FPSCR_C; 40362306a36Sopenharmony_ci } else if ((vfp_double_packed_sign(d) != 0) ^ (d < m)) { 40462306a36Sopenharmony_ci /* 40562306a36Sopenharmony_ci * d < m 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ci ret |= FPSCR_N; 40862306a36Sopenharmony_ci } else if ((vfp_double_packed_sign(d) != 0) ^ (d > m)) { 40962306a36Sopenharmony_ci /* 41062306a36Sopenharmony_ci * d > m 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci ret |= FPSCR_C; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return ret; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic u32 vfp_double_fcmp(int dd, int unused, int dm, u32 fpscr) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci return vfp_compare(dd, 0, dm, fpscr); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic u32 vfp_double_fcmpe(int dd, int unused, int dm, u32 fpscr) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci return vfp_compare(dd, 1, dm, fpscr); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic u32 vfp_double_fcmpz(int dd, int unused, int dm, u32 fpscr) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci return vfp_compare(dd, 0, VFP_REG_ZERO, fpscr); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic u32 vfp_double_fcmpez(int dd, int unused, int dm, u32 fpscr) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci return vfp_compare(dd, 1, VFP_REG_ZERO, fpscr); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic u32 vfp_double_fcvts(int sd, int unused, int dm, u32 fpscr) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct vfp_double vdm; 44262306a36Sopenharmony_ci struct vfp_single vsd; 44362306a36Sopenharmony_ci int tm; 44462306a36Sopenharmony_ci u32 exceptions = 0; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci tm = vfp_double_type(&vdm); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * If we have a signalling NaN, signal invalid operation. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci if (tm == VFP_SNAN) 45462306a36Sopenharmony_ci exceptions = FPSCR_IOC; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (tm & VFP_DENORMAL) 45762306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci vsd.sign = vdm.sign; 46062306a36Sopenharmony_ci vsd.significand = vfp_hi64to32jamming(vdm.significand); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* 46362306a36Sopenharmony_ci * If we have an infinity or a NaN, the exponent must be 255 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci if (tm & (VFP_INFINITY|VFP_NAN)) { 46662306a36Sopenharmony_ci vsd.exponent = 255; 46762306a36Sopenharmony_ci if (tm == VFP_QNAN) 46862306a36Sopenharmony_ci vsd.significand |= VFP_SINGLE_SIGNIFICAND_QNAN; 46962306a36Sopenharmony_ci goto pack_nan; 47062306a36Sopenharmony_ci } else if (tm & VFP_ZERO) 47162306a36Sopenharmony_ci vsd.exponent = 0; 47262306a36Sopenharmony_ci else 47362306a36Sopenharmony_ci vsd.exponent = vdm.exponent - (1023 - 127); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fcvts"); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci pack_nan: 47862306a36Sopenharmony_ci vfp_put_float(vfp_single_pack(&vsd), sd); 47962306a36Sopenharmony_ci return exceptions; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic u32 vfp_double_fuito(int dd, int unused, int dm, u32 fpscr) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct vfp_double vdm; 48562306a36Sopenharmony_ci u32 m = vfp_get_float(dm); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci vdm.sign = 0; 48862306a36Sopenharmony_ci vdm.exponent = 1023 + 63 - 1; 48962306a36Sopenharmony_ci vdm.significand = (u64)m; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fuito"); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic u32 vfp_double_fsito(int dd, int unused, int dm, u32 fpscr) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct vfp_double vdm; 49762306a36Sopenharmony_ci u32 m = vfp_get_float(dm); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci vdm.sign = (m & 0x80000000) >> 16; 50062306a36Sopenharmony_ci vdm.exponent = 1023 + 63 - 1; 50162306a36Sopenharmony_ci vdm.significand = vdm.sign ? -m : m; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fsito"); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic u32 vfp_double_ftoui(int sd, int unused, int dm, u32 fpscr) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct vfp_double vdm; 50962306a36Sopenharmony_ci u32 d, exceptions = 0; 51062306a36Sopenharmony_ci int rmode = fpscr & FPSCR_RMODE_MASK; 51162306a36Sopenharmony_ci int tm; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* 51662306a36Sopenharmony_ci * Do we have a denormalised number? 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci tm = vfp_double_type(&vdm); 51962306a36Sopenharmony_ci if (tm & VFP_DENORMAL) 52062306a36Sopenharmony_ci exceptions |= FPSCR_IDC; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (tm & VFP_NAN) 52362306a36Sopenharmony_ci vdm.sign = 0; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (vdm.exponent >= 1023 + 32) { 52662306a36Sopenharmony_ci d = vdm.sign ? 0 : 0xffffffff; 52762306a36Sopenharmony_ci exceptions = FPSCR_IOC; 52862306a36Sopenharmony_ci } else if (vdm.exponent >= 1023 - 1) { 52962306a36Sopenharmony_ci int shift = 1023 + 63 - vdm.exponent; 53062306a36Sopenharmony_ci u64 rem, incr = 0; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* 53362306a36Sopenharmony_ci * 2^0 <= m < 2^32-2^8 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci d = (vdm.significand << 1) >> shift; 53662306a36Sopenharmony_ci rem = vdm.significand << (65 - shift); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (rmode == FPSCR_ROUND_NEAREST) { 53962306a36Sopenharmony_ci incr = 0x8000000000000000ULL; 54062306a36Sopenharmony_ci if ((d & 1) == 0) 54162306a36Sopenharmony_ci incr -= 1; 54262306a36Sopenharmony_ci } else if (rmode == FPSCR_ROUND_TOZERO) { 54362306a36Sopenharmony_ci incr = 0; 54462306a36Sopenharmony_ci } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) { 54562306a36Sopenharmony_ci incr = ~0ULL; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if ((rem + incr) < rem) { 54962306a36Sopenharmony_ci if (d < 0xffffffff) 55062306a36Sopenharmony_ci d += 1; 55162306a36Sopenharmony_ci else 55262306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (d && vdm.sign) { 55662306a36Sopenharmony_ci d = 0; 55762306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 55862306a36Sopenharmony_ci } else if (rem) 55962306a36Sopenharmony_ci exceptions |= FPSCR_IXC; 56062306a36Sopenharmony_ci } else { 56162306a36Sopenharmony_ci d = 0; 56262306a36Sopenharmony_ci if (vdm.exponent | vdm.significand) { 56362306a36Sopenharmony_ci exceptions |= FPSCR_IXC; 56462306a36Sopenharmony_ci if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) 56562306a36Sopenharmony_ci d = 1; 56662306a36Sopenharmony_ci else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) { 56762306a36Sopenharmony_ci d = 0; 56862306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci vfp_put_float(d, sd); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return exceptions; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic u32 vfp_double_ftouiz(int sd, int unused, int dm, u32 fpscr) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci return vfp_double_ftoui(sd, unused, dm, FPSCR_ROUND_TOZERO); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic u32 vfp_double_ftosi(int sd, int unused, int dm, u32 fpscr) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct vfp_double vdm; 58862306a36Sopenharmony_ci u32 d, exceptions = 0; 58962306a36Sopenharmony_ci int rmode = fpscr & FPSCR_RMODE_MASK; 59062306a36Sopenharmony_ci int tm; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 59362306a36Sopenharmony_ci vfp_double_dump("VDM", &vdm); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* 59662306a36Sopenharmony_ci * Do we have denormalised number? 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ci tm = vfp_double_type(&vdm); 59962306a36Sopenharmony_ci if (tm & VFP_DENORMAL) 60062306a36Sopenharmony_ci exceptions |= FPSCR_IDC; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (tm & VFP_NAN) { 60362306a36Sopenharmony_ci d = 0; 60462306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 60562306a36Sopenharmony_ci } else if (vdm.exponent >= 1023 + 32) { 60662306a36Sopenharmony_ci d = 0x7fffffff; 60762306a36Sopenharmony_ci if (vdm.sign) 60862306a36Sopenharmony_ci d = ~d; 60962306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 61062306a36Sopenharmony_ci } else if (vdm.exponent >= 1023 - 1) { 61162306a36Sopenharmony_ci int shift = 1023 + 63 - vdm.exponent; /* 58 */ 61262306a36Sopenharmony_ci u64 rem, incr = 0; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci d = (vdm.significand << 1) >> shift; 61562306a36Sopenharmony_ci rem = vdm.significand << (65 - shift); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (rmode == FPSCR_ROUND_NEAREST) { 61862306a36Sopenharmony_ci incr = 0x8000000000000000ULL; 61962306a36Sopenharmony_ci if ((d & 1) == 0) 62062306a36Sopenharmony_ci incr -= 1; 62162306a36Sopenharmony_ci } else if (rmode == FPSCR_ROUND_TOZERO) { 62262306a36Sopenharmony_ci incr = 0; 62362306a36Sopenharmony_ci } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) { 62462306a36Sopenharmony_ci incr = ~0ULL; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if ((rem + incr) < rem && d < 0xffffffff) 62862306a36Sopenharmony_ci d += 1; 62962306a36Sopenharmony_ci if (d > 0x7fffffff + (vdm.sign != 0)) { 63062306a36Sopenharmony_ci d = 0x7fffffff + (vdm.sign != 0); 63162306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 63262306a36Sopenharmony_ci } else if (rem) 63362306a36Sopenharmony_ci exceptions |= FPSCR_IXC; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (vdm.sign) 63662306a36Sopenharmony_ci d = -d; 63762306a36Sopenharmony_ci } else { 63862306a36Sopenharmony_ci d = 0; 63962306a36Sopenharmony_ci if (vdm.exponent | vdm.significand) { 64062306a36Sopenharmony_ci exceptions |= FPSCR_IXC; 64162306a36Sopenharmony_ci if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) 64262306a36Sopenharmony_ci d = 1; 64362306a36Sopenharmony_ci else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) 64462306a36Sopenharmony_ci d = -1; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci vfp_put_float((s32)d, sd); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return exceptions; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic u32 vfp_double_ftosiz(int dd, int unused, int dm, u32 fpscr) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci return vfp_double_ftosi(dd, unused, dm, FPSCR_ROUND_TOZERO); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic struct op fops_ext[32] = { 66262306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCPY)] = { vfp_double_fcpy, 0 }, 66362306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FABS)] = { vfp_double_fabs, 0 }, 66462306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FNEG)] = { vfp_double_fneg, 0 }, 66562306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FSQRT)] = { vfp_double_fsqrt, 0 }, 66662306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMP)] = { vfp_double_fcmp, OP_SCALAR }, 66762306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMPE)] = { vfp_double_fcmpe, OP_SCALAR }, 66862306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMPZ)] = { vfp_double_fcmpz, OP_SCALAR }, 66962306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMPEZ)] = { vfp_double_fcmpez, OP_SCALAR }, 67062306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCVT)] = { vfp_double_fcvts, OP_SCALAR|OP_SD }, 67162306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FUITO)] = { vfp_double_fuito, OP_SCALAR|OP_SM }, 67262306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FSITO)] = { vfp_double_fsito, OP_SCALAR|OP_SM }, 67362306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOUI)] = { vfp_double_ftoui, OP_SCALAR|OP_SD }, 67462306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOUIZ)] = { vfp_double_ftouiz, OP_SCALAR|OP_SD }, 67562306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOSI)] = { vfp_double_ftosi, OP_SCALAR|OP_SD }, 67662306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOSIZ)] = { vfp_double_ftosiz, OP_SCALAR|OP_SD }, 67762306a36Sopenharmony_ci}; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic u32 68362306a36Sopenharmony_civfp_double_fadd_nonnumber(struct vfp_double *vdd, struct vfp_double *vdn, 68462306a36Sopenharmony_ci struct vfp_double *vdm, u32 fpscr) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct vfp_double *vdp; 68762306a36Sopenharmony_ci u32 exceptions = 0; 68862306a36Sopenharmony_ci int tn, tm; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci tn = vfp_double_type(vdn); 69162306a36Sopenharmony_ci tm = vfp_double_type(vdm); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (tn & tm & VFP_INFINITY) { 69462306a36Sopenharmony_ci /* 69562306a36Sopenharmony_ci * Two infinities. Are they different signs? 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_ci if (vdn->sign ^ vdm->sign) { 69862306a36Sopenharmony_ci /* 69962306a36Sopenharmony_ci * different signs -> invalid 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_ci exceptions = FPSCR_IOC; 70262306a36Sopenharmony_ci vdp = &vfp_double_default_qnan; 70362306a36Sopenharmony_ci } else { 70462306a36Sopenharmony_ci /* 70562306a36Sopenharmony_ci * same signs -> valid 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ci vdp = vdn; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci } else if (tn & VFP_INFINITY && tm & VFP_NUMBER) { 71062306a36Sopenharmony_ci /* 71162306a36Sopenharmony_ci * One infinity and one number -> infinity 71262306a36Sopenharmony_ci */ 71362306a36Sopenharmony_ci vdp = vdn; 71462306a36Sopenharmony_ci } else { 71562306a36Sopenharmony_ci /* 71662306a36Sopenharmony_ci * 'n' is a NaN of some type 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_ci return vfp_propagate_nan(vdd, vdn, vdm, fpscr); 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci *vdd = *vdp; 72162306a36Sopenharmony_ci return exceptions; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic u32 72562306a36Sopenharmony_civfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn, 72662306a36Sopenharmony_ci struct vfp_double *vdm, u32 fpscr) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci u32 exp_diff; 72962306a36Sopenharmony_ci u64 m_sig; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (vdn->significand & (1ULL << 63) || 73262306a36Sopenharmony_ci vdm->significand & (1ULL << 63)) { 73362306a36Sopenharmony_ci pr_info("VFP: bad FP values in %s\n", __func__); 73462306a36Sopenharmony_ci vfp_double_dump("VDN", vdn); 73562306a36Sopenharmony_ci vfp_double_dump("VDM", vdm); 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* 73962306a36Sopenharmony_ci * Ensure that 'n' is the largest magnitude number. Note that 74062306a36Sopenharmony_ci * if 'n' and 'm' have equal exponents, we do not swap them. 74162306a36Sopenharmony_ci * This ensures that NaN propagation works correctly. 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_ci if (vdn->exponent < vdm->exponent) { 74462306a36Sopenharmony_ci struct vfp_double *t = vdn; 74562306a36Sopenharmony_ci vdn = vdm; 74662306a36Sopenharmony_ci vdm = t; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* 75062306a36Sopenharmony_ci * Is 'n' an infinity or a NaN? Note that 'm' may be a number, 75162306a36Sopenharmony_ci * infinity or a NaN here. 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci if (vdn->exponent == 2047) 75462306a36Sopenharmony_ci return vfp_double_fadd_nonnumber(vdd, vdn, vdm, fpscr); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* 75762306a36Sopenharmony_ci * We have two proper numbers, where 'vdn' is the larger magnitude. 75862306a36Sopenharmony_ci * 75962306a36Sopenharmony_ci * Copy 'n' to 'd' before doing the arithmetic. 76062306a36Sopenharmony_ci */ 76162306a36Sopenharmony_ci *vdd = *vdn; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* 76462306a36Sopenharmony_ci * Align 'm' with the result. 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ci exp_diff = vdn->exponent - vdm->exponent; 76762306a36Sopenharmony_ci m_sig = vfp_shiftright64jamming(vdm->significand, exp_diff); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* 77062306a36Sopenharmony_ci * If the signs are different, we are really subtracting. 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_ci if (vdn->sign ^ vdm->sign) { 77362306a36Sopenharmony_ci m_sig = vdn->significand - m_sig; 77462306a36Sopenharmony_ci if ((s64)m_sig < 0) { 77562306a36Sopenharmony_ci vdd->sign = vfp_sign_negate(vdd->sign); 77662306a36Sopenharmony_ci m_sig = -m_sig; 77762306a36Sopenharmony_ci } else if (m_sig == 0) { 77862306a36Sopenharmony_ci vdd->sign = (fpscr & FPSCR_RMODE_MASK) == 77962306a36Sopenharmony_ci FPSCR_ROUND_MINUSINF ? 0x8000 : 0; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci } else { 78262306a36Sopenharmony_ci m_sig += vdn->significand; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci vdd->significand = m_sig; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci return 0; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic u32 79062306a36Sopenharmony_civfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn, 79162306a36Sopenharmony_ci struct vfp_double *vdm, u32 fpscr) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci vfp_double_dump("VDN", vdn); 79462306a36Sopenharmony_ci vfp_double_dump("VDM", vdm); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* 79762306a36Sopenharmony_ci * Ensure that 'n' is the largest magnitude number. Note that 79862306a36Sopenharmony_ci * if 'n' and 'm' have equal exponents, we do not swap them. 79962306a36Sopenharmony_ci * This ensures that NaN propagation works correctly. 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_ci if (vdn->exponent < vdm->exponent) { 80262306a36Sopenharmony_ci struct vfp_double *t = vdn; 80362306a36Sopenharmony_ci vdn = vdm; 80462306a36Sopenharmony_ci vdm = t; 80562306a36Sopenharmony_ci pr_debug("VFP: swapping M <-> N\n"); 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci vdd->sign = vdn->sign ^ vdm->sign; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* 81162306a36Sopenharmony_ci * If 'n' is an infinity or NaN, handle it. 'm' may be anything. 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_ci if (vdn->exponent == 2047) { 81462306a36Sopenharmony_ci if (vdn->significand || (vdm->exponent == 2047 && vdm->significand)) 81562306a36Sopenharmony_ci return vfp_propagate_nan(vdd, vdn, vdm, fpscr); 81662306a36Sopenharmony_ci if ((vdm->exponent | vdm->significand) == 0) { 81762306a36Sopenharmony_ci *vdd = vfp_double_default_qnan; 81862306a36Sopenharmony_ci return FPSCR_IOC; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci vdd->exponent = vdn->exponent; 82162306a36Sopenharmony_ci vdd->significand = 0; 82262306a36Sopenharmony_ci return 0; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* 82662306a36Sopenharmony_ci * If 'm' is zero, the result is always zero. In this case, 82762306a36Sopenharmony_ci * 'n' may be zero or a number, but it doesn't matter which. 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_ci if ((vdm->exponent | vdm->significand) == 0) { 83062306a36Sopenharmony_ci vdd->exponent = 0; 83162306a36Sopenharmony_ci vdd->significand = 0; 83262306a36Sopenharmony_ci return 0; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* 83662306a36Sopenharmony_ci * We add 2 to the destination exponent for the same reason 83762306a36Sopenharmony_ci * as the addition case - though this time we have +1 from 83862306a36Sopenharmony_ci * each input operand. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci vdd->exponent = vdn->exponent + vdm->exponent - 1023 + 2; 84162306a36Sopenharmony_ci vdd->significand = vfp_hi64multiply64(vdn->significand, vdm->significand); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci vfp_double_dump("VDD", vdd); 84462306a36Sopenharmony_ci return 0; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci#define NEG_MULTIPLY (1 << 0) 84862306a36Sopenharmony_ci#define NEG_SUBTRACT (1 << 1) 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic u32 85162306a36Sopenharmony_civfp_double_multiply_accumulate(int dd, int dn, int dm, u32 fpscr, u32 negate, char *func) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct vfp_double vdd, vdp, vdn, vdm; 85462306a36Sopenharmony_ci u32 exceptions; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 85762306a36Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 85862306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 86162306a36Sopenharmony_ci if (vdm.exponent == 0 && vdm.significand) 86262306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr); 86562306a36Sopenharmony_ci if (negate & NEG_MULTIPLY) 86662306a36Sopenharmony_ci vdp.sign = vfp_sign_negate(vdp.sign); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dd)); 86962306a36Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 87062306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 87162306a36Sopenharmony_ci if (negate & NEG_SUBTRACT) 87262306a36Sopenharmony_ci vdn.sign = vfp_sign_negate(vdn.sign); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, func); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci/* 88062306a36Sopenharmony_ci * Standard operations 88162306a36Sopenharmony_ci */ 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci/* 88462306a36Sopenharmony_ci * sd = sd + (sn * sm) 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_cistatic u32 vfp_double_fmac(int dd, int dn, int dm, u32 fpscr) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, 0, "fmac"); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci/* 89262306a36Sopenharmony_ci * sd = sd - (sn * sm) 89362306a36Sopenharmony_ci */ 89462306a36Sopenharmony_cistatic u32 vfp_double_fnmac(int dd, int dn, int dm, u32 fpscr) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac"); 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/* 90062306a36Sopenharmony_ci * sd = -sd + (sn * sm) 90162306a36Sopenharmony_ci */ 90262306a36Sopenharmony_cistatic u32 vfp_double_fmsc(int dd, int dn, int dm, u32 fpscr) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc"); 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci/* 90862306a36Sopenharmony_ci * sd = -sd - (sn * sm) 90962306a36Sopenharmony_ci */ 91062306a36Sopenharmony_cistatic u32 vfp_double_fnmsc(int dd, int dn, int dm, u32 fpscr) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc"); 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci/* 91662306a36Sopenharmony_ci * sd = sn * sm 91762306a36Sopenharmony_ci */ 91862306a36Sopenharmony_cistatic u32 vfp_double_fmul(int dd, int dn, int dm, u32 fpscr) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci struct vfp_double vdd, vdn, vdm; 92162306a36Sopenharmony_ci u32 exceptions; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 92462306a36Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 92562306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 92862306a36Sopenharmony_ci if (vdm.exponent == 0 && vdm.significand) 92962306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); 93262306a36Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fmul"); 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci/* 93662306a36Sopenharmony_ci * sd = -(sn * sm) 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_cistatic u32 vfp_double_fnmul(int dd, int dn, int dm, u32 fpscr) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci struct vfp_double vdd, vdn, vdm; 94162306a36Sopenharmony_ci u32 exceptions; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 94462306a36Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 94562306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 94862306a36Sopenharmony_ci if (vdm.exponent == 0 && vdm.significand) 94962306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); 95262306a36Sopenharmony_ci vdd.sign = vfp_sign_negate(vdd.sign); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fnmul"); 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci/* 95862306a36Sopenharmony_ci * sd = sn + sm 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_cistatic u32 vfp_double_fadd(int dd, int dn, int dm, u32 fpscr) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct vfp_double vdd, vdn, vdm; 96362306a36Sopenharmony_ci u32 exceptions; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 96662306a36Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 96762306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 97062306a36Sopenharmony_ci if (vdm.exponent == 0 && vdm.significand) 97162306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fadd"); 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci/* 97962306a36Sopenharmony_ci * sd = sn - sm 98062306a36Sopenharmony_ci */ 98162306a36Sopenharmony_cistatic u32 vfp_double_fsub(int dd, int dn, int dm, u32 fpscr) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci struct vfp_double vdd, vdn, vdm; 98462306a36Sopenharmony_ci u32 exceptions; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 98762306a36Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 98862306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 99162306a36Sopenharmony_ci if (vdm.exponent == 0 && vdm.significand) 99262306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* 99562306a36Sopenharmony_ci * Subtraction is like addition, but with a negated operand. 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_ci vdm.sign = vfp_sign_negate(vdm.sign); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fsub"); 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci/* 100562306a36Sopenharmony_ci * sd = sn / sm 100662306a36Sopenharmony_ci */ 100762306a36Sopenharmony_cistatic u32 vfp_double_fdiv(int dd, int dn, int dm, u32 fpscr) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct vfp_double vdd, vdn, vdm; 101062306a36Sopenharmony_ci u32 exceptions = 0; 101162306a36Sopenharmony_ci int tm, tn; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 101462306a36Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci vdd.sign = vdn.sign ^ vdm.sign; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci tn = vfp_double_type(&vdn); 101962306a36Sopenharmony_ci tm = vfp_double_type(&vdm); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* 102262306a36Sopenharmony_ci * Is n a NAN? 102362306a36Sopenharmony_ci */ 102462306a36Sopenharmony_ci if (tn & VFP_NAN) 102562306a36Sopenharmony_ci goto vdn_nan; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* 102862306a36Sopenharmony_ci * Is m a NAN? 102962306a36Sopenharmony_ci */ 103062306a36Sopenharmony_ci if (tm & VFP_NAN) 103162306a36Sopenharmony_ci goto vdm_nan; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci /* 103462306a36Sopenharmony_ci * If n and m are infinity, the result is invalid 103562306a36Sopenharmony_ci * If n and m are zero, the result is invalid 103662306a36Sopenharmony_ci */ 103762306a36Sopenharmony_ci if (tm & tn & (VFP_INFINITY|VFP_ZERO)) 103862306a36Sopenharmony_ci goto invalid; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* 104162306a36Sopenharmony_ci * If n is infinity, the result is infinity 104262306a36Sopenharmony_ci */ 104362306a36Sopenharmony_ci if (tn & VFP_INFINITY) 104462306a36Sopenharmony_ci goto infinity; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci /* 104762306a36Sopenharmony_ci * If m is zero, raise div0 exceptions 104862306a36Sopenharmony_ci */ 104962306a36Sopenharmony_ci if (tm & VFP_ZERO) 105062306a36Sopenharmony_ci goto divzero; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* 105362306a36Sopenharmony_ci * If m is infinity, or n is zero, the result is zero 105462306a36Sopenharmony_ci */ 105562306a36Sopenharmony_ci if (tm & VFP_INFINITY || tn & VFP_ZERO) 105662306a36Sopenharmony_ci goto zero; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (tn & VFP_DENORMAL) 105962306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 106062306a36Sopenharmony_ci if (tm & VFP_DENORMAL) 106162306a36Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* 106462306a36Sopenharmony_ci * Ok, we have two numbers, we can perform division. 106562306a36Sopenharmony_ci */ 106662306a36Sopenharmony_ci vdd.exponent = vdn.exponent - vdm.exponent + 1023 - 1; 106762306a36Sopenharmony_ci vdm.significand <<= 1; 106862306a36Sopenharmony_ci if (vdm.significand <= (2 * vdn.significand)) { 106962306a36Sopenharmony_ci vdn.significand >>= 1; 107062306a36Sopenharmony_ci vdd.exponent++; 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci vdd.significand = vfp_estimate_div128to64(vdn.significand, 0, vdm.significand); 107362306a36Sopenharmony_ci if ((vdd.significand & 0x1ff) <= 2) { 107462306a36Sopenharmony_ci u64 termh, terml, remh, reml; 107562306a36Sopenharmony_ci mul64to128(&termh, &terml, vdm.significand, vdd.significand); 107662306a36Sopenharmony_ci sub128(&remh, &reml, vdn.significand, 0, termh, terml); 107762306a36Sopenharmony_ci while ((s64)remh < 0) { 107862306a36Sopenharmony_ci vdd.significand -= 1; 107962306a36Sopenharmony_ci add128(&remh, &reml, remh, reml, 0, vdm.significand); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci vdd.significand |= (reml != 0); 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fdiv"); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci vdn_nan: 108662306a36Sopenharmony_ci exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr); 108762306a36Sopenharmony_ci pack: 108862306a36Sopenharmony_ci vfp_put_double(vfp_double_pack(&vdd), dd); 108962306a36Sopenharmony_ci return exceptions; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci vdm_nan: 109262306a36Sopenharmony_ci exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr); 109362306a36Sopenharmony_ci goto pack; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci zero: 109662306a36Sopenharmony_ci vdd.exponent = 0; 109762306a36Sopenharmony_ci vdd.significand = 0; 109862306a36Sopenharmony_ci goto pack; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci divzero: 110162306a36Sopenharmony_ci exceptions = FPSCR_DZC; 110262306a36Sopenharmony_ci infinity: 110362306a36Sopenharmony_ci vdd.exponent = 2047; 110462306a36Sopenharmony_ci vdd.significand = 0; 110562306a36Sopenharmony_ci goto pack; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci invalid: 110862306a36Sopenharmony_ci vfp_put_double(vfp_double_pack(&vfp_double_default_qnan), dd); 110962306a36Sopenharmony_ci return FPSCR_IOC; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic struct op fops[16] = { 111362306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FMAC)] = { vfp_double_fmac, 0 }, 111462306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FNMAC)] = { vfp_double_fnmac, 0 }, 111562306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FMSC)] = { vfp_double_fmsc, 0 }, 111662306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FNMSC)] = { vfp_double_fnmsc, 0 }, 111762306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FMUL)] = { vfp_double_fmul, 0 }, 111862306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FNMUL)] = { vfp_double_fnmul, 0 }, 111962306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FADD)] = { vfp_double_fadd, 0 }, 112062306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FSUB)] = { vfp_double_fsub, 0 }, 112162306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FDIV)] = { vfp_double_fdiv, 0 }, 112262306a36Sopenharmony_ci}; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci#define FREG_BANK(x) ((x) & 0x0c) 112562306a36Sopenharmony_ci#define FREG_IDX(x) ((x) & 3) 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ciu32 vfp_double_cpdo(u32 inst, u32 fpscr) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci u32 op = inst & FOP_MASK; 113062306a36Sopenharmony_ci u32 exceptions = 0; 113162306a36Sopenharmony_ci unsigned int dest; 113262306a36Sopenharmony_ci unsigned int dn = vfp_get_dn(inst); 113362306a36Sopenharmony_ci unsigned int dm; 113462306a36Sopenharmony_ci unsigned int vecitr, veclen, vecstride; 113562306a36Sopenharmony_ci struct op *fop; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK)); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)]; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* 114262306a36Sopenharmony_ci * fcvtds takes an sN register number as destination, not dN. 114362306a36Sopenharmony_ci * It also always operates on scalars. 114462306a36Sopenharmony_ci */ 114562306a36Sopenharmony_ci if (fop->flags & OP_SD) 114662306a36Sopenharmony_ci dest = vfp_get_sd(inst); 114762306a36Sopenharmony_ci else 114862306a36Sopenharmony_ci dest = vfp_get_dd(inst); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci /* 115162306a36Sopenharmony_ci * f[us]ito takes a sN operand, not a dN operand. 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_ci if (fop->flags & OP_SM) 115462306a36Sopenharmony_ci dm = vfp_get_sm(inst); 115562306a36Sopenharmony_ci else 115662306a36Sopenharmony_ci dm = vfp_get_dm(inst); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* 115962306a36Sopenharmony_ci * If destination bank is zero, vector length is always '1'. 116062306a36Sopenharmony_ci * ARM DDI0100F C5.1.3, C5.3.2. 116162306a36Sopenharmony_ci */ 116262306a36Sopenharmony_ci if ((fop->flags & OP_SCALAR) || (FREG_BANK(dest) == 0)) 116362306a36Sopenharmony_ci veclen = 0; 116462306a36Sopenharmony_ci else 116562306a36Sopenharmony_ci veclen = fpscr & FPSCR_LENGTH_MASK; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride, 116862306a36Sopenharmony_ci (veclen >> FPSCR_LENGTH_BIT) + 1); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci if (!fop->fn) 117162306a36Sopenharmony_ci goto invalid; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) { 117462306a36Sopenharmony_ci u32 except; 117562306a36Sopenharmony_ci char type; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci type = fop->flags & OP_SD ? 's' : 'd'; 117862306a36Sopenharmony_ci if (op == FOP_EXT) 117962306a36Sopenharmony_ci pr_debug("VFP: itr%d (%c%u) = op[%u] (d%u)\n", 118062306a36Sopenharmony_ci vecitr >> FPSCR_LENGTH_BIT, 118162306a36Sopenharmony_ci type, dest, dn, dm); 118262306a36Sopenharmony_ci else 118362306a36Sopenharmony_ci pr_debug("VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)\n", 118462306a36Sopenharmony_ci vecitr >> FPSCR_LENGTH_BIT, 118562306a36Sopenharmony_ci type, dest, dn, FOP_TO_IDX(op), dm); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci except = fop->fn(dest, dn, dm, fpscr); 118862306a36Sopenharmony_ci pr_debug("VFP: itr%d: exceptions=%08x\n", 118962306a36Sopenharmony_ci vecitr >> FPSCR_LENGTH_BIT, except); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci exceptions |= except; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /* 119462306a36Sopenharmony_ci * CHECK: It appears to be undefined whether we stop when 119562306a36Sopenharmony_ci * we encounter an exception. We continue. 119662306a36Sopenharmony_ci */ 119762306a36Sopenharmony_ci dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 3); 119862306a36Sopenharmony_ci dn = FREG_BANK(dn) + ((FREG_IDX(dn) + vecstride) & 3); 119962306a36Sopenharmony_ci if (FREG_BANK(dm) != 0) 120062306a36Sopenharmony_ci dm = FREG_BANK(dm) + ((FREG_IDX(dm) + vecstride) & 3); 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci return exceptions; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci invalid: 120562306a36Sopenharmony_ci return ~0; 120662306a36Sopenharmony_ci} 1207