18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/arch/arm/vfp/vfpdouble.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This code is derived in part from John R. Housers softfloat library, which 58c2ecf20Sopenharmony_ci * carries the following notice: 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * =========================================================================== 88c2ecf20Sopenharmony_ci * This C source file is part of the SoftFloat IEC/IEEE Floating-point 98c2ecf20Sopenharmony_ci * Arithmetic Package, Release 2. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Written by John R. Hauser. This work was made possible in part by the 128c2ecf20Sopenharmony_ci * International Computer Science Institute, located at Suite 600, 1947 Center 138c2ecf20Sopenharmony_ci * Street, Berkeley, California 94704. Funding was partially provided by the 148c2ecf20Sopenharmony_ci * National Science Foundation under grant MIP-9311980. The original version 158c2ecf20Sopenharmony_ci * of this code was written as part of a project to build a fixed-point vector 168c2ecf20Sopenharmony_ci * processor in collaboration with the University of California at Berkeley, 178c2ecf20Sopenharmony_ci * overseen by Profs. Nelson Morgan and John Wawrzynek. More information 188c2ecf20Sopenharmony_ci * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ 198c2ecf20Sopenharmony_ci * arithmetic/softfloat.html'. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort 228c2ecf20Sopenharmony_ci * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT 238c2ecf20Sopenharmony_ci * TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO 248c2ecf20Sopenharmony_ci * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY 258c2ecf20Sopenharmony_ci * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Derivative works are acceptable, even for commercial purposes, so long as 288c2ecf20Sopenharmony_ci * (1) they include prominent notice that the work is derivative, and (2) they 298c2ecf20Sopenharmony_ci * include prominent notice akin to these three paragraphs for those parts of 308c2ecf20Sopenharmony_ci * this code that are retained. 318c2ecf20Sopenharmony_ci * =========================================================================== 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#include <linux/kernel.h> 348c2ecf20Sopenharmony_ci#include <linux/bitops.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <asm/div64.h> 378c2ecf20Sopenharmony_ci#include <asm/vfp.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include "vfpinstr.h" 408c2ecf20Sopenharmony_ci#include "vfp.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct vfp_double vfp_double_default_qnan = { 438c2ecf20Sopenharmony_ci .exponent = 2047, 448c2ecf20Sopenharmony_ci .sign = 0, 458c2ecf20Sopenharmony_ci .significand = VFP_DOUBLE_SIGNIFICAND_QNAN, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void vfp_double_dump(const char *str, struct vfp_double *d) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci pr_debug("VFP: %s: sign=%d exponent=%d significand=%016llx\n", 518c2ecf20Sopenharmony_ci str, d->sign != 0, d->exponent, d->significand); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void vfp_double_normalise_denormal(struct vfp_double *vd) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci int bits = 31 - fls(vd->significand >> 32); 578c2ecf20Sopenharmony_ci if (bits == 31) 588c2ecf20Sopenharmony_ci bits = 63 - fls(vd->significand); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci vfp_double_dump("normalise_denormal: in", vd); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (bits) { 638c2ecf20Sopenharmony_ci vd->exponent -= bits - 1; 648c2ecf20Sopenharmony_ci vd->significand <<= bits; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci vfp_double_dump("normalise_denormal: out", vd); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ciu32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci u64 significand, incr; 738c2ecf20Sopenharmony_ci int exponent, shift, underflow; 748c2ecf20Sopenharmony_ci u32 rmode; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci vfp_double_dump("pack: in", vd); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * Infinities and NaNs are a special case. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci if (vd->exponent == 2047 && (vd->significand == 0 || exceptions)) 828c2ecf20Sopenharmony_ci goto pack; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* 858c2ecf20Sopenharmony_ci * Special-case zero. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci if (vd->significand == 0) { 888c2ecf20Sopenharmony_ci vd->exponent = 0; 898c2ecf20Sopenharmony_ci goto pack; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci exponent = vd->exponent; 938c2ecf20Sopenharmony_ci significand = vd->significand; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci shift = 32 - fls(significand >> 32); 968c2ecf20Sopenharmony_ci if (shift == 32) 978c2ecf20Sopenharmony_ci shift = 64 - fls(significand); 988c2ecf20Sopenharmony_ci if (shift) { 998c2ecf20Sopenharmony_ci exponent -= shift; 1008c2ecf20Sopenharmony_ci significand <<= shift; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#ifdef DEBUG 1048c2ecf20Sopenharmony_ci vd->exponent = exponent; 1058c2ecf20Sopenharmony_ci vd->significand = significand; 1068c2ecf20Sopenharmony_ci vfp_double_dump("pack: normalised", vd); 1078c2ecf20Sopenharmony_ci#endif 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * Tiny number? 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci underflow = exponent < 0; 1138c2ecf20Sopenharmony_ci if (underflow) { 1148c2ecf20Sopenharmony_ci significand = vfp_shiftright64jamming(significand, -exponent); 1158c2ecf20Sopenharmony_ci exponent = 0; 1168c2ecf20Sopenharmony_ci#ifdef DEBUG 1178c2ecf20Sopenharmony_ci vd->exponent = exponent; 1188c2ecf20Sopenharmony_ci vd->significand = significand; 1198c2ecf20Sopenharmony_ci vfp_double_dump("pack: tiny number", vd); 1208c2ecf20Sopenharmony_ci#endif 1218c2ecf20Sopenharmony_ci if (!(significand & ((1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1))) 1228c2ecf20Sopenharmony_ci underflow = 0; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* 1268c2ecf20Sopenharmony_ci * Select rounding increment. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci incr = 0; 1298c2ecf20Sopenharmony_ci rmode = fpscr & FPSCR_RMODE_MASK; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (rmode == FPSCR_ROUND_NEAREST) { 1328c2ecf20Sopenharmony_ci incr = 1ULL << VFP_DOUBLE_LOW_BITS; 1338c2ecf20Sopenharmony_ci if ((significand & (1ULL << (VFP_DOUBLE_LOW_BITS + 1))) == 0) 1348c2ecf20Sopenharmony_ci incr -= 1; 1358c2ecf20Sopenharmony_ci } else if (rmode == FPSCR_ROUND_TOZERO) { 1368c2ecf20Sopenharmony_ci incr = 0; 1378c2ecf20Sopenharmony_ci } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0)) 1388c2ecf20Sopenharmony_ci incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pr_debug("VFP: rounding increment = 0x%08llx\n", incr); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * Is our rounding going to overflow? 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci if ((significand + incr) < significand) { 1468c2ecf20Sopenharmony_ci exponent += 1; 1478c2ecf20Sopenharmony_ci significand = (significand >> 1) | (significand & 1); 1488c2ecf20Sopenharmony_ci incr >>= 1; 1498c2ecf20Sopenharmony_ci#ifdef DEBUG 1508c2ecf20Sopenharmony_ci vd->exponent = exponent; 1518c2ecf20Sopenharmony_ci vd->significand = significand; 1528c2ecf20Sopenharmony_ci vfp_double_dump("pack: overflow", vd); 1538c2ecf20Sopenharmony_ci#endif 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * If any of the low bits (which will be shifted out of the 1588c2ecf20Sopenharmony_ci * number) are non-zero, the result is inexact. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci if (significand & ((1 << (VFP_DOUBLE_LOW_BITS + 1)) - 1)) 1618c2ecf20Sopenharmony_ci exceptions |= FPSCR_IXC; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * Do our rounding. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci significand += incr; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * Infinity? 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci if (exponent >= 2046) { 1728c2ecf20Sopenharmony_ci exceptions |= FPSCR_OFC | FPSCR_IXC; 1738c2ecf20Sopenharmony_ci if (incr == 0) { 1748c2ecf20Sopenharmony_ci vd->exponent = 2045; 1758c2ecf20Sopenharmony_ci vd->significand = 0x7fffffffffffffffULL; 1768c2ecf20Sopenharmony_ci } else { 1778c2ecf20Sopenharmony_ci vd->exponent = 2047; /* infinity */ 1788c2ecf20Sopenharmony_ci vd->significand = 0; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci } else { 1818c2ecf20Sopenharmony_ci if (significand >> (VFP_DOUBLE_LOW_BITS + 1) == 0) 1828c2ecf20Sopenharmony_ci exponent = 0; 1838c2ecf20Sopenharmony_ci if (exponent || significand > 0x8000000000000000ULL) 1848c2ecf20Sopenharmony_ci underflow = 0; 1858c2ecf20Sopenharmony_ci if (underflow) 1868c2ecf20Sopenharmony_ci exceptions |= FPSCR_UFC; 1878c2ecf20Sopenharmony_ci vd->exponent = exponent; 1888c2ecf20Sopenharmony_ci vd->significand = significand >> 1; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci pack: 1928c2ecf20Sopenharmony_ci vfp_double_dump("pack: final", vd); 1938c2ecf20Sopenharmony_ci { 1948c2ecf20Sopenharmony_ci s64 d = vfp_double_pack(vd); 1958c2ecf20Sopenharmony_ci pr_debug("VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func, 1968c2ecf20Sopenharmony_ci dd, d, exceptions); 1978c2ecf20Sopenharmony_ci vfp_put_double(d, dd); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci return exceptions; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* 2038c2ecf20Sopenharmony_ci * Propagate the NaN, setting exceptions if it is signalling. 2048c2ecf20Sopenharmony_ci * 'n' is always a NaN. 'm' may be a number, NaN or infinity. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_cistatic u32 2078c2ecf20Sopenharmony_civfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn, 2088c2ecf20Sopenharmony_ci struct vfp_double *vdm, u32 fpscr) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct vfp_double *nan; 2118c2ecf20Sopenharmony_ci int tn, tm = 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci tn = vfp_double_type(vdn); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (vdm) 2168c2ecf20Sopenharmony_ci tm = vfp_double_type(vdm); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (fpscr & FPSCR_DEFAULT_NAN) 2198c2ecf20Sopenharmony_ci /* 2208c2ecf20Sopenharmony_ci * Default NaN mode - always returns a quiet NaN 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ci nan = &vfp_double_default_qnan; 2238c2ecf20Sopenharmony_ci else { 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * Contemporary mode - select the first signalling 2268c2ecf20Sopenharmony_ci * NAN, or if neither are signalling, the first 2278c2ecf20Sopenharmony_ci * quiet NAN. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN)) 2308c2ecf20Sopenharmony_ci nan = vdn; 2318c2ecf20Sopenharmony_ci else 2328c2ecf20Sopenharmony_ci nan = vdm; 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * Make the NaN quiet. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci nan->significand |= VFP_DOUBLE_SIGNIFICAND_QNAN; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci *vdd = *nan; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* 2428c2ecf20Sopenharmony_ci * If one was a signalling NAN, raise invalid operation. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : VFP_NAN_FLAG; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * Extended operations 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic u32 vfp_double_fabs(int dd, int unused, int dm, u32 fpscr) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci vfp_put_double(vfp_double_packed_abs(vfp_get_double(dm)), dd); 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic u32 vfp_double_fcpy(int dd, int unused, int dm, u32 fpscr) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci vfp_put_double(vfp_get_double(dm), dd); 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic u32 vfp_double_fneg(int dd, int unused, int dm, u32 fpscr) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci vfp_put_double(vfp_double_packed_negate(vfp_get_double(dm)), dd); 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic u32 vfp_double_fsqrt(int dd, int unused, int dm, u32 fpscr) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct vfp_double vdm, vdd; 2718c2ecf20Sopenharmony_ci int ret, tm; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 2748c2ecf20Sopenharmony_ci tm = vfp_double_type(&vdm); 2758c2ecf20Sopenharmony_ci if (tm & (VFP_NAN|VFP_INFINITY)) { 2768c2ecf20Sopenharmony_ci struct vfp_double *vdp = &vdd; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (tm & VFP_NAN) 2798c2ecf20Sopenharmony_ci ret = vfp_propagate_nan(vdp, &vdm, NULL, fpscr); 2808c2ecf20Sopenharmony_ci else if (vdm.sign == 0) { 2818c2ecf20Sopenharmony_ci sqrt_copy: 2828c2ecf20Sopenharmony_ci vdp = &vdm; 2838c2ecf20Sopenharmony_ci ret = 0; 2848c2ecf20Sopenharmony_ci } else { 2858c2ecf20Sopenharmony_ci sqrt_invalid: 2868c2ecf20Sopenharmony_ci vdp = &vfp_double_default_qnan; 2878c2ecf20Sopenharmony_ci ret = FPSCR_IOC; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci vfp_put_double(vfp_double_pack(vdp), dd); 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* 2948c2ecf20Sopenharmony_ci * sqrt(+/- 0) == +/- 0 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_ci if (tm & VFP_ZERO) 2978c2ecf20Sopenharmony_ci goto sqrt_copy; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* 3008c2ecf20Sopenharmony_ci * Normalise a denormalised number 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_ci if (tm & VFP_DENORMAL) 3038c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* 3068c2ecf20Sopenharmony_ci * sqrt(<0) = invalid 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci if (vdm.sign) 3098c2ecf20Sopenharmony_ci goto sqrt_invalid; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci vfp_double_dump("sqrt", &vdm); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* 3148c2ecf20Sopenharmony_ci * Estimate the square root. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci vdd.sign = 0; 3178c2ecf20Sopenharmony_ci vdd.exponent = ((vdm.exponent - 1023) >> 1) + 1023; 3188c2ecf20Sopenharmony_ci vdd.significand = (u64)vfp_estimate_sqrt_significand(vdm.exponent, vdm.significand >> 32) << 31; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci vfp_double_dump("sqrt estimate1", &vdd); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci vdm.significand >>= 1 + (vdm.exponent & 1); 3238c2ecf20Sopenharmony_ci vdd.significand += 2 + vfp_estimate_div128to64(vdm.significand, 0, vdd.significand); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci vfp_double_dump("sqrt estimate2", &vdd); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * And now adjust. 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_ci if ((vdd.significand & VFP_DOUBLE_LOW_BITS_MASK) <= 5) { 3318c2ecf20Sopenharmony_ci if (vdd.significand < 2) { 3328c2ecf20Sopenharmony_ci vdd.significand = ~0ULL; 3338c2ecf20Sopenharmony_ci } else { 3348c2ecf20Sopenharmony_ci u64 termh, terml, remh, reml; 3358c2ecf20Sopenharmony_ci vdm.significand <<= 2; 3368c2ecf20Sopenharmony_ci mul64to128(&termh, &terml, vdd.significand, vdd.significand); 3378c2ecf20Sopenharmony_ci sub128(&remh, &reml, vdm.significand, 0, termh, terml); 3388c2ecf20Sopenharmony_ci while ((s64)remh < 0) { 3398c2ecf20Sopenharmony_ci vdd.significand -= 1; 3408c2ecf20Sopenharmony_ci shift64left(&termh, &terml, vdd.significand); 3418c2ecf20Sopenharmony_ci terml |= 1; 3428c2ecf20Sopenharmony_ci add128(&remh, &reml, remh, reml, termh, terml); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci vdd.significand |= (remh | reml) != 0; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci vdd.significand = vfp_shiftright64jamming(vdd.significand, 1); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fsqrt"); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* 3538c2ecf20Sopenharmony_ci * Equal := ZC 3548c2ecf20Sopenharmony_ci * Less than := N 3558c2ecf20Sopenharmony_ci * Greater than := C 3568c2ecf20Sopenharmony_ci * Unordered := CV 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_cistatic u32 vfp_compare(int dd, int signal_on_qnan, int dm, u32 fpscr) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci s64 d, m; 3618c2ecf20Sopenharmony_ci u32 ret = 0; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci m = vfp_get_double(dm); 3648c2ecf20Sopenharmony_ci if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) { 3658c2ecf20Sopenharmony_ci ret |= FPSCR_C | FPSCR_V; 3668c2ecf20Sopenharmony_ci if (signal_on_qnan || !(vfp_double_packed_mantissa(m) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1)))) 3678c2ecf20Sopenharmony_ci /* 3688c2ecf20Sopenharmony_ci * Signalling NaN, or signalling on quiet NaN 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci ret |= FPSCR_IOC; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci d = vfp_get_double(dd); 3748c2ecf20Sopenharmony_ci if (vfp_double_packed_exponent(d) == 2047 && vfp_double_packed_mantissa(d)) { 3758c2ecf20Sopenharmony_ci ret |= FPSCR_C | FPSCR_V; 3768c2ecf20Sopenharmony_ci if (signal_on_qnan || !(vfp_double_packed_mantissa(d) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1)))) 3778c2ecf20Sopenharmony_ci /* 3788c2ecf20Sopenharmony_ci * Signalling NaN, or signalling on quiet NaN 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci ret |= FPSCR_IOC; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (ret == 0) { 3848c2ecf20Sopenharmony_ci if (d == m || vfp_double_packed_abs(d | m) == 0) { 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * equal 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci ret |= FPSCR_Z | FPSCR_C; 3898c2ecf20Sopenharmony_ci } else if (vfp_double_packed_sign(d ^ m)) { 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * different signs 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci if (vfp_double_packed_sign(d)) 3948c2ecf20Sopenharmony_ci /* 3958c2ecf20Sopenharmony_ci * d is negative, so d < m 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci ret |= FPSCR_N; 3988c2ecf20Sopenharmony_ci else 3998c2ecf20Sopenharmony_ci /* 4008c2ecf20Sopenharmony_ci * d is positive, so d > m 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_ci ret |= FPSCR_C; 4038c2ecf20Sopenharmony_ci } else if ((vfp_double_packed_sign(d) != 0) ^ (d < m)) { 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * d < m 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci ret |= FPSCR_N; 4088c2ecf20Sopenharmony_ci } else if ((vfp_double_packed_sign(d) != 0) ^ (d > m)) { 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * d > m 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci ret |= FPSCR_C; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return ret; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic u32 vfp_double_fcmp(int dd, int unused, int dm, u32 fpscr) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci return vfp_compare(dd, 0, dm, fpscr); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic u32 vfp_double_fcmpe(int dd, int unused, int dm, u32 fpscr) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci return vfp_compare(dd, 1, dm, fpscr); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic u32 vfp_double_fcmpz(int dd, int unused, int dm, u32 fpscr) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci return vfp_compare(dd, 0, VFP_REG_ZERO, fpscr); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic u32 vfp_double_fcmpez(int dd, int unused, int dm, u32 fpscr) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci return vfp_compare(dd, 1, VFP_REG_ZERO, fpscr); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic u32 vfp_double_fcvts(int sd, int unused, int dm, u32 fpscr) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct vfp_double vdm; 4428c2ecf20Sopenharmony_ci struct vfp_single vsd; 4438c2ecf20Sopenharmony_ci int tm; 4448c2ecf20Sopenharmony_ci u32 exceptions = 0; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci tm = vfp_double_type(&vdm); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* 4518c2ecf20Sopenharmony_ci * If we have a signalling NaN, signal invalid operation. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci if (tm == VFP_SNAN) 4548c2ecf20Sopenharmony_ci exceptions = FPSCR_IOC; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (tm & VFP_DENORMAL) 4578c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci vsd.sign = vdm.sign; 4608c2ecf20Sopenharmony_ci vsd.significand = vfp_hi64to32jamming(vdm.significand); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* 4638c2ecf20Sopenharmony_ci * If we have an infinity or a NaN, the exponent must be 255 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci if (tm & (VFP_INFINITY|VFP_NAN)) { 4668c2ecf20Sopenharmony_ci vsd.exponent = 255; 4678c2ecf20Sopenharmony_ci if (tm == VFP_QNAN) 4688c2ecf20Sopenharmony_ci vsd.significand |= VFP_SINGLE_SIGNIFICAND_QNAN; 4698c2ecf20Sopenharmony_ci goto pack_nan; 4708c2ecf20Sopenharmony_ci } else if (tm & VFP_ZERO) 4718c2ecf20Sopenharmony_ci vsd.exponent = 0; 4728c2ecf20Sopenharmony_ci else 4738c2ecf20Sopenharmony_ci vsd.exponent = vdm.exponent - (1023 - 127); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fcvts"); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci pack_nan: 4788c2ecf20Sopenharmony_ci vfp_put_float(vfp_single_pack(&vsd), sd); 4798c2ecf20Sopenharmony_ci return exceptions; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic u32 vfp_double_fuito(int dd, int unused, int dm, u32 fpscr) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct vfp_double vdm; 4858c2ecf20Sopenharmony_ci u32 m = vfp_get_float(dm); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci vdm.sign = 0; 4888c2ecf20Sopenharmony_ci vdm.exponent = 1023 + 63 - 1; 4898c2ecf20Sopenharmony_ci vdm.significand = (u64)m; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fuito"); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic u32 vfp_double_fsito(int dd, int unused, int dm, u32 fpscr) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct vfp_double vdm; 4978c2ecf20Sopenharmony_ci u32 m = vfp_get_float(dm); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci vdm.sign = (m & 0x80000000) >> 16; 5008c2ecf20Sopenharmony_ci vdm.exponent = 1023 + 63 - 1; 5018c2ecf20Sopenharmony_ci vdm.significand = vdm.sign ? -m : m; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fsito"); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic u32 vfp_double_ftoui(int sd, int unused, int dm, u32 fpscr) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct vfp_double vdm; 5098c2ecf20Sopenharmony_ci u32 d, exceptions = 0; 5108c2ecf20Sopenharmony_ci int rmode = fpscr & FPSCR_RMODE_MASK; 5118c2ecf20Sopenharmony_ci int tm; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* 5168c2ecf20Sopenharmony_ci * Do we have a denormalised number? 5178c2ecf20Sopenharmony_ci */ 5188c2ecf20Sopenharmony_ci tm = vfp_double_type(&vdm); 5198c2ecf20Sopenharmony_ci if (tm & VFP_DENORMAL) 5208c2ecf20Sopenharmony_ci exceptions |= FPSCR_IDC; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (tm & VFP_NAN) 5238c2ecf20Sopenharmony_ci vdm.sign = 0; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (vdm.exponent >= 1023 + 32) { 5268c2ecf20Sopenharmony_ci d = vdm.sign ? 0 : 0xffffffff; 5278c2ecf20Sopenharmony_ci exceptions = FPSCR_IOC; 5288c2ecf20Sopenharmony_ci } else if (vdm.exponent >= 1023 - 1) { 5298c2ecf20Sopenharmony_ci int shift = 1023 + 63 - vdm.exponent; 5308c2ecf20Sopenharmony_ci u64 rem, incr = 0; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * 2^0 <= m < 2^32-2^8 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci d = (vdm.significand << 1) >> shift; 5368c2ecf20Sopenharmony_ci rem = vdm.significand << (65 - shift); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (rmode == FPSCR_ROUND_NEAREST) { 5398c2ecf20Sopenharmony_ci incr = 0x8000000000000000ULL; 5408c2ecf20Sopenharmony_ci if ((d & 1) == 0) 5418c2ecf20Sopenharmony_ci incr -= 1; 5428c2ecf20Sopenharmony_ci } else if (rmode == FPSCR_ROUND_TOZERO) { 5438c2ecf20Sopenharmony_ci incr = 0; 5448c2ecf20Sopenharmony_ci } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) { 5458c2ecf20Sopenharmony_ci incr = ~0ULL; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if ((rem + incr) < rem) { 5498c2ecf20Sopenharmony_ci if (d < 0xffffffff) 5508c2ecf20Sopenharmony_ci d += 1; 5518c2ecf20Sopenharmony_ci else 5528c2ecf20Sopenharmony_ci exceptions |= FPSCR_IOC; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (d && vdm.sign) { 5568c2ecf20Sopenharmony_ci d = 0; 5578c2ecf20Sopenharmony_ci exceptions |= FPSCR_IOC; 5588c2ecf20Sopenharmony_ci } else if (rem) 5598c2ecf20Sopenharmony_ci exceptions |= FPSCR_IXC; 5608c2ecf20Sopenharmony_ci } else { 5618c2ecf20Sopenharmony_ci d = 0; 5628c2ecf20Sopenharmony_ci if (vdm.exponent | vdm.significand) { 5638c2ecf20Sopenharmony_ci exceptions |= FPSCR_IXC; 5648c2ecf20Sopenharmony_ci if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) 5658c2ecf20Sopenharmony_ci d = 1; 5668c2ecf20Sopenharmony_ci else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) { 5678c2ecf20Sopenharmony_ci d = 0; 5688c2ecf20Sopenharmony_ci exceptions |= FPSCR_IOC; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci vfp_put_float(d, sd); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci return exceptions; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic u32 vfp_double_ftouiz(int sd, int unused, int dm, u32 fpscr) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci return vfp_double_ftoui(sd, unused, dm, FPSCR_ROUND_TOZERO); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic u32 vfp_double_ftosi(int sd, int unused, int dm, u32 fpscr) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct vfp_double vdm; 5888c2ecf20Sopenharmony_ci u32 d, exceptions = 0; 5898c2ecf20Sopenharmony_ci int rmode = fpscr & FPSCR_RMODE_MASK; 5908c2ecf20Sopenharmony_ci int tm; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 5938c2ecf20Sopenharmony_ci vfp_double_dump("VDM", &vdm); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* 5968c2ecf20Sopenharmony_ci * Do we have denormalised number? 5978c2ecf20Sopenharmony_ci */ 5988c2ecf20Sopenharmony_ci tm = vfp_double_type(&vdm); 5998c2ecf20Sopenharmony_ci if (tm & VFP_DENORMAL) 6008c2ecf20Sopenharmony_ci exceptions |= FPSCR_IDC; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (tm & VFP_NAN) { 6038c2ecf20Sopenharmony_ci d = 0; 6048c2ecf20Sopenharmony_ci exceptions |= FPSCR_IOC; 6058c2ecf20Sopenharmony_ci } else if (vdm.exponent >= 1023 + 32) { 6068c2ecf20Sopenharmony_ci d = 0x7fffffff; 6078c2ecf20Sopenharmony_ci if (vdm.sign) 6088c2ecf20Sopenharmony_ci d = ~d; 6098c2ecf20Sopenharmony_ci exceptions |= FPSCR_IOC; 6108c2ecf20Sopenharmony_ci } else if (vdm.exponent >= 1023 - 1) { 6118c2ecf20Sopenharmony_ci int shift = 1023 + 63 - vdm.exponent; /* 58 */ 6128c2ecf20Sopenharmony_ci u64 rem, incr = 0; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci d = (vdm.significand << 1) >> shift; 6158c2ecf20Sopenharmony_ci rem = vdm.significand << (65 - shift); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (rmode == FPSCR_ROUND_NEAREST) { 6188c2ecf20Sopenharmony_ci incr = 0x8000000000000000ULL; 6198c2ecf20Sopenharmony_ci if ((d & 1) == 0) 6208c2ecf20Sopenharmony_ci incr -= 1; 6218c2ecf20Sopenharmony_ci } else if (rmode == FPSCR_ROUND_TOZERO) { 6228c2ecf20Sopenharmony_ci incr = 0; 6238c2ecf20Sopenharmony_ci } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) { 6248c2ecf20Sopenharmony_ci incr = ~0ULL; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if ((rem + incr) < rem && d < 0xffffffff) 6288c2ecf20Sopenharmony_ci d += 1; 6298c2ecf20Sopenharmony_ci if (d > 0x7fffffff + (vdm.sign != 0)) { 6308c2ecf20Sopenharmony_ci d = 0x7fffffff + (vdm.sign != 0); 6318c2ecf20Sopenharmony_ci exceptions |= FPSCR_IOC; 6328c2ecf20Sopenharmony_ci } else if (rem) 6338c2ecf20Sopenharmony_ci exceptions |= FPSCR_IXC; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (vdm.sign) 6368c2ecf20Sopenharmony_ci d = -d; 6378c2ecf20Sopenharmony_ci } else { 6388c2ecf20Sopenharmony_ci d = 0; 6398c2ecf20Sopenharmony_ci if (vdm.exponent | vdm.significand) { 6408c2ecf20Sopenharmony_ci exceptions |= FPSCR_IXC; 6418c2ecf20Sopenharmony_ci if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0) 6428c2ecf20Sopenharmony_ci d = 1; 6438c2ecf20Sopenharmony_ci else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) 6448c2ecf20Sopenharmony_ci d = -1; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci vfp_put_float((s32)d, sd); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci return exceptions; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic u32 vfp_double_ftosiz(int dd, int unused, int dm, u32 fpscr) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci return vfp_double_ftosi(dd, unused, dm, FPSCR_ROUND_TOZERO); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic struct op fops_ext[32] = { 6628c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCPY)] = { vfp_double_fcpy, 0 }, 6638c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FABS)] = { vfp_double_fabs, 0 }, 6648c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FNEG)] = { vfp_double_fneg, 0 }, 6658c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FSQRT)] = { vfp_double_fsqrt, 0 }, 6668c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMP)] = { vfp_double_fcmp, OP_SCALAR }, 6678c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMPE)] = { vfp_double_fcmpe, OP_SCALAR }, 6688c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMPZ)] = { vfp_double_fcmpz, OP_SCALAR }, 6698c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCMPEZ)] = { vfp_double_fcmpez, OP_SCALAR }, 6708c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FCVT)] = { vfp_double_fcvts, OP_SCALAR|OP_SD }, 6718c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FUITO)] = { vfp_double_fuito, OP_SCALAR|OP_SM }, 6728c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FSITO)] = { vfp_double_fsito, OP_SCALAR|OP_SM }, 6738c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOUI)] = { vfp_double_ftoui, OP_SCALAR|OP_SD }, 6748c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOUIZ)] = { vfp_double_ftouiz, OP_SCALAR|OP_SD }, 6758c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOSI)] = { vfp_double_ftosi, OP_SCALAR|OP_SD }, 6768c2ecf20Sopenharmony_ci [FEXT_TO_IDX(FEXT_FTOSIZ)] = { vfp_double_ftosiz, OP_SCALAR|OP_SD }, 6778c2ecf20Sopenharmony_ci}; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic u32 6838c2ecf20Sopenharmony_civfp_double_fadd_nonnumber(struct vfp_double *vdd, struct vfp_double *vdn, 6848c2ecf20Sopenharmony_ci struct vfp_double *vdm, u32 fpscr) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct vfp_double *vdp; 6878c2ecf20Sopenharmony_ci u32 exceptions = 0; 6888c2ecf20Sopenharmony_ci int tn, tm; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci tn = vfp_double_type(vdn); 6918c2ecf20Sopenharmony_ci tm = vfp_double_type(vdm); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (tn & tm & VFP_INFINITY) { 6948c2ecf20Sopenharmony_ci /* 6958c2ecf20Sopenharmony_ci * Two infinities. Are they different signs? 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_ci if (vdn->sign ^ vdm->sign) { 6988c2ecf20Sopenharmony_ci /* 6998c2ecf20Sopenharmony_ci * different signs -> invalid 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_ci exceptions = FPSCR_IOC; 7028c2ecf20Sopenharmony_ci vdp = &vfp_double_default_qnan; 7038c2ecf20Sopenharmony_ci } else { 7048c2ecf20Sopenharmony_ci /* 7058c2ecf20Sopenharmony_ci * same signs -> valid 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci vdp = vdn; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci } else if (tn & VFP_INFINITY && tm & VFP_NUMBER) { 7108c2ecf20Sopenharmony_ci /* 7118c2ecf20Sopenharmony_ci * One infinity and one number -> infinity 7128c2ecf20Sopenharmony_ci */ 7138c2ecf20Sopenharmony_ci vdp = vdn; 7148c2ecf20Sopenharmony_ci } else { 7158c2ecf20Sopenharmony_ci /* 7168c2ecf20Sopenharmony_ci * 'n' is a NaN of some type 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_ci return vfp_propagate_nan(vdd, vdn, vdm, fpscr); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci *vdd = *vdp; 7218c2ecf20Sopenharmony_ci return exceptions; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic u32 7258c2ecf20Sopenharmony_civfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn, 7268c2ecf20Sopenharmony_ci struct vfp_double *vdm, u32 fpscr) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci u32 exp_diff; 7298c2ecf20Sopenharmony_ci u64 m_sig; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (vdn->significand & (1ULL << 63) || 7328c2ecf20Sopenharmony_ci vdm->significand & (1ULL << 63)) { 7338c2ecf20Sopenharmony_ci pr_info("VFP: bad FP values in %s\n", __func__); 7348c2ecf20Sopenharmony_ci vfp_double_dump("VDN", vdn); 7358c2ecf20Sopenharmony_ci vfp_double_dump("VDM", vdm); 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* 7398c2ecf20Sopenharmony_ci * Ensure that 'n' is the largest magnitude number. Note that 7408c2ecf20Sopenharmony_ci * if 'n' and 'm' have equal exponents, we do not swap them. 7418c2ecf20Sopenharmony_ci * This ensures that NaN propagation works correctly. 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_ci if (vdn->exponent < vdm->exponent) { 7448c2ecf20Sopenharmony_ci struct vfp_double *t = vdn; 7458c2ecf20Sopenharmony_ci vdn = vdm; 7468c2ecf20Sopenharmony_ci vdm = t; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* 7508c2ecf20Sopenharmony_ci * Is 'n' an infinity or a NaN? Note that 'm' may be a number, 7518c2ecf20Sopenharmony_ci * infinity or a NaN here. 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_ci if (vdn->exponent == 2047) 7548c2ecf20Sopenharmony_ci return vfp_double_fadd_nonnumber(vdd, vdn, vdm, fpscr); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* 7578c2ecf20Sopenharmony_ci * We have two proper numbers, where 'vdn' is the larger magnitude. 7588c2ecf20Sopenharmony_ci * 7598c2ecf20Sopenharmony_ci * Copy 'n' to 'd' before doing the arithmetic. 7608c2ecf20Sopenharmony_ci */ 7618c2ecf20Sopenharmony_ci *vdd = *vdn; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci /* 7648c2ecf20Sopenharmony_ci * Align 'm' with the result. 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_ci exp_diff = vdn->exponent - vdm->exponent; 7678c2ecf20Sopenharmony_ci m_sig = vfp_shiftright64jamming(vdm->significand, exp_diff); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci /* 7708c2ecf20Sopenharmony_ci * If the signs are different, we are really subtracting. 7718c2ecf20Sopenharmony_ci */ 7728c2ecf20Sopenharmony_ci if (vdn->sign ^ vdm->sign) { 7738c2ecf20Sopenharmony_ci m_sig = vdn->significand - m_sig; 7748c2ecf20Sopenharmony_ci if ((s64)m_sig < 0) { 7758c2ecf20Sopenharmony_ci vdd->sign = vfp_sign_negate(vdd->sign); 7768c2ecf20Sopenharmony_ci m_sig = -m_sig; 7778c2ecf20Sopenharmony_ci } else if (m_sig == 0) { 7788c2ecf20Sopenharmony_ci vdd->sign = (fpscr & FPSCR_RMODE_MASK) == 7798c2ecf20Sopenharmony_ci FPSCR_ROUND_MINUSINF ? 0x8000 : 0; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci } else { 7828c2ecf20Sopenharmony_ci m_sig += vdn->significand; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci vdd->significand = m_sig; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return 0; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic u32 7908c2ecf20Sopenharmony_civfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn, 7918c2ecf20Sopenharmony_ci struct vfp_double *vdm, u32 fpscr) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci vfp_double_dump("VDN", vdn); 7948c2ecf20Sopenharmony_ci vfp_double_dump("VDM", vdm); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* 7978c2ecf20Sopenharmony_ci * Ensure that 'n' is the largest magnitude number. Note that 7988c2ecf20Sopenharmony_ci * if 'n' and 'm' have equal exponents, we do not swap them. 7998c2ecf20Sopenharmony_ci * This ensures that NaN propagation works correctly. 8008c2ecf20Sopenharmony_ci */ 8018c2ecf20Sopenharmony_ci if (vdn->exponent < vdm->exponent) { 8028c2ecf20Sopenharmony_ci struct vfp_double *t = vdn; 8038c2ecf20Sopenharmony_ci vdn = vdm; 8048c2ecf20Sopenharmony_ci vdm = t; 8058c2ecf20Sopenharmony_ci pr_debug("VFP: swapping M <-> N\n"); 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci vdd->sign = vdn->sign ^ vdm->sign; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* 8118c2ecf20Sopenharmony_ci * If 'n' is an infinity or NaN, handle it. 'm' may be anything. 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_ci if (vdn->exponent == 2047) { 8148c2ecf20Sopenharmony_ci if (vdn->significand || (vdm->exponent == 2047 && vdm->significand)) 8158c2ecf20Sopenharmony_ci return vfp_propagate_nan(vdd, vdn, vdm, fpscr); 8168c2ecf20Sopenharmony_ci if ((vdm->exponent | vdm->significand) == 0) { 8178c2ecf20Sopenharmony_ci *vdd = vfp_double_default_qnan; 8188c2ecf20Sopenharmony_ci return FPSCR_IOC; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci vdd->exponent = vdn->exponent; 8218c2ecf20Sopenharmony_ci vdd->significand = 0; 8228c2ecf20Sopenharmony_ci return 0; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* 8268c2ecf20Sopenharmony_ci * If 'm' is zero, the result is always zero. In this case, 8278c2ecf20Sopenharmony_ci * 'n' may be zero or a number, but it doesn't matter which. 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_ci if ((vdm->exponent | vdm->significand) == 0) { 8308c2ecf20Sopenharmony_ci vdd->exponent = 0; 8318c2ecf20Sopenharmony_ci vdd->significand = 0; 8328c2ecf20Sopenharmony_ci return 0; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* 8368c2ecf20Sopenharmony_ci * We add 2 to the destination exponent for the same reason 8378c2ecf20Sopenharmony_ci * as the addition case - though this time we have +1 from 8388c2ecf20Sopenharmony_ci * each input operand. 8398c2ecf20Sopenharmony_ci */ 8408c2ecf20Sopenharmony_ci vdd->exponent = vdn->exponent + vdm->exponent - 1023 + 2; 8418c2ecf20Sopenharmony_ci vdd->significand = vfp_hi64multiply64(vdn->significand, vdm->significand); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci vfp_double_dump("VDD", vdd); 8448c2ecf20Sopenharmony_ci return 0; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci#define NEG_MULTIPLY (1 << 0) 8488c2ecf20Sopenharmony_ci#define NEG_SUBTRACT (1 << 1) 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic u32 8518c2ecf20Sopenharmony_civfp_double_multiply_accumulate(int dd, int dn, int dm, u32 fpscr, u32 negate, char *func) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct vfp_double vdd, vdp, vdn, vdm; 8548c2ecf20Sopenharmony_ci u32 exceptions; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 8578c2ecf20Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 8588c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 8618c2ecf20Sopenharmony_ci if (vdm.exponent == 0 && vdm.significand) 8628c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr); 8658c2ecf20Sopenharmony_ci if (negate & NEG_MULTIPLY) 8668c2ecf20Sopenharmony_ci vdp.sign = vfp_sign_negate(vdp.sign); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dd)); 8698c2ecf20Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 8708c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 8718c2ecf20Sopenharmony_ci if (negate & NEG_SUBTRACT) 8728c2ecf20Sopenharmony_ci vdn.sign = vfp_sign_negate(vdn.sign); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, func); 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci/* 8808c2ecf20Sopenharmony_ci * Standard operations 8818c2ecf20Sopenharmony_ci */ 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci/* 8848c2ecf20Sopenharmony_ci * sd = sd + (sn * sm) 8858c2ecf20Sopenharmony_ci */ 8868c2ecf20Sopenharmony_cistatic u32 vfp_double_fmac(int dd, int dn, int dm, u32 fpscr) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, 0, "fmac"); 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci/* 8928c2ecf20Sopenharmony_ci * sd = sd - (sn * sm) 8938c2ecf20Sopenharmony_ci */ 8948c2ecf20Sopenharmony_cistatic u32 vfp_double_fnmac(int dd, int dn, int dm, u32 fpscr) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac"); 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci/* 9008c2ecf20Sopenharmony_ci * sd = -sd + (sn * sm) 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_cistatic u32 vfp_double_fmsc(int dd, int dn, int dm, u32 fpscr) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc"); 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci/* 9088c2ecf20Sopenharmony_ci * sd = -sd - (sn * sm) 9098c2ecf20Sopenharmony_ci */ 9108c2ecf20Sopenharmony_cistatic u32 vfp_double_fnmsc(int dd, int dn, int dm, u32 fpscr) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc"); 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci/* 9168c2ecf20Sopenharmony_ci * sd = sn * sm 9178c2ecf20Sopenharmony_ci */ 9188c2ecf20Sopenharmony_cistatic u32 vfp_double_fmul(int dd, int dn, int dm, u32 fpscr) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci struct vfp_double vdd, vdn, vdm; 9218c2ecf20Sopenharmony_ci u32 exceptions; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 9248c2ecf20Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 9258c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 9288c2ecf20Sopenharmony_ci if (vdm.exponent == 0 && vdm.significand) 9298c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); 9328c2ecf20Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fmul"); 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci/* 9368c2ecf20Sopenharmony_ci * sd = -(sn * sm) 9378c2ecf20Sopenharmony_ci */ 9388c2ecf20Sopenharmony_cistatic u32 vfp_double_fnmul(int dd, int dn, int dm, u32 fpscr) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct vfp_double vdd, vdn, vdm; 9418c2ecf20Sopenharmony_ci u32 exceptions; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 9448c2ecf20Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 9458c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 9488c2ecf20Sopenharmony_ci if (vdm.exponent == 0 && vdm.significand) 9498c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); 9528c2ecf20Sopenharmony_ci vdd.sign = vfp_sign_negate(vdd.sign); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fnmul"); 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci/* 9588c2ecf20Sopenharmony_ci * sd = sn + sm 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_cistatic u32 vfp_double_fadd(int dd, int dn, int dm, u32 fpscr) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci struct vfp_double vdd, vdn, vdm; 9638c2ecf20Sopenharmony_ci u32 exceptions; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 9668c2ecf20Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 9678c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 9708c2ecf20Sopenharmony_ci if (vdm.exponent == 0 && vdm.significand) 9718c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fadd"); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci/* 9798c2ecf20Sopenharmony_ci * sd = sn - sm 9808c2ecf20Sopenharmony_ci */ 9818c2ecf20Sopenharmony_cistatic u32 vfp_double_fsub(int dd, int dn, int dm, u32 fpscr) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci struct vfp_double vdd, vdn, vdm; 9848c2ecf20Sopenharmony_ci u32 exceptions; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 9878c2ecf20Sopenharmony_ci if (vdn.exponent == 0 && vdn.significand) 9888c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 9918c2ecf20Sopenharmony_ci if (vdm.exponent == 0 && vdm.significand) 9928c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci /* 9958c2ecf20Sopenharmony_ci * Subtraction is like addition, but with a negated operand. 9968c2ecf20Sopenharmony_ci */ 9978c2ecf20Sopenharmony_ci vdm.sign = vfp_sign_negate(vdm.sign); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fsub"); 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci/* 10058c2ecf20Sopenharmony_ci * sd = sn / sm 10068c2ecf20Sopenharmony_ci */ 10078c2ecf20Sopenharmony_cistatic u32 vfp_double_fdiv(int dd, int dn, int dm, u32 fpscr) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci struct vfp_double vdd, vdn, vdm; 10108c2ecf20Sopenharmony_ci u32 exceptions = 0; 10118c2ecf20Sopenharmony_ci int tm, tn; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci vfp_double_unpack(&vdn, vfp_get_double(dn)); 10148c2ecf20Sopenharmony_ci vfp_double_unpack(&vdm, vfp_get_double(dm)); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci vdd.sign = vdn.sign ^ vdm.sign; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci tn = vfp_double_type(&vdn); 10198c2ecf20Sopenharmony_ci tm = vfp_double_type(&vdm); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci /* 10228c2ecf20Sopenharmony_ci * Is n a NAN? 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_ci if (tn & VFP_NAN) 10258c2ecf20Sopenharmony_ci goto vdn_nan; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* 10288c2ecf20Sopenharmony_ci * Is m a NAN? 10298c2ecf20Sopenharmony_ci */ 10308c2ecf20Sopenharmony_ci if (tm & VFP_NAN) 10318c2ecf20Sopenharmony_ci goto vdm_nan; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* 10348c2ecf20Sopenharmony_ci * If n and m are infinity, the result is invalid 10358c2ecf20Sopenharmony_ci * If n and m are zero, the result is invalid 10368c2ecf20Sopenharmony_ci */ 10378c2ecf20Sopenharmony_ci if (tm & tn & (VFP_INFINITY|VFP_ZERO)) 10388c2ecf20Sopenharmony_ci goto invalid; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* 10418c2ecf20Sopenharmony_ci * If n is infinity, the result is infinity 10428c2ecf20Sopenharmony_ci */ 10438c2ecf20Sopenharmony_ci if (tn & VFP_INFINITY) 10448c2ecf20Sopenharmony_ci goto infinity; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* 10478c2ecf20Sopenharmony_ci * If m is zero, raise div0 exceptions 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci if (tm & VFP_ZERO) 10508c2ecf20Sopenharmony_ci goto divzero; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* 10538c2ecf20Sopenharmony_ci * If m is infinity, or n is zero, the result is zero 10548c2ecf20Sopenharmony_ci */ 10558c2ecf20Sopenharmony_ci if (tm & VFP_INFINITY || tn & VFP_ZERO) 10568c2ecf20Sopenharmony_ci goto zero; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (tn & VFP_DENORMAL) 10598c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdn); 10608c2ecf20Sopenharmony_ci if (tm & VFP_DENORMAL) 10618c2ecf20Sopenharmony_ci vfp_double_normalise_denormal(&vdm); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* 10648c2ecf20Sopenharmony_ci * Ok, we have two numbers, we can perform division. 10658c2ecf20Sopenharmony_ci */ 10668c2ecf20Sopenharmony_ci vdd.exponent = vdn.exponent - vdm.exponent + 1023 - 1; 10678c2ecf20Sopenharmony_ci vdm.significand <<= 1; 10688c2ecf20Sopenharmony_ci if (vdm.significand <= (2 * vdn.significand)) { 10698c2ecf20Sopenharmony_ci vdn.significand >>= 1; 10708c2ecf20Sopenharmony_ci vdd.exponent++; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci vdd.significand = vfp_estimate_div128to64(vdn.significand, 0, vdm.significand); 10738c2ecf20Sopenharmony_ci if ((vdd.significand & 0x1ff) <= 2) { 10748c2ecf20Sopenharmony_ci u64 termh, terml, remh, reml; 10758c2ecf20Sopenharmony_ci mul64to128(&termh, &terml, vdm.significand, vdd.significand); 10768c2ecf20Sopenharmony_ci sub128(&remh, &reml, vdn.significand, 0, termh, terml); 10778c2ecf20Sopenharmony_ci while ((s64)remh < 0) { 10788c2ecf20Sopenharmony_ci vdd.significand -= 1; 10798c2ecf20Sopenharmony_ci add128(&remh, &reml, remh, reml, 0, vdm.significand); 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci vdd.significand |= (reml != 0); 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fdiv"); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci vdn_nan: 10868c2ecf20Sopenharmony_ci exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr); 10878c2ecf20Sopenharmony_ci pack: 10888c2ecf20Sopenharmony_ci vfp_put_double(vfp_double_pack(&vdd), dd); 10898c2ecf20Sopenharmony_ci return exceptions; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci vdm_nan: 10928c2ecf20Sopenharmony_ci exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr); 10938c2ecf20Sopenharmony_ci goto pack; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci zero: 10968c2ecf20Sopenharmony_ci vdd.exponent = 0; 10978c2ecf20Sopenharmony_ci vdd.significand = 0; 10988c2ecf20Sopenharmony_ci goto pack; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci divzero: 11018c2ecf20Sopenharmony_ci exceptions = FPSCR_DZC; 11028c2ecf20Sopenharmony_ci infinity: 11038c2ecf20Sopenharmony_ci vdd.exponent = 2047; 11048c2ecf20Sopenharmony_ci vdd.significand = 0; 11058c2ecf20Sopenharmony_ci goto pack; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci invalid: 11088c2ecf20Sopenharmony_ci vfp_put_double(vfp_double_pack(&vfp_double_default_qnan), dd); 11098c2ecf20Sopenharmony_ci return FPSCR_IOC; 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cistatic struct op fops[16] = { 11138c2ecf20Sopenharmony_ci [FOP_TO_IDX(FOP_FMAC)] = { vfp_double_fmac, 0 }, 11148c2ecf20Sopenharmony_ci [FOP_TO_IDX(FOP_FNMAC)] = { vfp_double_fnmac, 0 }, 11158c2ecf20Sopenharmony_ci [FOP_TO_IDX(FOP_FMSC)] = { vfp_double_fmsc, 0 }, 11168c2ecf20Sopenharmony_ci [FOP_TO_IDX(FOP_FNMSC)] = { vfp_double_fnmsc, 0 }, 11178c2ecf20Sopenharmony_ci [FOP_TO_IDX(FOP_FMUL)] = { vfp_double_fmul, 0 }, 11188c2ecf20Sopenharmony_ci [FOP_TO_IDX(FOP_FNMUL)] = { vfp_double_fnmul, 0 }, 11198c2ecf20Sopenharmony_ci [FOP_TO_IDX(FOP_FADD)] = { vfp_double_fadd, 0 }, 11208c2ecf20Sopenharmony_ci [FOP_TO_IDX(FOP_FSUB)] = { vfp_double_fsub, 0 }, 11218c2ecf20Sopenharmony_ci [FOP_TO_IDX(FOP_FDIV)] = { vfp_double_fdiv, 0 }, 11228c2ecf20Sopenharmony_ci}; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci#define FREG_BANK(x) ((x) & 0x0c) 11258c2ecf20Sopenharmony_ci#define FREG_IDX(x) ((x) & 3) 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ciu32 vfp_double_cpdo(u32 inst, u32 fpscr) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci u32 op = inst & FOP_MASK; 11308c2ecf20Sopenharmony_ci u32 exceptions = 0; 11318c2ecf20Sopenharmony_ci unsigned int dest; 11328c2ecf20Sopenharmony_ci unsigned int dn = vfp_get_dn(inst); 11338c2ecf20Sopenharmony_ci unsigned int dm; 11348c2ecf20Sopenharmony_ci unsigned int vecitr, veclen, vecstride; 11358c2ecf20Sopenharmony_ci struct op *fop; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK)); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)]; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* 11428c2ecf20Sopenharmony_ci * fcvtds takes an sN register number as destination, not dN. 11438c2ecf20Sopenharmony_ci * It also always operates on scalars. 11448c2ecf20Sopenharmony_ci */ 11458c2ecf20Sopenharmony_ci if (fop->flags & OP_SD) 11468c2ecf20Sopenharmony_ci dest = vfp_get_sd(inst); 11478c2ecf20Sopenharmony_ci else 11488c2ecf20Sopenharmony_ci dest = vfp_get_dd(inst); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci /* 11518c2ecf20Sopenharmony_ci * f[us]ito takes a sN operand, not a dN operand. 11528c2ecf20Sopenharmony_ci */ 11538c2ecf20Sopenharmony_ci if (fop->flags & OP_SM) 11548c2ecf20Sopenharmony_ci dm = vfp_get_sm(inst); 11558c2ecf20Sopenharmony_ci else 11568c2ecf20Sopenharmony_ci dm = vfp_get_dm(inst); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* 11598c2ecf20Sopenharmony_ci * If destination bank is zero, vector length is always '1'. 11608c2ecf20Sopenharmony_ci * ARM DDI0100F C5.1.3, C5.3.2. 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_ci if ((fop->flags & OP_SCALAR) || (FREG_BANK(dest) == 0)) 11638c2ecf20Sopenharmony_ci veclen = 0; 11648c2ecf20Sopenharmony_ci else 11658c2ecf20Sopenharmony_ci veclen = fpscr & FPSCR_LENGTH_MASK; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride, 11688c2ecf20Sopenharmony_ci (veclen >> FPSCR_LENGTH_BIT) + 1); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if (!fop->fn) 11718c2ecf20Sopenharmony_ci goto invalid; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) { 11748c2ecf20Sopenharmony_ci u32 except; 11758c2ecf20Sopenharmony_ci char type; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci type = fop->flags & OP_SD ? 's' : 'd'; 11788c2ecf20Sopenharmony_ci if (op == FOP_EXT) 11798c2ecf20Sopenharmony_ci pr_debug("VFP: itr%d (%c%u) = op[%u] (d%u)\n", 11808c2ecf20Sopenharmony_ci vecitr >> FPSCR_LENGTH_BIT, 11818c2ecf20Sopenharmony_ci type, dest, dn, dm); 11828c2ecf20Sopenharmony_ci else 11838c2ecf20Sopenharmony_ci pr_debug("VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)\n", 11848c2ecf20Sopenharmony_ci vecitr >> FPSCR_LENGTH_BIT, 11858c2ecf20Sopenharmony_ci type, dest, dn, FOP_TO_IDX(op), dm); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci except = fop->fn(dest, dn, dm, fpscr); 11888c2ecf20Sopenharmony_ci pr_debug("VFP: itr%d: exceptions=%08x\n", 11898c2ecf20Sopenharmony_ci vecitr >> FPSCR_LENGTH_BIT, except); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci exceptions |= except; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* 11948c2ecf20Sopenharmony_ci * CHECK: It appears to be undefined whether we stop when 11958c2ecf20Sopenharmony_ci * we encounter an exception. We continue. 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_ci dest = FREG_BANK(dest) + ((FREG_IDX(dest) + vecstride) & 3); 11988c2ecf20Sopenharmony_ci dn = FREG_BANK(dn) + ((FREG_IDX(dn) + vecstride) & 3); 11998c2ecf20Sopenharmony_ci if (FREG_BANK(dm) != 0) 12008c2ecf20Sopenharmony_ci dm = FREG_BANK(dm) + ((FREG_IDX(dm) + vecstride) & 3); 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci return exceptions; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci invalid: 12058c2ecf20Sopenharmony_ci return ~0; 12068c2ecf20Sopenharmony_ci} 1207