162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/arch/arm/vfp/vfpsingle.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_single vfp_single_default_qnan = { 4362306a36Sopenharmony_ci .exponent = 255, 4462306a36Sopenharmony_ci .sign = 0, 4562306a36Sopenharmony_ci .significand = VFP_SINGLE_SIGNIFICAND_QNAN, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void vfp_single_dump(const char *str, struct vfp_single *s) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci pr_debug("VFP: %s: sign=%d exponent=%d significand=%08x\n", 5162306a36Sopenharmony_ci str, s->sign != 0, s->exponent, s->significand); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void vfp_single_normalise_denormal(struct vfp_single *vs) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int bits = 31 - fls(vs->significand); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci vfp_single_dump("normalise_denormal: in", vs); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (bits) { 6162306a36Sopenharmony_ci vs->exponent -= bits - 1; 6262306a36Sopenharmony_ci vs->significand <<= bits; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci vfp_single_dump("normalise_denormal: out", vs); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#ifndef DEBUG 6962306a36Sopenharmony_ci#define vfp_single_normaliseround(sd,vsd,fpscr,except,func) __vfp_single_normaliseround(sd,vsd,fpscr,except) 7062306a36Sopenharmony_ciu32 __vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions) 7162306a36Sopenharmony_ci#else 7262306a36Sopenharmony_ciu32 vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func) 7362306a36Sopenharmony_ci#endif 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci u32 significand, incr, rmode; 7662306a36Sopenharmony_ci int exponent, shift, underflow; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci vfp_single_dump("pack: in", vs); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * Infinities and NaNs are a special case. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci if (vs->exponent == 255 && (vs->significand == 0 || exceptions)) 8462306a36Sopenharmony_ci goto pack; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * Special-case zero. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci if (vs->significand == 0) { 9062306a36Sopenharmony_ci vs->exponent = 0; 9162306a36Sopenharmony_ci goto pack; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci exponent = vs->exponent; 9562306a36Sopenharmony_ci significand = vs->significand; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* 9862306a36Sopenharmony_ci * Normalise first. Note that we shift the significand up to 9962306a36Sopenharmony_ci * bit 31, so we have VFP_SINGLE_LOW_BITS + 1 below the least 10062306a36Sopenharmony_ci * significant bit. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci shift = 32 - fls(significand); 10362306a36Sopenharmony_ci if (shift < 32 && shift) { 10462306a36Sopenharmony_ci exponent -= shift; 10562306a36Sopenharmony_ci significand <<= shift; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#ifdef DEBUG 10962306a36Sopenharmony_ci vs->exponent = exponent; 11062306a36Sopenharmony_ci vs->significand = significand; 11162306a36Sopenharmony_ci vfp_single_dump("pack: normalised", vs); 11262306a36Sopenharmony_ci#endif 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* 11562306a36Sopenharmony_ci * Tiny number? 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci underflow = exponent < 0; 11862306a36Sopenharmony_ci if (underflow) { 11962306a36Sopenharmony_ci significand = vfp_shiftright32jamming(significand, -exponent); 12062306a36Sopenharmony_ci exponent = 0; 12162306a36Sopenharmony_ci#ifdef DEBUG 12262306a36Sopenharmony_ci vs->exponent = exponent; 12362306a36Sopenharmony_ci vs->significand = significand; 12462306a36Sopenharmony_ci vfp_single_dump("pack: tiny number", vs); 12562306a36Sopenharmony_ci#endif 12662306a36Sopenharmony_ci if (!(significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1))) 12762306a36Sopenharmony_ci underflow = 0; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * Select rounding increment. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci incr = 0; 13462306a36Sopenharmony_ci rmode = fpscr & FPSCR_RMODE_MASK; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (rmode == FPSCR_ROUND_NEAREST) { 13762306a36Sopenharmony_ci incr = 1 << VFP_SINGLE_LOW_BITS; 13862306a36Sopenharmony_ci if ((significand & (1 << (VFP_SINGLE_LOW_BITS + 1))) == 0) 13962306a36Sopenharmony_ci incr -= 1; 14062306a36Sopenharmony_ci } else if (rmode == FPSCR_ROUND_TOZERO) { 14162306a36Sopenharmony_ci incr = 0; 14262306a36Sopenharmony_ci } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vs->sign != 0)) 14362306a36Sopenharmony_ci incr = (1 << (VFP_SINGLE_LOW_BITS + 1)) - 1; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci pr_debug("VFP: rounding increment = 0x%08x\n", incr); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* 14862306a36Sopenharmony_ci * Is our rounding going to overflow? 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci if ((significand + incr) < significand) { 15162306a36Sopenharmony_ci exponent += 1; 15262306a36Sopenharmony_ci significand = (significand >> 1) | (significand & 1); 15362306a36Sopenharmony_ci incr >>= 1; 15462306a36Sopenharmony_ci#ifdef DEBUG 15562306a36Sopenharmony_ci vs->exponent = exponent; 15662306a36Sopenharmony_ci vs->significand = significand; 15762306a36Sopenharmony_ci vfp_single_dump("pack: overflow", vs); 15862306a36Sopenharmony_ci#endif 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * If any of the low bits (which will be shifted out of the 16362306a36Sopenharmony_ci * number) are non-zero, the result is inexact. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci if (significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1)) 16662306a36Sopenharmony_ci exceptions |= FPSCR_IXC; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * Do our rounding. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci significand += incr; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * Infinity? 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci if (exponent >= 254) { 17762306a36Sopenharmony_ci exceptions |= FPSCR_OFC | FPSCR_IXC; 17862306a36Sopenharmony_ci if (incr == 0) { 17962306a36Sopenharmony_ci vs->exponent = 253; 18062306a36Sopenharmony_ci vs->significand = 0x7fffffff; 18162306a36Sopenharmony_ci } else { 18262306a36Sopenharmony_ci vs->exponent = 255; /* infinity */ 18362306a36Sopenharmony_ci vs->significand = 0; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci } else { 18662306a36Sopenharmony_ci if (significand >> (VFP_SINGLE_LOW_BITS + 1) == 0) 18762306a36Sopenharmony_ci exponent = 0; 18862306a36Sopenharmony_ci if (exponent || significand > 0x80000000) 18962306a36Sopenharmony_ci underflow = 0; 19062306a36Sopenharmony_ci if (underflow) 19162306a36Sopenharmony_ci exceptions |= FPSCR_UFC; 19262306a36Sopenharmony_ci vs->exponent = exponent; 19362306a36Sopenharmony_ci vs->significand = significand >> 1; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci pack: 19762306a36Sopenharmony_ci vfp_single_dump("pack: final", vs); 19862306a36Sopenharmony_ci { 19962306a36Sopenharmony_ci s32 d = vfp_single_pack(vs); 20062306a36Sopenharmony_ci#ifdef DEBUG 20162306a36Sopenharmony_ci pr_debug("VFP: %s: d(s%d)=%08x exceptions=%08x\n", func, 20262306a36Sopenharmony_ci sd, d, exceptions); 20362306a36Sopenharmony_ci#endif 20462306a36Sopenharmony_ci vfp_put_float(d, sd); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return exceptions; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* 21162306a36Sopenharmony_ci * Propagate the NaN, setting exceptions if it is signalling. 21262306a36Sopenharmony_ci * 'n' is always a NaN. 'm' may be a number, NaN or infinity. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_cistatic u32 21562306a36Sopenharmony_civfp_propagate_nan(struct vfp_single *vsd, struct vfp_single *vsn, 21662306a36Sopenharmony_ci struct vfp_single *vsm, u32 fpscr) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct vfp_single *nan; 21962306a36Sopenharmony_ci int tn, tm = 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci tn = vfp_single_type(vsn); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (vsm) 22462306a36Sopenharmony_ci tm = vfp_single_type(vsm); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (fpscr & FPSCR_DEFAULT_NAN) 22762306a36Sopenharmony_ci /* 22862306a36Sopenharmony_ci * Default NaN mode - always returns a quiet NaN 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci nan = &vfp_single_default_qnan; 23162306a36Sopenharmony_ci else { 23262306a36Sopenharmony_ci /* 23362306a36Sopenharmony_ci * Contemporary mode - select the first signalling 23462306a36Sopenharmony_ci * NAN, or if neither are signalling, the first 23562306a36Sopenharmony_ci * quiet NAN. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN)) 23862306a36Sopenharmony_ci nan = vsn; 23962306a36Sopenharmony_ci else 24062306a36Sopenharmony_ci nan = vsm; 24162306a36Sopenharmony_ci /* 24262306a36Sopenharmony_ci * Make the NaN quiet. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci nan->significand |= VFP_SINGLE_SIGNIFICAND_QNAN; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci *vsd = *nan; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* 25062306a36Sopenharmony_ci * If one was a signalling NAN, raise invalid operation. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* 25762306a36Sopenharmony_ci * Extended operations 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_cistatic u32 vfp_single_fabs(int sd, int unused, s32 m, u32 fpscr) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci vfp_put_float(vfp_single_packed_abs(m), sd); 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic u32 vfp_single_fcpy(int sd, int unused, s32 m, u32 fpscr) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci vfp_put_float(m, sd); 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic u32 vfp_single_fneg(int sd, int unused, s32 m, u32 fpscr) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci vfp_put_float(vfp_single_packed_negate(m), sd); 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic const u16 sqrt_oddadjust[] = { 27862306a36Sopenharmony_ci 0x0004, 0x0022, 0x005d, 0x00b1, 0x011d, 0x019f, 0x0236, 0x02e0, 27962306a36Sopenharmony_ci 0x039c, 0x0468, 0x0545, 0x0631, 0x072b, 0x0832, 0x0946, 0x0a67 28062306a36Sopenharmony_ci}; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic const u16 sqrt_evenadjust[] = { 28362306a36Sopenharmony_ci 0x0a2d, 0x08af, 0x075a, 0x0629, 0x051a, 0x0429, 0x0356, 0x029e, 28462306a36Sopenharmony_ci 0x0200, 0x0179, 0x0109, 0x00af, 0x0068, 0x0034, 0x0012, 0x0002 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ciu32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci int index; 29062306a36Sopenharmony_ci u32 z, a; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if ((significand & 0xc0000000) != 0x40000000) { 29362306a36Sopenharmony_ci pr_warn("VFP: estimate_sqrt: invalid significand\n"); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci a = significand << 1; 29762306a36Sopenharmony_ci index = (a >> 27) & 15; 29862306a36Sopenharmony_ci if (exponent & 1) { 29962306a36Sopenharmony_ci z = 0x4000 + (a >> 17) - sqrt_oddadjust[index]; 30062306a36Sopenharmony_ci z = ((a / z) << 14) + (z << 15); 30162306a36Sopenharmony_ci a >>= 1; 30262306a36Sopenharmony_ci } else { 30362306a36Sopenharmony_ci z = 0x8000 + (a >> 17) - sqrt_evenadjust[index]; 30462306a36Sopenharmony_ci z = a / z + z; 30562306a36Sopenharmony_ci z = (z >= 0x20000) ? 0xffff8000 : (z << 15); 30662306a36Sopenharmony_ci if (z <= a) 30762306a36Sopenharmony_ci return (s32)a >> 1; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci { 31062306a36Sopenharmony_ci u64 v = (u64)a << 31; 31162306a36Sopenharmony_ci do_div(v, z); 31262306a36Sopenharmony_ci return v + (z >> 1); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic u32 vfp_single_fsqrt(int sd, int unused, s32 m, u32 fpscr) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct vfp_single vsm, vsd; 31962306a36Sopenharmony_ci int ret, tm; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci vfp_single_unpack(&vsm, m); 32262306a36Sopenharmony_ci tm = vfp_single_type(&vsm); 32362306a36Sopenharmony_ci if (tm & (VFP_NAN|VFP_INFINITY)) { 32462306a36Sopenharmony_ci struct vfp_single *vsp = &vsd; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (tm & VFP_NAN) 32762306a36Sopenharmony_ci ret = vfp_propagate_nan(vsp, &vsm, NULL, fpscr); 32862306a36Sopenharmony_ci else if (vsm.sign == 0) { 32962306a36Sopenharmony_ci sqrt_copy: 33062306a36Sopenharmony_ci vsp = &vsm; 33162306a36Sopenharmony_ci ret = 0; 33262306a36Sopenharmony_ci } else { 33362306a36Sopenharmony_ci sqrt_invalid: 33462306a36Sopenharmony_ci vsp = &vfp_single_default_qnan; 33562306a36Sopenharmony_ci ret = FPSCR_IOC; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci vfp_put_float(vfp_single_pack(vsp), sd); 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * sqrt(+/- 0) == +/- 0 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci if (tm & VFP_ZERO) 34562306a36Sopenharmony_ci goto sqrt_copy; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* 34862306a36Sopenharmony_ci * Normalise a denormalised number 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci if (tm & VFP_DENORMAL) 35162306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsm); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* 35462306a36Sopenharmony_ci * sqrt(<0) = invalid 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci if (vsm.sign) 35762306a36Sopenharmony_ci goto sqrt_invalid; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci vfp_single_dump("sqrt", &vsm); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* 36262306a36Sopenharmony_ci * Estimate the square root. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci vsd.sign = 0; 36562306a36Sopenharmony_ci vsd.exponent = ((vsm.exponent - 127) >> 1) + 127; 36662306a36Sopenharmony_ci vsd.significand = vfp_estimate_sqrt_significand(vsm.exponent, vsm.significand) + 2; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci vfp_single_dump("sqrt estimate", &vsd); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * And now adjust. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci if ((vsd.significand & VFP_SINGLE_LOW_BITS_MASK) <= 5) { 37462306a36Sopenharmony_ci if (vsd.significand < 2) { 37562306a36Sopenharmony_ci vsd.significand = 0xffffffff; 37662306a36Sopenharmony_ci } else { 37762306a36Sopenharmony_ci u64 term; 37862306a36Sopenharmony_ci s64 rem; 37962306a36Sopenharmony_ci vsm.significand <<= !(vsm.exponent & 1); 38062306a36Sopenharmony_ci term = (u64)vsd.significand * vsd.significand; 38162306a36Sopenharmony_ci rem = ((u64)vsm.significand << 32) - term; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci pr_debug("VFP: term=%016llx rem=%016llx\n", term, rem); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci while (rem < 0) { 38662306a36Sopenharmony_ci vsd.significand -= 1; 38762306a36Sopenharmony_ci rem += ((u64)vsd.significand << 1) | 1; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci vsd.significand |= rem != 0; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci vsd.significand = vfp_shiftright32jamming(vsd.significand, 1); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fsqrt"); 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/* 39862306a36Sopenharmony_ci * Equal := ZC 39962306a36Sopenharmony_ci * Less than := N 40062306a36Sopenharmony_ci * Greater than := C 40162306a36Sopenharmony_ci * Unordered := CV 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_cistatic u32 vfp_compare(int sd, int signal_on_qnan, s32 m, u32 fpscr) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci s32 d; 40662306a36Sopenharmony_ci u32 ret = 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci d = vfp_get_float(sd); 40962306a36Sopenharmony_ci if (vfp_single_packed_exponent(m) == 255 && vfp_single_packed_mantissa(m)) { 41062306a36Sopenharmony_ci ret |= FPSCR_C | FPSCR_V; 41162306a36Sopenharmony_ci if (signal_on_qnan || !(vfp_single_packed_mantissa(m) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1)))) 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * Signalling NaN, or signalling on quiet NaN 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ci ret |= FPSCR_IOC; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (vfp_single_packed_exponent(d) == 255 && vfp_single_packed_mantissa(d)) { 41962306a36Sopenharmony_ci ret |= FPSCR_C | FPSCR_V; 42062306a36Sopenharmony_ci if (signal_on_qnan || !(vfp_single_packed_mantissa(d) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1)))) 42162306a36Sopenharmony_ci /* 42262306a36Sopenharmony_ci * Signalling NaN, or signalling on quiet NaN 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci ret |= FPSCR_IOC; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (ret == 0) { 42862306a36Sopenharmony_ci if (d == m || vfp_single_packed_abs(d | m) == 0) { 42962306a36Sopenharmony_ci /* 43062306a36Sopenharmony_ci * equal 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci ret |= FPSCR_Z | FPSCR_C; 43362306a36Sopenharmony_ci } else if (vfp_single_packed_sign(d ^ m)) { 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * different signs 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci if (vfp_single_packed_sign(d)) 43862306a36Sopenharmony_ci /* 43962306a36Sopenharmony_ci * d is negative, so d < m 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_ci ret |= FPSCR_N; 44262306a36Sopenharmony_ci else 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * d is positive, so d > m 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci ret |= FPSCR_C; 44762306a36Sopenharmony_ci } else if ((vfp_single_packed_sign(d) != 0) ^ (d < m)) { 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * d < m 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci ret |= FPSCR_N; 45262306a36Sopenharmony_ci } else if ((vfp_single_packed_sign(d) != 0) ^ (d > m)) { 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * d > m 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci ret |= FPSCR_C; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci return ret; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic u32 vfp_single_fcmp(int sd, int unused, s32 m, u32 fpscr) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci return vfp_compare(sd, 0, m, fpscr); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic u32 vfp_single_fcmpe(int sd, int unused, s32 m, u32 fpscr) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci return vfp_compare(sd, 1, m, fpscr); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic u32 vfp_single_fcmpz(int sd, int unused, s32 m, u32 fpscr) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci return vfp_compare(sd, 0, 0, fpscr); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic u32 vfp_single_fcmpez(int sd, int unused, s32 m, u32 fpscr) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci return vfp_compare(sd, 1, 0, fpscr); 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic u32 vfp_single_fcvtd(int dd, int unused, s32 m, u32 fpscr) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct vfp_single vsm; 48562306a36Sopenharmony_ci struct vfp_double vdd; 48662306a36Sopenharmony_ci int tm; 48762306a36Sopenharmony_ci u32 exceptions = 0; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci vfp_single_unpack(&vsm, m); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci tm = vfp_single_type(&vsm); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* 49462306a36Sopenharmony_ci * If we have a signalling NaN, signal invalid operation. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci if (tm == VFP_SNAN) 49762306a36Sopenharmony_ci exceptions = FPSCR_IOC; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (tm & VFP_DENORMAL) 50062306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsm); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci vdd.sign = vsm.sign; 50362306a36Sopenharmony_ci vdd.significand = (u64)vsm.significand << 32; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* 50662306a36Sopenharmony_ci * If we have an infinity or NaN, the exponent must be 2047. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_ci if (tm & (VFP_INFINITY|VFP_NAN)) { 50962306a36Sopenharmony_ci vdd.exponent = 2047; 51062306a36Sopenharmony_ci if (tm == VFP_QNAN) 51162306a36Sopenharmony_ci vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN; 51262306a36Sopenharmony_ci goto pack_nan; 51362306a36Sopenharmony_ci } else if (tm & VFP_ZERO) 51462306a36Sopenharmony_ci vdd.exponent = 0; 51562306a36Sopenharmony_ci else 51662306a36Sopenharmony_ci vdd.exponent = vsm.exponent + (1023 - 127); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fcvtd"); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci pack_nan: 52162306a36Sopenharmony_ci vfp_put_double(vfp_double_pack(&vdd), dd); 52262306a36Sopenharmony_ci return exceptions; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic u32 vfp_single_fuito(int sd, int unused, s32 m, u32 fpscr) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct vfp_single vs; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci vs.sign = 0; 53062306a36Sopenharmony_ci vs.exponent = 127 + 31 - 1; 53162306a36Sopenharmony_ci vs.significand = (u32)m; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return vfp_single_normaliseround(sd, &vs, fpscr, 0, "fuito"); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic u32 vfp_single_fsito(int sd, int unused, s32 m, u32 fpscr) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct vfp_single vs; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci vs.sign = (m & 0x80000000) >> 16; 54162306a36Sopenharmony_ci vs.exponent = 127 + 31 - 1; 54262306a36Sopenharmony_ci vs.significand = vs.sign ? -m : m; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return vfp_single_normaliseround(sd, &vs, fpscr, 0, "fsito"); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic u32 vfp_single_ftoui(int sd, int unused, s32 m, u32 fpscr) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct vfp_single vsm; 55062306a36Sopenharmony_ci u32 d, exceptions = 0; 55162306a36Sopenharmony_ci int rmode = fpscr & FPSCR_RMODE_MASK; 55262306a36Sopenharmony_ci int tm; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci vfp_single_unpack(&vsm, m); 55562306a36Sopenharmony_ci vfp_single_dump("VSM", &vsm); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* 55862306a36Sopenharmony_ci * Do we have a denormalised number? 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ci tm = vfp_single_type(&vsm); 56162306a36Sopenharmony_ci if (tm & VFP_DENORMAL) 56262306a36Sopenharmony_ci exceptions |= FPSCR_IDC; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (tm & VFP_NAN) 56562306a36Sopenharmony_ci vsm.sign = 0; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (vsm.exponent >= 127 + 32) { 56862306a36Sopenharmony_ci d = vsm.sign ? 0 : 0xffffffff; 56962306a36Sopenharmony_ci exceptions = FPSCR_IOC; 57062306a36Sopenharmony_ci } else if (vsm.exponent >= 127 - 1) { 57162306a36Sopenharmony_ci int shift = 127 + 31 - vsm.exponent; 57262306a36Sopenharmony_ci u32 rem, incr = 0; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* 57562306a36Sopenharmony_ci * 2^0 <= m < 2^32-2^8 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_ci d = (vsm.significand << 1) >> shift; 57862306a36Sopenharmony_ci rem = vsm.significand << (33 - shift); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (rmode == FPSCR_ROUND_NEAREST) { 58162306a36Sopenharmony_ci incr = 0x80000000; 58262306a36Sopenharmony_ci if ((d & 1) == 0) 58362306a36Sopenharmony_ci incr -= 1; 58462306a36Sopenharmony_ci } else if (rmode == FPSCR_ROUND_TOZERO) { 58562306a36Sopenharmony_ci incr = 0; 58662306a36Sopenharmony_ci } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) { 58762306a36Sopenharmony_ci incr = ~0; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if ((rem + incr) < rem) { 59162306a36Sopenharmony_ci if (d < 0xffffffff) 59262306a36Sopenharmony_ci d += 1; 59362306a36Sopenharmony_ci else 59462306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (d && vsm.sign) { 59862306a36Sopenharmony_ci d = 0; 59962306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 60062306a36Sopenharmony_ci } else if (rem) 60162306a36Sopenharmony_ci exceptions |= FPSCR_IXC; 60262306a36Sopenharmony_ci } else { 60362306a36Sopenharmony_ci d = 0; 60462306a36Sopenharmony_ci if (vsm.exponent | vsm.significand) { 60562306a36Sopenharmony_ci exceptions |= FPSCR_IXC; 60662306a36Sopenharmony_ci if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) 60762306a36Sopenharmony_ci d = 1; 60862306a36Sopenharmony_ci else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) { 60962306a36Sopenharmony_ci d = 0; 61062306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci vfp_put_float(d, sd); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return exceptions; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic u32 vfp_single_ftouiz(int sd, int unused, s32 m, u32 fpscr) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci return vfp_single_ftoui(sd, unused, m, FPSCR_ROUND_TOZERO); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic u32 vfp_single_ftosi(int sd, int unused, s32 m, u32 fpscr) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct vfp_single vsm; 63062306a36Sopenharmony_ci u32 d, exceptions = 0; 63162306a36Sopenharmony_ci int rmode = fpscr & FPSCR_RMODE_MASK; 63262306a36Sopenharmony_ci int tm; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci vfp_single_unpack(&vsm, m); 63562306a36Sopenharmony_ci vfp_single_dump("VSM", &vsm); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* 63862306a36Sopenharmony_ci * Do we have a denormalised number? 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci tm = vfp_single_type(&vsm); 64162306a36Sopenharmony_ci if (vfp_single_type(&vsm) & VFP_DENORMAL) 64262306a36Sopenharmony_ci exceptions |= FPSCR_IDC; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (tm & VFP_NAN) { 64562306a36Sopenharmony_ci d = 0; 64662306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 64762306a36Sopenharmony_ci } else if (vsm.exponent >= 127 + 32) { 64862306a36Sopenharmony_ci /* 64962306a36Sopenharmony_ci * m >= 2^31-2^7: invalid 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci d = 0x7fffffff; 65262306a36Sopenharmony_ci if (vsm.sign) 65362306a36Sopenharmony_ci d = ~d; 65462306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 65562306a36Sopenharmony_ci } else if (vsm.exponent >= 127 - 1) { 65662306a36Sopenharmony_ci int shift = 127 + 31 - vsm.exponent; 65762306a36Sopenharmony_ci u32 rem, incr = 0; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* 2^0 <= m <= 2^31-2^7 */ 66062306a36Sopenharmony_ci d = (vsm.significand << 1) >> shift; 66162306a36Sopenharmony_ci rem = vsm.significand << (33 - shift); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (rmode == FPSCR_ROUND_NEAREST) { 66462306a36Sopenharmony_ci incr = 0x80000000; 66562306a36Sopenharmony_ci if ((d & 1) == 0) 66662306a36Sopenharmony_ci incr -= 1; 66762306a36Sopenharmony_ci } else if (rmode == FPSCR_ROUND_TOZERO) { 66862306a36Sopenharmony_ci incr = 0; 66962306a36Sopenharmony_ci } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) { 67062306a36Sopenharmony_ci incr = ~0; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if ((rem + incr) < rem && d < 0xffffffff) 67462306a36Sopenharmony_ci d += 1; 67562306a36Sopenharmony_ci if (d > 0x7fffffff + (vsm.sign != 0)) { 67662306a36Sopenharmony_ci d = 0x7fffffff + (vsm.sign != 0); 67762306a36Sopenharmony_ci exceptions |= FPSCR_IOC; 67862306a36Sopenharmony_ci } else if (rem) 67962306a36Sopenharmony_ci exceptions |= FPSCR_IXC; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (vsm.sign) 68262306a36Sopenharmony_ci d = -d; 68362306a36Sopenharmony_ci } else { 68462306a36Sopenharmony_ci d = 0; 68562306a36Sopenharmony_ci if (vsm.exponent | vsm.significand) { 68662306a36Sopenharmony_ci exceptions |= FPSCR_IXC; 68762306a36Sopenharmony_ci if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0) 68862306a36Sopenharmony_ci d = 1; 68962306a36Sopenharmony_ci else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) 69062306a36Sopenharmony_ci d = -1; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci vfp_put_float((s32)d, sd); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return exceptions; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic u32 vfp_single_ftosiz(int sd, int unused, s32 m, u32 fpscr) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci return vfp_single_ftosi(sd, unused, m, FPSCR_ROUND_TOZERO); 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic struct op fops_ext[32] = { 70762306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCPY)] = { vfp_single_fcpy, 0 }, 70862306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FABS)] = { vfp_single_fabs, 0 }, 70962306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FNEG)] = { vfp_single_fneg, 0 }, 71062306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FSQRT)] = { vfp_single_fsqrt, 0 }, 71162306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMP)] = { vfp_single_fcmp, OP_SCALAR }, 71262306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMPE)] = { vfp_single_fcmpe, OP_SCALAR }, 71362306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMPZ)] = { vfp_single_fcmpz, OP_SCALAR }, 71462306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMPEZ)] = { vfp_single_fcmpez, OP_SCALAR }, 71562306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCVT)] = { vfp_single_fcvtd, OP_SCALAR|OP_DD }, 71662306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FUITO)] = { vfp_single_fuito, OP_SCALAR }, 71762306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FSITO)] = { vfp_single_fsito, OP_SCALAR }, 71862306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOUI)] = { vfp_single_ftoui, OP_SCALAR }, 71962306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOUIZ)] = { vfp_single_ftouiz, OP_SCALAR }, 72062306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOSI)] = { vfp_single_ftosi, OP_SCALAR }, 72162306a36Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOSIZ)] = { vfp_single_ftosiz, OP_SCALAR }, 72262306a36Sopenharmony_ci}; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic u32 72962306a36Sopenharmony_civfp_single_fadd_nonnumber(struct vfp_single *vsd, struct vfp_single *vsn, 73062306a36Sopenharmony_ci struct vfp_single *vsm, u32 fpscr) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct vfp_single *vsp; 73362306a36Sopenharmony_ci u32 exceptions = 0; 73462306a36Sopenharmony_ci int tn, tm; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci tn = vfp_single_type(vsn); 73762306a36Sopenharmony_ci tm = vfp_single_type(vsm); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (tn & tm & VFP_INFINITY) { 74062306a36Sopenharmony_ci /* 74162306a36Sopenharmony_ci * Two infinities. Are they different signs? 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_ci if (vsn->sign ^ vsm->sign) { 74462306a36Sopenharmony_ci /* 74562306a36Sopenharmony_ci * different signs -> invalid 74662306a36Sopenharmony_ci */ 74762306a36Sopenharmony_ci exceptions = FPSCR_IOC; 74862306a36Sopenharmony_ci vsp = &vfp_single_default_qnan; 74962306a36Sopenharmony_ci } else { 75062306a36Sopenharmony_ci /* 75162306a36Sopenharmony_ci * same signs -> valid 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci vsp = vsn; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci } else if (tn & VFP_INFINITY && tm & VFP_NUMBER) { 75662306a36Sopenharmony_ci /* 75762306a36Sopenharmony_ci * One infinity and one number -> infinity 75862306a36Sopenharmony_ci */ 75962306a36Sopenharmony_ci vsp = vsn; 76062306a36Sopenharmony_ci } else { 76162306a36Sopenharmony_ci /* 76262306a36Sopenharmony_ci * 'n' is a NaN of some type 76362306a36Sopenharmony_ci */ 76462306a36Sopenharmony_ci return vfp_propagate_nan(vsd, vsn, vsm, fpscr); 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci *vsd = *vsp; 76762306a36Sopenharmony_ci return exceptions; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic u32 77162306a36Sopenharmony_civfp_single_add(struct vfp_single *vsd, struct vfp_single *vsn, 77262306a36Sopenharmony_ci struct vfp_single *vsm, u32 fpscr) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci u32 exp_diff, m_sig; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (vsn->significand & 0x80000000 || 77762306a36Sopenharmony_ci vsm->significand & 0x80000000) { 77862306a36Sopenharmony_ci pr_info("VFP: bad FP values in %s\n", __func__); 77962306a36Sopenharmony_ci vfp_single_dump("VSN", vsn); 78062306a36Sopenharmony_ci vfp_single_dump("VSM", vsm); 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* 78462306a36Sopenharmony_ci * Ensure that 'n' is the largest magnitude number. Note that 78562306a36Sopenharmony_ci * if 'n' and 'm' have equal exponents, we do not swap them. 78662306a36Sopenharmony_ci * This ensures that NaN propagation works correctly. 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ci if (vsn->exponent < vsm->exponent) { 78962306a36Sopenharmony_ci struct vfp_single *t = vsn; 79062306a36Sopenharmony_ci vsn = vsm; 79162306a36Sopenharmony_ci vsm = t; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * Is 'n' an infinity or a NaN? Note that 'm' may be a number, 79662306a36Sopenharmony_ci * infinity or a NaN here. 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci if (vsn->exponent == 255) 79962306a36Sopenharmony_ci return vfp_single_fadd_nonnumber(vsd, vsn, vsm, fpscr); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* 80262306a36Sopenharmony_ci * We have two proper numbers, where 'vsn' is the larger magnitude. 80362306a36Sopenharmony_ci * 80462306a36Sopenharmony_ci * Copy 'n' to 'd' before doing the arithmetic. 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_ci *vsd = *vsn; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* 80962306a36Sopenharmony_ci * Align both numbers. 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_ci exp_diff = vsn->exponent - vsm->exponent; 81262306a36Sopenharmony_ci m_sig = vfp_shiftright32jamming(vsm->significand, exp_diff); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* 81562306a36Sopenharmony_ci * If the signs are different, we are really subtracting. 81662306a36Sopenharmony_ci */ 81762306a36Sopenharmony_ci if (vsn->sign ^ vsm->sign) { 81862306a36Sopenharmony_ci m_sig = vsn->significand - m_sig; 81962306a36Sopenharmony_ci if ((s32)m_sig < 0) { 82062306a36Sopenharmony_ci vsd->sign = vfp_sign_negate(vsd->sign); 82162306a36Sopenharmony_ci m_sig = -m_sig; 82262306a36Sopenharmony_ci } else if (m_sig == 0) { 82362306a36Sopenharmony_ci vsd->sign = (fpscr & FPSCR_RMODE_MASK) == 82462306a36Sopenharmony_ci FPSCR_ROUND_MINUSINF ? 0x8000 : 0; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci } else { 82762306a36Sopenharmony_ci m_sig = vsn->significand + m_sig; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci vsd->significand = m_sig; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic u32 83562306a36Sopenharmony_civfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_single *vsm, u32 fpscr) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci vfp_single_dump("VSN", vsn); 83862306a36Sopenharmony_ci vfp_single_dump("VSM", vsm); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* 84162306a36Sopenharmony_ci * Ensure that 'n' is the largest magnitude number. Note that 84262306a36Sopenharmony_ci * if 'n' and 'm' have equal exponents, we do not swap them. 84362306a36Sopenharmony_ci * This ensures that NaN propagation works correctly. 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_ci if (vsn->exponent < vsm->exponent) { 84662306a36Sopenharmony_ci struct vfp_single *t = vsn; 84762306a36Sopenharmony_ci vsn = vsm; 84862306a36Sopenharmony_ci vsm = t; 84962306a36Sopenharmony_ci pr_debug("VFP: swapping M <-> N\n"); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci vsd->sign = vsn->sign ^ vsm->sign; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* 85562306a36Sopenharmony_ci * If 'n' is an infinity or NaN, handle it. 'm' may be anything. 85662306a36Sopenharmony_ci */ 85762306a36Sopenharmony_ci if (vsn->exponent == 255) { 85862306a36Sopenharmony_ci if (vsn->significand || (vsm->exponent == 255 && vsm->significand)) 85962306a36Sopenharmony_ci return vfp_propagate_nan(vsd, vsn, vsm, fpscr); 86062306a36Sopenharmony_ci if ((vsm->exponent | vsm->significand) == 0) { 86162306a36Sopenharmony_ci *vsd = vfp_single_default_qnan; 86262306a36Sopenharmony_ci return FPSCR_IOC; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci vsd->exponent = vsn->exponent; 86562306a36Sopenharmony_ci vsd->significand = 0; 86662306a36Sopenharmony_ci return 0; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci /* 87062306a36Sopenharmony_ci * If 'm' is zero, the result is always zero. In this case, 87162306a36Sopenharmony_ci * 'n' may be zero or a number, but it doesn't matter which. 87262306a36Sopenharmony_ci */ 87362306a36Sopenharmony_ci if ((vsm->exponent | vsm->significand) == 0) { 87462306a36Sopenharmony_ci vsd->exponent = 0; 87562306a36Sopenharmony_ci vsd->significand = 0; 87662306a36Sopenharmony_ci return 0; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* 88062306a36Sopenharmony_ci * We add 2 to the destination exponent for the same reason as 88162306a36Sopenharmony_ci * the addition case - though this time we have +1 from each 88262306a36Sopenharmony_ci * input operand. 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_ci vsd->exponent = vsn->exponent + vsm->exponent - 127 + 2; 88562306a36Sopenharmony_ci vsd->significand = vfp_hi64to32jamming((u64)vsn->significand * vsm->significand); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci vfp_single_dump("VSD", vsd); 88862306a36Sopenharmony_ci return 0; 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci#define NEG_MULTIPLY (1 << 0) 89262306a36Sopenharmony_ci#define NEG_SUBTRACT (1 << 1) 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic u32 89562306a36Sopenharmony_civfp_single_multiply_accumulate(int sd, int sn, s32 m, u32 fpscr, u32 negate, char *func) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct vfp_single vsd, vsp, vsn, vsm; 89862306a36Sopenharmony_ci u32 exceptions; 89962306a36Sopenharmony_ci s32 v; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci v = vfp_get_float(sn); 90262306a36Sopenharmony_ci pr_debug("VFP: s%u = %08x\n", sn, v); 90362306a36Sopenharmony_ci vfp_single_unpack(&vsn, v); 90462306a36Sopenharmony_ci if (vsn.exponent == 0 && vsn.significand) 90562306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsn); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci vfp_single_unpack(&vsm, m); 90862306a36Sopenharmony_ci if (vsm.exponent == 0 && vsm.significand) 90962306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsm); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr); 91262306a36Sopenharmony_ci if (negate & NEG_MULTIPLY) 91362306a36Sopenharmony_ci vsp.sign = vfp_sign_negate(vsp.sign); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci v = vfp_get_float(sd); 91662306a36Sopenharmony_ci pr_debug("VFP: s%u = %08x\n", sd, v); 91762306a36Sopenharmony_ci vfp_single_unpack(&vsn, v); 91862306a36Sopenharmony_ci if (vsn.exponent == 0 && vsn.significand) 91962306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsn); 92062306a36Sopenharmony_ci if (negate & NEG_SUBTRACT) 92162306a36Sopenharmony_ci vsn.sign = vfp_sign_negate(vsn.sign); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, func); 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci/* 92962306a36Sopenharmony_ci * Standard operations 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci/* 93362306a36Sopenharmony_ci * sd = sd + (sn * sm) 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_cistatic u32 vfp_single_fmac(int sd, int sn, s32 m, u32 fpscr) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci return vfp_single_multiply_accumulate(sd, sn, m, fpscr, 0, "fmac"); 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci/* 94162306a36Sopenharmony_ci * sd = sd - (sn * sm) 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_cistatic u32 vfp_single_fnmac(int sd, int sn, s32 m, u32 fpscr) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_MULTIPLY, "fnmac"); 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/* 94962306a36Sopenharmony_ci * sd = -sd + (sn * sm) 95062306a36Sopenharmony_ci */ 95162306a36Sopenharmony_cistatic u32 vfp_single_fmsc(int sd, int sn, s32 m, u32 fpscr) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_SUBTRACT, "fmsc"); 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci/* 95762306a36Sopenharmony_ci * sd = -sd - (sn * sm) 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_cistatic u32 vfp_single_fnmsc(int sd, int sn, s32 m, u32 fpscr) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc"); 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci/* 96562306a36Sopenharmony_ci * sd = sn * sm 96662306a36Sopenharmony_ci */ 96762306a36Sopenharmony_cistatic u32 vfp_single_fmul(int sd, int sn, s32 m, u32 fpscr) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci struct vfp_single vsd, vsn, vsm; 97062306a36Sopenharmony_ci u32 exceptions; 97162306a36Sopenharmony_ci s32 n = vfp_get_float(sn); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci pr_debug("VFP: s%u = %08x\n", sn, n); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci vfp_single_unpack(&vsn, n); 97662306a36Sopenharmony_ci if (vsn.exponent == 0 && vsn.significand) 97762306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsn); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci vfp_single_unpack(&vsm, m); 98062306a36Sopenharmony_ci if (vsm.exponent == 0 && vsm.significand) 98162306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsm); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); 98462306a36Sopenharmony_ci return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fmul"); 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci/* 98862306a36Sopenharmony_ci * sd = -(sn * sm) 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_cistatic u32 vfp_single_fnmul(int sd, int sn, s32 m, u32 fpscr) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct vfp_single vsd, vsn, vsm; 99362306a36Sopenharmony_ci u32 exceptions; 99462306a36Sopenharmony_ci s32 n = vfp_get_float(sn); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci pr_debug("VFP: s%u = %08x\n", sn, n); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci vfp_single_unpack(&vsn, n); 99962306a36Sopenharmony_ci if (vsn.exponent == 0 && vsn.significand) 100062306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsn); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci vfp_single_unpack(&vsm, m); 100362306a36Sopenharmony_ci if (vsm.exponent == 0 && vsm.significand) 100462306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsm); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); 100762306a36Sopenharmony_ci vsd.sign = vfp_sign_negate(vsd.sign); 100862306a36Sopenharmony_ci return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fnmul"); 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci/* 101262306a36Sopenharmony_ci * sd = sn + sm 101362306a36Sopenharmony_ci */ 101462306a36Sopenharmony_cistatic u32 vfp_single_fadd(int sd, int sn, s32 m, u32 fpscr) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct vfp_single vsd, vsn, vsm; 101762306a36Sopenharmony_ci u32 exceptions; 101862306a36Sopenharmony_ci s32 n = vfp_get_float(sn); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci pr_debug("VFP: s%u = %08x\n", sn, n); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci /* 102362306a36Sopenharmony_ci * Unpack and normalise denormals. 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_ci vfp_single_unpack(&vsn, n); 102662306a36Sopenharmony_ci if (vsn.exponent == 0 && vsn.significand) 102762306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsn); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci vfp_single_unpack(&vsm, m); 103062306a36Sopenharmony_ci if (vsm.exponent == 0 && vsm.significand) 103162306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsm); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci exceptions = vfp_single_add(&vsd, &vsn, &vsm, fpscr); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fadd"); 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci/* 103962306a36Sopenharmony_ci * sd = sn - sm 104062306a36Sopenharmony_ci */ 104162306a36Sopenharmony_cistatic u32 vfp_single_fsub(int sd, int sn, s32 m, u32 fpscr) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci /* 104462306a36Sopenharmony_ci * Subtraction is addition with one sign inverted. 104562306a36Sopenharmony_ci */ 104662306a36Sopenharmony_ci return vfp_single_fadd(sd, sn, vfp_single_packed_negate(m), fpscr); 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci/* 105062306a36Sopenharmony_ci * sd = sn / sm 105162306a36Sopenharmony_ci */ 105262306a36Sopenharmony_cistatic u32 vfp_single_fdiv(int sd, int sn, s32 m, u32 fpscr) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci struct vfp_single vsd, vsn, vsm; 105562306a36Sopenharmony_ci u32 exceptions = 0; 105662306a36Sopenharmony_ci s32 n = vfp_get_float(sn); 105762306a36Sopenharmony_ci int tm, tn; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci pr_debug("VFP: s%u = %08x\n", sn, n); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci vfp_single_unpack(&vsn, n); 106262306a36Sopenharmony_ci vfp_single_unpack(&vsm, m); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci vsd.sign = vsn.sign ^ vsm.sign; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci tn = vfp_single_type(&vsn); 106762306a36Sopenharmony_ci tm = vfp_single_type(&vsm); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* 107062306a36Sopenharmony_ci * Is n a NAN? 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_ci if (tn & VFP_NAN) 107362306a36Sopenharmony_ci goto vsn_nan; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* 107662306a36Sopenharmony_ci * Is m a NAN? 107762306a36Sopenharmony_ci */ 107862306a36Sopenharmony_ci if (tm & VFP_NAN) 107962306a36Sopenharmony_ci goto vsm_nan; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* 108262306a36Sopenharmony_ci * If n and m are infinity, the result is invalid 108362306a36Sopenharmony_ci * If n and m are zero, the result is invalid 108462306a36Sopenharmony_ci */ 108562306a36Sopenharmony_ci if (tm & tn & (VFP_INFINITY|VFP_ZERO)) 108662306a36Sopenharmony_ci goto invalid; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci /* 108962306a36Sopenharmony_ci * If n is infinity, the result is infinity 109062306a36Sopenharmony_ci */ 109162306a36Sopenharmony_ci if (tn & VFP_INFINITY) 109262306a36Sopenharmony_ci goto infinity; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci /* 109562306a36Sopenharmony_ci * If m is zero, raise div0 exception 109662306a36Sopenharmony_ci */ 109762306a36Sopenharmony_ci if (tm & VFP_ZERO) 109862306a36Sopenharmony_ci goto divzero; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci /* 110162306a36Sopenharmony_ci * If m is infinity, or n is zero, the result is zero 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ci if (tm & VFP_INFINITY || tn & VFP_ZERO) 110462306a36Sopenharmony_ci goto zero; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci if (tn & VFP_DENORMAL) 110762306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsn); 110862306a36Sopenharmony_ci if (tm & VFP_DENORMAL) 110962306a36Sopenharmony_ci vfp_single_normalise_denormal(&vsm); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci /* 111262306a36Sopenharmony_ci * Ok, we have two numbers, we can perform division. 111362306a36Sopenharmony_ci */ 111462306a36Sopenharmony_ci vsd.exponent = vsn.exponent - vsm.exponent + 127 - 1; 111562306a36Sopenharmony_ci vsm.significand <<= 1; 111662306a36Sopenharmony_ci if (vsm.significand <= (2 * vsn.significand)) { 111762306a36Sopenharmony_ci vsn.significand >>= 1; 111862306a36Sopenharmony_ci vsd.exponent++; 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci { 112162306a36Sopenharmony_ci u64 significand = (u64)vsn.significand << 32; 112262306a36Sopenharmony_ci do_div(significand, vsm.significand); 112362306a36Sopenharmony_ci vsd.significand = significand; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci if ((vsd.significand & 0x3f) == 0) 112662306a36Sopenharmony_ci vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fdiv"); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci vsn_nan: 113162306a36Sopenharmony_ci exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr); 113262306a36Sopenharmony_ci pack: 113362306a36Sopenharmony_ci vfp_put_float(vfp_single_pack(&vsd), sd); 113462306a36Sopenharmony_ci return exceptions; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci vsm_nan: 113762306a36Sopenharmony_ci exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr); 113862306a36Sopenharmony_ci goto pack; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci zero: 114162306a36Sopenharmony_ci vsd.exponent = 0; 114262306a36Sopenharmony_ci vsd.significand = 0; 114362306a36Sopenharmony_ci goto pack; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci divzero: 114662306a36Sopenharmony_ci exceptions = FPSCR_DZC; 114762306a36Sopenharmony_ci infinity: 114862306a36Sopenharmony_ci vsd.exponent = 255; 114962306a36Sopenharmony_ci vsd.significand = 0; 115062306a36Sopenharmony_ci goto pack; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci invalid: 115362306a36Sopenharmony_ci vfp_put_float(vfp_single_pack(&vfp_single_default_qnan), sd); 115462306a36Sopenharmony_ci return FPSCR_IOC; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic struct op fops[16] = { 115862306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FMAC)] = { vfp_single_fmac, 0 }, 115962306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FNMAC)] = { vfp_single_fnmac, 0 }, 116062306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FMSC)] = { vfp_single_fmsc, 0 }, 116162306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FNMSC)] = { vfp_single_fnmsc, 0 }, 116262306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FMUL)] = { vfp_single_fmul, 0 }, 116362306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FNMUL)] = { vfp_single_fnmul, 0 }, 116462306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FADD)] = { vfp_single_fadd, 0 }, 116562306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FSUB)] = { vfp_single_fsub, 0 }, 116662306a36Sopenharmony_ci [FOP_TO_IDX(FOP_FDIV)] = { vfp_single_fdiv, 0 }, 116762306a36Sopenharmony_ci}; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci#define FREG_BANK(x) ((x) & 0x18) 117062306a36Sopenharmony_ci#define FREG_IDX(x) ((x) & 7) 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ciu32 vfp_single_cpdo(u32 inst, u32 fpscr) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci u32 op = inst & FOP_MASK; 117562306a36Sopenharmony_ci u32 exceptions = 0; 117662306a36Sopenharmony_ci unsigned int dest; 117762306a36Sopenharmony_ci unsigned int sn = vfp_get_sn(inst); 117862306a36Sopenharmony_ci unsigned int sm = vfp_get_sm(inst); 117962306a36Sopenharmony_ci unsigned int vecitr, veclen, vecstride; 118062306a36Sopenharmony_ci struct op *fop; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)]; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci /* 118762306a36Sopenharmony_ci * fcvtsd takes a dN register number as destination, not sN. 118862306a36Sopenharmony_ci * Technically, if bit 0 of dd is set, this is an invalid 118962306a36Sopenharmony_ci * instruction. However, we ignore this for efficiency. 119062306a36Sopenharmony_ci * It also only operates on scalars. 119162306a36Sopenharmony_ci */ 119262306a36Sopenharmony_ci if (fop->flags & OP_DD) 119362306a36Sopenharmony_ci dest = vfp_get_dd(inst); 119462306a36Sopenharmony_ci else 119562306a36Sopenharmony_ci dest = vfp_get_sd(inst); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci /* 119862306a36Sopenharmony_ci * If destination bank is zero, vector length is always '1'. 119962306a36Sopenharmony_ci * ARM DDI0100F C5.1.3, C5.3.2. 120062306a36Sopenharmony_ci */ 120162306a36Sopenharmony_ci if ((fop->flags & OP_SCALAR) || FREG_BANK(dest) == 0) 120262306a36Sopenharmony_ci veclen = 0; 120362306a36Sopenharmony_ci else 120462306a36Sopenharmony_ci veclen = fpscr & FPSCR_LENGTH_MASK; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride, 120762306a36Sopenharmony_ci (veclen >> FPSCR_LENGTH_BIT) + 1); 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (!fop->fn) 121062306a36Sopenharmony_ci goto invalid; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) { 121362306a36Sopenharmony_ci s32 m = vfp_get_float(sm); 121462306a36Sopenharmony_ci u32 except; 121562306a36Sopenharmony_ci char type; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci type = fop->flags & OP_DD ? 'd' : 's'; 121862306a36Sopenharmony_ci if (op == FOP_EXT) 121962306a36Sopenharmony_ci pr_debug("VFP: itr%d (%c%u) = op[%u] (s%u=%08x)\n", 122062306a36Sopenharmony_ci vecitr >> FPSCR_LENGTH_BIT, type, dest, sn, 122162306a36Sopenharmony_ci sm, m); 122262306a36Sopenharmony_ci else 122362306a36Sopenharmony_ci pr_debug("VFP: itr%d (%c%u) = (s%u) op[%u] (s%u=%08x)\n", 122462306a36Sopenharmony_ci vecitr >> FPSCR_LENGTH_BIT, type, dest, sn, 122562306a36Sopenharmony_ci FOP_TO_IDX(op), sm, m); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci except = fop->fn(dest, sn, m, fpscr); 122862306a36Sopenharmony_ci pr_debug("VFP: itr%d: exceptions=%08x\n", 122962306a36Sopenharmony_ci vecitr >> FPSCR_LENGTH_BIT, except); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci exceptions |= except; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* 123462306a36Sopenharmony_ci * CHECK: It appears to be undefined whether we stop when 123562306a36Sopenharmony_ci * we encounter an exception. We continue. 123662306a36Sopenharmony_ci */ 123762306a36Sopenharmony_ci dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 7); 123862306a36Sopenharmony_ci sn = FREG_BANK(sn) + ((FREG_IDX(sn) + vecstride) & 7); 123962306a36Sopenharmony_ci if (FREG_BANK(sm) != 0) 124062306a36Sopenharmony_ci sm = FREG_BANK(sm) + ((FREG_IDX(sm) + vecstride) & 7); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci return exceptions; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci invalid: 124562306a36Sopenharmony_ci return (u32)-1; 124662306a36Sopenharmony_ci} 1247